vserver 1.9.5.x5
[linux-2.6.git] / drivers / pci / hotplug / rpadlpar_core.c
index ed4b23c..705e5b3 100644 (file)
 
 static DECLARE_MUTEX(rpadlpar_sem);
 
+#define NODE_TYPE_VIO  1
+#define NODE_TYPE_SLOT 2
+#define NODE_TYPE_PHB  3
+
 static struct device_node *find_php_slot_vio_node(char *drc_name)
 {
        struct device_node *child;
@@ -44,21 +48,50 @@ static struct device_node *find_php_slot_vio_node(char *drc_name)
        return NULL;
 }
 
-static struct device_node *find_php_slot_pci_node(char *drc_name)
+/* Find dlpar-capable pci node that contains the specified name and type */
+static struct device_node *find_php_slot_pci_node(char *drc_name,
+                                                 char *drc_type)
 {
        struct device_node *np = NULL;
        char *name;
+       char *type;
+       int rc;
 
-       while ((np = of_find_node_by_type(np, "pci")))
-               if (is_hotplug_capable(np)) {
-                       name = rpaphp_get_drc_name(np);
-                       if (name && (!strcmp(drc_name, name)))
+       while ((np = of_find_node_by_type(np, "pci"))) {
+               rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
+               if (rc == 0)
+                       if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
                                break;
-               }
+       }
 
        return np;
 }
 
+static struct device_node *find_newly_added_node(char *drc_name, int *node_type)
+{
+       struct device_node *dn;
+
+       dn = find_php_slot_pci_node(drc_name, "SLOT");
+       if (dn) {
+               *node_type = NODE_TYPE_SLOT;
+               return dn;
+       }
+
+       dn = find_php_slot_pci_node(drc_name, "PHB");
+       if (dn) {
+               *node_type = NODE_TYPE_PHB;
+               return dn;
+       }
+
+       dn = find_php_slot_vio_node(drc_name);
+       if (dn) {
+               *node_type = NODE_TYPE_VIO;
+               return dn;
+       }
+
+       return NULL;
+}
+
 static struct slot *find_slot(char *drc_name)
 {
        struct list_head *tmp, *n;
@@ -125,12 +158,8 @@ static int pci_add_secondary_bus(struct device_node *dn,
 
        dn->bussubno = child->number;
 
-       /* ioremap() for child bus */
-       if (remap_bus_range(child)) {
-               printk(KERN_ERR "%s: could not ioremap() child bus\n",
-                       __FUNCTION__);
-               return 1;
-       }
+       /* ioremap() for child bus, which may or may not succeed */
+       remap_bus_range(child);
 
        return 0;
 }
@@ -205,6 +234,71 @@ static inline int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
        return 0;
 }
 
+static int dlpar_remove_root_bus(struct pci_controller *phb)
+{
+       struct pci_bus *phb_bus;
+       int rc;
+
+       phb_bus = phb->bus;
+       if (!(list_empty(&phb_bus->children) &&
+             list_empty(&phb_bus->devices))) {
+               return -EBUSY;
+       }
+
+       rc = pcibios_remove_root_bus(phb);
+       if (rc)
+               return -EIO;
+
+       device_unregister(phb_bus->bridge);
+       pci_remove_bus(phb_bus);
+
+       return 0;
+}
+
+static int dlpar_remove_phb(struct slot *slot)
+{
+       struct pci_controller *phb;
+       struct device_node *dn;
+       int rc = 0;
+
+       dn = slot->dn;
+       if (!dn) {
+               printk(KERN_ERR "%s: unexpected NULL slot device node\n",
+                               __FUNCTION__);
+               return -EIO;
+       }
+
+       phb = dn->phb;
+       if (!phb) {
+               printk(KERN_ERR "%s: unexpected NULL phb pointer\n",
+                               __FUNCTION__);
+               return -EIO;
+       }
+
+       if (rpaphp_remove_slot(slot)) {
+               printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
+                       __FUNCTION__, slot->location);
+               return -EIO;
+       }
+
+       rc = dlpar_remove_root_bus(phb);
+       if (rc)
+               return rc;
+
+       return 0;
+}
+
+static int dlpar_add_phb(struct device_node *dn)
+{
+       struct pci_controller *phb;
+
+       phb = init_phb_dynamic(dn);
+       if (!phb)
+               return 1;
+
+       return 0;
+}
+
 /**
  * dlpar_add_slot - DLPAR add an I/O Slot
  * @drc_name: drc-name of newly added slot
@@ -220,7 +314,8 @@ static inline int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
  */
 int dlpar_add_slot(char *drc_name)
 {
-       struct device_node *dn;
+       struct device_node *dn = NULL;
+       int node_type;
        int rc = 0;
 
        if (down_interruptible(&rpadlpar_sem))
@@ -232,18 +327,27 @@ int dlpar_add_slot(char *drc_name)
                goto exit;
        }
 
-       dn = find_php_slot_vio_node(drc_name);
+       dn = find_newly_added_node(drc_name, &node_type);
        if (!dn) {
-               dn = find_php_slot_pci_node(drc_name);
-               if (dn)
+               rc = -ENODEV;
+               goto exit;
+       }
+
+       switch (node_type) {
+               case NODE_TYPE_VIO:
+                       /* Just add hotplug slot */
+                       break;
+               case NODE_TYPE_SLOT:
                        rc = dlpar_add_pci_slot(drc_name, dn);
-               else {
-                       rc = -ENODEV;
-                       goto exit;
-               }
+                       break;
+               case NODE_TYPE_PHB:
+                       rc = dlpar_add_phb(dn);
+                       break;
+               default:
+                       printk("%s: unexpected node type\n", __FUNCTION__);
+                       return -EIO;
        }
 
-       /* Add hotplug slot for new VIOA or PCI */
        if (!rc && rpaphp_add_slot(dn)) {
                printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
                        __FUNCTION__, drc_name);
@@ -337,7 +441,8 @@ int dlpar_remove_slot(char *drc_name)
                return -ERESTARTSYS;
 
        if (!find_php_slot_vio_node(drc_name) &&
-           !find_php_slot_pci_node(drc_name)) {
+           !find_php_slot_pci_node(drc_name, "SLOT") &&
+           !find_php_slot_pci_node(drc_name, "PHB")) {
                rc = -ENODEV;
                goto exit;
        }
@@ -348,17 +453,18 @@ int dlpar_remove_slot(char *drc_name)
                goto exit;
        }
        
-       switch (slot->dev_type) {
-               case PCI_DEV:
-                       rc = dlpar_remove_pci_slot(slot, drc_name);
-                       break;
-
-               case VIO_DEV:
-                       rc = dlpar_remove_vio_slot(slot, drc_name);
-                       break;
+       if (slot->type == PHB) {
+               rc = dlpar_remove_phb(slot);
+       } else {
+               switch (slot->dev_type) {
+                       case PCI_DEV:
+                               rc = dlpar_remove_pci_slot(slot, drc_name);
+                               break;
 
-               default:
-                       rc = -EIO;
+                       case VIO_DEV:
+                               rc = dlpar_remove_vio_slot(slot, drc_name);
+                               break;
+               }
        }
 exit:
        up(&rpadlpar_sem);