]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/pci/probe.c
Merge branch 'pci-for-jesse' of git://git.kernel.org/pub/scm/linux/kernel/git/tip...
[linux-2.6.git] / drivers / pci / probe.c
index f8291191ef914a3619adde78ed67e0d278d67c87..cce2f4cb1fbf37985d7303ec2e34d9f507d92aee 100644 (file)
@@ -52,27 +52,49 @@ EXPORT_SYMBOL(no_pci_devices);
  * Some platforms allow access to legacy I/O port and ISA memory space on
  * a per-bus basis.  This routine creates the files and ties them into
  * their associated read, write and mmap files from pci-sysfs.c
+ *
+ * On error unwind, but don't propogate the error to the caller
+ * as it is ok to set up the PCI bus without these files.
  */
 static void pci_create_legacy_files(struct pci_bus *b)
 {
+       int error;
+
        b->legacy_io = kzalloc(sizeof(struct bin_attribute) * 2,
                               GFP_ATOMIC);
-       if (b->legacy_io) {
-               b->legacy_io->attr.name = "legacy_io";
-               b->legacy_io->size = 0xffff;
-               b->legacy_io->attr.mode = S_IRUSR | S_IWUSR;
-               b->legacy_io->read = pci_read_legacy_io;
-               b->legacy_io->write = pci_write_legacy_io;
-               device_create_bin_file(&b->dev, b->legacy_io);
-
-               /* Allocated above after the legacy_io struct */
-               b->legacy_mem = b->legacy_io + 1;
-               b->legacy_mem->attr.name = "legacy_mem";
-               b->legacy_mem->size = 1024*1024;
-               b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR;
-               b->legacy_mem->mmap = pci_mmap_legacy_mem;
-               device_create_bin_file(&b->dev, b->legacy_mem);
-       }
+       if (!b->legacy_io)
+               goto kzalloc_err;
+
+       b->legacy_io->attr.name = "legacy_io";
+       b->legacy_io->size = 0xffff;
+       b->legacy_io->attr.mode = S_IRUSR | S_IWUSR;
+       b->legacy_io->read = pci_read_legacy_io;
+       b->legacy_io->write = pci_write_legacy_io;
+       error = device_create_bin_file(&b->dev, b->legacy_io);
+       if (error)
+               goto legacy_io_err;
+
+       /* Allocated above after the legacy_io struct */
+       b->legacy_mem = b->legacy_io + 1;
+       b->legacy_mem->attr.name = "legacy_mem";
+       b->legacy_mem->size = 1024*1024;
+       b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR;
+       b->legacy_mem->mmap = pci_mmap_legacy_mem;
+       error = device_create_bin_file(&b->dev, b->legacy_mem);
+       if (error)
+               goto legacy_mem_err;
+
+       return;
+
+legacy_mem_err:
+       device_remove_bin_file(&b->dev, b->legacy_io);
+legacy_io_err:
+       kfree(b->legacy_io);
+       b->legacy_io = NULL;
+kzalloc_err:
+       printk(KERN_WARNING "pci: warning: could not create legacy I/O port "
+              "and ISA memory resources to sysfs\n");
+       return;
 }
 
 void pci_remove_legacy_files(struct pci_bus *b)
@@ -163,12 +185,9 @@ static inline unsigned int pci_calc_resource_flags(unsigned int flags)
        return IORESOURCE_MEM;
 }
 
-/*
- * Find the extent of a PCI decode..
- */
-static u32 pci_size(u32 base, u32 maxbase, u32 mask)
+static u64 pci_size(u64 base, u64 maxbase, u64 mask)
 {
-       u32 size = mask & maxbase;      /* Find the significant bits */
+       u64 size = mask & maxbase;      /* Find the significant bits */
        if (!size)
                return 0;
 
@@ -184,135 +203,142 @@ static u32 pci_size(u32 base, u32 maxbase, u32 mask)
        return size;
 }
 
-static u64 pci_size64(u64 base, u64 maxbase, u64 mask)
-{
-       u64 size = mask & maxbase;      /* Find the significant bits */
-       if (!size)
-               return 0;
+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 */
+};
 
-       /* Get the lowest of them to find the decode size, and
-          from that the extent.  */
-       size = (size & ~(size-1)) - 1;
+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;
+       }
 
-       /* base == maxbase can be valid only if the BAR has
-          already been programmed with all 1s.  */
-       if (base == maxbase && ((base | size) & mask) != mask)
-               return 0;
+       res->flags = bar & ~PCI_BASE_ADDRESS_MEM_MASK;
 
-       return size;
+       if (res->flags == PCI_BASE_ADDRESS_MEM_TYPE_64)
+               return pci_bar_mem64;
+       return pci_bar_mem32;
 }
 
-static inline int is_64bit_memory(u32 mask)
+/*
+ * 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)
 {
-       if ((mask & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
-           (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64))
-               return 1;
-       return 0;
-}
+       u32 l, sz, mask;
 
-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;
+       mask = type ? ~PCI_ROM_ADDRESS_ENABLE : ~0;
 
-       for(pos=0; pos<howmany; pos = next) {
-               u64 l64;
-               u64 sz64;
-               u32 raw_sz;
+       res->name = pci_name(dev);
 
-               next = pos+1;
-               res = &dev->resource[pos];
-               res->name = pci_name(dev);
-               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;
-               raw_sz = sz;
-               if ((l & PCI_BASE_ADDRESS_SPACE) ==
-                               PCI_BASE_ADDRESS_SPACE_MEMORY) {
-                       sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK);
-                       /*
-                        * For 64bit prefetchable memory sz could be 0, if the
-                        * real size is bigger than 4G, so we need to check
-                        * szhi for that.
-                        */
-                       if (!is_64bit_memory(l) && !sz)
-                               continue;
-                       res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
-                       res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK;
+       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) | IORESOURCE_SIZEALIGN;
+               if (type == pci_bar_io) {
+                       l &= PCI_BASE_ADDRESS_IO_MASK;
+                       mask = PCI_BASE_ADDRESS_IO_MASK & 0xffff;
                } 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;
+                       l &= PCI_BASE_ADDRESS_MEM_MASK;
+                       mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
                }
-               res->end = res->start + (unsigned long) sz;
-               res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN;
-               if (is_64bit_memory(l)) {
-                       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);
-                       sz64 = ((u64)szhi << 32) | raw_sz;
-                       l64 = ((u64)lhi << 32) | l;
-                       sz64 = pci_size64(l64, sz64, PCI_BASE_ADDRESS_MEM_MASK);
-                       next++;
-#if BITS_PER_LONG == 64
-                       if (!sz64) {
-                               res->start = 0;
-                               res->end = 0;
-                               res->flags = 0;
-                               continue;
-                       }
-                       res->start = l64 & PCI_BASE_ADDRESS_MEM_MASK;
-                       res->end = res->start + sz64;
-#else
-                       if (sz64 > 0x100000000ULL) {
-                               dev_err(&dev->dev, "BAR %d: can't handle 64-bit"
-                                       " BAR\n", pos);
-                               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
+       } 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 ((sizeof(resource_size_t) < 8) && (sz64 > 0x100000000ULL)) {
+                       dev_err(&dev->dev, "can't handle 64-bit BAR\n");
+                       goto fail;
+               } else if ((sizeof(resource_size_t) < 8) && 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;
+
+       for (pos = 0; pos < howmany; pos++) {
+               struct resource *res = &dev->resource[pos];
+               reg = PCI_BASE_ADDRESS_0 + (pos << 2);
+               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 |
-                                 IORESOURCE_SIZEALIGN;
-                               res->start = l & PCI_ROM_ADDRESS_MASK;
-                               res->end = res->start + (unsigned long) sz;
-                       }
-               }
+               res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH |
+                               IORESOURCE_READONLY | IORESOURCE_CACHEABLE |
+                               IORESOURCE_SIZEALIGN;
+               __pci_read_base(dev, pci_bar_mem32, res, rom);
        }
 }
 
@@ -357,6 +383,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
                        res->start = base;
                if (!res->end)
                        res->end = limit + 0xfff;
+               printk(KERN_INFO "PCI: bridge %s io port: [%llx, %llx]\n", pci_name(dev), res->start, res->end);
        }
 
        res = child->resource[1];
@@ -368,6 +395,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
                res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
                res->start = base;
                res->end = limit + 0xfffff;
+               printk(KERN_INFO "PCI: bridge %s 32bit mmio: [%llx, %llx]\n", pci_name(dev), res->start, res->end);
        }
 
        res = child->resource[2];
@@ -403,6 +431,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
                res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH;
                res->start = base;
                res->end = limit + 0xfffff;
+               printk(KERN_INFO "PCI: bridge %s %sbit mmio pref: [%llx, %llx]\n", pci_name(dev), (res->flags & PCI_PREF_RANGE_TYPE_64)?"64":"32",res->start, res->end);
        }
 }
 
@@ -713,8 +742,9 @@ static int pci_setup_device(struct pci_dev * dev)
 {
        u32 class;
 
-       sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
-               dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+       dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
+                    dev->bus->number, PCI_SLOT(dev->devfn),
+                    PCI_FUNC(dev->devfn));
 
        pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
        dev->revision = class & 0xff;
@@ -859,49 +889,6 @@ int pci_cfg_space_size_ext(struct pci_dev *dev)
        return PCI_CFG_SPACE_SIZE;
 }
 
-/**
- * pci_disable_pme - Disable the PME function of PCI device
- * @dev: PCI device affected
- * -EINVAL is returned if PCI device doesn't support PME.
- * Zero is returned if the PME is supported and can be disabled.
- */
-static int pci_disable_pme(struct pci_dev *dev)
-{
-       int pm;
-       u16 value;
-
-       /* find PCI PM capability in list */
-       pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-
-       /* If device doesn't support PM Capabilities, it means that PME is
-        * not supported.
-        */
-       if (!pm)
-               return -EINVAL;
-       /* Check device's ability to generate PME# */
-       pci_read_config_word(dev, pm + PCI_PM_PMC, &value);
-
-       value &= PCI_PM_CAP_PME_MASK;
-       /* Check if it can generate PME# */
-       if (!value) {
-               /*
-                * If it is zero, it means that PME is still unsupported
-                * although there exists the PM capability.
-                */
-               return -EINVAL;
-       }
-
-       pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
-
-       /* Clear PME_Status by writing 1 to it */
-       value |= PCI_PM_CTRL_PME_STATUS ;
-       /* Disable PME enable bit */
-       value &= ~PCI_PM_CTRL_PME_ENABLE;
-       pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
-
-       return 0;
-}
-
 int pci_cfg_space_size(struct pci_dev *dev)
 {
        int pos;
@@ -1009,7 +996,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
        }
 
        pci_vpd_pci22_init(dev);
-       pci_disable_pme(dev);
 
        return dev;
 }
@@ -1030,6 +1016,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
        /* Fix up broken headers */
        pci_fixup_device(pci_fixup_header, dev);
 
+       /* Initialize power management of the device */
+       pci_pm_init(dev);
+
        /*
         * Add the device to our list of discovered devices
         * and the bus list for fixup functions, etc.
@@ -1093,7 +1082,8 @@ int pci_scan_slot(struct pci_bus *bus, int devfn)
                }
        }
 
-       if (bus->self)
+       /* only one slot has pcie device */
+       if (bus->self && nr)
                pcie_aspm_init_link_state(bus->self);
 
        return nr;