]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/pci/hotplug/fakephp.c
Merge branch 'virtex-for-2.6.25' of git://git.secretlab.ca/git/linux-2.6-virtex into...
[linux-2.6-omap-h63xx.git] / drivers / pci / hotplug / fakephp.c
index 05a4f0f9018620b5f6999c701d8a73ffbb477659..94b640146d4456b4aa0b96b1022f7efcfadbd256 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/pci_hotplug.h>
 #include <linux/init.h>
 #include <linux/string.h>
 #include <linux/slab.h>
-#include "pci_hotplug.h"
+#include <linux/workqueue.h>
 #include "../pci.h"
 
 #if !defined(MODULE)
@@ -63,10 +64,16 @@ struct dummy_slot {
        struct list_head node;
        struct hotplug_slot *slot;
        struct pci_dev *dev;
+       struct work_struct remove_work;
+       unsigned long removed;
 };
 
 static int debug;
 static LIST_HEAD(slot_list);
+static struct workqueue_struct *dummyphp_wq;
+
+static void pci_rescan_worker(struct work_struct *work);
+static DECLARE_WORK(pci_rescan_work, pci_rescan_worker);
 
 static int enable_slot (struct hotplug_slot *slot);
 static int disable_slot (struct hotplug_slot *slot);
@@ -109,7 +116,7 @@ static int add_slot(struct pci_dev *dev)
        slot->name = &dev->dev.bus_id[0];
        dbg("slot->name = %s\n", slot->name);
 
-       dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
+       dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL);
        if (!dslot)
                goto error_info;
 
@@ -164,12 +171,20 @@ static void remove_slot(struct dummy_slot *dslot)
                err("Problem unregistering a slot %s\n", dslot->slot->name);
 }
 
+/* called from the single-threaded workqueue handler to remove a slot */
+static void remove_slot_worker(struct work_struct *work)
+{
+       struct dummy_slot *dslot =
+               container_of(work, struct dummy_slot, remove_work);
+       remove_slot(dslot);
+}
+
 /**
- * Rescan slot.
- * Tries hard not to re-enable already existing devices
- * also handles scanning of subfunctions
+ * pci_rescan_slot - Rescan slot
+ * @temp: Device template. Should be set: bus and devfn.
  *
- * @param temp   Device template. Should be set: bus and devfn.
+ * Tries hard not to re-enable already existing devices;
+ * also handles scanning of subfunctions.
  */
 static void pci_rescan_slot(struct pci_dev *temp)
 {
@@ -181,7 +196,9 @@ static void pci_rescan_slot(struct pci_dev *temp)
 
        if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
                temp->hdr_type = hdr_type & 0x7f;
-               if (!pci_find_slot(bus->number, temp->devfn)) {
+               if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
+                       pci_dev_put(dev);
+               else {
                        dev = pci_scan_single_device(bus, temp->devfn);
                        if (dev) {
                                dbg("New device on %s function %x:%x\n",
@@ -205,7 +222,9 @@ static void pci_rescan_slot(struct pci_dev *temp)
                                continue;
                        temp->hdr_type = hdr_type & 0x7f;
 
-                       if (!pci_find_slot(bus->number, temp->devfn)) {
+                       if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
+                               pci_dev_put(dev);
+                       else {
                                dev = pci_scan_single_device(bus, temp->devfn);
                                if (dev) {
                                        dbg("New device on %s function %x:%x\n",
@@ -225,16 +244,16 @@ static void pci_rescan_slot(struct pci_dev *temp)
 
 
 /**
- * Rescan PCI bus.
- * call pci_rescan_slot for each possible function of the bus
+ * pci_rescan_bus - Rescan PCI bus
+ * @bus: the PCI bus to rescan
  *
- * @param bus
+ * Call pci_rescan_slot for each possible function of the bus.
  */
 static void pci_rescan_bus(const struct pci_bus *bus)
 {
        unsigned int devfn;
        struct pci_dev *dev;
-       dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
+       dev = alloc_pci_dev();
        if (!dev)
                return;
 
@@ -263,11 +282,17 @@ static inline void pci_rescan(void) {
        pci_rescan_buses(&pci_root_buses);
 }
 
+/* called from the single-threaded workqueue handler to rescan all pci buses */
+static void pci_rescan_worker(struct work_struct *work)
+{
+       pci_rescan();
+}
 
 static int enable_slot(struct hotplug_slot *hotplug_slot)
 {
        /* mis-use enable_slot for rescanning of the pci bus */
-       pci_rescan();
+       cancel_work_sync(&pci_rescan_work);
+       queue_work(dummyphp_wq, &pci_rescan_work);
        return -ENODEV;
 }
 
@@ -302,10 +327,14 @@ static int disable_slot(struct hotplug_slot *slot)
                err("Can't remove PCI devices with other PCI devices behind it yet.\n");
                return -ENODEV;
        }
+       if (test_and_set_bit(0, &dslot->removed)) {
+               dbg("Slot already scheduled for removal\n");
+               return -ENODEV;
+       }
        /* search for subfunctions and disable them first */
        if (!(dslot->dev->devfn & 7)) {
                for (func = 1; func < 8; func++) {
-                       dev = pci_find_slot(dslot->dev->bus->number,
+                       dev = pci_get_slot(dslot->dev->bus,
                                        dslot->dev->devfn + func);
                        if (dev) {
                                hslot = get_slot_from_dev(dev);
@@ -315,6 +344,7 @@ static int disable_slot(struct hotplug_slot *slot)
                                        err("Hotplug slot not found for subfunction of PCI device\n");
                                        return -ENODEV;
                                }
+                               pci_dev_put(dev);
                        } else
                                dbg("No device in slot found\n");
                }
@@ -323,8 +353,9 @@ static int disable_slot(struct hotplug_slot *slot)
        /* remove the device from the pci core */
        pci_remove_bus_device(dslot->dev);
 
-       /* blow away this sysfs entry and other parts. */
-       remove_slot(dslot);
+       /* queue work item to blow away this sysfs entry and other parts. */
+       INIT_WORK(&dslot->remove_work, remove_slot_worker);
+       queue_work(dummyphp_wq, &dslot->remove_work);
 
        return 0;
 }
@@ -335,6 +366,7 @@ static void cleanup_slots (void)
        struct list_head *next;
        struct dummy_slot *dslot;
 
+       destroy_workqueue(dummyphp_wq);
        list_for_each_safe (tmp, next, &slot_list) {
                dslot = list_entry (tmp, struct dummy_slot, node);
                remove_slot(dslot);
@@ -346,6 +378,10 @@ static int __init dummyphp_init(void)
 {
        info(DRIVER_DESC "\n");
 
+       dummyphp_wq = create_singlethread_workqueue(MY_NAME);
+       if (!dummyphp_wq)
+               return -ENOMEM;
+
        return pci_scan_buses();
 }