diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-13 12:14:47 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-13 12:14:47 -0800 |
commit | 193c0d682525987db59ac3a24531a77e4947aa95 (patch) | |
tree | 7b58346171c4d07e2c2ee6c3c469c325495149a4 /drivers/pci | |
parent | 8b0cab14951fbf8126795ab301835a8f8126a988 (diff) | |
parent | 1cb73f8c479e66541fefd3f7fa547b1fa56cdc54 (diff) | |
download | op-kernel-dev-193c0d682525987db59ac3a24531a77e4947aa95.zip op-kernel-dev-193c0d682525987db59ac3a24531a77e4947aa95.tar.gz |
Merge tag 'for-3.8' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull PCI update from Bjorn Helgaas:
"Host bridge hotplug:
- Untangle _PRT from struct pci_bus (Bjorn Helgaas)
- Request _OSC control before scanning root bus (Taku Izumi)
- Assign resources when adding host bridge (Yinghai Lu)
- Remove root bus when removing host bridge (Yinghai Lu)
- Remove _PRT during hot remove (Yinghai Lu)
SRIOV
- Add sysfs knobs to control numVFs (Don Dutile)
Power management
- Notify devices when power resource turned on (Huang Ying)
Bug fixes
- Work around broken _SEG on HP xw9300 (Bjorn Helgaas)
- Keep runtime PM enabled for unbound PCI devices (Huang Ying)
- Fix Optimus dual-GPU runtime D3 suspend issue (Dave Airlie)
- Fix xen frontend shutdown issue (David Vrabel)
- Work around PLX PCI 9050 BAR alignment erratum (Ian Abbott)
Miscellaneous
- Add GPL license for drivers/pci/ioapic (Andrew Cooks)
- Add standard PCI-X, PCIe ASPM register #defines (Bjorn Helgaas)
- NumaChip remote PCI support (Daniel Blueman)
- Fix PCIe Link Capabilities Supported Link Speed definition (Jingoo
Han)
- Convert dev_printk() to dev_info(), etc (Joe Perches)
- Add support for non PCI BAR ROM data (Matthew Garrett)
- Add x86 support for host bridge translation offset (Mike Yoknis)
- Report success only when every driver supports AER (Vijay
Pandarathil)"
Fix up trivial conflicts.
* tag 'for-3.8' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (48 commits)
PCI: Use phys_addr_t for physical ROM address
x86/PCI: Add NumaChip remote PCI support
ath9k: Use standard #defines for PCIe Capability ASPM fields
iwlwifi: Use standard #defines for PCIe Capability ASPM fields
iwlwifi: collapse wrapper for pcie_capability_read_word()
iwlegacy: Use standard #defines for PCIe Capability ASPM fields
iwlegacy: collapse wrapper for pcie_capability_read_word()
cxgb3: Use standard #defines for PCIe Capability ASPM fields
PCI: Add standard PCIe Capability Link ASPM field names
PCI/portdrv: Use PCI Express Capability accessors
PCI: Use standard PCIe Capability Link register field names
x86: Use PCI setup data
PCI: Add support for non-BAR ROMs
PCI: Add pcibios_add_device
EFI: Stash ROMs if they're not in the PCI BAR
PCI: Add and use standard PCI-X Capability register names
PCI/PM: Keep runtime PM enabled for unbound PCI devices
xen-pcifront: Handle backend CLOSED without CLOSING
PCI: SRIOV control and status via sysfs (documentation)
PCI/AER: Report success only when every device has AER-aware driver
...
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/bus.c | 5 | ||||
-rw-r--r-- | drivers/pci/ioapic.c | 2 | ||||
-rw-r--r-- | drivers/pci/iov.c | 87 | ||||
-rw-r--r-- | drivers/pci/irq.c | 10 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 73 | ||||
-rw-r--r-- | drivers/pci/pci-stub.c | 2 | ||||
-rw-r--r-- | drivers/pci/pci-sysfs.c | 172 | ||||
-rw-r--r-- | drivers/pci/pci.c | 48 | ||||
-rw-r--r-- | drivers/pci/pci.h | 8 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv.h | 5 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv_core.c | 22 | ||||
-rw-r--r-- | drivers/pci/pcie/aspm.c | 18 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_core.c | 3 | ||||
-rw-r--r-- | drivers/pci/probe.c | 42 | ||||
-rw-r--r-- | drivers/pci/quirks.c | 39 | ||||
-rw-r--r-- | drivers/pci/remove.c | 36 | ||||
-rw-r--r-- | drivers/pci/rom.c | 11 | ||||
-rw-r--r-- | drivers/pci/setup-bus.c | 22 | ||||
-rw-r--r-- | drivers/pci/xen-pcifront.c | 5 |
19 files changed, 479 insertions, 131 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index a543746..ad6a8b6 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -170,6 +170,11 @@ int pci_bus_add_device(struct pci_dev *dev) int retval; pci_fixup_device(pci_fixup_final, dev); + + retval = pcibios_add_device(dev); + if (retval) + return retval; + retval = device_add(&dev->dev); if (retval) return retval; diff --git a/drivers/pci/ioapic.c b/drivers/pci/ioapic.c index 2eca902..3c6bbdd 100644 --- a/drivers/pci/ioapic.c +++ b/drivers/pci/ioapic.c @@ -125,3 +125,5 @@ static void __exit ioapic_exit(void) module_init(ioapic_init); module_exit(ioapic_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index aeccc91..bafd2bb 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -106,7 +106,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) virtfn->resource[i].name = pci_name(virtfn); virtfn->resource[i].flags = res->flags; size = resource_size(res); - do_div(size, iov->total); + do_div(size, iov->total_VFs); virtfn->resource[i].start = res->start + size * id; virtfn->resource[i].end = virtfn->resource[i].start + size - 1; rc = request_resource(res, &virtfn->resource[i]); @@ -194,7 +194,7 @@ static int sriov_migration(struct pci_dev *dev) u16 status; struct pci_sriov *iov = dev->sriov; - if (!iov->nr_virtfn) + if (!iov->num_VFs) return 0; if (!(iov->cap & PCI_SRIOV_CAP_VFM)) @@ -216,7 +216,7 @@ static void sriov_migration_task(struct work_struct *work) u16 status; struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask); - for (i = iov->initial; i < iov->nr_virtfn; i++) { + for (i = iov->initial_VFs; i < iov->num_VFs; i++) { state = readb(iov->mstate + i); if (state == PCI_SRIOV_VFM_MI) { writeb(PCI_SRIOV_VFM_AV, iov->mstate + i); @@ -244,7 +244,7 @@ static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn) resource_size_t pa; struct pci_sriov *iov = dev->sriov; - if (nr_virtfn <= iov->initial) + if (nr_virtfn <= iov->initial_VFs) return 0; pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table); @@ -294,15 +294,15 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) if (!nr_virtfn) return 0; - if (iov->nr_virtfn) + if (iov->num_VFs) return -EINVAL; pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial); - if (initial > iov->total || - (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total))) + if (initial > iov->total_VFs || + (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs))) return -EIO; - if (nr_virtfn < 0 || nr_virtfn > iov->total || + if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs || (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial))) return -EINVAL; @@ -359,7 +359,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) msleep(100); pci_cfg_access_unlock(dev); - iov->initial = initial; + iov->initial_VFs = initial; if (nr_virtfn < initial) initial = nr_virtfn; @@ -376,7 +376,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) } kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); - iov->nr_virtfn = nr_virtfn; + iov->num_VFs = nr_virtfn; return 0; @@ -401,13 +401,13 @@ static void sriov_disable(struct pci_dev *dev) int i; struct pci_sriov *iov = dev->sriov; - if (!iov->nr_virtfn) + if (!iov->num_VFs) return; if (iov->cap & PCI_SRIOV_CAP_VFM) sriov_disable_migration(dev); - for (i = 0; i < iov->nr_virtfn; i++) + for (i = 0; i < iov->num_VFs; i++) virtfn_remove(dev, i, 0); iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); @@ -419,7 +419,7 @@ static void sriov_disable(struct pci_dev *dev) if (iov->link != dev->devfn) sysfs_remove_link(&dev->dev.kobj, "dep_link"); - iov->nr_virtfn = 0; + iov->num_VFs = 0; } static int sriov_init(struct pci_dev *dev, int pos) @@ -496,7 +496,7 @@ found: iov->pos = pos; iov->nres = nres; iov->ctrl = ctrl; - iov->total = total; + iov->total_VFs = total; iov->offset = offset; iov->stride = stride; iov->pgsz = pgsz; @@ -529,7 +529,7 @@ failed: static void sriov_release(struct pci_dev *dev) { - BUG_ON(dev->sriov->nr_virtfn); + BUG_ON(dev->sriov->num_VFs); if (dev != dev->sriov->dev) pci_dev_put(dev->sriov->dev); @@ -554,7 +554,7 @@ static void sriov_restore_state(struct pci_dev *dev) pci_update_resource(dev, i); pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); - pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn); + pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs); pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); if (iov->ctrl & PCI_SRIOV_CTRL_VFE) msleep(100); @@ -661,7 +661,7 @@ int pci_iov_bus_range(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { if (!dev->is_physfn) continue; - busnr = virtfn_bus(dev, dev->sriov->total - 1); + busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1); if (busnr > max) max = busnr; } @@ -729,9 +729,56 @@ EXPORT_SYMBOL_GPL(pci_sriov_migration); */ int pci_num_vf(struct pci_dev *dev) { - if (!dev || !dev->is_physfn) + if (!dev->is_physfn) return 0; - else - return dev->sriov->nr_virtfn; + + return dev->sriov->num_VFs; } EXPORT_SYMBOL_GPL(pci_num_vf); + +/** + * pci_sriov_set_totalvfs -- reduce the TotalVFs available + * @dev: the PCI PF device + * numvfs: number that should be used for TotalVFs supported + * + * Should be called from PF driver's probe routine with + * device's mutex held. + * + * Returns 0 if PF is an SRIOV-capable device and + * value of numvfs valid. If not a PF with VFS, return -EINVAL; + * if VFs already enabled, return -EBUSY. + */ +int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs) +{ + if (!dev->is_physfn || (numvfs > dev->sriov->total_VFs)) + return -EINVAL; + + /* Shouldn't change if VFs already enabled */ + if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE) + return -EBUSY; + else + dev->sriov->driver_max_VFs = numvfs; + + return 0; +} +EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs); + +/** + * pci_sriov_get_totalvfs -- get total VFs supported on this devic3 + * @dev: the PCI PF device + * + * For a PCIe device with SRIOV support, return the PCIe + * SRIOV capability value of TotalVFs or the value of driver_max_VFs + * if the driver reduced it. Otherwise, -EINVAL. + */ +int pci_sriov_get_totalvfs(struct pci_dev *dev) +{ + if (!dev->is_physfn) + return -EINVAL; + + if (dev->sriov->driver_max_VFs) + return dev->sriov->driver_max_VFs; + + return dev->sriov->total_VFs; +} +EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs); diff --git a/drivers/pci/irq.c b/drivers/pci/irq.c index e5f69a4..b008cf8 100644 --- a/drivers/pci/irq.c +++ b/drivers/pci/irq.c @@ -14,11 +14,11 @@ static void pci_note_irq_problem(struct pci_dev *pdev, const char *reason) { struct pci_dev *parent = to_pci_dev(pdev->dev.parent); - dev_printk(KERN_ERR, &pdev->dev, - "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n", - dev_name(&parent->dev), parent->vendor, parent->device); - dev_printk(KERN_ERR, &pdev->dev, "%s\n", reason); - dev_printk(KERN_ERR, &pdev->dev, "Please report to linux-kernel@vger.kernel.org\n"); + dev_err(&pdev->dev, + "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n", + dev_name(&parent->dev), parent->vendor, parent->device); + dev_err(&pdev->dev, "%s\n", reason); + dev_err(&pdev->dev, "Please report to linux-kernel@vger.kernel.org\n"); WARN_ON(1); } diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 1dc78c5..f79cbcd 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -248,31 +248,26 @@ struct drv_dev_and_id { static long local_pci_probe(void *_ddi) { struct drv_dev_and_id *ddi = _ddi; - struct device *dev = &ddi->dev->dev; - struct device *parent = dev->parent; + struct pci_dev *pci_dev = ddi->dev; + struct pci_driver *pci_drv = ddi->drv; + struct device *dev = &pci_dev->dev; int rc; - /* The parent bridge must be in active state when probing */ - if (parent) - pm_runtime_get_sync(parent); - /* Unbound PCI devices are always set to disabled and suspended. - * During probe, the device is set to enabled and active and the - * usage count is incremented. If the driver supports runtime PM, - * it should call pm_runtime_put_noidle() in its probe routine and - * pm_runtime_get_noresume() in its remove routine. + /* + * Unbound PCI devices are always put in D0, regardless of + * runtime PM status. During probe, the device is set to + * active and the usage count is incremented. If the driver + * supports runtime PM, it should call pm_runtime_put_noidle() + * in its probe routine and pm_runtime_get_noresume() in its + * remove routine. */ - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - rc = ddi->drv->probe(ddi->dev, ddi->id); + pm_runtime_get_sync(dev); + pci_dev->driver = pci_drv; + rc = pci_drv->probe(pci_dev, ddi->id); if (rc) { - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); + pci_dev->driver = NULL; + pm_runtime_put_sync(dev); } - if (parent) - pm_runtime_put(parent); return rc; } @@ -322,10 +317,8 @@ __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev) id = pci_match_device(drv, pci_dev); if (id) error = pci_call_probe(drv, pci_dev, id); - if (error >= 0) { - pci_dev->driver = drv; + if (error >= 0) error = 0; - } } return error; } @@ -361,9 +354,7 @@ static int pci_device_remove(struct device * dev) } /* Undo the runtime PM settings in local_pci_probe() */ - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); + pm_runtime_put_sync(dev); /* * If the device is still on, set the power state as "unknown", @@ -986,6 +977,13 @@ static int pci_pm_runtime_suspend(struct device *dev) pci_power_t prev = pci_dev->current_state; int error; + /* + * If pci_dev->driver is not set (unbound), the device should + * always remain in D0 regardless of the runtime PM status + */ + if (!pci_dev->driver) + return 0; + if (!pm || !pm->runtime_suspend) return -ENOSYS; @@ -1007,10 +1005,10 @@ static int pci_pm_runtime_suspend(struct device *dev) return 0; } - if (!pci_dev->state_saved) + if (!pci_dev->state_saved) { pci_save_state(pci_dev); - - pci_finish_runtime_suspend(pci_dev); + pci_finish_runtime_suspend(pci_dev); + } return 0; } @@ -1021,6 +1019,13 @@ static int pci_pm_runtime_resume(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + /* + * If pci_dev->driver is not set (unbound), the device should + * always remain in D0 regardless of the runtime PM status + */ + if (!pci_dev->driver) + return 0; + if (!pm || !pm->runtime_resume) return -ENOSYS; @@ -1038,8 +1043,16 @@ static int pci_pm_runtime_resume(struct device *dev) static int pci_pm_runtime_idle(struct device *dev) { + struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + /* + * If pci_dev->driver is not set (unbound), the device should + * always remain in D0 regardless of the runtime PM status + */ + if (!pci_dev->driver) + goto out; + if (!pm) return -ENOSYS; @@ -1049,8 +1062,8 @@ static int pci_pm_runtime_idle(struct device *dev) return ret; } +out: pm_runtime_suspend(dev); - return 0; } diff --git a/drivers/pci/pci-stub.c b/drivers/pci/pci-stub.c index 775e933..6e47c51 100644 --- a/drivers/pci/pci-stub.c +++ b/drivers/pci/pci-stub.c @@ -28,7 +28,7 @@ MODULE_PARM_DESC(ids, "Initial PCI IDs to add to the stub driver, format is " static int pci_stub_probe(struct pci_dev *dev, const struct pci_device_id *id) { - dev_printk(KERN_INFO, &dev->dev, "claimed by stub\n"); + dev_info(&dev->dev, "claimed by stub\n"); return 0; } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 68d56f0..05b78b1 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -401,6 +401,106 @@ static ssize_t d3cold_allowed_show(struct device *dev, } #endif +#ifdef CONFIG_PCI_IOV +static ssize_t sriov_totalvfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%u\n", pci_sriov_get_totalvfs(pdev)); +} + + +static ssize_t sriov_numvfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%u\n", pdev->sriov->num_VFs); +} + +/* + * num_vfs > 0; number of vfs to enable + * num_vfs = 0; disable all vfs + * + * Note: SRIOV spec doesn't allow partial VF + * disable, so its all or none. + */ +static ssize_t sriov_numvfs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int num_vfs_enabled = 0; + int num_vfs; + int ret = 0; + u16 total; + + if (kstrtoint(buf, 0, &num_vfs) < 0) + return -EINVAL; + + /* is PF driver loaded w/callback */ + if (!pdev->driver || !pdev->driver->sriov_configure) { + dev_info(&pdev->dev, + "Driver doesn't support SRIOV configuration via sysfs\n"); + return -ENOSYS; + } + + /* if enabling vf's ... */ + total = pci_sriov_get_totalvfs(pdev); + /* Requested VFs to enable < totalvfs and none enabled already */ + if ((num_vfs > 0) && (num_vfs <= total)) { + if (pdev->sriov->num_VFs == 0) { + num_vfs_enabled = + pdev->driver->sriov_configure(pdev, num_vfs); + if ((num_vfs_enabled >= 0) && + (num_vfs_enabled != num_vfs)) { + dev_warn(&pdev->dev, + "Only %d VFs enabled\n", + num_vfs_enabled); + return count; + } else if (num_vfs_enabled < 0) + /* error code from driver callback */ + return num_vfs_enabled; + } else if (num_vfs == pdev->sriov->num_VFs) { + dev_warn(&pdev->dev, + "%d VFs already enabled; no enable action taken\n", + num_vfs); + return count; + } else { + dev_warn(&pdev->dev, + "%d VFs already enabled. Disable before enabling %d VFs\n", + pdev->sriov->num_VFs, num_vfs); + return -EINVAL; + } + } + + /* disable vfs */ + if (num_vfs == 0) { + if (pdev->sriov->num_VFs != 0) { + ret = pdev->driver->sriov_configure(pdev, 0); + return ret ? ret : count; + } else { + dev_warn(&pdev->dev, + "All VFs disabled; no disable action taken\n"); + return count; + } + } + + dev_err(&pdev->dev, + "Invalid value for number of VFs to enable: %d\n", num_vfs); + + return -EINVAL; +} + +static struct device_attribute sriov_totalvfs_attr = __ATTR_RO(sriov_totalvfs); +static struct device_attribute sriov_numvfs_attr = + __ATTR(sriov_numvfs, (S_IRUGO|S_IWUSR|S_IWGRP), + sriov_numvfs_show, sriov_numvfs_store); +#endif /* CONFIG_PCI_IOV */ + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), __ATTR_RO(vendor), @@ -1262,29 +1362,20 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) pdev->rom_attr = attr; } - if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { - retval = device_create_file(&pdev->dev, &vga_attr); - if (retval) - goto err_rom_file; - } - /* add platform-specific attributes */ retval = pcibios_add_platform_entries(pdev); if (retval) - goto err_vga_file; + goto err_rom_file; /* add sysfs entries for various capabilities */ retval = pci_create_capabilities_sysfs(pdev); if (retval) - goto err_vga_file; + goto err_rom_file; pci_create_firmware_label_files(pdev); return 0; -err_vga_file: - if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) - device_remove_file(&pdev->dev, &vga_attr); err_rom_file: if (rom_size) { sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); @@ -1370,3 +1461,62 @@ static int __init pci_sysfs_init(void) } late_initcall(pci_sysfs_init); + +static struct attribute *pci_dev_dev_attrs[] = { + &vga_attr.attr, + NULL, +}; + +static umode_t pci_dev_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pci_dev *pdev = to_pci_dev(dev); + + if (a == &vga_attr.attr) + if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA) + return 0; + + return a->mode; +} + +#ifdef CONFIG_PCI_IOV +static struct attribute *sriov_dev_attrs[] = { + &sriov_totalvfs_attr.attr, + &sriov_numvfs_attr.attr, + NULL, +}; + +static umode_t sriov_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + + if (!dev_is_pf(dev)) + return 0; + + return a->mode; +} + +static struct attribute_group sriov_dev_attr_group = { + .attrs = sriov_dev_attrs, + .is_visible = sriov_attrs_are_visible, +}; +#endif /* CONFIG_PCI_IOV */ + +static struct attribute_group pci_dev_attr_group = { + .attrs = pci_dev_dev_attrs, + .is_visible = pci_dev_attrs_are_visible, +}; + +static const struct attribute_group *pci_dev_attr_groups[] = { + &pci_dev_attr_group, +#ifdef CONFIG_PCI_IOV + &sriov_dev_attr_group, +#endif + NULL, +}; + +struct device_type pci_dev_type = { + .groups = pci_dev_attr_groups, +}; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index bdf66b5..5cb5820 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1333,6 +1333,19 @@ void pcim_pin_device(struct pci_dev *pdev) dr->pinned = 1; } +/* + * pcibios_add_device - provide arch specific hooks when adding device dev + * @dev: the PCI device being added + * + * Permits the platform to provide architecture specific functionality when + * devices are added. This is the default implementation. Architecture + * implementations can override this. + */ +int __weak pcibios_add_device (struct pci_dev *dev) +{ + return 0; +} + /** * pcibios_disable_device - disable arch specific PCI resources for device dev * @dev: the PCI device to disable @@ -1578,15 +1591,25 @@ void pci_pme_active(struct pci_dev *dev, bool enable) pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); - /* PCI (as opposed to PCIe) PME requires that the device have - its PME# line hooked up correctly. Not all hardware vendors - do this, so the PME never gets delivered and the device - remains asleep. The easiest way around this is to - periodically walk the list of suspended devices and check - whether any have their PME flag set. The assumption is that - we'll wake up often enough anyway that this won't be a huge - hit, and the power savings from the devices will still be a - win. */ + /* + * PCI (as opposed to PCIe) PME requires that the device have + * its PME# line hooked up correctly. Not all hardware vendors + * do this, so the PME never gets delivered and the device + * remains asleep. The easiest way around this is to + * periodically walk the list of suspended devices and check + * whether any have their PME flag set. The assumption is that + * we'll wake up often enough anyway that this won't be a huge + * hit, and the power savings from the devices will still be a + * win. + * + * Although PCIe uses in-band PME message instead of PME# line + * to report PME, PME does not work for some PCIe devices in + * reality. For example, there are devices that set their PME + * status bits, but don't really bother to send a PME message; + * there are PCI Express Root Ports that don't bother to + * trigger interrupts when they receive PME messages from the + * devices below. So PME poll is used for PCIe devices too. + */ if (dev->pme_poll) { struct pci_pme_device *pme_dev; @@ -1900,6 +1923,8 @@ void pci_pm_init(struct pci_dev *dev) u16 pmc; pm_runtime_forbid(&dev->dev); + pm_runtime_set_active(&dev->dev); + pm_runtime_enable(&dev->dev); device_enable_async_suspend(&dev->dev); dev->wakeup_prepared = false; @@ -3865,14 +3890,13 @@ static void pci_no_domains(void) } /** - * pci_ext_cfg_enabled - can we access extended PCI config space? - * @dev: The PCI device of the root bridge. + * pci_ext_cfg_avail - can we access extended PCI config space? * * Returns 1 if we can access PCI extended config space (offsets * greater than 0xff). This is the default implementation. Architecture * implementations can override this. */ -int __weak pci_ext_cfg_avail(struct pci_dev *dev) +int __weak pci_ext_cfg_avail(void) { return 1; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e253881..e851829 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -158,6 +158,7 @@ static inline int pci_no_d1d2(struct pci_dev *dev) } extern struct device_attribute pci_dev_attrs[]; extern struct device_attribute pcibus_dev_attrs[]; +extern struct device_type pci_dev_type; extern struct bus_attribute pci_bus_attrs[]; @@ -229,13 +230,14 @@ struct pci_sriov { int nres; /* number of resources */ u32 cap; /* SR-IOV Capabilities */ u16 ctrl; /* SR-IOV Control */ - u16 total; /* total VFs associated with the PF */ - u16 initial; /* initial VFs associated with the PF */ - u16 nr_virtfn; /* number of VFs available */ + u16 total_VFs; /* total VFs associated with the PF */ + u16 initial_VFs; /* initial VFs associated with the PF */ + u16 num_VFs; /* number of VFs available */ u16 offset; /* first VF Routing ID offset */ u16 stride; /* following VF stride */ u32 pgsz; /* page size for BAR alignment */ u8 link; /* Function Dependency Link */ + u16 driver_max_VFs; /* max num VFs driver supports */ struct pci_dev *dev; /* lowest numbered PF */ struct pci_dev *self; /* this PF */ struct mutex lock; /* lock for VF bus */ diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 94a7598..22f840f 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -87,6 +87,9 @@ struct aer_broadcast_data { static inline pci_ers_result_t merge_result(enum pci_ers_result orig, enum pci_ers_result new) { + if (new == PCI_ERS_RESULT_NO_AER_DRIVER) + return PCI_ERS_RESULT_NO_AER_DRIVER; + if (new == PCI_ERS_RESULT_NONE) return orig; @@ -97,7 +100,7 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig, break; case PCI_ERS_RESULT_DISCONNECT: if (new == PCI_ERS_RESULT_NEED_RESET) - orig = new; + orig = PCI_ERS_RESULT_NEED_RESET; break; default: break; diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index af4e31c..421bbc5 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -232,13 +232,27 @@ static int report_error_detected(struct pci_dev *dev, void *data) dev->driver ? "no AER-aware driver" : "no driver"); } - goto out; + + /* + * If there's any device in the subtree that does not + * have an error_detected callback, returning + * PCI_ERS_RESULT_NO_AER_DRIVER prevents calling of + * the subsequent mmio_enabled/slot_reset/resume + * callbacks of "any" device in the subtree. All the + * devices in the subtree are left in the error state + * without recovery. + */ + + if (!(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) + vote = PCI_ERS_RESULT_NO_AER_DRIVER; + else + vote = PCI_ERS_RESULT_NONE; + } else { + err_handler = dev->driver->err_handler; + vote = err_handler->error_detected(dev, result_data->state); } - err_handler = dev->driver->err_handler; - vote = err_handler->error_detected(dev, result_data->state); result_data->result = merge_result(result_data->result, vote); -out: device_unlock(&dev->dev); return 0; } diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 213753b..b52630b 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -242,8 +242,7 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link) return; /* Training failed. Restore common clock configurations */ - dev_printk(KERN_ERR, &parent->dev, - "ASPM: Could not configure common clock\n"); + dev_err(&parent->dev, "ASPM: Could not configure common clock\n"); list_for_each_entry(child, &linkbus->devices, bus_list) pcie_capability_write_word(child, PCI_EXP_LNKCTL, child_reg[PCI_FUNC(child->devfn)]); @@ -427,7 +426,8 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) { - pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, 0x3, val); + pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, val); } static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) @@ -442,12 +442,12 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) return; /* Convert ASPM state to upstream/downstream ASPM register state */ if (state & ASPM_STATE_L0S_UP) - dwstream |= PCIE_LINK_STATE_L0S; + dwstream |= PCI_EXP_LNKCTL_ASPM_L0S; if (state & ASPM_STATE_L0S_DW) - upstream |= PCIE_LINK_STATE_L0S; + upstream |= PCI_EXP_LNKCTL_ASPM_L0S; if (state & ASPM_STATE_L1) { - upstream |= PCIE_LINK_STATE_L1; - dwstream |= PCIE_LINK_STATE_L1; + upstream |= PCI_EXP_LNKCTL_ASPM_L1; + dwstream |= PCI_EXP_LNKCTL_ASPM_L1; } /* * Spec 2.0 suggests all functions should be configured the @@ -507,9 +507,7 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) */ pcie_capability_read_dword(child, PCI_EXP_DEVCAP, ®32); if (!(reg32 & PCI_EXP_DEVCAP_RBER) && !aspm_force) { - dev_printk(KERN_INFO, &child->dev, "disabling ASPM" - " on pre-1.1 PCIe device. You can enable it" - " with 'pcie_aspm=force'\n"); + dev_info(&child->dev, "disabling ASPM on pre-1.1 PCIe device. You can enable it with 'pcie_aspm=force'\n"); return -EINVAL; } } diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index ed129b4..b42133a 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -120,8 +120,7 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) * the value in this field indicates which MSI-X Table entry is * used to generate the interrupt message." */ - pos = pci_pcie_cap(dev); - pci_read_config_word(dev, pos + PCI_EXP_FLAGS, ®16); + pcie_capability_read_word(dev, PCI_EXP_FLAGS, ®16); entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9; if (entry >= nr_entries) goto Error; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3683f60..6186f03 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -521,7 +521,7 @@ static unsigned char pcie_link_speed[] = { void pcie_update_link_speed(struct pci_bus *bus, u16 linksta) { - bus->cur_bus_speed = pcie_link_speed[linksta & 0xf]; + bus->cur_bus_speed = pcie_link_speed[linksta & PCI_EXP_LNKSTA_CLS]; } EXPORT_SYMBOL_GPL(pcie_update_link_speed); @@ -579,14 +579,16 @@ static void pci_set_bus_speed(struct pci_bus *bus) if (pos) { u16 status; enum pci_bus_speed max; - pci_read_config_word(bridge, pos + 2, &status); - if (status & 0x8000) { + pci_read_config_word(bridge, pos + PCI_X_BRIDGE_SSTATUS, + &status); + + if (status & PCI_X_SSTATUS_533MHZ) { max = PCI_SPEED_133MHz_PCIX_533; - } else if (status & 0x4000) { + } else if (status & PCI_X_SSTATUS_266MHZ) { max = PCI_SPEED_133MHz_PCIX_266; - } else if (status & 0x0002) { - if (((status >> 12) & 0x3) == 2) { + } else if (status & PCI_X_SSTATUS_133MHZ) { + if ((status & PCI_X_SSTATUS_VERS) == PCI_X_SSTATUS_V2) { max = PCI_SPEED_133MHz_PCIX_ECC; } else { max = PCI_SPEED_133MHz_PCIX; @@ -596,7 +598,8 @@ static void pci_set_bus_speed(struct pci_bus *bus) } bus->max_bus_speed = max; - bus->cur_bus_speed = pcix_bus_speed[(status >> 6) & 0xf]; + bus->cur_bus_speed = pcix_bus_speed[ + (status & PCI_X_SSTATUS_FREQ) >> 6]; return; } @@ -607,7 +610,7 @@ static void pci_set_bus_speed(struct pci_bus *bus) u16 linksta; pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap); - bus->max_bus_speed = pcie_link_speed[linkcap & 0xf]; + bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS]; pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta); pcie_update_link_speed(bus, linksta); @@ -975,6 +978,7 @@ int pci_setup_device(struct pci_dev *dev) dev->sysdata = dev->bus->sysdata; dev->dev.parent = dev->bus->bridge; dev->dev.bus = &pci_bus_type; + dev->dev.type = &pci_dev_type; dev->hdr_type = hdr_type & 0x7f; dev->multifunction = !!(hdr_type & 0x80); dev->error_state = pci_channel_io_normal; @@ -1889,6 +1893,28 @@ unsigned int __ref pci_rescan_bus_bridge_resize(struct pci_dev *bridge) return max; } +/** + * pci_rescan_bus - scan a PCI bus for devices. + * @bus: PCI bus to scan + * + * Scan a PCI bus and child buses for new devices, adds them, + * and enables them. + * + * Returns the max number of subordinate bus discovered. + */ +unsigned int __ref pci_rescan_bus(struct pci_bus *bus) +{ + unsigned int max; + + max = pci_scan_child_bus(bus); + pci_assign_unassigned_bus_resources(bus); + pci_enable_bridges(bus); + pci_bus_add_devices(bus); + + return max; +} +EXPORT_SYMBOL_GPL(pci_rescan_bus); + EXPORT_SYMBOL(pci_add_new_bus); EXPORT_SYMBOL(pci_scan_slot); EXPORT_SYMBOL(pci_scan_bridge); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 22ad3ee..8f7a634 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1790,6 +1790,45 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE, quirk_tc86c001_ide); +/* + * PLX PCI 9050 PCI Target bridge controller has an errata that prevents the + * local configuration registers accessible via BAR0 (memory) or BAR1 (i/o) + * being read correctly if bit 7 of the base address is set. + * The BAR0 or BAR1 region may be disabled (size 0) or enabled (size 128). + * Re-allocate the regions to a 256-byte boundary if necessary. + */ +static void quirk_plx_pci9050(struct pci_dev *dev) +{ + unsigned int bar; + + /* Fixed in revision 2 (PCI 9052). */ + if (dev->revision >= 2) + return; + for (bar = 0; bar <= 1; bar++) + if (pci_resource_len(dev, bar) == 0x80 && + (pci_resource_start(dev, bar) & 0x80)) { + struct resource *r = &dev->resource[bar]; + dev_info(&dev->dev, + "Re-allocating PLX PCI 9050 BAR %u to length 256 to avoid bit 7 bug\n", + bar); + r->start = 0; + r->end = 0xff; + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + quirk_plx_pci9050); +/* + * The following Meilhaus (vendor ID 0x1402) device IDs (amongst others) + * may be using the PLX PCI 9050: 0x0630, 0x0940, 0x0950, 0x0960, 0x100b, + * 0x1400, 0x140a, 0x140b, 0x14e0, 0x14ea, 0x14eb, 0x1604, 0x1608, 0x160c, + * 0x168f, 0x2000, 0x2600, 0x3000, 0x810a, 0x810b. + * + * Currently, device IDs 0x2000 and 0x2600 are used by the Comedi "me_daq" + * driver. + */ +DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2000, quirk_plx_pci9050); +DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2600, quirk_plx_pci9050); + static void quirk_netmos(struct pci_dev *dev) { unsigned int num_parallel = (dev->subsystem_device & 0xf0) >> 4; diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 513972f..7c0fd92 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -111,3 +111,39 @@ void pci_stop_and_remove_bus_device(struct pci_dev *dev) pci_remove_bus_device(dev); } EXPORT_SYMBOL(pci_stop_and_remove_bus_device); + +void pci_stop_root_bus(struct pci_bus *bus) +{ + struct pci_dev *child, *tmp; + struct pci_host_bridge *host_bridge; + + if (!pci_is_root_bus(bus)) + return; + + host_bridge = to_pci_host_bridge(bus->bridge); + list_for_each_entry_safe_reverse(child, tmp, + &bus->devices, bus_list) + pci_stop_bus_device(child); + + /* stop the host bridge */ + device_del(&host_bridge->dev); +} + +void pci_remove_root_bus(struct pci_bus *bus) +{ + struct pci_dev *child, *tmp; + struct pci_host_bridge *host_bridge; + + if (!pci_is_root_bus(bus)) + return; + + host_bridge = to_pci_host_bridge(bus->bridge); + list_for_each_entry_safe(child, tmp, + &bus->devices, bus_list) + pci_remove_bus_device(child); + pci_remove_bus(bus); + host_bridge->bus = NULL; + + /* remove the host bridge */ + put_device(&host_bridge->dev); +} diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 0b3037a..ab886b7 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -118,11 +118,17 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) void __iomem *rom; /* + * Some devices may provide ROMs via a source other than the BAR + */ + if (pdev->rom && pdev->romlen) { + *size = pdev->romlen; + return phys_to_virt(pdev->rom); + /* * IORESOURCE_ROM_SHADOW set on x86, x86_64 and IA64 supports legacy * memory map if the VGA enable bit of the Bridge Control register is * set for embedded VGA. */ - if (res->flags & IORESOURCE_ROM_SHADOW) { + } else if (res->flags & IORESOURCE_ROM_SHADOW) { /* primary video rom always starts here */ start = (loff_t)0xC0000; *size = 0x20000; /* cover C000:0 through E000:0 */ @@ -181,7 +187,8 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom) if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY)) return; - iounmap(rom); + if (!pdev->rom || !pdev->romlen) + iounmap(rom); /* Disable again before continuing, leave enabled if pci=rom */ if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW))) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 1e808ca..6d3591d 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1550,25 +1550,12 @@ enable_all: } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); -#ifdef CONFIG_HOTPLUG -/** - * pci_rescan_bus - scan a PCI bus for devices. - * @bus: PCI bus to scan - * - * Scan a PCI bus and child buses for new devices, adds them, - * and enables them. - * - * Returns the max number of subordinate bus discovered. - */ -unsigned int __ref pci_rescan_bus(struct pci_bus *bus) +void pci_assign_unassigned_bus_resources(struct pci_bus *bus) { - unsigned int max; struct pci_dev *dev; LIST_HEAD(add_list); /* list of resources that want additional resources */ - max = pci_scan_child_bus(bus); - down_read(&pci_bus_sem); list_for_each_entry(dev, &bus->devices, bus_list) if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || @@ -1579,11 +1566,4 @@ unsigned int __ref pci_rescan_bus(struct pci_bus *bus) up_read(&pci_bus_sem); __pci_bus_assign_resources(bus, &add_list, NULL); BUG_ON(!list_empty(&add_list)); - - pci_enable_bridges(bus); - pci_bus_add_devices(bus); - - return max; } -EXPORT_SYMBOL_GPL(pci_rescan_bus); -#endif diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index db542f4..966abc6 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -1068,13 +1068,16 @@ static void __init_refok pcifront_backend_changed(struct xenbus_device *xdev, case XenbusStateInitialising: case XenbusStateInitWait: case XenbusStateInitialised: - case XenbusStateClosed: break; case XenbusStateConnected: pcifront_try_connect(pdev); break; + case XenbusStateClosed: + if (xdev->state == XenbusStateClosed) + break; + /* Missed the backend's CLOSING state -- fallthrough */ case XenbusStateClosing: dev_warn(&xdev->dev, "backend going away!\n"); pcifront_try_disconnect(pdev); |