Hi everyone,

Berhard has developed a patch that should fix some of our issues with the r3000 ... please read on ...

It's probably useful for him to know our test results.

P.

-------- Original Message --------
Subject: Cardbus cards hidden, fixup parent bridges carefully (patch)
Date: Wed, 5 Apr 2006 18:13:24 +0200 (CEST)
From: Bernhard Kaindl <[EMAIL PROTECTED]>
To: [EMAIL PROTECTED]
References: <[EMAIL PROTECTED]>

Hi,
   I attached a patch for a kernel module for reviewing/testing my approach
to fixup the subordinate numbers of parent bridges of Cardbus bridges
to make cardbus cards insered into them show up.

On the affected systems, there are currently two ways to fix the problem
of hidden cardbus cards: One is to use

setpci -s 0:a.0 SUBORDINATE_BUS=0x0A

or similar, especiallly for Compaq R3000 laptops with TI1620 Controller,
Compaq R4000 series, and HP zv5000 series, this is a well-tested approach.

Using pci=assign-busses is the other, but as ACPI may contain references
to PCI bus numbers, it's not guaranteed to work and it would be a big
step to try to enable it by default on many or all machines.

Also, here is a report of strange problems with a video card caused by
using pci=assign-busses:
https://bugzilla.novell.com/show_bug.cgi?id=146438

The other possiblity is to fixup the parent subordinate numbers after
we completed scanning so what we can do checks to prevent overlapping
bus numbers when fixing up the parent subordinate numbers.

I created a kernel module which does the latter and I would like to
review the code. It works on my Samsung X20, fixing the subordinate
number of the parent bridge and takes care not to use a bus number
which is already taken by a different bridge.

As a lot of machines are affected, seems better than an approach to
do a full bus renumbering on all affected machines, as this would
require getting DMI values for all of them and changed bus numbers
may cause other problems.

For further info, see the releavant bugzilla entry:
http://bugzilla.kernel.org/show_bug.cgi?id=2944

Comments?

Greetings,
Bernhard

PS: This is the list of currently known-to-be-affected systems(at least):
* ASUS Z71V and L3s
* Samsung X20 (fixed in latest BIOS, but older BIOSes are affected)
* Compaq R3140us and all Compaq R3000 series laptops with TI1620 Controller,
  also Compaq R4000 series
* HP zv5000z (AMD64 3700+, known that fixup_parent_subordinate_busnr fixes it)
* HP zv5200z
* IBM ThinkPad 240
* An IBM ThinkPad (1.8 GHz Pentium M) debugged by Pavel Machek
  gives the correspondig message which detects the breakage.
* MSI S260 / Medion SIM 2100 MD 95600
--- drivers/pci/fixup-parent-busses.c   2006/04/05 14:25:08     1.1
+++ drivers/pci/fixup-parent-busses.c   2006/04/05 14:38:10
@@ -0,0 +1,134 @@
+#include <linux/module.h>
+#include <linux/pci.h>
+
+MODULE_DESCRIPTION("test module for fixing up parent subordinate numbers after 
PCI scan");
+MODULE_LICENSE("GPL");
+static char config[33] = "";
+static struct kparam_string kps = {
+       .string = config,
+       .maxlen = 33,
+};
+
+static int init_fixup_parent_subord(void)
+{
+       return 0;
+}
+
+static void cleanup_fixup_parent_subord(void)
+{
+}
+
+static int pci_bus_is_parent_of(struct pci_bus *bus, struct pci_bus *hidden) {
+       printk(KERN_WARNING "Bus #%02x-#%02x primary: #%02x is parent of\n",
+              bus->secondary, bus->subordinate, bus->primary);
+       printk(KERN_WARNING "Bus #%02x-#%02x primary: #%02x ?\n",
+              hidden->secondary, hidden->subordinate, hidden->primary);
+       for (hidden = hidden->parent; hidden; hidden=hidden->parent) {
+           if (bus == hidden) {
+               printk(KERN_WARNING "yes, it is.\n");
+               return 1;
+               }
+       }
+       printk(KERN_WARNING "no, it's not\n");
+       return 0;
+}
+
+static unsigned char pci_find_free_subordinate(struct pci_bus *bus, struct 
pci_bus *hidden)
+{
+       struct list_head *tmp;
+        unsigned char subordinate, highest_free_subordinate = 0xff;
+       printk(KERN_WARNING "find free subordinates - checking Bus #%02x-#%02x 
primary: #%02x\n",
+              bus->secondary, bus->subordinate, bus->primary);
+
+       list_for_each(tmp, &bus->children) {
+               subordinate = pci_find_free_subordinate(pci_bus_b(tmp), hidden);
+                       printk(KERN_WARNING "find max subordinate is #%02x 
(#%02x)!\n",subordinate,highest_free_subordinate);
+               if (subordinate < highest_free_subordinate)
+                       highest_free_subordinate = subordinate;
+       }
+
+       if (!pci_bus_is_parent_of(bus, hidden)) {
+               printk(KERN_WARNING "Secondary is #%02x, hidden's secondary: 
#%02x (max: #%02x)\n",
+                       bus->secondary, hidden->secondary, 
highest_free_subordinate);
+               if (bus->secondary > hidden->secondary && // It's above
+                   bus->secondary <= highest_free_subordinate) { // but 
overlaps
+                       printk(KERN_WARNING "Secondary is #%02x, don't overlap 
it!\n",bus->secondary);
+                       return bus->secondary-1;
+               }
+       }
+       return highest_free_subordinate;
+}
+                
+static unsigned char pci_get_free_subordinate(struct pci_bus *hidden)
+{
+       struct pci_bus *bus = NULL;
+        unsigned char subordinate, highest_free_subordinate = 0xff;
+
+       while ((bus = pci_find_next_bus(bus)) != NULL) {
+                subordinate = pci_find_free_subordinate(bus, hidden);
+                       printk(KERN_WARNING "max subordinate is #%02x 
(#%02x)!\n",subordinate,highest_free_subordinate);
+                if (subordinate < highest_free_subordinate)
+                        highest_free_subordinate = subordinate;
+       }
+        return highest_free_subordinate;
+}
+
+static void fixup_parent_subord_subordinate(struct pci_bus *hidden, struct 
pci_bus *parent)
+{
+       unsigned char free_subordinate;
+        printk(KERN_WARNING "PCI: Bus #%02x (-#%02x) is "
+                            "hidden behind bridge #%02x (-#%02x)\n",
+                               hidden->number, hidden->subordinate,
+                               parent->number, parent->subordinate);
+
+       free_subordinate = pci_get_free_subordinate(hidden);
+
+       printk("Result: free subordinate: #%02x parent's subordinate: #%02x\n",
+               free_subordinate, parent->subordinate);
+
+       if (free_subordinate > parent->subordinate) {
+               free_subordinate = min(free_subordinate, hidden->subordinate);
+               printk("Fixing up subordinate of #%02x from #%02x to #%02x\n",
+                       parent->number, parent->subordinate, free_subordinate);
+               parent->subordinate = free_subordinate;
+               pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, 
parent->subordinate);
+       }
+}
+
+static void fix_walk_bus(struct pci_bus *bus)
+{
+       struct list_head *tmp;
+       printk(KERN_WARNING "checking Bus #%02x-#%02x primary: #%02x\n",
+              bus->secondary, bus->subordinate, bus->primary);
+/* For testing only: sets the pci bus list entries to their initial values on 
Samsung X20:
+       if (bus->number == 6) bus->subordinate = 6;
+       if (bus->number == 0) bus->subordinate = 0x0a;
+       // This changes the X20's unused PCI-X bus to become a overlapping bus 
for testing:
+       if (bus->number == 2) bus->secondary = 0x0a;
+       if (bus->number == 2) bus->subordinate = 0x0b;
+*/
+
+       list_for_each(tmp, &bus->children)
+               fix_walk_bus(pci_bus_b(tmp));
+
+       if (bus->parent) {
+               struct pci_bus *parent;
+               for (parent = bus->parent; parent && parent->parent; 
parent=parent->parent)
+                   if (bus->subordinate > parent->subordinate)
+                       fixup_parent_subord_subordinate(bus, parent);
+                
+       }
+}
+
+static int param_set_fixup_parent_subord_var(const char *kmessage, struct 
kernel_param *kp)
+{
+       struct pci_bus * bus = NULL;
+       while ((bus = pci_find_next_bus(bus)) != NULL)
+               fix_walk_bus(bus);
+       return 0;
+}
+
+module_init(init_fixup_parent_subord);
+module_exit(cleanup_fixup_parent_subord);
+module_param_call(fixup_parent_subord, param_set_fixup_parent_subord_var, 
param_get_string, &kps, 0644);
+MODULE_PARM_DESC(fixup_parent_subord, "");
--- drivers/pci/Makefile        2006/04/05 14:25:29     1.1
+++ drivers/pci/Makefile        2006/04/05 14:27:18
@@ -40,6 +40,8 @@
 obj-y += syscall.o
 endif
 
+obj-m += fixup-parent-busses.o
+
 ifeq ($(CONFIG_PCI_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
 endif
_______________________________________________
LinuxR3000 mailing list
[email protected]
http://lists.pcxperience.com/cgi-bin/mailman/listinfo/linuxr3000
Wiki at http://prinsig.se/weekee/

Reply via email to