aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci/hotplug/rpadlpar_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/hotplug/rpadlpar_core.c')
-rw-r--r--drivers/pci/hotplug/rpadlpar_core.c284
1 files changed, 119 insertions, 165 deletions
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index fcb66b9a0e2..7660232ef46 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -14,12 +14,17 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
+
+#undef DEBUG
+
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/string.h>
+#include <linux/vmalloc.h>
#include <asm/pci-bridge.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
#include <asm/rtas.h>
#include <asm/vio.h>
@@ -27,7 +32,7 @@
#include "rpaphp.h"
#include "rpadlpar.h"
-static DECLARE_MUTEX(rpadlpar_sem);
+static DEFINE_MUTEX(rpadlpar_mutex);
#define DLPAR_MODULE_NAME "rpadlpar_io"
@@ -63,7 +68,7 @@ static struct device_node *find_php_slot_pci_node(char *drc_name,
char *type;
int rc;
- while ((np = of_find_node_by_type(np, "pci"))) {
+ while ((np = of_find_node_by_name(np, "pci"))) {
rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
if (rc == 0)
if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
@@ -98,77 +103,27 @@ static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
return NULL;
}
-static struct slot *find_slot(struct device_node *dn)
+/**
+ * find_php_slot - return hotplug slot structure for device node
+ * @dn: target &device_node
+ *
+ * This routine will return the hotplug slot structure
+ * for a given device node. Note that built-in PCI slots
+ * may be dlpar-able, but not hot-pluggable, so this routine
+ * will return NULL for built-in PCI slots.
+ */
+static struct slot *find_php_slot(struct device_node *dn)
{
struct list_head *tmp, *n;
struct slot *slot;
- list_for_each_safe(tmp, n, &rpaphp_slot_head) {
- slot = list_entry(tmp, struct slot, rpaphp_slot_list);
- if (slot->dn == dn)
- return slot;
- }
-
- return NULL;
-}
-
-static void rpadlpar_claim_one_bus(struct pci_bus *b)
-{
- struct list_head *ld;
- struct pci_bus *child_bus;
-
- for (ld = b->devices.next; ld != &b->devices; ld = ld->next) {
- struct pci_dev *dev = pci_dev_b(ld);
- int i;
-
- for (i = 0; i < PCI_NUM_RESOURCES; i++) {
- struct resource *r = &dev->resource[i];
-
- if (r->parent || !r->start || !r->flags)
- continue;
- rpaphp_claim_resource(dev, i);
- }
+ list_for_each_safe(tmp, n, &rpaphp_slot_head) {
+ slot = list_entry(tmp, struct slot, rpaphp_slot_list);
+ if (slot->dn == dn)
+ return slot;
}
- list_for_each_entry(child_bus, &b->children, node)
- rpadlpar_claim_one_bus(child_bus);
-}
-
-static int pci_add_secondary_bus(struct device_node *dn,
- struct pci_dev *bridge_dev)
-{
- struct pci_dn *pdn = dn->data;
- struct pci_controller *hose = pdn->phb;
- struct pci_bus *child;
- u8 sec_busno;
-
- /* Get busno of downstream bus */
- pci_read_config_byte(bridge_dev, PCI_SECONDARY_BUS, &sec_busno);
-
- /* Allocate and add to children of bridge_dev->bus */
- child = pci_add_new_bus(bridge_dev->bus, bridge_dev, sec_busno);
- if (!child) {
- printk(KERN_ERR "%s: could not add secondary bus\n", __FUNCTION__);
- return -ENOMEM;
- }
-
- sprintf(child->name, "PCI Bus #%02x", child->number);
-
- /* Fixup subordinate bridge bases and resources */
- pcibios_fixup_bus(child);
-
- /* Claim new bus resources */
- rpadlpar_claim_one_bus(bridge_dev->bus);
-
- if (hose->last_busno < child->number)
- hose->last_busno = child->number;
-
- pdn->bussubno = child->number;
-
- /* ioremap() for child bus, which may or may not succeed */
- remap_bus_range(child);
-
- return 0;
+ return NULL;
}
static struct pci_dev *dlpar_find_new_dev(struct pci_bus *parent,
@@ -185,115 +140,93 @@ static struct pci_dev *dlpar_find_new_dev(struct pci_bus *parent,
return NULL;
}
-static struct pci_dev *dlpar_pci_add_bus(struct device_node *dn)
+static void dlpar_pci_add_bus(struct device_node *dn)
{
- struct pci_dn *pdn = dn->data;
- struct pci_controller *hose = pdn->phb;
+ struct pci_dn *pdn = PCI_DN(dn);
+ struct pci_controller *phb = pdn->phb;
struct pci_dev *dev = NULL;
- /* Scan phb bus for EADS device, adding new one to bus->devices */
- if (!pci_scan_single_device(hose->bus, pdn->devfn)) {
- printk(KERN_ERR "%s: found no device on bus\n", __FUNCTION__);
- return NULL;
- }
+ eeh_add_device_tree_early(dn);
- /* Add new devices to global lists. Register in proc, sysfs. */
- pci_bus_add_devices(hose->bus);
+ /* Add EADS device to PHB bus, adding new entry to bus->devices */
+ dev = of_create_pci_dev(dn, phb->bus, pdn->devfn);
+ if (!dev) {
+ printk(KERN_ERR "%s: failed to create pci dev for %s\n",
+ __func__, dn->full_name);
+ return;
+ }
- /* Confirm new bridge dev was created */
- dev = dlpar_find_new_dev(hose->bus, dn);
- if (dev) {
- if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
- printk(KERN_ERR "%s: unexpected header type %d\n",
- __FUNCTION__, dev->hdr_type);
- return NULL;
- }
+ /* Scan below the new bridge */
+ if (pci_is_bridge(dev))
+ of_scan_pci_bridge(dev);
- if (pci_add_secondary_bus(dn, dev))
- return NULL;
- }
+ /* Map IO space for child bus, which may or may not succeed */
+ pcibios_map_io_space(dev->subordinate);
- return dev;
+ /* Finish adding it : resource allocation, adding devices, etc...
+ * Note that we need to perform the finish pass on the -parent-
+ * bus of the EADS bridge so the bridge device itself gets
+ * properly added
+ */
+ pcibios_finish_adding_to_bus(phb->bus);
}
static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
{
struct pci_dev *dev;
- int rc;
+ struct pci_controller *phb;
- if (rpaphp_find_pci_bus(dn))
+ if (pcibios_find_pci_bus(dn))
return -EINVAL;
/* Add pci bus */
- dev = dlpar_pci_add_bus(dn);
+ dlpar_pci_add_bus(dn);
+
+ /* Confirm new bridge dev was created */
+ phb = PCI_DN(dn)->phb;
+ dev = dlpar_find_new_dev(phb->bus, dn);
+
if (!dev) {
- printk(KERN_ERR "%s: unable to add bus %s\n", __FUNCTION__,
+ printk(KERN_ERR "%s: unable to add bus %s\n", __func__,
drc_name);
return -EIO;
}
- if (dn->child) {
- rc = rpaphp_config_pci_adapter(dev->subordinate);
- if (rc < 0) {
- printk(KERN_ERR "%s: unable to enable slot %s\n",
- __FUNCTION__, drc_name);
- return -EIO;
- }
+ if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
+ printk(KERN_ERR "%s: unexpected header type %d, unable to add bus %s\n",
+ __func__, dev->hdr_type, drc_name);
+ return -EIO;
}
/* Add hotplug slot */
if (rpaphp_add_slot(dn)) {
printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
- __FUNCTION__, drc_name);
+ __func__, drc_name);
return -EIO;
}
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(char *drc_name, struct device_node *dn)
{
struct slot *slot;
struct pci_dn *pdn;
int rc = 0;
- if (!rpaphp_find_pci_bus(dn))
+ if (!pcibios_find_pci_bus(dn))
return -EINVAL;
- slot = find_slot(dn);
- if (slot) {
- /* Remove hotplug slot */
- if (rpaphp_remove_slot(slot)) {
- printk(KERN_ERR
- "%s: unable to remove hotplug slot %s\n",
- __FUNCTION__, drc_name);
- return -EIO;
- }
+ /* If pci slot is hotpluggable, use hotplug to remove it */
+ slot = find_php_slot(dn);
+ if (slot && rpaphp_deregister_slot(slot)) {
+ printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
+ __func__, drc_name);
+ return -EIO;
}
pdn = dn->data;
BUG_ON(!pdn || !pdn->phb);
- rc = dlpar_remove_root_bus(pdn->phb);
+ rc = remove_phb_dynamic(pdn->phb);
if (rc < 0)
return rc;
@@ -306,7 +239,7 @@ static int dlpar_add_phb(char *drc_name, struct device_node *dn)
{
struct pci_controller *phb;
- if (PCI_DN(dn)->phb) {
+ if (PCI_DN(dn) && PCI_DN(dn)->phb) {
/* PHB already exists */
return -EINVAL;
}
@@ -317,7 +250,7 @@ static int dlpar_add_phb(char *drc_name, struct device_node *dn)
if (rpaphp_add_slot(dn)) {
printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
- __FUNCTION__, drc_name);
+ __func__, drc_name);
return -EIO;
}
return 0;
@@ -331,7 +264,7 @@ static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
if (!vio_register_device_node(dn)) {
printk(KERN_ERR
"%s: failed to register vio node %s\n",
- __FUNCTION__, drc_name);
+ __func__, drc_name);
return -EIO;
}
return 0;
@@ -341,9 +274,8 @@ static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
* dlpar_add_slot - DLPAR add an I/O Slot
* @drc_name: drc-name of newly added slot
*
- * Make the hotplug module and the kernel aware
- * of a newly added I/O Slot.
- * Return Codes -
+ * Make the hotplug module and the kernel aware of a newly added I/O Slot.
+ * Return Codes:
* 0 Success
* -ENODEV Not a valid drc_name
* -EINVAL Slot already added
@@ -356,7 +288,7 @@ int dlpar_add_slot(char *drc_name)
int node_type;
int rc = -EIO;
- if (down_interruptible(&rpadlpar_sem))
+ if (mutex_lock_interruptible(&rpadlpar_mutex))
return -ERESTARTSYS;
/* Find newly added node */
@@ -380,16 +312,16 @@ int dlpar_add_slot(char *drc_name)
printk(KERN_INFO "%s: slot %s added\n", DLPAR_MODULE_NAME, drc_name);
exit:
- up(&rpadlpar_sem);
+ mutex_unlock(&rpadlpar_mutex);
return rc;
}
/**
* dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot
* @drc_name: drc-name of newly added slot
+ * @dn: &device_node
*
- * Remove the kernel and hotplug representations
- * of an I/O Slot.
+ * Remove the kernel and hotplug representations of an I/O Slot.
* Return Codes:
* 0 Success
* -EINVAL Vio dev doesn't exist
@@ -407,11 +339,11 @@ static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
}
/**
- * dlpar_remove_slot - DLPAR remove a PCI I/O Slot
+ * dlpar_remove_pci_slot - DLPAR remove a PCI I/O Slot
* @drc_name: drc-name of newly added slot
+ * @dn: &device_node
*
- * Remove the kernel and hotplug representations
- * of a PCI I/O Slot.
+ * Remove the kernel and hotplug representations of a PCI I/O Slot.
* Return Codes:
* 0 Success
* -ENODEV Not a valid drc_name
@@ -421,39 +353,59 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
{
struct pci_bus *bus;
struct slot *slot;
+ int ret = 0;
- bus = rpaphp_find_pci_bus(dn);
- if (!bus)
- return -EINVAL;
+ pci_lock_rescan_remove();
- slot = find_slot(dn);
+ bus = pcibios_find_pci_bus(dn);
+ if (!bus) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",
+ bus->self ? pci_name(bus->self) : "<!PHB!>");
+
+ slot = find_php_slot(dn);
if (slot) {
- /* Remove hotplug slot */
- if (rpaphp_remove_slot(slot)) {
+ pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n",
+ pci_domain_nr(bus), bus->number);
+
+ if (rpaphp_deregister_slot(slot)) {
printk(KERN_ERR
"%s: unable to remove hotplug slot %s\n",
- __FUNCTION__, drc_name);
- return -EIO;
+ __func__, drc_name);
+ ret = -EIO;
+ goto out;
}
}
- if (unmap_bus_range(bus)) {
+ /* Remove all devices below slot */
+ pcibios_remove_pci_devices(bus);
+
+ /* Unmap PCI IO space */
+ if (pcibios_unmap_io_space(bus)) {
printk(KERN_ERR "%s: failed to unmap bus range\n",
- __FUNCTION__);
- return -ERANGE;
+ __func__);
+ ret = -ERANGE;
+ goto out;
}
+ /* Remove the EADS bridge device itself */
BUG_ON(!bus->self);
- pci_remove_bus_device(bus->self);
- return 0;
+ pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
+ pci_stop_and_remove_bus_device(bus->self);
+
+ out:
+ pci_unlock_rescan_remove();
+ return ret;
}
/**
* dlpar_remove_slot - DLPAR remove an I/O Slot
* @drc_name: drc-name of newly added slot
*
- * Remove the kernel and hotplug representations
- * of an I/O Slot.
+ * Remove the kernel and hotplug representations of an I/O Slot.
* Return Codes:
* 0 Success
* -ENODEV Not a valid drc_name
@@ -467,7 +419,7 @@ int dlpar_remove_slot(char *drc_name)
int node_type;
int rc = 0;
- if (down_interruptible(&rpadlpar_sem))
+ if (mutex_lock_interruptible(&rpadlpar_mutex))
return -ERESTARTSYS;
dn = find_dlpar_node(drc_name, &node_type);
@@ -487,9 +439,11 @@ int dlpar_remove_slot(char *drc_name)
rc = dlpar_remove_pci_slot(drc_name, dn);
break;
}
+ vm_unmap_aliases();
+
printk(KERN_INFO "%s: slot %s removed\n", DLPAR_MODULE_NAME, drc_name);
exit:
- up(&rpadlpar_sem);
+ mutex_unlock(&rpadlpar_mutex);
return rc;
}
@@ -506,7 +460,7 @@ int __init rpadlpar_io_init(void)
if (!is_dlpar_capable()) {
printk(KERN_WARNING "%s: partition not DLPAR capable\n",
- __FUNCTION__);
+ __func__);
return -EPERM;
}