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;
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;
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;
}
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
*/
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))
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);
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;
}
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);