diff options
Diffstat (limited to 'drivers/pci/pci-driver.c')
| -rw-r--r-- | drivers/pci/pci-driver.c | 542 |
1 files changed, 343 insertions, 199 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 8a6f797de8e..3f8e3dbcaa7 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -18,6 +18,8 @@ #include <linux/sched.h> #include <linux/cpu.h> #include <linux/pm_runtime.h> +#include <linux/suspend.h> +#include <linux/kexec.h> #include "pci.h" struct pci_dynid { @@ -71,12 +73,11 @@ int pci_add_dynid(struct pci_driver *drv, list_add_tail(&dynid->node, &drv->dynids.list); spin_unlock(&drv->dynids.lock); - get_driver(&drv->driver); retval = driver_attach(&drv->driver); - put_driver(&drv->driver); return retval; } +EXPORT_SYMBOL_GPL(pci_add_dynid); static void pci_free_dynids(struct pci_driver *drv) { @@ -90,10 +91,6 @@ static void pci_free_dynids(struct pci_driver *drv) spin_unlock(&drv->dynids.lock); } -/* - * Dynamic device ID manipulation via sysfs is disabled for !CONFIG_HOTPLUG - */ -#ifdef CONFIG_HOTPLUG /** * store_new_id - sysfs frontend to pci_add_dynid() * @driver: target device driver @@ -102,16 +99,16 @@ static void pci_free_dynids(struct pci_driver *drv) * * Allow PCI IDs to be added to an existing driver via sysfs. */ -static ssize_t -store_new_id(struct device_driver *driver, const char *buf, size_t count) +static ssize_t store_new_id(struct device_driver *driver, const char *buf, + size_t count) { struct pci_driver *pdrv = to_pci_driver(driver); const struct pci_device_id *ids = pdrv->id_table; - __u32 vendor, device, subvendor=PCI_ANY_ID, - subdevice=PCI_ANY_ID, class=0, class_mask=0; - unsigned long driver_data=0; - int fields=0; - int retval; + __u32 vendor, device, subvendor = PCI_ANY_ID, + subdevice = PCI_ANY_ID, class = 0, class_mask = 0; + unsigned long driver_data = 0; + int fields = 0; + int retval = 0; fields = sscanf(buf, "%x %x %x %x %x %x %lx", &vendor, &device, &subvendor, &subdevice, @@ -119,6 +116,26 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) if (fields < 2) return -EINVAL; + if (fields != 7) { + struct pci_dev *pdev = kzalloc(sizeof(*pdev), GFP_KERNEL); + if (!pdev) + return -ENOMEM; + + pdev->vendor = vendor; + pdev->device = device; + pdev->subsystem_vendor = subvendor; + pdev->subsystem_device = subdevice; + pdev->class = class; + + if (pci_match_id(pdrv->id_table, pdev)) + retval = -EEXIST; + + kfree(pdev); + + if (retval) + return retval; + } + /* Only accept driver_data values that match an existing id_table entry */ if (ids) { @@ -150,8 +167,8 @@ static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); * * Removes a dynamic pci device ID to this driver. */ -static ssize_t -store_remove_id(struct device_driver *driver, const char *buf, size_t count) +static ssize_t store_remove_id(struct device_driver *driver, const char *buf, + size_t count) { struct pci_dynid *dynid, *n; struct pci_driver *pdrv = to_pci_driver(driver); @@ -188,45 +205,12 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) } static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); -static int -pci_create_newid_file(struct pci_driver *drv) -{ - int error = 0; - if (drv->probe != NULL) - error = driver_create_file(&drv->driver, &driver_attr_new_id); - return error; -} - -static void pci_remove_newid_file(struct pci_driver *drv) -{ - driver_remove_file(&drv->driver, &driver_attr_new_id); -} - -static int -pci_create_removeid_file(struct pci_driver *drv) -{ - int error = 0; - if (drv->probe != NULL) - error = driver_create_file(&drv->driver,&driver_attr_remove_id); - return error; -} - -static void pci_remove_removeid_file(struct pci_driver *drv) -{ - driver_remove_file(&drv->driver, &driver_attr_remove_id); -} -#else /* !CONFIG_HOTPLUG */ -static inline int pci_create_newid_file(struct pci_driver *drv) -{ - return 0; -} -static inline void pci_remove_newid_file(struct pci_driver *drv) {} -static inline int pci_create_removeid_file(struct pci_driver *drv) -{ - return 0; -} -static inline void pci_remove_removeid_file(struct pci_driver *drv) {} -#endif +static struct attribute *pci_drv_attrs[] = { + &driver_attr_new_id.attr, + &driver_attr_remove_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(pci_drv); /** * pci_match_id - See if a pci device matches a given pci_id table @@ -252,6 +236,14 @@ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, } return NULL; } +EXPORT_SYMBOL(pci_match_id); + +static const struct pci_device_id pci_device_id_any = { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, +}; /** * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure @@ -266,18 +258,30 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv, struct pci_dev *dev) { struct pci_dynid *dynid; + const struct pci_device_id *found_id = NULL; + + /* When driver_override is set, only bind to the matching driver */ + if (dev->driver_override && strcmp(dev->driver_override, drv->name)) + return NULL; /* Look at the dynamic ids first, before the static ones */ spin_lock(&drv->dynids.lock); list_for_each_entry(dynid, &drv->dynids.list, node) { if (pci_match_one_device(&dynid->id, dev)) { - spin_unlock(&drv->dynids.lock); - return &dynid->id; + found_id = &dynid->id; + break; } } spin_unlock(&drv->dynids.lock); - return pci_match_id(drv->id_table, dev); + if (!found_id) + found_id = pci_match_id(drv->id_table, dev); + + /* driver_override will always match, send a dummy id */ + if (!found_id && dev->driver_override) + found_id = &pci_device_id_any; + + return found_id; } struct drv_dev_and_id { @@ -289,26 +293,35 @@ 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 pci_dev *pci_dev = ddi->dev; + struct pci_driver *pci_drv = ddi->drv; + struct device *dev = &pci_dev->dev; int rc; - /* 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); - if (rc) { - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); + pm_runtime_get_sync(dev); + pci_dev->driver = pci_drv; + rc = pci_drv->probe(pci_dev, ddi->id); + if (!rc) + return rc; + if (rc < 0) { + pci_dev->driver = NULL; + pm_runtime_put_sync(dev); + return rc; } - return rc; + /* + * Probe function should return < 0 for failure, 0 for success + * Treat values > 0 as success, but warn. + */ + dev_warn(dev, "Driver probe function unexpectedly returned %d\n", rc); + return 0; } static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, @@ -317,12 +330,27 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, int error, node; struct drv_dev_and_id ddi = { drv, dev, id }; - /* Execute driver initialization on node where the device's - bus is attached to. This way the driver likely allocates - its local memory on the right node without any need to - change it. */ + /* + * Execute driver initialization on node where the device is + * attached. This way the driver likely allocates its local memory + * on the right node. + */ node = dev_to_node(&dev->dev); - if (node >= 0) { + + /* + * On NUMA systems, we are likely to call a PF probe function using + * work_on_cpu(). If that probe calls pci_enable_sriov() (which + * adds the VF devices via pci_bus_add_device()), we may re-enter + * this function to call the VF probe function. Calling + * work_on_cpu() again will cause a lockdep warning. Since VFs are + * always on the same node as the PF, we can work around this by + * avoiding work_on_cpu() when we're already on the correct node. + * + * Preemption is enabled, so it's theoretically unsafe to use + * numa_node_id(), but even if we run the probe function on the + * wrong node, it should be functionally correct. + */ + if (node >= 0 && node != numa_node_id()) { int cpu; get_online_cpus(); @@ -334,19 +362,19 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, put_online_cpus(); } else error = local_pci_probe(&ddi); + return error; } /** - * __pci_device_probe() + * __pci_device_probe - check if a driver wants to claim a specific PCI device * @drv: driver to call to check if it wants the PCI device * @pci_dev: PCI device being probed - * + * * returns 0 on success, else error. * side-effect: pci_dev->driver is set to drv when drv claims pci_dev. */ -static int -__pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev) +static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev) { const struct pci_device_id *id; int error = 0; @@ -357,15 +385,13 @@ __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; } -static int pci_device_probe(struct device * dev) +static int pci_device_probe(struct device *dev) { int error = 0; struct pci_driver *drv; @@ -381,10 +407,10 @@ static int pci_device_probe(struct device * dev) return error; } -static int pci_device_remove(struct device * dev) +static int pci_device_remove(struct device *dev) { - struct pci_dev * pci_dev = to_pci_dev(dev); - struct pci_driver * drv = pci_dev->driver; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; if (drv) { if (drv->remove) { @@ -396,9 +422,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", @@ -411,7 +435,7 @@ static int pci_device_remove(struct device * dev) * We would love to complain here if pci_dev->is_enabled is set, that * the driver should have called pci_disable_device(), but the * unfortunate fact is there are too many odd BIOS and bridge setups - * that don't like drivers doing that all of the time. + * that don't like drivers doing that all of the time. * Oh well, we can dream of sane hardware when we sleep, no matter how * horrible the crap we have to deal with is when we are awake... */ @@ -425,13 +449,27 @@ static void pci_device_shutdown(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *drv = pci_dev->driver; + pm_runtime_resume(dev); + if (drv && drv->shutdown) drv->shutdown(pci_dev); pci_msi_shutdown(pci_dev); pci_msix_shutdown(pci_dev); + +#ifdef CONFIG_KEXEC + /* + * If this is a kexec reboot, turn off Bus Master bit on the + * device to tell it to not continue to do DMA. Don't touch + * devices in D3cold or unknown states. + * If it is not a kexec reboot, firmware will hit the PCI + * devices with big hammer and stop their DMA any way. + */ + if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot)) + pci_clear_master(pci_dev); +#endif } -#ifdef CONFIG_PM_OPS +#ifdef CONFIG_PM /* Auxiliary functions used for system resume and run-time resume. */ @@ -449,19 +487,21 @@ static int pci_restore_standard_config(struct pci_dev *pci_dev) return error; } - return pci_restore_state(pci_dev); + pci_restore_state(pci_dev); + return 0; } +#endif + +#ifdef CONFIG_PM_SLEEP + static void pci_pm_default_resume_early(struct pci_dev *pci_dev) { - pci_restore_standard_config(pci_dev); + pci_power_up(pci_dev); + pci_restore_state(pci_dev); pci_fixup_device(pci_fixup_resume_early, pci_dev); } -#endif - -#ifdef CONFIG_PM_SLEEP - /* * Default "suspend" method for devices that have no driver provided suspend, * or not even a driver at all (second part). @@ -498,8 +538,8 @@ static int pci_pm_reenable_device(struct pci_dev *pci_dev) static int pci_legacy_suspend(struct device *dev, pm_message_t state) { - struct pci_dev * pci_dev = to_pci_dev(dev); - struct pci_driver * drv = pci_dev->driver; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; if (drv && drv->suspend) { pci_power_t prev = pci_dev->current_state; @@ -525,8 +565,8 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) { - struct pci_dev * pci_dev = to_pci_dev(dev); - struct pci_driver * drv = pci_dev->driver; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; if (drv && drv->suspend_late) { pci_power_t prev = pci_dev->current_state; @@ -556,8 +596,8 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) static int pci_legacy_resume_early(struct device *dev) { - struct pci_dev * pci_dev = to_pci_dev(dev); - struct pci_driver * drv = pci_dev->driver; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; return drv && drv->resume_early ? drv->resume_early(pci_dev) : 0; @@ -565,8 +605,8 @@ static int pci_legacy_resume_early(struct device *dev) static int pci_legacy_resume(struct device *dev) { - struct pci_dev * pci_dev = to_pci_dev(dev); - struct pci_driver * drv = pci_dev->driver; + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; pci_fixup_device(pci_fixup_resume, pci_dev); @@ -580,14 +620,14 @@ static void pci_pm_default_resume(struct pci_dev *pci_dev) { pci_fixup_device(pci_fixup_resume, pci_dev); - if (!pci_is_bridge(pci_dev)) + if (!pci_has_subordinate(pci_dev)) pci_enable_wake(pci_dev, PCI_D0, false); } static void pci_pm_default_suspend(struct pci_dev *pci_dev) { /* Disable non-bridge devices without PM support */ - if (!pci_is_bridge(pci_dev)) + if (!pci_has_subordinate(pci_dev)) pci_disable_enabled_device(pci_dev); } @@ -602,7 +642,8 @@ static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) * supported as well. Drivers are supposed to support either the * former, or the latter, but not both at the same time. */ - WARN_ON(ret && drv->driver.pm); + WARN(ret && drv->driver.pm, "driver %s device %04x:%04x\n", + drv->name, pci_dev->vendor, pci_dev->device); return ret; } @@ -615,15 +656,11 @@ static int pci_pm_prepare(struct device *dev) int error = 0; /* - * PCI devices suspended at run time need to be resumed at this - * point, because in general it is necessary to reconfigure them for - * system suspend. Namely, if the device is supposed to wake up the - * system from the sleep state, we may need to reconfigure it for this - * purpose. In turn, if the device is not supposed to wake up the - * system from the sleep state, we'll have to prevent it from signaling - * wake-up. + * Devices having power.ignore_children set may still be necessary for + * suspending their children in the next phase of device suspend. */ - pm_runtime_resume(dev); + if (dev->power.ignore_children) + pm_runtime_resume(dev); if (drv && drv->pm && drv->pm->prepare) error = drv->pm->prepare(dev); @@ -631,18 +668,10 @@ static int pci_pm_prepare(struct device *dev) return error; } -static void pci_pm_complete(struct device *dev) -{ - struct device_driver *drv = dev->driver; - - if (drv && drv->pm && drv->pm->complete) - drv->pm->complete(dev); -} #else /* !CONFIG_PM_SLEEP */ #define pci_pm_prepare NULL -#define pci_pm_complete NULL #endif /* !CONFIG_PM_SLEEP */ @@ -661,6 +690,17 @@ static int pci_pm_suspend(struct device *dev) goto Fixup; } + /* + * PCI devices suspended at run time need to be resumed at this point, + * because in general it is necessary to reconfigure them for system + * suspend. Namely, if the device is supposed to wake up the system + * from the sleep state, we may need to reconfigure it for this purpose. + * In turn, if the device is not supposed to wake up the system from the + * sleep state, we'll have to prevent it from signaling wake-up. + */ + pm_runtime_resume(dev); + + pci_dev->state_saved = false; if (pm->suspend) { pci_power_t prev = pci_dev->current_state; int error; @@ -717,12 +757,24 @@ static int pci_pm_suspend_noirq(struct device *dev) if (!pci_dev->state_saved) { pci_save_state(pci_dev); - if (!pci_is_bridge(pci_dev)) + if (!pci_has_subordinate(pci_dev)) pci_prepare_to_sleep(pci_dev); } pci_pm_set_unknown_state(pci_dev); + /* + * Some BIOSes from ASUS have a bug: If a USB EHCI host controller's + * PCI COMMAND register isn't 0, the BIOS assumes that the controller + * hasn't been quiesced and tries to turn it off. If the controller + * is already in D3, this can hang or cause memory corruption. + * + * Since the value of the COMMAND register doesn't matter once the + * device has been suspended, we can safely set it to 0 here. + */ + if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) + pci_write_config_word(pci_dev, PCI_COMMAND, 0); + return 0; } @@ -780,7 +832,14 @@ static int pci_pm_resume(struct device *dev) #endif /* !CONFIG_SUSPEND */ -#ifdef CONFIG_HIBERNATION +#ifdef CONFIG_HIBERNATE_CALLBACKS + + +/* + * pcibios_pm_ops - provide arch-specific hooks when a PCI device is doing + * a hibernate transition + */ +struct dev_pm_ops __weak pcibios_pm_ops; static int pci_pm_freeze(struct device *dev) { @@ -795,6 +854,15 @@ static int pci_pm_freeze(struct device *dev) return 0; } + /* + * This used to be done in pci_pm_prepare() for all devices and some + * drivers may depend on it, so do it here. Ideally, runtime-suspended + * devices should not be touched during freeze/thaw transitions, + * however. + */ + pm_runtime_resume(dev); + + pci_dev->state_saved = false; if (pm->freeze) { int error; @@ -804,6 +872,9 @@ static int pci_pm_freeze(struct device *dev) return error; } + if (pcibios_pm_ops.freeze) + return pcibios_pm_ops.freeze(dev); + return 0; } @@ -829,6 +900,9 @@ static int pci_pm_freeze_noirq(struct device *dev) pci_pm_set_unknown_state(pci_dev); + if (pcibios_pm_ops.freeze_noirq) + return pcibios_pm_ops.freeze_noirq(dev); + return 0; } @@ -838,6 +912,12 @@ static int pci_pm_thaw_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pcibios_pm_ops.thaw_noirq) { + error = pcibios_pm_ops.thaw_noirq(dev); + if (error) + return error; + } + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); @@ -855,6 +935,12 @@ static int pci_pm_thaw(struct device *dev) const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; + if (pcibios_pm_ops.thaw) { + error = pcibios_pm_ops.thaw(dev); + if (error) + return error; + } + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); @@ -883,6 +969,10 @@ static int pci_pm_poweroff(struct device *dev) goto Fixup; } + /* The reason to do that is the same as in pci_pm_suspend(). */ + pm_runtime_resume(dev); + + pci_dev->state_saved = false; if (pm->poweroff) { int error; @@ -895,6 +985,9 @@ static int pci_pm_poweroff(struct device *dev) Fixup: pci_fixup_device(pci_fixup_suspend, pci_dev); + if (pcibios_pm_ops.poweroff) + return pcibios_pm_ops.poweroff(dev); + return 0; } @@ -918,9 +1011,19 @@ static int pci_pm_poweroff_noirq(struct device *dev) return error; } - if (!pci_dev->state_saved && !pci_is_bridge(pci_dev)) + if (!pci_dev->state_saved && !pci_has_subordinate(pci_dev)) pci_prepare_to_sleep(pci_dev); + /* + * The reason for doing this here is the same as for the analogous code + * in pci_pm_suspend_noirq(). + */ + if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) + pci_write_config_word(pci_dev, PCI_COMMAND, 0); + + if (pcibios_pm_ops.poweroff_noirq) + return pcibios_pm_ops.poweroff_noirq(dev); + return 0; } @@ -930,6 +1033,12 @@ static int pci_pm_restore_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pcibios_pm_ops.restore_noirq) { + error = pcibios_pm_ops.restore_noirq(dev); + if (error) + return error; + } + pci_pm_default_resume_early(pci_dev); if (pci_has_legacy_pm_support(pci_dev)) @@ -947,6 +1056,12 @@ static int pci_pm_restore(struct device *dev) const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; + if (pcibios_pm_ops.restore) { + error = pcibios_pm_ops.restore(dev); + if (error) + return error; + } + /* * This is necessary for the hibernation error path in which restore is * called without restoring the standard config registers of the device. @@ -969,7 +1084,7 @@ static int pci_pm_restore(struct device *dev) return error; } -#else /* !CONFIG_HIBERNATION */ +#else /* !CONFIG_HIBERNATE_CALLBACKS */ #define pci_pm_freeze NULL #define pci_pm_freeze_noirq NULL @@ -980,7 +1095,7 @@ static int pci_pm_restore(struct device *dev) #define pci_pm_restore NULL #define pci_pm_restore_noirq NULL -#endif /* !CONFIG_HIBERNATION */ +#endif /* !CONFIG_HIBERNATE_CALLBACKS */ #ifdef CONFIG_PM_RUNTIME @@ -991,13 +1106,24 @@ 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; + pci_dev->state_saved = false; + pci_dev->no_d3cold = false; error = pm->runtime_suspend(dev); suspend_report_result(pm->runtime_suspend, error); if (error) return error; + if (!pci_dev->d3cold_allowed) + pci_dev->no_d3cold = true; pci_fixup_device(pci_fixup_suspend, pci_dev); @@ -1009,45 +1135,62 @@ 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; } static int pci_pm_runtime_resume(struct device *dev) { + int rc; 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; - pci_pm_default_resume_early(pci_dev); + pci_restore_standard_config(pci_dev); + pci_fixup_device(pci_fixup_resume_early, pci_dev); __pci_enable_wake(pci_dev, PCI_D0, true, false); pci_fixup_device(pci_fixup_resume, pci_dev); - return pm->runtime_resume(dev); + rc = pm->runtime_resume(dev); + + pci_dev->runtime_d3cold = false; + + return rc; } 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; + int ret = 0; + + /* + * 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) return -ENOSYS; - if (pm->runtime_idle) { - int ret = pm->runtime_idle(dev); - if (ret) - return ret; - } - - pm_runtime_suspend(dev); + if (pm->runtime_idle) + ret = pm->runtime_idle(dev); - return 0; + return ret; } #else /* !CONFIG_PM_RUNTIME */ @@ -1058,11 +1201,10 @@ static int pci_pm_runtime_idle(struct device *dev) #endif /* !CONFIG_PM_RUNTIME */ -#ifdef CONFIG_PM_OPS +#ifdef CONFIG_PM -const struct dev_pm_ops pci_dev_pm_ops = { +static const struct dev_pm_ops pci_dev_pm_ops = { .prepare = pci_pm_prepare, - .complete = pci_pm_complete, .suspend = pci_pm_suspend, .resume = pci_pm_resume, .freeze = pci_pm_freeze, @@ -1093,17 +1235,15 @@ const struct dev_pm_ops pci_dev_pm_ops = { * @drv: the driver structure to register * @owner: owner module of drv * @mod_name: module name string - * + * * Adds the driver structure to the list of registered drivers. - * Returns a negative value on error, otherwise 0. - * If no error occurred, the driver remains registered even if + * Returns a negative value on error, otherwise 0. + * If no error occurred, the driver remains registered even if * no device was claimed during registration. */ int __pci_register_driver(struct pci_driver *drv, struct module *owner, const char *mod_name) { - int error; - /* initialize common driver fields */ drv->driver.name = drv->name; drv->driver.bus = &pci_bus_type; @@ -1114,45 +1254,26 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, INIT_LIST_HEAD(&drv->dynids.list); /* register with core */ - error = driver_register(&drv->driver); - if (error) - goto out; - - error = pci_create_newid_file(drv); - if (error) - goto out_newid; - - error = pci_create_removeid_file(drv); - if (error) - goto out_removeid; -out: - return error; - -out_removeid: - pci_remove_newid_file(drv); -out_newid: - driver_unregister(&drv->driver); - goto out; + return driver_register(&drv->driver); } +EXPORT_SYMBOL(__pci_register_driver); /** * pci_unregister_driver - unregister a pci driver * @drv: the driver structure to unregister - * + * * Deletes the driver structure from the list of registered PCI drivers, * gives it a chance to clean up by calling its remove() function for * each device it was responsible for, and marks those devices as * driverless. */ -void -pci_unregister_driver(struct pci_driver *drv) +void pci_unregister_driver(struct pci_driver *drv) { - pci_remove_removeid_file(drv); - pci_remove_newid_file(drv); driver_unregister(&drv->driver); pci_free_dynids(drv); } +EXPORT_SYMBOL(pci_unregister_driver); static struct pci_driver pci_compat_driver = { .name = "compat" @@ -1162,28 +1283,28 @@ static struct pci_driver pci_compat_driver = { * pci_dev_driver - get the pci_driver of a device * @dev: the device to query * - * Returns the appropriate pci_driver structure or %NULL if there is no + * Returns the appropriate pci_driver structure or %NULL if there is no * registered driver for the device. */ -struct pci_driver * -pci_dev_driver(const struct pci_dev *dev) +struct pci_driver *pci_dev_driver(const struct pci_dev *dev) { if (dev->driver) return dev->driver; else { int i; - for(i=0; i<=PCI_ROM_RESOURCE; i++) + for (i = 0; i <= PCI_ROM_RESOURCE; i++) if (dev->resource[i].flags & IORESOURCE_BUSY) return &pci_compat_driver; } return NULL; } +EXPORT_SYMBOL(pci_dev_driver); /** * pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure * @dev: the PCI device structure to match against * @drv: the device driver to search for matching PCI device id structures - * + * * Used by a driver to check whether a PCI device present in the * system is in its list of supported devices. Returns the matching * pci_device_id structure or %NULL if there is no match. @@ -1191,9 +1312,13 @@ pci_dev_driver(const struct pci_dev *dev) static int pci_bus_match(struct device *dev, struct device_driver *drv) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *pci_drv = to_pci_driver(drv); + struct pci_driver *pci_drv; const struct pci_device_id *found_id; + if (!pci_dev->match_driver) + return 0; + + pci_drv = to_pci_driver(drv); found_id = pci_match_device(pci_drv, pci_dev); if (found_id) return 1; @@ -1219,6 +1344,7 @@ struct pci_dev *pci_dev_get(struct pci_dev *dev) get_device(&dev->dev); return dev; } +EXPORT_SYMBOL(pci_dev_get); /** * pci_dev_put - release a use of the pci device structure @@ -1232,13 +1358,39 @@ void pci_dev_put(struct pci_dev *dev) if (dev) put_device(&dev->dev); } +EXPORT_SYMBOL(pci_dev_put); -#ifndef CONFIG_HOTPLUG -int pci_uevent(struct device *dev, struct kobj_uevent_env *env) +static int pci_uevent(struct device *dev, struct kobj_uevent_env *env) { - return -ENODEV; + struct pci_dev *pdev; + + if (!dev) + return -ENODEV; + + pdev = to_pci_dev(dev); + + if (add_uevent_var(env, "PCI_CLASS=%04X", pdev->class)) + return -ENOMEM; + + if (add_uevent_var(env, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device)) + return -ENOMEM; + + if (add_uevent_var(env, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, + pdev->subsystem_device)) + return -ENOMEM; + + if (add_uevent_var(env, "PCI_SLOT_NAME=%s", pci_name(pdev))) + return -ENOMEM; + + if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device, + (u8)(pdev->class >> 16), (u8)(pdev->class >> 8), + (u8)(pdev->class))) + return -ENOMEM; + + return 0; } -#endif struct bus_type pci_bus_type = { .name = "pci", @@ -1247,23 +1399,15 @@ struct bus_type pci_bus_type = { .probe = pci_device_probe, .remove = pci_device_remove, .shutdown = pci_device_shutdown, - .dev_attrs = pci_dev_attrs, - .bus_attrs = pci_bus_attrs, + .dev_groups = pci_dev_groups, + .bus_groups = pci_bus_groups, + .drv_groups = pci_drv_groups, .pm = PCI_PM_OPS_PTR, }; +EXPORT_SYMBOL(pci_bus_type); static int __init pci_driver_init(void) { return bus_register(&pci_bus_type); } - postcore_initcall(pci_driver_init); - -EXPORT_SYMBOL_GPL(pci_add_dynid); -EXPORT_SYMBOL(pci_match_id); -EXPORT_SYMBOL(__pci_register_driver); -EXPORT_SYMBOL(pci_unregister_driver); -EXPORT_SYMBOL(pci_dev_driver); -EXPORT_SYMBOL(pci_bus_type); -EXPORT_SYMBOL(pci_dev_get); -EXPORT_SYMBOL(pci_dev_put); |
