# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1236929988 0
# Node ID bca9d4cfbca27ddd955c533367a11a20062fdce5
# Parent 0430b1dbfb3af5f374921b903c96b9b3234cd77c
Backport: PCI: rewrite PCI BAR reading code
commit 6ac665c63dcac8fcec534a1d224ecbb8b867ad59
Author: Matthew Wilcox <matthew@xxxxxx>
Date: Mon Jul 28 13:38:59 2008 -0400
PCI: rewrite PCI BAR reading code
Factor out the code to read one BAR from the loop in
pci_read_bases into
a new function, __pci_read_base. The new code is slightly more
readable, better commented and removes the ifdef.
Signed-off-by: Matthew Wilcox <willy@xxxxxxxxxxxxxxx>
Signed-off-by: Jesse Barnes <jbarnes@xxxxxxxxxxxxxxxx>
Signed-off-by: Yu Zhao <yu.zhao@xxxxxxxxx>
---
drivers/pci/probe.c | 219 +++++++++++++++++++++++++++++++---------------------
1 files changed, 133 insertions(+), 86 deletions(-)
diff -r 0430b1dbfb3a -r bca9d4cfbca2 drivers/pci/probe.c
--- a/drivers/pci/probe.c Fri Mar 06 12:51:33 2009 +0000
+++ b/drivers/pci/probe.c Fri Mar 13 07:39:48 2009 +0000
@@ -123,12 +123,9 @@ static inline unsigned int pci_calc_reso
return IORESOURCE_MEM;
}
-/*
- * Find the extent of a PCI decode..
- */
-static u32 pci_size(u32 base, u32 maxbase, u32 mask)
-{
- u32 size = mask & maxbase; /* Find the significant bits */
+static u64 pci_size(u64 base, u64 maxbase, u64 mask)
+{
+ u64 size = mask & maxbase; /* Find the significant bits */
if (!size)
return 0;
@@ -144,91 +141,141 @@ static u32 pci_size(u32 base, u32 maxbas
return size;
}
+enum pci_bar_type {
+ pci_bar_unknown, /* Standard PCI BAR probe */
+ pci_bar_io, /* An io port BAR */
+ pci_bar_mem32, /* A 32-bit memory BAR */
+ pci_bar_mem64, /* A 64-bit memory BAR */
+};
+
+static inline enum pci_bar_type decode_bar(struct resource *res, u32 bar)
+{
+ if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+ res->flags = bar & ~PCI_BASE_ADDRESS_IO_MASK;
+ return pci_bar_io;
+ }
+
+ res->flags = bar & ~PCI_BASE_ADDRESS_MEM_MASK;
+
+ if (res->flags == PCI_BASE_ADDRESS_MEM_TYPE_64)
+ return pci_bar_mem64;
+ return pci_bar_mem32;
+}
+
+/*
+ * If the type is not unknown, we assume that the lowest bit is 'enable'.
+ * Returns 1 if the BAR was 64-bit and 0 if it was 32-bit.
+ */
+static int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
+ struct resource *res, unsigned int pos)
+{
+ u32 l, sz, mask;
+
+ mask = type ? ~PCI_ROM_ADDRESS_ENABLE : ~0;
+
+ res->name = pci_name(dev);
+
+ pci_read_config_dword(dev, pos, &l);
+ pci_write_config_dword(dev, pos, mask);
+ pci_read_config_dword(dev, pos, &sz);
+ pci_write_config_dword(dev, pos, l);
+
+ /*
+ * All bits set in sz means the device isn't working properly.
+ * If the BAR isn't implemented, all bits must be 0. If it's a
+ * memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit
+ * 1 must be clear.
+ */
+ if (!sz || sz == 0xffffffff)
+ goto fail;
+
+ /*
+ * I don't know how l can have all bits set. Copied from old code.
+ * Maybe it fixes a bug on some ancient platform.
+ */
+ if (l == 0xffffffff)
+ l = 0;
+
+ if (type == pci_bar_unknown) {
+ type = decode_bar(res, l);
+ res->flags |= pci_calc_resource_flags(l);
+ if (type == pci_bar_io) {
+ l &= PCI_BASE_ADDRESS_IO_MASK;
+ mask = PCI_BASE_ADDRESS_IO_MASK & 0xffff;
+ } else {
+ l &= PCI_BASE_ADDRESS_MEM_MASK;
+ mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ } else {
+ res->flags |= (l & IORESOURCE_ROM_ENABLE);
+ l &= PCI_ROM_ADDRESS_MASK;
+ mask = (u32)PCI_ROM_ADDRESS_MASK;
+ }
+
+ if (type == pci_bar_mem64) {
+ u64 l64 = l;
+ u64 sz64 = sz;
+ u64 mask64 = mask | (u64)~0 << 32;
+
+ pci_read_config_dword(dev, pos + 4, &l);
+ pci_write_config_dword(dev, pos + 4, ~0);
+ pci_read_config_dword(dev, pos + 4, &sz);
+ pci_write_config_dword(dev, pos + 4, l);
+
+ l64 |= ((u64)l << 32);
+ sz64 |= ((u64)sz << 32);
+
+ sz64 = pci_size(l64, sz64, mask64);
+
+ if (!sz64)
+ goto fail;
+
+ if ((BITS_PER_LONG < 64) && (sz64 > 0x100000000ULL)) {
+ dev_err(&dev->dev, "can't handle 64-bit BAR\n");
+ goto fail;
+ } else if ((BITS_PER_LONG < 64) && l) {
+ /* Address above 32-bit boundary; disable the BAR */
+ pci_write_config_dword(dev, pos, 0);
+ pci_write_config_dword(dev, pos + 4, 0);
+ res->start = 0;
+ res->end = sz64;
+ } else {
+ res->start = l64;
+ res->end = l64 + sz64;
+ }
+ } else {
+ sz = pci_size(l, sz, mask);
+
+ if (!sz)
+ goto fail;
+
+ res->start = l;
+ res->end = l + sz;
+ }
+
+ out:
+ return (type == pci_bar_mem64) ? 1 : 0;
+ fail:
+ res->flags = 0;
+ goto out;
+}
+
static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
{
- unsigned int pos, reg, next;
- u32 l, sz;
- struct resource *res;
-
- for(pos=0; pos<howmany; pos = next) {
- next = pos+1;
- res = &dev->resource[pos];
- res->name = pci_name(dev);
+ unsigned int pos, reg;
+
+ for (pos = 0; pos < howmany; pos++) {
+ struct resource *res = &dev->resource[pos];
reg = PCI_BASE_ADDRESS_0 + (pos << 2);
- pci_read_config_dword(dev, reg, &l);
- pci_write_config_dword(dev, reg, ~0);
- pci_read_config_dword(dev, reg, &sz);
- pci_write_config_dword(dev, reg, l);
- if (!sz || sz == 0xffffffff)
- continue;
- if (l == 0xffffffff)
- l = 0;
- if ((l & PCI_BASE_ADDRESS_SPACE) ==
PCI_BASE_ADDRESS_SPACE_MEMORY) {
- sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK);
- if (!sz)
- continue;
- res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
- res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK;
- } else {
- sz = pci_size(l, sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff);
- if (!sz)
- continue;
- res->start = l & PCI_BASE_ADDRESS_IO_MASK;
- res->flags |= l & ~PCI_BASE_ADDRESS_IO_MASK;
- }
- res->end = res->start + (unsigned long) sz;
- res->flags |= pci_calc_resource_flags(l);
- if ((l & (PCI_BASE_ADDRESS_SPACE |
PCI_BASE_ADDRESS_MEM_TYPE_MASK))
- == (PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_TYPE_64)) {
- u32 szhi, lhi;
- pci_read_config_dword(dev, reg+4, &lhi);
- pci_write_config_dword(dev, reg+4, ~0);
- pci_read_config_dword(dev, reg+4, &szhi);
- pci_write_config_dword(dev, reg+4, lhi);
- szhi = pci_size(lhi, szhi, 0xffffffff);
- next++;
-#if BITS_PER_LONG == 64
- res->start |= ((unsigned long) lhi) << 32;
- res->end = res->start + sz;
- if (szhi) {
- /* This BAR needs > 4GB? Wow. */
- res->end |= (unsigned long)szhi<<32;
- }
-#else
- if (szhi) {
- printk(KERN_ERR "PCI: Unable to handle 64-bit
BAR for device %s\n", pci_name(dev));
- res->start = 0;
- res->flags = 0;
- } else if (lhi) {
- /* 64-bit wide address, treat as disabled */
- pci_write_config_dword(dev, reg, l &
~(u32)PCI_BASE_ADDRESS_MEM_MASK);
- pci_write_config_dword(dev, reg+4, 0);
- res->start = 0;
- res->end = sz;
- }
-#endif
- }
- }
+ pos += __pci_read_base(dev, pci_bar_unknown, res, reg);
+ }
+
if (rom) {
+ struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
dev->rom_base_reg = rom;
- res = &dev->resource[PCI_ROM_RESOURCE];
- res->name = pci_name(dev);
- pci_read_config_dword(dev, rom, &l);
- pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE);
- pci_read_config_dword(dev, rom, &sz);
- pci_write_config_dword(dev, rom, l);
- if (l == 0xffffffff)
- l = 0;
- if (sz && sz != 0xffffffff) {
- sz = pci_size(l, sz, (u32)PCI_ROM_ADDRESS_MASK);
- if (sz) {
- res->flags = (l & IORESOURCE_ROM_ENABLE) |
- IORESOURCE_MEM | IORESOURCE_PREFETCH |
- IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
- res->start = l & PCI_ROM_ADDRESS_MASK;
- res->end = res->start + (unsigned long) sz;
- }
- }
+ res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH |
+ IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
+ __pci_read_base(dev, pci_bar_mem32, res, rom);
}
}
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|