VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / drivers / pci / hotplug / rpaphp_pci.c
index 6f5b61b..0dd2d23 100644 (file)
  */
 #include <linux/pci.h>
 #include <asm/pci-bridge.h>
+#include <asm/rtas.h>
 #include "../pci.h"            /* for pci_add_new_bus */
 
 #include "rpaphp.h"
 
 struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn)
 {
-       struct pci_dev *retval_dev = NULL, *dev;
+       struct pci_dev *retval_dev = NULL, *dev = NULL;
        char bus_id[BUS_ID_SIZE];
 
        sprintf(bus_id, "%04x:%02x:%02x.%d",dn->phb->global_number,
                dn->busno, PCI_SLOT(dn->devfn), PCI_FUNC(dn->devfn));
-
-       dbg("Enter rpaphp_find_pci_dev() full_name=%s bus_id=%s\n", 
-               dn->full_name, bus_id);
-       
        while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
-               if (!strcmp(pci_name(dev), bus_id)) {
+               if (!strcmp(pci_name(dev), bus_id)) {
                        retval_dev = dev;
-                       dbg("rpaphp_find_pci_dev(): found dev=%p\n\n", dev);
                        break;
                }
        }
        return retval_dev;
-
 }
 
 EXPORT_SYMBOL_GPL(rpaphp_find_pci_dev);
@@ -79,11 +74,6 @@ static struct pci_dev *rpaphp_find_bridge_pdev(struct slot *slot)
        return rpaphp_find_pci_dev(slot->dn);
 }
 
-static struct pci_dev *rpaphp_find_adapter_pdev(struct slot *slot)
-{
-       return rpaphp_find_pci_dev(slot->dn->child);
-}
-
 static int rpaphp_get_sensor_state(struct slot *slot, int *state)
 {
        int rc;
@@ -144,7 +134,8 @@ int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value)
                        else if (rpaphp_find_pci_dev(slot->dn->child))
                                *value = CONFIGURED;
                        else {
-                               dbg("%s: can't find pdev of adapter in slot[%s]\n", __FUNCTION__, slot->name);
+                               err("%s: can't find pdev of adapter in slot[%s]\n", 
+                                       __FUNCTION__, slot->dn->full_name);
                                *value = NOT_CONFIGURED;
                        }
                }
@@ -158,7 +149,8 @@ exit:
 }
 
 /* Must be called before pci_bus_add_devices */
-static void rpaphp_fixup_new_pci_devices(struct pci_bus *bus)
+static void 
+rpaphp_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus)
 {
        struct pci_dev *dev;
 
@@ -169,8 +161,9 @@ static void rpaphp_fixup_new_pci_devices(struct pci_bus *bus)
                 */
                if (list_empty(&dev->global_list)) {
                        int i;
-
-                       pcibios_fixup_device_resources(dev, bus);
+                       
+                       if(fix_bus)
+                               pcibios_fixup_device_resources(dev, bus);
                        pci_read_irq_line(dev);
                        for (i = 0; i < PCI_NUM_RESOURCES; i++) {
                                struct resource *r = &dev->resource[i];
@@ -183,69 +176,139 @@ static void rpaphp_fixup_new_pci_devices(struct pci_bus *bus)
        }
 }
 
-static void
-rpaphp_pci_config_device(struct pci_bus *pci_bus, struct device_node *dn)
-{
-       int num;
-
-       num = pci_scan_slot(pci_bus, PCI_DEVFN(PCI_SLOT(dn->devfn), 0));
-       if (num) {
-               rpaphp_fixup_new_pci_devices(pci_bus);
-               pci_bus_add_devices(pci_bus);
-       }
-}
-
-static int rpaphp_pci_config_bridge(struct pci_dev *dev, struct device_node *dn);
+static int rpaphp_pci_config_bridge(struct pci_dev *dev);
 
 /*****************************************************************************
- rpaphp_pci_config_dn() will recursively configure all devices under the 
- given slot->dn and return the dn's pci_dev.
+ rpaphp_pci_config_slot() will  configure all devices under the 
+ given slot->dn and return the the first pci_dev.
  *****************************************************************************/
 static struct pci_dev *
-rpaphp_pci_config_dn(struct device_node *dn, struct pci_bus *bus)
+rpaphp_pci_config_slot(struct device_node *dn, struct pci_bus *bus)
 {
-       struct device_node *local;
+       struct device_node *eads_first_child = dn->child;
        struct pci_dev *dev;
-
-       for (local = dn->child; local; local = local->sibling) {
-               rpaphp_pci_config_device(bus, local);
-               dev = rpaphp_find_pci_dev(local);
-               if (!rpaphp_pci_config_bridge(dev, local))
+       int num;
+       
+       dbg("Enter %s: dn=%s bus=%s\n", __FUNCTION__, dn->full_name, bus->name);
+
+       if (eads_first_child) {
+               /* pci_scan_slot should find all children of EADs */
+               num = pci_scan_slot(bus, PCI_DEVFN(PCI_SLOT(eads_first_child->devfn), 0));
+               if (num) {
+                       rpaphp_fixup_new_pci_devices(bus, 1); 
+                       pci_bus_add_devices(bus);
+               }
+               dev = rpaphp_find_pci_dev(eads_first_child);
+               if (!dev) {
+                       err("No new device found\n");
                        return NULL;
+               }
+               if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) 
+                       rpaphp_pci_config_bridge(dev);
        }
-
        return dev;
 }
 
-static int rpaphp_pci_config_bridge(struct pci_dev *dev, struct device_node *dn)
+static int rpaphp_pci_config_bridge(struct pci_dev *dev)
+{
+       u8 sec_busno;
+       struct pci_bus *child_bus;
+       struct pci_dev *child_dev;
+
+       dbg("Enter %s:  BRIDGE dev=%s\n", __FUNCTION__, pci_name(dev));
+
+       /* get busno of downstream bus */
+       pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno);
+               
+       /* add to children of PCI bridge dev->bus */
+       child_bus = pci_add_new_bus(dev->bus, dev, sec_busno);
+       if (!child_bus) {
+               err("%s: could not add second bus\n", __FUNCTION__);
+               return -EIO;
+       }
+       sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number);
+       /* do pci_scan_child_bus */
+       pci_scan_child_bus(child_bus);
+
+       list_for_each_entry(child_dev, &child_bus->devices, bus_list) {
+               eeh_add_device_late(child_dev);
+       }
+
+        /* fixup new pci devices without touching bus struct */
+       rpaphp_fixup_new_pci_devices(child_bus, 0);
+
+       /* Make the discovered devices available */
+       pci_bus_add_devices(child_bus);
+       return 0;
+}
+
+static void enable_eeh(struct device_node *dn)
+{
+       struct device_node *sib;
+
+       for (sib = dn->child; sib; sib = sib->sibling) 
+               enable_eeh(sib);
+       eeh_add_device_early(dn);
+       return;
+       
+}
+
+#ifdef DEBUG
+static void print_slot_pci_funcs(struct slot *slot)
 {
-       if (dev && dn->child) { /* dn is a PCI bridge node */
-               struct pci_bus *child;
-               u8 sec_busno;
-
-               /* get busno of downstream bus */
-               pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno);
-
-               /* add to children of PCI bridge dev->bus */
-               child = pci_add_new_bus(dev->bus, dev, sec_busno);
-               if (!child) {
-                       err("%s: could not add second bus\n", __FUNCTION__);
-                       return 0;
+       struct list_head *l;
+
+       if (slot->dev_type == PCI_DEV) {
+               printk("pci_funcs of slot[%s]\n", slot->name);
+               if (list_empty(&slot->dev.pci_funcs))
+                       printk("        pci_funcs is EMPTY\n");
+
+               list_for_each (l, &slot->dev.pci_funcs) {
+                       struct rpaphp_pci_func *func =
+                               list_entry(l, struct rpaphp_pci_func, sibling);
+                       printk("        FOUND dev=%s\n", pci_name(func->pci_dev));
                }
-               sprintf(child->name, "PCI Bus #%02x", child->number);
-               /* Fixup subordinate bridge bases and resureces */
-               pcibios_fixup_bus(child);
+       }
+       return;
+}
+#else
+static void print_slot_pci_funcs(struct slot *slot)
+{
+       return;
+}
+#endif
 
-               /* may need do more stuff here */
-               rpaphp_pci_config_dn(dn, dev->subordinate);
+static int init_slot_pci_funcs(struct slot *slot)
+{
+       struct device_node *child;
+
+       for (child = slot->dn->child; child != NULL; child = child->sibling) {
+               struct pci_dev *pdev = rpaphp_find_pci_dev(child);
+
+               if (pdev) {
+                       struct rpaphp_pci_func *func;
+                       func = kmalloc(sizeof(struct rpaphp_pci_func), GFP_KERNEL);
+                       if (!func) 
+                               return -ENOMEM;
+                       memset(func, 0, sizeof(struct rpaphp_pci_func));
+                       INIT_LIST_HEAD(&func->sibling);
+                       func->pci_dev = pdev;
+                       list_add_tail(&func->sibling, &slot->dev.pci_funcs);
+                       print_slot_pci_funcs(slot);
+               } else {
+                       err("%s: dn=%s has no pci_dev\n", 
+                               __FUNCTION__, child->full_name);
+                       return -EIO;
+               }
        }
-       return 1;
+       return 0;
 }
 
-static struct pci_dev *rpaphp_config_pci_adapter(struct slot *slot)
+static int rpaphp_config_pci_adapter(struct slot *slot)
 {
        struct pci_bus *pci_bus;
-       struct pci_dev *dev = NULL;
+       struct pci_dev *dev;
+       int rc = -ENODEV;
 
        dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name);
 
@@ -256,38 +319,74 @@ static struct pci_dev *rpaphp_config_pci_adapter(struct slot *slot)
                        err("%s: can't find bus structure\n", __FUNCTION__);
                        goto exit;
                }
-
-               eeh_add_device_early(slot->dn->child);
-               dev = rpaphp_pci_config_dn(slot->dn, pci_bus);
-               eeh_add_device_late(dev);
+               enable_eeh(slot->dn);
+               dev = rpaphp_pci_config_slot(slot->dn, pci_bus);
+               if (!dev) {
+                       err("%s: can't find any devices.\n", __FUNCTION__);
+                       goto exit;
+               }
+               /* associate corresponding pci_dev */   
+               rc = init_slot_pci_funcs(slot);
+               if (rc)
+                       goto exit;
+               print_slot_pci_funcs(slot);
+               if (!list_empty(&slot->dev.pci_funcs)) 
+                       rc = 0;
        } else {
                /* slot is not enabled */
                err("slot doesn't have pci_dev structure\n");
-               dev = NULL;
        }
-
 exit:
-       dbg("Exit %s: pci_dev %s\n", __FUNCTION__, dev ? "found" : "not found");
-       return dev;
+       dbg("Exit %s:  rc=%d\n", __FUNCTION__, rc);
+       return rc;
+}
+
+
+static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev)
+{
+       eeh_remove_device(dev);
+       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+               struct pci_bus *bus = dev->subordinate;
+               struct list_head *ln;
+               if (!bus)
+                       return; 
+               for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) {
+                       struct pci_dev *pdev = pci_dev_b(ln);
+                       if (pdev)
+                               rpaphp_eeh_remove_bus_device(pdev);
+               }
+
+       }
+       return;
 }
 
 int rpaphp_unconfig_pci_adapter(struct slot *slot)
 {
        int retval = 0;
+       struct list_head *ln;
 
        dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name);
-       if (!slot->dev.pci_dev) {
-               info("%s: no card in slot[%s]\n", __FUNCTION__, slot->name);
+       if (list_empty(&slot->dev.pci_funcs)) {
+               err("%s: slot[%s] doesn't have any devices.\n", __FUNCTION__, 
+                       slot->name);
 
                retval = -EINVAL;
                goto exit;
        }
-       /* remove the device from the pci core */
-       eeh_remove_device(slot->dev.pci_dev);
-       pci_remove_bus_device(slot->dev.pci_dev);
-
+       /* remove the devices from the pci core */
+       list_for_each (ln, &slot->dev.pci_funcs) {
+               struct rpaphp_pci_func *func;
+       
+               func = list_entry(ln, struct rpaphp_pci_func, sibling);
+               if (func->pci_dev) {
+                       pci_remove_bus_device(func->pci_dev); 
+                       rpaphp_eeh_remove_bus_device(func->pci_dev);
+               }
+               kfree(func);
+       }
+       INIT_LIST_HEAD(&slot->dev.pci_funcs);
        slot->state = NOT_CONFIGURED;
-       info("%s: adapter in slot[%s] unconfigured.\n", __FUNCTION__,
+       info("%s: devices in slot[%s] unconfigured.\n", __FUNCTION__,
             slot->name);
 exit:
        dbg("Exit %s, rc=0x%x\n", __FUNCTION__, retval);
@@ -303,7 +402,7 @@ static int setup_pci_hotplug_slot_info(struct slot *slot)
                                      &slot->hotplug_slot->info->
                                      adapter_status);
        if (slot->hotplug_slot->info->adapter_status == NOT_VALID) {
-               dbg("%s: NOT_VALID: skip dn->full_name=%s\n",
+               err("%s: NOT_VALID: skip dn->full_name=%s\n",
                    __FUNCTION__, slot->dn->full_name);
                return -1;
        }
@@ -314,35 +413,41 @@ static int setup_pci_slot(struct slot *slot)
 {
        slot->bridge = rpaphp_find_bridge_pdev(slot);
        if (!slot->bridge) {    /* slot being added doesn't have pci_dev yet */
-               dbg("%s: no pci_dev for bridge dn %s\n", __FUNCTION__, slot->name);
-               dealloc_slot_struct(slot);
-               return 1;
+               err("%s: no pci_dev for bridge dn %s\n", __FUNCTION__, slot->name);
+               goto exit_rc;
        }
-       
+       dbg("%s set slot->name to %s\n",  __FUNCTION__, pci_name(slot->bridge));
        strcpy(slot->name, pci_name(slot->bridge));
+
        /* find slot's pci_dev if it's not empty */
        if (slot->hotplug_slot->info->adapter_status == EMPTY) {
                slot->state = EMPTY;    /* slot is empty */
-               slot->dev.pci_dev = NULL;
        } else {
                /* slot is occupied */
                if (!(slot->dn->child)) {
                        /* non-empty slot has to have child */
-                       err("%s: slot[%s]'s device_node doesn't have child for adapter\n", __FUNCTION__, slot->name);
-                       dealloc_slot_struct(slot);
-                       return 1;
+                       err("%s: slot[%s]'s device_node doesn't have child for adapter\n", 
+                               __FUNCTION__, slot->name);
+                       goto exit_rc;
                }
-               slot->dev.pci_dev = rpaphp_find_adapter_pdev(slot);
-               if (slot->dev.pci_dev) {
+               if (init_slot_pci_funcs(slot)) {
+                       err("%s: init_slot_pci_funcs failed\n", __FUNCTION__);
+                       goto exit_rc;
+               }
+               print_slot_pci_funcs(slot);
+               if (!list_empty(&slot->dev.pci_funcs)) {
                        slot->state = CONFIGURED;
-               
+       
                } else {
                        /* DLPAR add as opposed to 
-                        * boot time */
+                        * boot time */
                        slot->state = NOT_CONFIGURED;
                }
        }
        return 0;
+exit_rc:
+       dealloc_slot_struct(slot);
+       return 1;
 }
 
 int register_pci_slot(struct slot *slot)
@@ -350,14 +455,17 @@ int register_pci_slot(struct slot *slot)
        int rc = 1;
 
        slot->dev_type = PCI_DEV;
+       if (slot->type == EMBEDDED)
+               slot->removable = EMBEDDED;
+       else
+               slot->removable = HOTPLUG;
+       INIT_LIST_HEAD(&slot->dev.pci_funcs);
        if (setup_pci_hotplug_slot_info(slot))
                goto exit_rc;
        if (setup_pci_slot(slot))
                goto exit_rc;
        rc = register_slot(slot);
 exit_rc:
-       if (rc)
-               dealloc_slot_struct(slot);
        return rc;
 }
 
@@ -371,12 +479,12 @@ int rpaphp_enable_pci_slot(struct slot *slot)
        dbg("%s: sensor state[%d]\n", __FUNCTION__, state);
        /* if slot is not empty, enable the adapter */
        if (state == PRESENT) {
-               dbg("%s : slot[%s] is occupid.\n", __FUNCTION__, slot->name);
-               if ((slot->dev.pci_dev =
-                    rpaphp_config_pci_adapter(slot)) != NULL) {
+               dbg("%s : slot[%s] is occupied.\n", __FUNCTION__, slot->name);
+               retval = rpaphp_config_pci_adapter(slot);
+               if (!retval) {
                        slot->state = CONFIGURED;
-                       dbg("%s: PCI adapter %s in slot[%s] has been configured\n", 
-                               __FUNCTION__, pci_name(slot->dev.pci_dev), slot->name);
+                       dbg("%s: PCI devices in slot[%s] has been configured\n", 
+                               __FUNCTION__, slot->name);
                } else {
                        slot->state = NOT_CONFIGURED;
                        dbg("%s: no pci_dev struct for adapter in slot[%s]\n",
@@ -392,10 +500,40 @@ int rpaphp_enable_pci_slot(struct slot *slot)
                retval = -EINVAL;
        }
 exit:
-       if (slot->state != NOT_VALID)
-               rpaphp_set_attention_status(slot, LED_ON);
-       else
-               rpaphp_set_attention_status(slot, LED_ID);
        dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
        return retval;
 }
+
+struct hotplug_slot *rpaphp_find_hotplug_slot(struct pci_dev *dev)
+{
+       struct list_head        *tmp, *n;
+       struct slot             *slot;
+
+       list_for_each_safe(tmp, n, &rpaphp_slot_head) {
+               struct pci_bus *bus;
+               struct list_head *ln;
+
+               slot = list_entry(tmp, struct slot, rpaphp_slot_list);
+               if (slot->bridge == NULL) {
+                       if (slot->dev_type == PCI_DEV) {
+                               printk(KERN_WARNING "PCI slot missing bridge %s %s \n", 
+                                                   slot->name, slot->location);
+                       }
+                       continue;
+               }
+
+               bus = slot->bridge->subordinate;
+               if (!bus) {
+                       continue;  /* should never happen? */
+               }
+               for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) {
+                                struct pci_dev *pdev = pci_dev_b(ln);
+                               if (pdev == dev)
+                                       return slot->hotplug_slot;
+               }
+       }
+
+       return NULL;
+}
+
+EXPORT_SYMBOL_GPL(rpaphp_find_hotplug_slot);