diff options
Diffstat (limited to 'drivers/pci/hotplug')
-rw-r--r-- | drivers/pci/hotplug/Kconfig | 4 | ||||
-rw-r--r-- | drivers/pci/hotplug/Makefile | 4 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp.h | 1 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 5 | ||||
-rw-r--r-- | drivers/pci/hotplug/fakephp.c | 39 | ||||
-rw-r--r-- | drivers/pci/hotplug/ibmphp_core.c | 11 | ||||
-rw-r--r-- | drivers/pci/hotplug/pci_hotplug_core.c | 4 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp.h | 9 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 33 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 27 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 314 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_pci.c | 43 | ||||
-rw-r--r-- | drivers/pci/hotplug/rpaphp.h | 1 | ||||
-rw-r--r-- | drivers/pci/hotplug/rpaphp_pci.c | 14 | ||||
-rw-r--r-- | drivers/pci/hotplug/rpaphp_slot.c | 47 | ||||
-rw-r--r-- | drivers/pci/hotplug/shpchp_hpc.c | 2 |
16 files changed, 332 insertions, 226 deletions
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index a64449d489d6..2cdd8326f136 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -3,8 +3,8 @@ # menuconfig HOTPLUG_PCI - tristate "Support for PCI Hotplug (EXPERIMENTAL)" - depends on PCI && EXPERIMENTAL && HOTPLUG + tristate "Support for PCI Hotplug" + depends on PCI && HOTPLUG ---help--- Say Y here if you have a motherboard with a PCI Hotplug controller. This allows you to add and remove PCI cards while the machine is diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 34a1891191fd..9bdbe1a6688f 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -3,7 +3,6 @@ # obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o -obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o @@ -16,6 +15,9 @@ obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o +# Link this last so it doesn't claim devices that have a real hotplug driver +obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o + pci_hotplug-objs := pci_hotplug_core.o ifdef CONFIG_HOTPLUG_PCI_CPCI diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 1ef417cca2db..7a29164d4b32 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -113,7 +113,6 @@ struct acpiphp_slot { u8 device; /* pci device# */ u32 sun; /* ACPI _SUN (slot unique number) */ - u32 slotno; /* slot number relative to bridge */ u32 flags; /* see below */ }; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index ff1b1c71291a..cf22f9e01e00 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -102,7 +102,7 @@ static int is_ejectable(acpi_handle handle) } -/* callback routine to check the existence of ejectable slots */ +/* callback routine to check for the existence of ejectable slots */ static acpi_status is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv) { @@ -117,7 +117,7 @@ is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv) } } -/* callback routine to check for the existance of a pci dock device */ +/* callback routine to check for the existence of a pci dock device */ static acpi_status is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv) { @@ -1528,7 +1528,6 @@ check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); dbg("%s: re-enumerating slots under %s\n", __FUNCTION__, objname); - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); acpiphp_check_bridge(bridge); } return AE_OK ; diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index d7a293e3faf5..94b640146d44 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c @@ -39,6 +39,7 @@ #include <linux/init.h> #include <linux/string.h> #include <linux/slab.h> +#include <linux/workqueue.h> #include "../pci.h" #if !defined(MODULE) @@ -63,10 +64,16 @@ struct dummy_slot { struct list_head node; struct hotplug_slot *slot; struct pci_dev *dev; + struct work_struct remove_work; + unsigned long removed; }; static int debug; static LIST_HEAD(slot_list); +static struct workqueue_struct *dummyphp_wq; + +static void pci_rescan_worker(struct work_struct *work); +static DECLARE_WORK(pci_rescan_work, pci_rescan_worker); static int enable_slot (struct hotplug_slot *slot); static int disable_slot (struct hotplug_slot *slot); @@ -109,7 +116,7 @@ static int add_slot(struct pci_dev *dev) slot->name = &dev->dev.bus_id[0]; dbg("slot->name = %s\n", slot->name); - dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL); + dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL); if (!dslot) goto error_info; @@ -164,6 +171,14 @@ static void remove_slot(struct dummy_slot *dslot) err("Problem unregistering a slot %s\n", dslot->slot->name); } +/* called from the single-threaded workqueue handler to remove a slot */ +static void remove_slot_worker(struct work_struct *work) +{ + struct dummy_slot *dslot = + container_of(work, struct dummy_slot, remove_work); + remove_slot(dslot); +} + /** * pci_rescan_slot - Rescan slot * @temp: Device template. Should be set: bus and devfn. @@ -267,11 +282,17 @@ static inline void pci_rescan(void) { pci_rescan_buses(&pci_root_buses); } +/* called from the single-threaded workqueue handler to rescan all pci buses */ +static void pci_rescan_worker(struct work_struct *work) +{ + pci_rescan(); +} static int enable_slot(struct hotplug_slot *hotplug_slot) { /* mis-use enable_slot for rescanning of the pci bus */ - pci_rescan(); + cancel_work_sync(&pci_rescan_work); + queue_work(dummyphp_wq, &pci_rescan_work); return -ENODEV; } @@ -306,6 +327,10 @@ static int disable_slot(struct hotplug_slot *slot) err("Can't remove PCI devices with other PCI devices behind it yet.\n"); return -ENODEV; } + if (test_and_set_bit(0, &dslot->removed)) { + dbg("Slot already scheduled for removal\n"); + return -ENODEV; + } /* search for subfunctions and disable them first */ if (!(dslot->dev->devfn & 7)) { for (func = 1; func < 8; func++) { @@ -328,8 +353,9 @@ static int disable_slot(struct hotplug_slot *slot) /* remove the device from the pci core */ pci_remove_bus_device(dslot->dev); - /* blow away this sysfs entry and other parts. */ - remove_slot(dslot); + /* queue work item to blow away this sysfs entry and other parts. */ + INIT_WORK(&dslot->remove_work, remove_slot_worker); + queue_work(dummyphp_wq, &dslot->remove_work); return 0; } @@ -340,6 +366,7 @@ static void cleanup_slots (void) struct list_head *next; struct dummy_slot *dslot; + destroy_workqueue(dummyphp_wq); list_for_each_safe (tmp, next, &slot_list) { dslot = list_entry (tmp, struct dummy_slot, node); remove_slot(dslot); @@ -351,6 +378,10 @@ static int __init dummyphp_init(void) { info(DRIVER_DESC "\n"); + dummyphp_wq = create_singlethread_workqueue(MY_NAME); + if (!dummyphp_wq) + return -ENOMEM; + return pci_scan_buses(); } diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index a90c28d0c69d..87b6b8b280e6 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -761,10 +761,13 @@ static void ibm_unconfigure_device(struct pci_func *func) debug("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0); for (j = 0; j < 0x08; j++) { - temp = pci_find_slot(func->busno, (func->device << 3) | j); - if (temp) + temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j); + if (temp) { pci_remove_bus_device(temp); + pci_dev_put(temp); + } } + pci_dev_put(func->dev); } /* @@ -823,7 +826,7 @@ static int ibm_configure_device(struct pci_func *func) if (!(bus_structure_fixup(func->busno))) flag = 1; if (func->dev == NULL) - func->dev = pci_find_slot(func->busno, + func->dev = pci_get_bus_and_slot(func->busno, PCI_DEVFN(func->device, func->function)); if (func->dev == NULL) { @@ -836,7 +839,7 @@ static int ibm_configure_device(struct pci_func *func) if (num) pci_bus_add_devices(bus); - func->dev = pci_find_slot(func->busno, + func->dev = pci_get_bus_and_slot(func->busno, PCI_DEVFN(func->device, func->function)); if (func->dev == NULL) { err("ERROR... : pci_dev still NULL\n"); diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 47bb0e1ff3fa..dd59a050260f 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -137,7 +137,7 @@ static int get_##name (struct hotplug_slot *slot, type *value) \ int retval = 0; \ if (try_module_get(ops->owner)) { \ if (ops->get_##name) \ - retval = ops->get_##name (slot, value); \ + retval = ops->get_##name(slot, value); \ else \ *value = slot->info->name; \ module_put(ops->owner); \ @@ -625,7 +625,7 @@ int pci_hp_register (struct hotplug_slot *slot) if ((slot->info == NULL) || (slot->ops == NULL)) return -EINVAL; if (slot->release == NULL) { - dbg("Why are you trying to register a hotplug slot" + dbg("Why are you trying to register a hotplug slot " "without a proper release function?\n"); return -EINVAL; } diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 7959c222dc24..ca656b27a500 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -82,24 +82,18 @@ struct event_info { }; struct controller { - struct controller *next; struct mutex crit_sect; /* critical section mutex */ struct mutex ctrl_lock; /* controller lock */ int num_slots; /* Number of slots on ctlr */ int slot_num_inc; /* 1 or -1 */ struct pci_dev *pci_dev; struct list_head slot_list; - struct slot *slot; struct hpc_ops *hpc_ops; wait_queue_head_t queue; /* sleep & wake process */ - u8 bus; - u8 device; - u8 function; u8 slot_device_offset; u32 first_slot; /* First physical slot number */ /* PCIE only has 1 slot */ u8 slot_bus; /* Bus where the slots handled by this controller sit */ u8 ctrlcap; - u16 vendor_id; u8 cap_base; struct timer_list poll_timer; volatile int cmd_busy; @@ -161,6 +155,9 @@ extern int pciehp_configure_device(struct slot *p_slot); extern int pciehp_unconfigure_device(struct slot *p_slot); extern void pciehp_queue_pushbutton_work(struct work_struct *work); int pcie_init(struct controller *ctrl, struct pcie_device *dev); +int pciehp_enable_slot(struct slot *p_slot); +int pciehp_disable_slot(struct slot *p_slot); +int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev); static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device) { diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 6462ac3b405f..7f4836b8e71e 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -453,13 +453,9 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_ pci_set_drvdata(pdev, ctrl); - ctrl->bus = pdev->bus->number; /* ctrl bus */ - ctrl->slot_bus = pdev->subordinate->number; /* bus controlled by this HPC */ - - ctrl->device = PCI_SLOT(pdev->devfn); - ctrl->function = PCI_FUNC(pdev->devfn); - dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", __FUNCTION__, - ctrl->bus, ctrl->device, ctrl->function, pdev->irq); + dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", + __FUNCTION__, pdev->bus->number, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn), pdev->irq); /* Setup the slot information structures */ rc = init_slots(ctrl); @@ -471,6 +467,11 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_ t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */ + if (value) { + rc = pciehp_enable_slot(t_slot); + if (rc) /* -ENODEV: shouldn't happen, but deal with it */ + value = 0; + } if ((POWER_CTRL(ctrl->ctrlcap)) && !value) { rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/ if (rc) @@ -509,6 +510,24 @@ static int pciehp_suspend (struct pcie_device *dev, pm_message_t state) static int pciehp_resume (struct pcie_device *dev) { printk("%s ENTRY\n", __FUNCTION__); + if (pciehp_force) { + struct pci_dev *pdev = dev->port; + struct controller *ctrl = pci_get_drvdata(pdev); + struct slot *t_slot; + u8 status; + + /* reinitialize the chipset's event detection logic */ + pcie_init_hardware_part2(ctrl, dev); + + t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); + + /* Check if slot is occupied */ + t_slot->hpc_ops->get_adapter_status(t_slot, &status); + if (status) + pciehp_enable_slot(t_slot); + else + pciehp_disable_slot(t_slot); + } return 0; } #endif diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index f1e0966cee95..b23061c56115 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -37,8 +37,6 @@ #include "pciehp.h" static void interrupt_event_handler(struct work_struct *work); -static int pciehp_enable_slot(struct slot *p_slot); -static int pciehp_disable_slot(struct slot *p_slot); static int queue_interrupt_event(struct slot *p_slot, u32 event_type) { @@ -197,12 +195,6 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) __FUNCTION__); return; } - /* - * After turning power off, we must wait for at least - * 1 second before taking any action that relies on - * power having been removed from the slot/adapter. - */ - msleep(1000); } } @@ -215,15 +207,12 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) */ static int board_added(struct slot *p_slot) { - u8 hp_slot; int retval = 0; struct controller *ctrl = p_slot->ctrl; - hp_slot = p_slot->device - ctrl->slot_device_offset; - dbg("%s: slot device, slot offset, hp slot = %d, %d ,%d\n", __FUNCTION__, p_slot->device, - ctrl->slot_device_offset, hp_slot); + ctrl->slot_device_offset, p_slot->hp_slot); if (POWER_CTRL(ctrl->ctrlcap)) { /* Power on slot */ @@ -281,8 +270,6 @@ err_exit: */ static int remove_board(struct slot *p_slot) { - u8 device; - u8 hp_slot; int retval = 0; struct controller *ctrl = p_slot->ctrl; @@ -290,11 +277,7 @@ static int remove_board(struct slot *p_slot) if (retval) return retval; - device = p_slot->device; - hp_slot = p_slot->device - ctrl->slot_device_offset; - p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); - - dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); + dbg("In %s, hp_slot = %d\n", __FUNCTION__, p_slot->hp_slot); if (POWER_CTRL(ctrl->ctrlcap)) { /* power off slot */ @@ -621,12 +604,6 @@ int pciehp_disable_slot(struct slot *p_slot) mutex_unlock(&p_slot->ctrl->crit_sect); return -EINVAL; } - /* - * After turning power off, we must wait for at least - * 1 second before taking any action that relies on - * power having been removed from the slot/adapter. - */ - msleep(1000); } ret = remove_board(p_slot); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 06d025b8b13f..6eba9b2cfb90 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -636,15 +636,57 @@ static int hpc_power_on_slot(struct slot * slot) return retval; } +static inline int pcie_mask_bad_dllp(struct controller *ctrl) +{ + struct pci_dev *dev = ctrl->pci_dev; + int pos; + u32 reg; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + if (!pos) + return 0; + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®); + if (reg & PCI_ERR_COR_BAD_DLLP) + return 0; + reg |= PCI_ERR_COR_BAD_DLLP; + pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg); + return 1; +} + +static inline void pcie_unmask_bad_dllp(struct controller *ctrl) +{ + struct pci_dev *dev = ctrl->pci_dev; + u32 reg; + int pos; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + if (!pos) + return; + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®); + if (!(reg & PCI_ERR_COR_BAD_DLLP)) + return; + reg &= ~PCI_ERR_COR_BAD_DLLP; + pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg); +} + static int hpc_power_off_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; u16 cmd_mask; int retval = 0; + int changed; dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); + /* + * Set Bad DLLP Mask bit in Correctable Error Mask + * Register. This is the workaround against Bad DLLP error + * that sometimes happens during turning power off the slot + * which conforms to PCI Express 1.0a spec. + */ + changed = pcie_mask_bad_dllp(ctrl); + slot_cmd = POWER_OFF; cmd_mask = PWR_CTRL; /* @@ -674,6 +716,16 @@ static int hpc_power_off_slot(struct slot * slot) dbg("%s: SLOTCTRL %x write cmd %x\n", __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); + + if (changed) + pcie_unmask_bad_dllp(ctrl); + return retval; } @@ -1067,13 +1119,143 @@ int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev) } #endif -int pcie_init(struct controller * ctrl, struct pcie_device *dev) +static int pcie_init_hardware_part1(struct controller *ctrl, + struct pcie_device *dev) +{ + int rc; + u16 temp_word; + u32 slot_cap; + u16 slot_status; + + rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); + if (rc) { + err("%s: Cannot read SLOTCAP register\n", __FUNCTION__); + return -1; + } + + /* Mask Hot-plug Interrupt Enable */ + rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); + if (rc) { + err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); + return -1; + } + + dbg("%s: SLOTCTRL %x value read %x\n", + __FUNCTION__, ctrl->cap_base + SLOTCTRL, temp_word); + temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | + 0x00; + + rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); + if (rc) { + err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); + return -1; + } + + rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + if (rc) { + err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); + return -1; + } + + temp_word = 0x1F; /* Clear all events */ + rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); + if (rc) { + err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); + return -1; + } + return 0; +} + +int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev) { int rc; u16 temp_word; - u16 cap_reg; u16 intr_enable = 0; u32 slot_cap; + u16 slot_status; + + rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); + if (rc) { + err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); + goto abort; + } + + intr_enable = intr_enable | PRSN_DETECT_ENABLE; + + rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); + if (rc) { + err("%s: Cannot read SLOTCAP register\n", __FUNCTION__); + goto abort; + } + + if (ATTN_BUTTN(slot_cap)) + intr_enable = intr_enable | ATTN_BUTTN_ENABLE; + + if (POWER_CTRL(slot_cap)) + intr_enable = intr_enable | PWR_FAULT_DETECT_ENABLE; + + if (MRL_SENS(slot_cap)) + intr_enable = intr_enable | MRL_DETECT_ENABLE; + + temp_word = (temp_word & ~intr_enable) | intr_enable; + + if (pciehp_poll_mode) { + temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0; + } else { + temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; + } + + /* + * Unmask Hot-plug Interrupt Enable for the interrupt + * notification mechanism case. + */ + rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); + if (rc) { + err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); + goto abort; + } + rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + if (rc) { + err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); + goto abort_disable_intr; + } + + temp_word = 0x1F; /* Clear all events */ + rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); + if (rc) { + err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); + goto abort_disable_intr; + } + + if (pciehp_force) { + dbg("Bypassing BIOS check for pciehp use on %s\n", + pci_name(ctrl->pci_dev)); + } else { + rc = pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev); + if (rc) + goto abort_disable_intr; + } + + return 0; + + /* We end up here for the many possible ways to fail this API. */ +abort_disable_intr: + rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); + if (!rc) { + temp_word &= ~(intr_enable | HP_INTR_ENABLE); + rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); + } + if (rc) + err("%s : disabling interrupts failed\n", __FUNCTION__); +abort: + return -1; +} + +int pcie_init(struct controller *ctrl, struct pcie_device *dev) +{ + int rc; + u16 cap_reg; + u32 slot_cap; int cap_base; u16 slot_status, slot_ctrl; struct pci_dev *pdev; @@ -1084,9 +1266,10 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) dbg("%s: hotplug controller vendor id 0x%x device id 0x%x\n", __FUNCTION__, pdev->vendor, pdev->device); - if ((cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP)) == 0) { + cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (cap_base == 0) { dbg("%s: Can't find PCI_CAP_ID_EXP (0x10)\n", __FUNCTION__); - goto abort_free_ctlr; + goto abort; } ctrl->cap_base = cap_base; @@ -1096,7 +1279,7 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) rc = pciehp_readw(ctrl, CAPREG, &cap_reg); if (rc) { err("%s: Cannot read CAPREG register\n", __FUNCTION__); - goto abort_free_ctlr; + goto abort; } dbg("%s: CAPREG offset %x cap_reg %x\n", __FUNCTION__, ctrl->cap_base + CAPREG, cap_reg); @@ -1106,26 +1289,26 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) && ((cap_reg & DEV_PORT_TYPE) != 0x0060))) { dbg("%s : This is not a root port or the port is not " "connected to a slot\n", __FUNCTION__); - goto abort_free_ctlr; + goto abort; } rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); if (rc) { err("%s: Cannot read SLOTCAP register\n", __FUNCTION__); - goto abort_free_ctlr; + goto abort; } dbg("%s: SLOTCAP offset %x slot_cap %x\n", __FUNCTION__, ctrl->cap_base + SLOTCAP, slot_cap); if (!(slot_cap & HP_CAP)) { dbg("%s : This slot is not hot-plug capable\n", __FUNCTION__); - goto abort_free_ctlr; + goto abort; } /* For debugging purpose */ rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); if (rc) { err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); - goto abort_free_ctlr; + goto abort; } dbg("%s: SLOTSTATUS offset %x slot_status %x\n", __FUNCTION__, ctrl->cap_base + SLOTSTATUS, slot_status); @@ -1133,7 +1316,7 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); if (rc) { err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); - goto abort_free_ctlr; + goto abort; } dbg("%s: SLOTCTRL offset %x slot_ctrl %x\n", __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_ctrl); @@ -1161,36 +1344,9 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) ctrl->first_slot = slot_cap >> 19; ctrl->ctrlcap = slot_cap & 0x0000007f; - /* Mask Hot-plug Interrupt Enable */ - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); - if (rc) { - err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); - goto abort_free_ctlr; - } - - dbg("%s: SLOTCTRL %x value read %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, temp_word); - temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | - 0x00; - - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); - if (rc) { - err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); - goto abort_free_ctlr; - } - - rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); - if (rc) { - err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); - goto abort_free_ctlr; - } - - temp_word = 0x1F; /* Clear all events */ - rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); - if (rc) { - err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); - goto abort_free_ctlr; - } + rc = pcie_init_hardware_part1(ctrl, dev); + if (rc) + goto abort; if (pciehp_poll_mode) { /* Install interrupt polling timer. Start with 10 sec delay */ @@ -1206,7 +1362,7 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) if (rc) { err("Can't get irq %d for the hotplug controller\n", ctrl->pci_dev->irq); - goto abort_free_ctlr; + goto abort; } } dbg("pciehp ctrl b:d:f:irq=0x%x:%x:%x:%x\n", pdev->bus->number, @@ -1224,82 +1380,16 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev) } } - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); - if (rc) { - err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); - goto abort_free_irq; + rc = pcie_init_hardware_part2(ctrl, dev); + if (rc == 0) { + ctrl->hpc_ops = &pciehp_hpc_ops; + return 0; } - - intr_enable = intr_enable | PRSN_DETECT_ENABLE; - - if (ATTN_BUTTN(slot_cap)) - intr_enable = intr_enable | ATTN_BUTTN_ENABLE; - - if (POWER_CTRL(slot_cap)) - intr_enable = intr_enable | PWR_FAULT_DETECT_ENABLE; - - if (MRL_SENS(slot_cap)) - intr_enable = intr_enable | MRL_DETECT_ENABLE; - - temp_word = (temp_word & ~intr_enable) | intr_enable; - - if (pciehp_poll_mode) { - temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0; - } else { - temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; - } - - /* - * Unmask Hot-plug Interrupt Enable for the interrupt - * notification mechanism case. - */ - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); - if (rc) { - err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); - goto abort_free_irq; - } - rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); - if (rc) { - err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); - goto abort_disable_intr; - } - - temp_word = 0x1F; /* Clear all events */ - rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); - if (rc) { - err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); - goto abort_disable_intr; - } - - if (pciehp_force) { - dbg("Bypassing BIOS check for pciehp use on %s\n", - pci_name(ctrl->pci_dev)); - } else { - rc = pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev); - if (rc) - goto abort_disable_intr; - } - - ctrl->hpc_ops = &pciehp_hpc_ops; - - return 0; - - /* We end up here for the many possible ways to fail this API. */ -abort_disable_intr: - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); - if (!rc) { - temp_word &= ~(intr_enable | HP_INTR_ENABLE); - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); - } - if (rc) - err("%s : disabling interrupts failed\n", __FUNCTION__); - abort_free_irq: if (pciehp_poll_mode) del_timer_sync(&ctrl->poll_timer); else free_irq(ctrl->pci_dev->irq, ctrl); - -abort_free_ctlr: +abort: return -1; } diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index c424aded13fb..dd50713966d1 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -105,12 +105,7 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) } /* Find Advanced Error Reporting Enhanced Capability */ - pos = 256; - do { - pci_read_config_dword(dev, pos, ®32); - if (PCI_EXT_CAP_ID(reg32) == PCI_EXT_CAP_ID_ERR) - break; - } while ((pos = PCI_EXT_CAP_NEXT(reg32))); + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (!pos) return; @@ -248,11 +243,15 @@ int pciehp_unconfigure_device(struct slot *p_slot) u8 bctl = 0; u8 presence = 0; struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate; + u16 command; dbg("%s: bus/dev = %x/%x\n", __FUNCTION__, p_slot->bus, p_slot->device); + ret = p_slot->hpc_ops->get_adapter_status(p_slot, &presence); + if (ret) + presence = 0; - for (j=0; j<8 ; j++) { + for (j = 0; j < 8; j++) { struct pci_dev* temp = pci_get_slot(parent, (p_slot->device << 3) | j); if (!temp) @@ -263,21 +262,26 @@ int pciehp_unconfigure_device(struct slot *p_slot) pci_dev_put(temp); continue; } - if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - ret = p_slot->hpc_ops->get_adapter_status(p_slot, - &presence); - if (!ret && presence) { - pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, - &bctl); - if (bctl & PCI_BRIDGE_CTL_VGA) { - err("Cannot remove display device %s\n", - pci_name(temp)); - pci_dev_put(temp); - continue; - } + if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) { + pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl); + if (bctl & PCI_BRIDGE_CTL_VGA) { + err("Cannot remove display device %s\n", + pci_name(temp)); + pci_dev_put(temp); + continue; } } pci_remove_bus_device(temp); + /* + * Ensure that no new Requests will be generated from + * the device. + */ + if (presence) { + pci_read_config_word(temp, PCI_COMMAND, &command); + command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR); + command |= PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(temp, PCI_COMMAND, command); + } pci_dev_put(temp); } /* @@ -288,4 +292,3 @@ int pciehp_unconfigure_device(struct slot *p_slot) return rc; } - diff --git a/drivers/pci/hotplug/rpaphp.h b/drivers/pci/hotplug/rpaphp.h index c822a779653f..7d5921b1ee78 100644 --- a/drivers/pci/hotplug/rpaphp.h +++ b/drivers/pci/hotplug/rpaphp.h @@ -74,7 +74,6 @@ struct slot { u32 type; u32 power_domain; char *name; - char *location; struct device_node *dn; struct pci_bus *bus; struct list_head *pci_devs; diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c index 0de84533cd80..6571e9b4c2ec 100644 --- a/drivers/pci/hotplug/rpaphp_pci.c +++ b/drivers/pci/hotplug/rpaphp_pci.c @@ -64,19 +64,6 @@ int rpaphp_get_sensor_state(struct slot *slot, int *state) return rc; } -static void set_slot_name(struct slot *slot) -{ - struct pci_bus *bus = slot->bus; - struct pci_dev *bridge; - - bridge = bus->self; - if (bridge) - strcpy(slot->name, pci_name(bridge)); - else - sprintf(slot->name, "%04x:%02x:00.0", pci_domain_nr(bus), - bus->number); -} - /** * rpaphp_enable_slot - record slot state, config pci device * @slot: target &slot @@ -115,7 +102,6 @@ int rpaphp_enable_slot(struct slot *slot) info->adapter_status = EMPTY; slot->bus = bus; slot->pci_devs = &bus->devices; - set_slot_name(slot); /* if there's an adapter in the slot, go add the pci devices */ if (state == PRESENT) { diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c index d4ee8723fcb3..8ad3debb3794 100644 --- a/drivers/pci/hotplug/rpaphp_slot.c +++ b/drivers/pci/hotplug/rpaphp_slot.c @@ -33,23 +33,31 @@ #include <asm/rtas.h> #include "rpaphp.h" -static ssize_t location_read_file (struct hotplug_slot *php_slot, char *buf) +static ssize_t address_read_file (struct hotplug_slot *php_slot, char *buf) { - char *value; - int retval = -ENOENT; + int retval; struct slot *slot = (struct slot *)php_slot->private; + struct pci_bus *bus; if (!slot) - return retval; + return -ENOENT; + + bus = slot->bus; + if (!bus) + return -ENOENT; + + if (bus->self) + retval = sprintf(buf, pci_name(bus->self)); + else + retval = sprintf(buf, "%04x:%02x:00.0", + pci_domain_nr(bus), bus->number); - value = slot->location; - retval = sprintf (buf, "%s\n", value); return retval; } -static struct hotplug_slot_attribute php_attr_location = { - .attr = {.name = "phy_location", .mode = S_IFREG | S_IRUGO}, - .show = location_read_file, +static struct hotplug_slot_attribute php_attr_address = { + .attr = {.name = "address", .mode = S_IFREG | S_IRUGO}, + .show = address_read_file, }; /* free up the memory used by a slot */ @@ -64,7 +72,6 @@ void dealloc_slot_struct(struct slot *slot) kfree(slot->hotplug_slot->info); kfree(slot->hotplug_slot->name); kfree(slot->hotplug_slot); - kfree(slot->location); kfree(slot); } @@ -83,16 +90,13 @@ struct slot *alloc_slot_struct(struct device_node *dn, GFP_KERNEL); if (!slot->hotplug_slot->info) goto error_hpslot; - slot->hotplug_slot->name = kmalloc(BUS_ID_SIZE + 1, GFP_KERNEL); + slot->hotplug_slot->name = kmalloc(strlen(drc_name) + 1, GFP_KERNEL); if (!slot->hotplug_slot->name) goto error_info; - slot->location = kmalloc(strlen(drc_name) + 1, GFP_KERNEL); - if (!slot->location) - goto error_name; slot->name = slot->hotplug_slot->name; + strcpy(slot->name, drc_name); slot->dn = dn; slot->index = drc_index; - strcpy(slot->location, drc_name); slot->power_domain = power_domain; slot->hotplug_slot->private = slot; slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops; @@ -100,8 +104,6 @@ struct slot *alloc_slot_struct(struct device_node *dn, return (slot); -error_name: - kfree(slot->hotplug_slot->name); error_info: kfree(slot->hotplug_slot->info); error_hpslot: @@ -133,8 +135,8 @@ int rpaphp_deregister_slot(struct slot *slot) list_del(&slot->rpaphp_slot_list); - /* remove "phy_location" file */ - sysfs_remove_file(&php_slot->kobj, &php_attr_location.attr); + /* remove "address" file */ + sysfs_remove_file(&php_slot->kobj, &php_attr_address.attr); retval = pci_hp_deregister(php_slot); if (retval) @@ -166,8 +168,8 @@ int rpaphp_register_slot(struct slot *slot) return retval; } - /* create "phy_location" file */ - retval = sysfs_create_file(&php_slot->kobj, &php_attr_location.attr); + /* create "address" file */ + retval = sysfs_create_file(&php_slot->kobj, &php_attr_address.attr); if (retval) { err("sysfs_create_file failed with error %d\n", retval); goto sysfs_fail; @@ -175,8 +177,7 @@ int rpaphp_register_slot(struct slot *slot) /* add slot to our internal list */ list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head); - info("Slot [%s](PCI location=%s) registered\n", slot->name, - slot->location); + info("Slot [%s] registered\n", slot->name); return 0; sysfs_fail: diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c index 5183a45d45b5..e8aa138128ce 100644 --- a/drivers/pci/hotplug/shpchp_hpc.c +++ b/drivers/pci/hotplug/shpchp_hpc.c @@ -597,7 +597,7 @@ static void hpc_release_ctlr(struct controller *ctrl) cleanup_slots(ctrl); /* - * Mask SERR and System Interrut generation + * Mask SERR and System Interrupt generation */ serr_int = shpc_readl(ctrl, SERR_INTR_ENABLE); serr_int |= (GLOBAL_INTR_MASK | GLOBAL_SERR_MASK | |