X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fpci%2Fhotplug%2Frpaphp_pci.c;h=9e81bdedca5a2efbfb511ce70badc3529b4c565f;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=6f5b61b7c61a3ac3157f6c0ebda8ea9a82ec2f2e;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c index 6f5b61b7c..9e81bdedc 100644 --- a/drivers/pci/hotplug/rpaphp_pci.c +++ b/drivers/pci/hotplug/rpaphp_pci.c @@ -24,30 +24,25 @@ */ #include #include +#include +#include #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 *dev = NULL; char bus_id[BUS_ID_SIZE]; - sprintf(bus_id, "%04x:%02x:%02x.%d",dn->phb->global_number, + 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)) { - retval_dev = dev; - dbg("rpaphp_find_pci_dev(): found dev=%p\n\n", dev); + for_each_pci_dev(dev) { + if (!strcmp(pci_name(dev), bus_id)) { break; } } - return retval_dev; - + return 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; @@ -127,38 +117,47 @@ static int rpaphp_get_sensor_state(struct slot *slot, int *state) int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value) { int state, rc; - *value = NOT_VALID; + struct device_node *child_dn; + struct pci_dev *child_dev = NULL; + *value = NOT_VALID; rc = rpaphp_get_sensor_state(slot, &state); if (rc) goto exit; - if (state == PRESENT) { + + if ((state == EMPTY) || (slot->type == PHB)) { + dbg("slot is empty\n"); + *value = EMPTY; + } + else if (state == PRESENT) { if (!is_init) /* at run-time slot->state can be changed by */ /* config/unconfig adapter */ *value = slot->state; else { - if (!slot->dn->child) + child_dn = slot->dn->child; + if (child_dn) + child_dev = rpaphp_find_pci_dev(child_dn); + + if (child_dev) + *value = CONFIGURED; + else if (!child_dn) dbg("%s: %s is not valid OFDT node\n", __FUNCTION__, slot->dn->full_name); - 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; } } - } else if (state == EMPTY) { - dbg("slot is empty\n"); - *value = state; } - exit: return rc; } /* 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 +168,12 @@ static void rpaphp_fixup_new_pci_devices(struct pci_bus *bus) */ if (list_empty(&dev->global_list)) { int i; + + /* Need to setup IOMMU tables */ + ppc_md.iommu_dev_setup(dev); - 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 +186,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) +static int rpaphp_pci_config_bridge(struct pci_dev *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_slot(struct device_node *dn, struct pci_bus *bus) { + struct device_node *eads_first_child = dn->child; + struct pci_dev *dev = NULL; 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); + + 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); - -/***************************************************************************** - rpaphp_pci_config_dn() will recursively configure all devices under the - given slot->dn and return the dn's pci_dev. - *****************************************************************************/ -static struct pci_dev * -rpaphp_pci_config_dn(struct device_node *dn, struct pci_bus *bus) +static int rpaphp_pci_config_bridge(struct pci_dev *dev) { - struct device_node *local; - struct pci_dev *dev; + u8 sec_busno; + struct pci_bus *child_bus; + struct pci_dev *child_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)) - return NULL; + 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); - return dev; + 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 int rpaphp_pci_config_bridge(struct pci_dev *dev, struct device_node *dn) +static void enable_eeh(struct device_node *dn) { - 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 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) +{ + 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 +329,73 @@ 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, *tmp; 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_safe (ln, tmp, &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,46 +411,105 @@ 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; } return 0; } -static int setup_pci_slot(struct slot *slot) +static int set_phb_slot_name(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); + struct device_node *dn; + struct pci_controller *phb; + struct pci_bus *bus; + + dn = slot->dn; + if (!dn) { return 1; } - - strcpy(slot->name, pci_name(slot->bridge)); + phb = dn->phb; + if (!phb) { + return 1; + } + bus = phb->bus; + if (!bus) { + return 1; + } + + sprintf(slot->name, "%04x:%02x:%02x.%x", pci_domain_nr(bus), + bus->number, 0, 0); + return 0; +} + +static int setup_pci_slot(struct slot *slot) +{ + int rc; + + if (slot->type == PHB) { + rc = set_phb_slot_name(slot); + if (rc) { + err("%s: failed to set phb slot name\n", __FUNCTION__); + goto exit_rc; + } + } else { + slot->bridge = rpaphp_find_bridge_pdev(slot); + if (!slot->bridge) { + /* slot being added doesn't have pci_dev yet */ + 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; + } + + if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) { + dbg("%s CONFIGURING pci adapter in slot[%s]\n", + __FUNCTION__, slot->name); + if (rpaphp_config_pci_adapter(slot)) { + err("%s: CONFIG pci adapter failed\n", __FUNCTION__); + goto exit_rc; + } + } else if (slot->hotplug_slot->info->adapter_status == CONFIGURED) { + if (init_slot_pci_funcs(slot)) { + err("%s: init_slot_pci_funcs failed\n", __FUNCTION__); + goto exit_rc; + } + + } else { + err("%s: slot[%s]'s adapter_status is NOT_VALID.\n", + __FUNCTION__, slot->name); + goto exit_rc; } - slot->dev.pci_dev = rpaphp_find_adapter_pdev(slot); - if (slot->dev.pci_dev) { - slot->state = CONFIGURED; + 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 +517,17 @@ int register_pci_slot(struct slot *slot) int rc = 1; slot->dev_type = PCI_DEV; + if ((slot->type == EMBEDDED) || (slot->type == PHB)) + slot->removable = 0; + else + slot->removable = 1; + 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 +541,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 +562,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);