diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-02-02 14:29:33 +1100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-02-02 14:29:33 +1100 |
commit | 215e871aaa3d94540121a3809d80d0c5e5686e4f (patch) | |
tree | 0ed6469c5ad04db8cfa0edb58c676d5155df20cd /drivers/pci | |
parent | b6cf160c4b788a31f6a4017a469b956ca77febf4 (diff) | |
parent | fd7d1ced29e5beb88c9068801da7a362606d8273 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/pci-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/pci-2.6: (64 commits)
PCI: make pci_bus a struct device
PCI: fix codingstyle issues in include/linux/pci.h
PCI: fix codingstyle issues in drivers/pci/pci.h
PCI: PCIE ASPM support
PCI: Fix fakephp deadlock
PCI: modify SB700 SATA MSI quirk
PCI: Run ACPI _OSC method on root bridges only
PCI ACPI: AER driver should only register PCIe devices with _OSC
PCI ACPI: Added a function to register _OSC with only PCIe devices.
PCI: constify function pointer tables
PCI: Convert drivers/pci/proc.c to use unlocked_ioctl
pciehp: block new requests from the device before power off
pciehp: workaround against Bad DLLP during power off
pciehp: wait for 1000ms before LED operation after power off
PCI: Remove pci_enable_device_bars() from documentation
PCI: Remove pci_enable_device_bars()
PCI: Remove users of pci_enable_device_bars()
PCI: Add pci_enable_device_{io,mem} intefaces
PCI: avoid save the same type of cap multiple times
PCI: correctly initialize a structure for pcie_save_pcix_state()
...
Diffstat (limited to 'drivers/pci')
38 files changed, 1714 insertions, 635 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 9e5ea074ad2..ef5a6a245f5 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -108,6 +108,7 @@ int pci_bus_add_device(struct pci_dev *dev) void pci_bus_add_devices(struct pci_bus *bus) { struct pci_dev *dev; + struct pci_bus *child_bus; int retval; list_for_each_entry(dev, &bus->devices, bus_list) { @@ -138,11 +139,19 @@ void pci_bus_add_devices(struct pci_bus *bus) up_write(&pci_bus_sem); } pci_bus_add_devices(dev->subordinate); - retval = sysfs_create_link(&dev->subordinate->class_dev.kobj, - &dev->dev.kobj, "bridge"); + + /* register the bus with sysfs as the parent is now + * properly registered. */ + child_bus = dev->subordinate; + child_bus->dev.parent = child_bus->bridge; + retval = device_register(&child_bus->dev); + if (!retval) + retval = device_create_file(&child_bus->dev, + &dev_attr_cpuaffinity); if (retval) - dev_err(&dev->dev, "Error creating sysfs " - "bridge symlink, continuing...\n"); + dev_err(&dev->dev, "Error registering pci_bus" + " device bridge symlink," + " continuing...\n"); } } } @@ -204,7 +213,6 @@ void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *), } up_read(&pci_bus_sem); } -EXPORT_SYMBOL_GPL(pci_walk_bus); EXPORT_SYMBOL(pci_bus_alloc_resource); EXPORT_SYMBOL_GPL(pci_bus_add_device); diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 5dfdfdac92e..91b2dc956be 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -25,6 +25,7 @@ #include <linux/pci.h> #include <linux/dmar.h> +#include "iova.h" #undef PREFIX #define PREFIX "DMAR:" @@ -263,8 +264,8 @@ parse_dmar_table(void) if (!dmar) return -ENODEV; - if (!dmar->width) { - printk (KERN_WARNING PREFIX "Zero: Invalid DMAR haw\n"); + if (dmar->width < PAGE_SHIFT_4K - 1) { + printk(KERN_WARNING PREFIX "Invalid DMAR haw\n"); return -EINVAL; } @@ -301,11 +302,24 @@ parse_dmar_table(void) int __init dmar_table_init(void) { - parse_dmar_table(); + int ret; + + ret = parse_dmar_table(); + if (ret) { + printk(KERN_INFO PREFIX "parse DMAR table failure.\n"); + return ret; + } + if (list_empty(&dmar_drhd_units)) { printk(KERN_INFO PREFIX "No DMAR devices found\n"); return -ENODEV; } + + if (list_empty(&dmar_rmrr_units)) { + printk(KERN_INFO PREFIX "No RMRR found\n"); + return -ENODEV; + } + return 0; } diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index a64449d489d..2cdd8326f13 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 34a1891191f..9bdbe1a6688 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 1ef417cca2d..7a29164d4b3 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 ff1b1c71291..cf22f9e01e0 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 d7a293e3faf..94b640146d4 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 a90c28d0c69..87b6b8b280e 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 47bb0e1ff3f..dd59a050260 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 7959c222dc2..ca656b27a50 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 6462ac3b405..7f4836b8e71 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 f1e0966cee9..b23061c5611 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 06d025b8b13..6eba9b2cfb9 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) +{ |