PCI: use /sys/bus/pci/drivers/<driver>/new_id first
[sfrench/cifs-2.6.git] / drivers / pci / pci-driver.c
index 474e9cd0e9e474ce81bb70ca4d54b2a01566adcc..de5b9011d038adaf7dc95624165257b37219ee4c 100644 (file)
  *  Registration of PCI drivers and handling of hot-pluggable devices.
  */
 
+/* multithreaded probe logic */
+static int pci_multithread_probe =
+#ifdef CONFIG_PCI_MULTITHREAD_PROBE
+       1;
+#else
+       0;
+#endif
+__module_param_call("", pci_multithread_probe, param_set_bool, param_get_bool, &pci_multithread_probe, 0644);
+
+
 /*
  * Dynamic device IDs are disabled for !CONFIG_HOTPLUG
  */
@@ -46,6 +56,7 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
                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 %lux",
                        &vendor, &device, &subvendor, &subdevice,
@@ -72,10 +83,12 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
        spin_unlock(&pdrv->dynids.lock);
 
        if (get_driver(&pdrv->driver)) {
-               driver_attach(&pdrv->driver);
+               retval = driver_attach(&pdrv->driver);
                put_driver(&pdrv->driver);
        }
 
+       if (retval)
+               return retval;
        return count;
 }
 static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
@@ -149,14 +162,9 @@ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
 const struct pci_device_id *pci_match_device(struct pci_driver *drv,
                                             struct pci_dev *dev)
 {
-       const struct pci_device_id *id;
        struct pci_dynid *dynid;
 
-       id = pci_match_id(drv->id_table, dev);
-       if (id)
-               return id;
-
-       /* static ids didn't match, lets look at the dynamic ones */
+       /* 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)) {
@@ -165,7 +173,8 @@ const struct pci_device_id *pci_match_device(struct pci_driver *drv,
                }
        }
        spin_unlock(&drv->dynids.lock);
-       return NULL;
+
+       return pci_match_id(drv->id_table, dev);
 }
 
 static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
@@ -251,6 +260,13 @@ static int pci_device_remove(struct device * dev)
                pci_dev->driver = NULL;
        }
 
+       /*
+        * If the device is still on, set the power state as "unknown",
+        * since it might change by the next time we load the driver.
+        */
+       if (pci_dev->current_state == PCI_D0)
+               pci_dev->current_state = PCI_UNKNOWN;
+
        /*
         * We would love to complain here if pci_dev->is_enabled is set, that
         * the driver should have called pci_disable_device(), but the
@@ -275,10 +291,28 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
                suspend_report_result(drv->suspend, i);
        } else {
                pci_save_state(pci_dev);
+               /*
+                * mark its power state as "unknown", since we don't know if
+                * e.g. the BIOS will change its device state when we suspend.
+                */
+               if (pci_dev->current_state == PCI_D0)
+                       pci_dev->current_state = PCI_UNKNOWN;
        }
        return i;
 }
 
+static int pci_device_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;
+       int i = 0;
+
+       if (drv && drv->suspend_late) {
+               i = drv->suspend_late(pci_dev, state);
+               suspend_report_result(drv->suspend_late, i);
+       }
+       return i;
+}
 
 /*
  * Default resume method for devices that have no driver provided resume,
@@ -291,8 +325,8 @@ static int pci_default_resume(struct pci_dev *pci_dev)
        /* restore the PCI config space */
        pci_restore_state(pci_dev);
        /* if the device was enabled before suspend, reenable */
-       if (pci_dev->is_enabled)
-               retval = pci_enable_device(pci_dev);
+       if (atomic_read(&pci_dev->enable_cnt))
+               retval = __pci_enable_device(pci_dev);
        /* if the device was busmaster before the suspend, make it busmaster again */
        if (pci_dev->is_busmaster)
                pci_set_master(pci_dev);
@@ -313,6 +347,17 @@ static int pci_device_resume(struct device * dev)
        return error;
 }
 
+static int pci_device_resume_early(struct device * dev)
+{
+       int error = 0;
+       struct pci_dev * pci_dev = to_pci_dev(dev);
+       struct pci_driver * drv = pci_dev->driver;
+
+       if (drv && drv->resume_early)
+               error = drv->resume_early(pci_dev);
+       return error;
+}
+
 static void pci_device_shutdown(struct device *dev)
 {
        struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -386,14 +431,22 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner)
        drv->driver.owner = owner;
        drv->driver.kobj.ktype = &pci_driver_kobj_type;
 
+       if (pci_multithread_probe)
+               drv->driver.multithread_probe = pci_multithread_probe;
+       else
+               drv->driver.multithread_probe = drv->multithread_probe;
+
        spin_lock_init(&drv->dynids.lock);
        INIT_LIST_HEAD(&drv->dynids.list);
 
        /* register with core */
        error = driver_register(&drv->driver);
+       if (error)
+               return error;
 
-       if (!error)
-               error = pci_create_newid_file(drv);
+       error = pci_create_newid_file(drv);
+       if (error)
+               driver_unregister(&drv->driver);
 
        return error;
 }
@@ -509,8 +562,10 @@ struct bus_type pci_bus_type = {
        .probe          = pci_device_probe,
        .remove         = pci_device_remove,
        .suspend        = pci_device_suspend,
-       .shutdown       = pci_device_shutdown,
+       .suspend_late   = pci_device_suspend_late,
+       .resume_early   = pci_device_resume_early,
        .resume         = pci_device_resume,
+       .shutdown       = pci_device_shutdown,
        .dev_attrs      = pci_dev_attrs,
 };