Re: Updated 2.4 TODO List - more on PCI resources...]

From: Linus Torvalds (
Date: Thu Oct 12 2000 - 11:57:36 EST

On Thu, 12 Oct 2000, Dag Bakke wrote:
> Linus,
> I realized there was one more test to do before deeming the hardware sane.
> PCMCIA (16-bit) in my laptop is tested and works fine with three different
> types of cards.
> Another Xircom card behaved just the same (non-functional) in my latop.
> My Xircom card was tested in another laptop and found working.
> Today I took my card *and* disk and tested it in an identical laptop.
> It works.
> So it appears that the *cardbus* logic is broken on my machine. Plain, old
> hardware defect. And I have been wasting your time. Sorry about that.

No problem. This was good to have it out of the way. And I don't think you
actually wasted my time: this may still be a Linux bug. See more below.

> In any case, debug output follows for both the non-working and the working
> case. Feel free to pin-point the hw-bug or flat out ignore it.....

Yup, bug pin-pointed, and clear: these are the resources for the failing

> device PCI device 115d:0003 resource 1 size 04000800
> device PCI device 115d:0003 resource 2 size 04000800
> device PCI device 115d:0003 resource 6 size 04004000
> device PCI device 115d:0003 resource 6 size 04004000
> device PCI device 115d:0103 resource 1 size 04000800
> device PCI device 115d:0103 resource 2 size 04000800
> device PCI device 115d:0103 resource 6 size 04004000
> device PCI device 115d:0103 resource 6 size 04004000

While I'll bet you $20 that the working case (which doesn't print out the
same printk because it doesn't fail) has the exact same resources, except
the "size" field doesn't have that pesky high bit set, so the sizes are
all 0x0800 of 0x4000 instead of being 64MB+.

Now, that said, I do think that this is a linux bug. Sure, your hardware
is strange too, and hardware obviously _does_ make a difference, but there
is no way a non-power-of-two size field can ever be valid, and Linux is
buggy for allowing the hardware to confuse it this way.

What seems to be happening is that somehow your cardbus bridge (or docking
station bridge) is set up so that it doesn't let through that high bit
that defines the size of the PCI bridge map, and the way Linux calculates
the size is basically the simplistic

        write all 1's to the base register
        read the base register
        sz = ~(base & mask) & maxmask;

and while that works most of the time in normal situations, it's really
not correct. What we _should_ do is more akin to this patch. Can you test
this on your broken system, and see if the system magically comes to life?

NOTE! I do worry about the masking off of a bit in the bridge. There may
be a real hardware bug there - but the fact that the mask seems to move
depending on how big a bridge window there is (judging by your past mails
about how behaviour did actually change when changing the cardbus window
size) implies to me that it's not something as simple as a stuck bit.

If it is a stuck bit somehow, then you're screwed, and the patch below
won't help you at all. But if it's the bridge snooping (and modifying) the
decode window sizes, then the patch below should make a difference.



--- v2.4.0-test9/linux/drivers/pci/pci.c	Sun Oct  8 10:50:20 2000
+++ linux/drivers/pci/pci.c	Thu Oct 12 09:52:07 2000
@@ -474,6 +474,16 @@
+ * Find the extent of a PCI decode..
+ */
+static u32 pci_size(u32 base, u32 mask)
+	u32 size = mask & base;		/* Find the significant bits */
+	size = size & ~(size-1);	/* Get the lowest of them to find the decode size */
+	return size-1;			/* extent = size - 1 */
 static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
 	unsigned int pos, reg, next;
@@ -501,10 +511,10 @@
 			l = 0;
 			res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
-			sz = ~(sz & PCI_BASE_ADDRESS_MEM_MASK);
+			sz = pci_size(sz, PCI_BASE_ADDRESS_MEM_MASK);
 		} else {
 			res->start = l & PCI_BASE_ADDRESS_IO_MASK;
-			sz = ~(sz & PCI_BASE_ADDRESS_IO_MASK) & 0xffff;
+			sz = pci_size(sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff);
 		res->end = res->start + (unsigned long) sz;
 		res->flags |= (l & 0xf) | pci_calc_resource_flags(l);
@@ -543,7 +553,7 @@
 			res->flags = (l & PCI_ROM_ADDRESS_ENABLE) |
 			res->start = l & PCI_ROM_ADDRESS_MASK;
-			sz = ~(sz & PCI_ROM_ADDRESS_MASK);
+			sz = pci_size(sz, PCI_ROM_ADDRESS_MASK);
 			res->end = res->start + (unsigned long) sz;
 		res->name = dev->name;

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to Please read the FAQ at

This archive was generated by hypermail 2b29 : Sun Oct 15 2000 - 21:00:23 EST