diff options
Diffstat (limited to 'drivers/pci/hotplug')
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 138 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_acpi.c | 1 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 3 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 27 | ||||
-rw-r--r-- | drivers/pci/hotplug/pcihp_slot.c | 1 | ||||
-rw-r--r-- | drivers/pci/hotplug/rpadlpar_core.c | 1 | ||||
-rw-r--r-- | drivers/pci/hotplug/shpchp_core.c | 4 | ||||
-rw-r--r-- | drivers/pci/hotplug/shpchp_hpc.c | 4 |
8 files changed, 143 insertions, 36 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 220285760b68..fce1c54a0c8d 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -48,6 +48,7 @@ #include <linux/pci-acpi.h> #include <linux/mutex.h> #include <linux/slab.h> +#include <linux/acpi.h> #include "../pci.h" #include "acpiphp.h" @@ -458,8 +459,17 @@ static int add_bridge(acpi_handle handle) { acpi_status status; unsigned long long tmp; + struct acpi_pci_root *root; acpi_handle dummy_handle; + /* + * We shouldn't use this bridge if PCIe native hotplug control has been + * granted by the BIOS for it. + */ + root = acpi_pci_find_root(handle); + if (root && (root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)) + return -ENODEV; + /* if the bridge doesn't have _STA, we assume it is always there */ status = acpi_get_handle(handle, "_STA", &dummy_handle); if (ACPI_SUCCESS(status)) { @@ -1149,15 +1159,35 @@ check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK ; } -/** - * handle_hotplug_event_bridge - handle ACPI event on bridges - * @handle: Notify()'ed acpi_handle - * @type: Notify code - * @context: pointer to acpiphp_bridge structure - * - * Handles ACPI event notification on {host,p2p} bridges. - */ -static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *context) +struct acpiphp_hp_work { + struct work_struct work; + acpi_handle handle; + u32 type; + void *context; +}; + +static void alloc_acpiphp_hp_work(acpi_handle handle, u32 type, + void *context, + void (*func)(struct work_struct *work)) +{ + struct acpiphp_hp_work *hp_work; + int ret; + + hp_work = kmalloc(sizeof(*hp_work), GFP_KERNEL); + if (!hp_work) + return; + + hp_work->handle = handle; + hp_work->type = type; + hp_work->context = context; + + INIT_WORK(&hp_work->work, func); + ret = queue_work(kacpi_hotplug_wq, &hp_work->work); + if (!ret) + kfree(hp_work); +} + +static void _handle_hotplug_event_bridge(struct work_struct *work) { struct acpiphp_bridge *bridge; char objname[64]; @@ -1165,11 +1195,18 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont .pointer = objname }; struct acpi_device *device; int num_sub_bridges = 0; + struct acpiphp_hp_work *hp_work; + acpi_handle handle; + u32 type; + + hp_work = container_of(work, struct acpiphp_hp_work, work); + handle = hp_work->handle; + type = hp_work->type; if (acpi_bus_get_device(handle, &device)) { /* This bridge must have just been physically inserted */ handle_bridge_insertion(handle, type); - return; + goto out; } bridge = acpiphp_handle_to_bridge(handle); @@ -1180,7 +1217,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont if (!bridge && !num_sub_bridges) { err("cannot get bridge info\n"); - return; + goto out; } acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); @@ -1241,22 +1278,49 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); break; } + +out: + kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ } /** - * handle_hotplug_event_func - handle ACPI event on functions (i.e. slots) + * handle_hotplug_event_bridge - handle ACPI event on bridges * @handle: Notify()'ed acpi_handle * @type: Notify code - * @context: pointer to acpiphp_func structure + * @context: pointer to acpiphp_bridge structure * - * Handles ACPI event notification on slots. + * Handles ACPI event notification on {host,p2p} bridges. */ -static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context) +static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, + void *context) +{ + /* + * Currently the code adds all hotplug events to the kacpid_wq + * queue when it should add hotplug events to the kacpi_hotplug_wq. + * The proper way to fix this is to reorganize the code so that + * drivers (dock, etc.) do not call acpi_os_execute(), etc. + * For now just re-add this work to the kacpi_hotplug_wq so we + * don't deadlock on hotplug actions. + */ + alloc_acpiphp_hp_work(handle, type, context, + _handle_hotplug_event_bridge); +} + +static void _handle_hotplug_event_func(struct work_struct *work) { struct acpiphp_func *func; char objname[64]; struct acpi_buffer buffer = { .length = sizeof(objname), .pointer = objname }; + struct acpiphp_hp_work *hp_work; + acpi_handle handle; + u32 type; + void *context; + + hp_work = container_of(work, struct acpiphp_hp_work, work); + handle = hp_work->handle; + type = hp_work->type; + context = hp_work->context; acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); @@ -1291,19 +1355,53 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *contex warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); break; } + + kfree(hp_work); /* allocated in handle_hotplug_event_func */ } +/** + * handle_hotplug_event_func - handle ACPI event on functions (i.e. slots) + * @handle: Notify()'ed acpi_handle + * @type: Notify code + * @context: pointer to acpiphp_func structure + * + * Handles ACPI event notification on slots. + */ +static void handle_hotplug_event_func(acpi_handle handle, u32 type, + void *context) +{ + /* + * Currently the code adds all hotplug events to the kacpid_wq + * queue when it should add hotplug events to the kacpi_hotplug_wq. + * The proper way to fix this is to reorganize the code so that + * drivers (dock, etc.) do not call acpi_os_execute(), etc. + * For now just re-add this work to the kacpi_hotplug_wq so we + * don't deadlock on hotplug actions. + */ + alloc_acpiphp_hp_work(handle, type, context, + _handle_hotplug_event_func); +} static acpi_status find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) { + struct acpi_pci_root *root; int *count = (int *)context; - if (acpi_is_root_bridge(handle)) { - acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - handle_hotplug_event_bridge, NULL); - (*count)++; - } + if (!acpi_is_root_bridge(handle)) + return AE_OK; + + root = acpi_pci_find_root(handle); + if (!root) + return AE_OK; + + if (root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL) + return AE_OK; + + (*count)++; + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_bridge, NULL); + return AE_OK ; } diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index 5f7226223a62..376d70d17176 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -27,6 +27,7 @@ #include <linux/pci.h> #include <linux/pci_hotplug.h> #include <linux/slab.h> +#include <linux/module.h> #include "pciehp.h" #define PCIEHP_DETECT_PCIE (0) diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 1e9c9aacc3a6..085dbb5fc168 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -213,9 +213,6 @@ static int board_added(struct slot *p_slot) goto err_exit; } - /* Wait for 1 second after checking link training status */ - msleep(1000); - /* Check for a power fault */ if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) { ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot)); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 96dc4734e4af..7b1414810ae3 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -280,6 +280,14 @@ int pciehp_check_link_status(struct controller *ctrl) else msleep(1000); + /* + * Need to wait for 1000 ms after Data Link Layer Link Active + * (DLLLA) bit reads 1b before sending configuration request. + * We need it before checking Link Training (LT) bit becuase + * LT is still set even after DLLLA bit is set on some platform. + */ + msleep(1000); + retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); if (retval) { ctrl_err(ctrl, "Cannot read LNKSTATUS register\n"); @@ -294,6 +302,16 @@ int pciehp_check_link_status(struct controller *ctrl) return retval; } + /* + * If the port supports Link speeds greater than 5.0 GT/s, we + * must wait for 100 ms after Link training completes before + * sending configuration request. + */ + if (ctrl->pcie->port->subordinate->max_bus_speed > PCIE_SPEED_5_0GT) + msleep(100); + + pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); + return retval; } @@ -484,7 +502,6 @@ int pciehp_power_on_slot(struct slot * slot) u16 slot_cmd; u16 cmd_mask; u16 slot_status; - u16 lnk_status; int retval = 0; /* Clear sticky power-fault bit from previous power failures */ @@ -516,14 +533,6 @@ int pciehp_power_on_slot(struct slot * slot) ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); - retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read LNKSTA register\n", - __func__); - return retval; - } - pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); - return retval; } diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c index 3ffd9c1acc0a..8c05a18c9770 100644 --- a/drivers/pci/hotplug/pcihp_slot.c +++ b/drivers/pci/hotplug/pcihp_slot.c @@ -24,6 +24,7 @@ */ #include <linux/pci.h> +#include <linux/export.h> #include <linux/pci_hotplug.h> static struct hpp_type0 pci_default_type0 = { diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index 1d002b1c2bf4..c56a9413e1af 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -18,6 +18,7 @@ #undef DEBUG #include <linux/init.h> +#include <linux/module.h> #include <linux/pci.h> #include <linux/string.h> #include <linux/vmalloc.h> diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index aca972bbfb4c..dd7e0c51a33e 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -278,8 +278,8 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) static int is_shpc_capable(struct pci_dev *dev) { - if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device == - PCI_DEVICE_ID_AMD_GOLAM_7450)) + if (dev->vendor == PCI_VENDOR_ID_AMD && + dev->device == PCI_DEVICE_ID_AMD_GOLAM_7450) return 1; if (!pci_find_capability(dev, PCI_CAP_ID_SHPC)) return 0; diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c index 36547f0ce305..75ba2311b54f 100644 --- a/drivers/pci/hotplug/shpchp_hpc.c +++ b/drivers/pci/hotplug/shpchp_hpc.c @@ -944,8 +944,8 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev) ctrl->pci_dev = pdev; /* pci_dev of the P2P bridge */ ctrl_dbg(ctrl, "Hotplug Controller:\n"); - if ((pdev->vendor == PCI_VENDOR_ID_AMD) || (pdev->device == - PCI_DEVICE_ID_AMD_GOLAM_7450)) { + if (pdev->vendor == PCI_VENDOR_ID_AMD && + pdev->device == PCI_DEVICE_ID_AMD_GOLAM_7450) { /* amd shpc driver doesn't use Base Offset; assume 0 */ ctrl->mmio_base = pci_resource_start(pdev, 0); ctrl->mmio_size = pci_resource_len(pdev, 0); |