diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2018-08-15 15:58:52 -0400 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2018-08-15 15:58:52 -0400 |
| commit | c0638a455382e01e42bf66d8d41e4b703f1550a5 (patch) | |
| tree | 87e95b7eec0ea23bb23fc385d3b83333532fcd3d /drivers/pci/hotplug | |
| parent | a8bcb5e5966ccbd99a6d06cf69ada7f95416aabc (diff) | |
| parent | 4e6a13356f1c1dc27ff48ff35576a478d73f8713 (diff) | |
Merge branch 'pci/hotplug'
- Simplify SHPC existence/permission checks (Bjorn Helgaas)
- Remove hotplug sample skeleton driver (Lukas Wunner)
- Convert pciehp to threaded IRQ handling (Lukas Wunner)
- Improve pciehp tolerance of missed events and initially unstable links
(Lukas Wunner)
- Clear spurious pciehp events on resume (Lukas Wunner)
- Add pciehp runtime PM support, including for Thunderbolt controllers
(Lukas Wunner)
- Support interrupts from pciehp bridges in D3hot (Lukas Wunner)
* pci/hotplug:
PCI: pciehp: Deduplicate presence check on probe & resume
PCI: pciehp: Avoid implicit fallthroughs in switch statements
PCI: Whitelist Thunderbolt ports for runtime D3
PCI: Whitelist native hotplug ports for runtime D3
PCI: sysfs: Resume to D0 on function reset
PCI: pciehp: Resume parent to D0 on config space access
PCI: pciehp: Resume to D0 on enable/disable
PCI: pciehp: Support interrupts sent from D3hot
PCI: pciehp: Obey compulsory command delay after resume
PCI: pciehp: Clear spurious events earlier on resume
PCI: portdrv: Deduplicate PM callback iterator
PCI: pciehp: Avoid slot access during reset
PCI: pciehp: Always enable occupied slot on probe
PCI: pciehp: Become resilient to missed events
PCI: pciehp: Tolerate initially unstable link
PCI: pciehp: Declare pciehp_enable/disable_slot() static
PCI: pciehp: Drop enable/disable lock
PCI: pciehp: Enable/disable exclusively from IRQ thread
PCI: pciehp: Track enable/disable status
PCI: pciehp: Publish to user space last on probe
PCI: hotplug: Demidlayer registration with the core
PCI: pciehp: Drop slot workqueue
PCI: pciehp: Handle events synchronously
PCI: pciehp: Stop blinking on slot enable failure
PCI: pciehp: Convert to threaded polling
PCI: pciehp: Convert to threaded IRQ
PCI: pciehp: Document struct slot and struct controller
PCI: pciehp: Declare pciehp_unconfigure_device() void
PCI: pciehp: Drop unnecessary NULL pointer check
PCI: pciehp: Fix unprotected list iteration in IRQ handler
PCI: pciehp: Fix use-after-free on unplug
PCI: hotplug: Don't leak pci_slot on registration failure
PCI: hotplug: Delete skeleton driver
PCI: shpchp: Separate existence of SHPC and permission to use it
Diffstat (limited to 'drivers/pci/hotplug')
| -rw-r--r-- | drivers/pci/hotplug/acpi_pcihp.c | 36 | ||||
| -rw-r--r-- | drivers/pci/hotplug/acpiphp_core.c | 22 | ||||
| -rw-r--r-- | drivers/pci/hotplug/cpci_hotplug_core.c | 14 | ||||
| -rw-r--r-- | drivers/pci/hotplug/cpqphp_core.c | 16 | ||||
| -rw-r--r-- | drivers/pci/hotplug/ibmphp_core.c | 15 | ||||
| -rw-r--r-- | drivers/pci/hotplug/ibmphp_ebda.c | 20 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pci_hotplug_core.c | 134 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp.h | 117 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 127 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 351 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 276 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pciehp_pci.c | 4 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pcihp_skeleton.c | 348 | ||||
| -rw-r--r-- | drivers/pci/hotplug/pnv_php.c | 5 | ||||
| -rw-r--r-- | drivers/pci/hotplug/rpaphp_core.c | 2 | ||||
| -rw-r--r-- | drivers/pci/hotplug/rpaphp_slot.c | 13 | ||||
| -rw-r--r-- | drivers/pci/hotplug/s390_pci_hpc.c | 13 | ||||
| -rw-r--r-- | drivers/pci/hotplug/sgi_hotplug.c | 9 | ||||
| -rw-r--r-- | drivers/pci/hotplug/shpchp_core.c | 41 |
19 files changed, 658 insertions, 905 deletions
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 5bd6c1573295..6b7c1ed58e7e 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c | |||
| @@ -74,20 +74,6 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) | |||
| 74 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; | 74 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; |
| 75 | 75 | ||
| 76 | /* | 76 | /* |
| 77 | * Per PCI firmware specification, we should run the ACPI _OSC | ||
| 78 | * method to get control of hotplug hardware before using it. If | ||
| 79 | * an _OSC is missing, we look for an OSHP to do the same thing. | ||
| 80 | * To handle different BIOS behavior, we look for _OSC on a root | ||
| 81 | * bridge preferentially (according to PCI fw spec). Later for | ||
| 82 | * OSHP within the scope of the hotplug controller and its parents, | ||
| 83 | * up to the host bridge under which this controller exists. | ||
| 84 | */ | ||
| 85 | if (shpchp_is_native(pdev)) | ||
| 86 | return 0; | ||
| 87 | |||
| 88 | /* If _OSC exists, we should not evaluate OSHP */ | ||
| 89 | |||
| 90 | /* | ||
| 91 | * If there's no ACPI host bridge (i.e., ACPI support is compiled | 77 | * If there's no ACPI host bridge (i.e., ACPI support is compiled |
| 92 | * into the kernel but the hardware platform doesn't support ACPI), | 78 | * into the kernel but the hardware platform doesn't support ACPI), |
| 93 | * there's nothing to do here. | 79 | * there's nothing to do here. |
| @@ -97,9 +83,25 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) | |||
| 97 | if (!root) | 83 | if (!root) |
| 98 | return 0; | 84 | return 0; |
| 99 | 85 | ||
| 100 | if (root->osc_support_set) | 86 | /* |
| 101 | goto no_control; | 87 | * If _OSC exists, it determines whether we're allowed to manage |
| 88 | * the SHPC. We executed it while enumerating the host bridge. | ||
| 89 | */ | ||
| 90 | if (root->osc_support_set) { | ||
| 91 | if (host->native_shpc_hotplug) | ||
| 92 | return 0; | ||
| 93 | return -ENODEV; | ||
| 94 | } | ||
| 102 | 95 | ||
| 96 | /* | ||
| 97 | * In the absence of _OSC, we're always allowed to manage the SHPC. | ||
| 98 | * However, if an OSHP method is present, we must execute it so the | ||
| 99 | * firmware can transfer control to the OS, e.g., direct interrupts | ||
| 100 | * to the OS instead of to the firmware. | ||
| 101 | * | ||
| 102 | * N.B. The PCI Firmware Spec (r3.2, sec 4.8) does not endorse | ||
| 103 | * searching up the ACPI hierarchy, so the loops below are suspect. | ||
| 104 | */ | ||
| 103 | handle = ACPI_HANDLE(&pdev->dev); | 105 | handle = ACPI_HANDLE(&pdev->dev); |
| 104 | if (!handle) { | 106 | if (!handle) { |
| 105 | /* | 107 | /* |
| @@ -128,7 +130,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) | |||
| 128 | if (ACPI_FAILURE(status)) | 130 | if (ACPI_FAILURE(status)) |
| 129 | break; | 131 | break; |
| 130 | } | 132 | } |
| 131 | no_control: | 133 | |
| 132 | pci_info(pdev, "Cannot get control of SHPC hotplug\n"); | 134 | pci_info(pdev, "Cannot get control of SHPC hotplug\n"); |
| 133 | kfree(string.pointer); | 135 | kfree(string.pointer); |
| 134 | return -ENODEV; | 136 | return -ENODEV; |
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index 12b5655fd390..ad32ffbc4b91 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c | |||
| @@ -254,20 +254,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | |||
| 254 | return 0; | 254 | return 0; |
| 255 | } | 255 | } |
| 256 | 256 | ||
| 257 | /** | ||
| 258 | * release_slot - free up the memory used by a slot | ||
| 259 | * @hotplug_slot: slot to free | ||
| 260 | */ | ||
| 261 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
| 262 | { | ||
| 263 | struct slot *slot = hotplug_slot->private; | ||
| 264 | |||
| 265 | pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); | ||
| 266 | |||
| 267 | kfree(slot->hotplug_slot); | ||
| 268 | kfree(slot); | ||
| 269 | } | ||
| 270 | |||
| 271 | /* callback routine to initialize 'struct slot' for each slot */ | 257 | /* callback routine to initialize 'struct slot' for each slot */ |
| 272 | int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot, | 258 | int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot, |
| 273 | unsigned int sun) | 259 | unsigned int sun) |
| @@ -287,7 +273,6 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot, | |||
| 287 | slot->hotplug_slot->info = &slot->info; | 273 | slot->hotplug_slot->info = &slot->info; |
| 288 | 274 | ||
| 289 | slot->hotplug_slot->private = slot; | 275 | slot->hotplug_slot->private = slot; |
| 290 | slot->hotplug_slot->release = &release_slot; | ||
| 291 | slot->hotplug_slot->ops = &acpi_hotplug_slot_ops; | 276 | slot->hotplug_slot->ops = &acpi_hotplug_slot_ops; |
| 292 | 277 | ||
| 293 | slot->acpi_slot = acpiphp_slot; | 278 | slot->acpi_slot = acpiphp_slot; |
| @@ -324,13 +309,12 @@ error: | |||
| 324 | void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot) | 309 | void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot) |
| 325 | { | 310 | { |
| 326 | struct slot *slot = acpiphp_slot->slot; | 311 | struct slot *slot = acpiphp_slot->slot; |
| 327 | int retval = 0; | ||
| 328 | 312 | ||
| 329 | pr_info("Slot [%s] unregistered\n", slot_name(slot)); | 313 | pr_info("Slot [%s] unregistered\n", slot_name(slot)); |
| 330 | 314 | ||
| 331 | retval = pci_hp_deregister(slot->hotplug_slot); | 315 | pci_hp_deregister(slot->hotplug_slot); |
| 332 | if (retval) | 316 | kfree(slot->hotplug_slot); |
| 333 | pr_err("pci_hp_deregister failed with error %d\n", retval); | 317 | kfree(slot); |
| 334 | } | 318 | } |
| 335 | 319 | ||
| 336 | 320 | ||
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index 07b533adc9df..52a339baf06c 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c | |||
| @@ -195,10 +195,8 @@ get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) | |||
| 195 | return 0; | 195 | return 0; |
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | static void release_slot(struct hotplug_slot *hotplug_slot) | 198 | static void release_slot(struct slot *slot) |
| 199 | { | 199 | { |
| 200 | struct slot *slot = hotplug_slot->private; | ||
| 201 | |||
| 202 | kfree(slot->hotplug_slot->info); | 200 | kfree(slot->hotplug_slot->info); |
| 203 | kfree(slot->hotplug_slot); | 201 | kfree(slot->hotplug_slot); |
| 204 | pci_dev_put(slot->dev); | 202 | pci_dev_put(slot->dev); |
| @@ -253,7 +251,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) | |||
| 253 | snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i); | 251 | snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i); |
| 254 | 252 | ||
| 255 | hotplug_slot->private = slot; | 253 | hotplug_slot->private = slot; |
| 256 | hotplug_slot->release = &release_slot; | ||
| 257 | hotplug_slot->ops = &cpci_hotplug_slot_ops; | 254 | hotplug_slot->ops = &cpci_hotplug_slot_ops; |
| 258 | 255 | ||
| 259 | /* | 256 | /* |
| @@ -308,12 +305,8 @@ cpci_hp_unregister_bus(struct pci_bus *bus) | |||
| 308 | slots--; | 305 | slots--; |
| 309 | 306 | ||
| 310 | dbg("deregistering slot %s", slot_name(slot)); | 307 | dbg("deregistering slot %s", slot_name(slot)); |
| 311 | status = pci_hp_deregister(slot->hotplug_slot); | 308 | pci_hp_deregister(slot->hotplug_slot); |
| 312 | if (status) { | 309 | release_slot(slot); |
| 313 | err("pci_hp_deregister failed with error %d", | ||
| 314 | status); | ||
| 315 | break; | ||
| 316 | } | ||
| 317 | } | 310 | } |
| 318 | } | 311 | } |
| 319 | up_write(&list_rwsem); | 312 | up_write(&list_rwsem); |
| @@ -623,6 +616,7 @@ cleanup_slots(void) | |||
| 623 | list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) { | 616 | list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) { |
| 624 | list_del(&slot->slot_list); | 617 | list_del(&slot->slot_list); |
| 625 | pci_hp_deregister(slot->hotplug_slot); | 618 | pci_hp_deregister(slot->hotplug_slot); |
| 619 | release_slot(slot); | ||
| 626 | } | 620 | } |
| 627 | cleanup_null: | 621 | cleanup_null: |
| 628 | up_write(&list_rwsem); | 622 | up_write(&list_rwsem); |
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 1797e36ec586..5a06636e910a 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c | |||
| @@ -266,17 +266,6 @@ static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start, | |||
| 266 | return previous; | 266 | return previous; |
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
| 270 | { | ||
| 271 | struct slot *slot = hotplug_slot->private; | ||
| 272 | |||
| 273 | dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); | ||
| 274 | |||
| 275 | kfree(slot->hotplug_slot->info); | ||
| 276 | kfree(slot->hotplug_slot); | ||
| 277 | kfree(slot); | ||
| 278 | } | ||
| 279 | |||
| 280 | static int ctrl_slot_cleanup(struct controller *ctrl) | 269 | static int ctrl_slot_cleanup(struct controller *ctrl) |
| 281 | { | 270 | { |
| 282 | struct slot *old_slot, *next_slot; | 271 | struct slot *old_slot, *next_slot; |
| @@ -285,9 +274,11 @@ static int ctrl_slot_cleanup(struct controller *ctrl) | |||
| 285 | ctrl->slot = NULL; | 274 | ctrl->slot = NULL; |
| 286 | 275 | ||
| 287 | while (old_slot) { | 276 | while (old_slot) { |
| 288 | /* memory will be freed by the release_slot callback */ | ||
| 289 | next_slot = old_slot->next; | 277 | next_slot = old_slot->next; |
| 290 | pci_hp_deregister(old_slot->hotplug_slot); | 278 | pci_hp_deregister(old_slot->hotplug_slot); |
| 279 | kfree(old_slot->hotplug_slot->info); | ||
| 280 | kfree(old_slot->hotplug_slot); | ||
| 281 | kfree(old_slot); | ||
| 291 | old_slot = next_slot; | 282 | old_slot = next_slot; |
| 292 | } | 283 | } |
| 293 | 284 | ||
| @@ -678,7 +669,6 @@ static int ctrl_slot_setup(struct controller *ctrl, | |||
| 678 | ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; | 669 | ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; |
| 679 | 670 | ||
| 680 | /* register this slot with the hotplug pci core */ | 671 | /* register this slot with the hotplug pci core */ |
| 681 | hotplug_slot->release = &release_slot; | ||
| 682 | hotplug_slot->private = slot; | 672 | hotplug_slot->private = slot; |
| 683 | snprintf(name, SLOT_NAME_SIZE, "%u", slot->number); | 673 | snprintf(name, SLOT_NAME_SIZE, "%u", slot->number); |
| 684 | hotplug_slot->ops = &cpqphp_hotplug_slot_ops; | 674 | hotplug_slot->ops = &cpqphp_hotplug_slot_ops; |
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 1869b0411ce0..4ea57e9019f1 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c | |||
| @@ -673,7 +673,20 @@ static void free_slots(void) | |||
| 673 | 673 | ||
| 674 | list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head, | 674 | list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head, |
| 675 | ibm_slot_list) { | 675 | ibm_slot_list) { |
| 676 | pci_hp_deregister(slot_cur->hotplug_slot); | 676 | pci_hp_del(slot_cur->hotplug_slot); |
| 677 | slot_cur->ctrl = NULL; | ||
| 678 | slot_cur->bus_on = NULL; | ||
| 679 | |||
| 680 | /* | ||
| 681 | * We don't want to actually remove the resources, | ||
| 682 | * since ibmphp_free_resources() will do just that. | ||
| 683 | */ | ||
| 684 | ibmphp_unconfigure_card(&slot_cur, -1); | ||
| 685 | |||
| 686 | pci_hp_destroy(slot_cur->hotplug_slot); | ||
| 687 | kfree(slot_cur->hotplug_slot->info); | ||
| 688 | kfree(slot_cur->hotplug_slot); | ||
| 689 | kfree(slot_cur); | ||
| 677 | } | 690 | } |
| 678 | debug("%s -- exit\n", __func__); | 691 | debug("%s -- exit\n", __func__); |
| 679 | } | 692 | } |
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c index 64549aa24c0f..6f8e90e3ec08 100644 --- a/drivers/pci/hotplug/ibmphp_ebda.c +++ b/drivers/pci/hotplug/ibmphp_ebda.c | |||
| @@ -699,25 +699,6 @@ static int fillslotinfo(struct hotplug_slot *hotplug_slot) | |||
| 699 | return rc; | 699 | return rc; |
| 700 | } | 700 | } |
| 701 | 701 | ||
| 702 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
| 703 | { | ||
| 704 | struct slot *slot; | ||
| 705 | |||
| 706 | if (!hotplug_slot || !hotplug_slot->private) | ||
| 707 | return; | ||
| 708 | |||
| 709 | slot = hotplug_slot->private; | ||
| 710 | kfree(slot->hotplug_slot->info); | ||
| 711 | kfree(slot->hotplug_slot); | ||
| 712 | slot->ctrl = NULL; | ||
| 713 | slot->bus_on = NULL; | ||
| 714 | |||
| 715 | /* we don't want to actually remove the resources, since free_resources will do just that */ | ||
| 716 | ibmphp_unconfigure_card(&slot, -1); | ||
| 717 | |||
| 718 | kfree(slot); | ||
| 719 | } | ||
| 720 | |||
| 721 | static struct pci_driver ibmphp_driver; | 702 | static struct pci_driver ibmphp_driver; |
| 722 | 703 | ||
| 723 | /* | 704 | /* |
| @@ -941,7 +922,6 @@ static int __init ebda_rsrc_controller(void) | |||
| 941 | tmp_slot->hotplug_slot = hp_slot_ptr; | 922 | tmp_slot->hotplug_slot = hp_slot_ptr; |
| 942 | 923 | ||
| 943 | hp_slot_ptr->private = tmp_slot; | 924 | hp_slot_ptr->private = tmp_slot; |
| 944 | hp_slot_ptr->release = release_slot; | ||
| 945 | 925 | ||
| 946 | rc = fillslotinfo(hp_slot_ptr); | 926 | rc = fillslotinfo(hp_slot_ptr); |
| 947 | if (rc) | 927 | if (rc) |
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index af92fed46ab7..90fde5f106d8 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c | |||
| @@ -396,8 +396,9 @@ static struct hotplug_slot *get_slot_from_name(const char *name) | |||
| 396 | * @owner: caller module owner | 396 | * @owner: caller module owner |
| 397 | * @mod_name: caller module name | 397 | * @mod_name: caller module name |
| 398 | * | 398 | * |
| 399 | * Registers a hotplug slot with the pci hotplug subsystem, which will allow | 399 | * Prepares a hotplug slot for in-kernel use and immediately publishes it to |
| 400 | * userspace interaction to the slot. | 400 | * user space in one go. Drivers may alternatively carry out the two steps |
| 401 | * separately by invoking pci_hp_initialize() and pci_hp_add(). | ||
| 401 | * | 402 | * |
| 402 | * Returns 0 if successful, anything else for an error. | 403 | * Returns 0 if successful, anything else for an error. |
| 403 | */ | 404 | */ |
| @@ -406,45 +407,91 @@ int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, | |||
| 406 | struct module *owner, const char *mod_name) | 407 | struct module *owner, const char *mod_name) |
| 407 | { | 408 | { |
| 408 | int result; | 409 | int result; |
| 410 | |||
| 411 | result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name); | ||
| 412 | if (result) | ||
| 413 | return result; | ||
| 414 | |||
| 415 | result = pci_hp_add(slot); | ||
| 416 | if (result) | ||
| 417 | pci_hp_destroy(slot); | ||
| 418 | |||
| 419 | return result; | ||
| 420 | } | ||
| 421 | EXPORT_SYMBOL_GPL(__pci_hp_register); | ||
| 422 | |||
| 423 | /** | ||
| 424 | * __pci_hp_initialize - prepare hotplug slot for in-kernel use | ||
| 425 | * @slot: pointer to the &struct hotplug_slot to initialize | ||
| 426 | * @bus: bus this slot is on | ||
| 427 | * @devnr: slot number | ||
| 428 | * @name: name registered with kobject core | ||
| 429 | * @owner: caller module owner | ||
| 430 | * @mod_name: caller module name | ||
| 431 | * | ||
| 432 | * Allocate and fill in a PCI slot for use by a hotplug driver. Once this has | ||
| 433 | * been called, the driver may invoke hotplug_slot_name() to get the slot's | ||
| 434 | * unique name. The driver must be prepared to handle a ->reset_slot callback | ||
| 435 | * from this point on. | ||
| 436 | * | ||
| 437 | * Returns 0 on success or a negative int on error. | ||
| 438 | */ | ||
| 439 | int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus, | ||
| 440 | int devnr, const char *name, struct module *owner, | ||
| 441 | const char *mod_name) | ||
| 442 | { | ||
| 409 | struct pci_slot *pci_slot; | 443 | struct pci_slot *pci_slot; |
| 410 | 444 | ||
| 411 | if (slot == NULL) | 445 | if (slot == NULL) |
| 412 | return -ENODEV; | 446 | return -ENODEV; |
| 413 | if ((slot->info == NULL) || (slot->ops == NULL)) | 447 | if ((slot->info == NULL) || (slot->ops == NULL)) |
| 414 | return -EINVAL; | 448 | return -EINVAL; |
| 415 | if (slot->release == NULL) { | ||
| 416 | dbg("Why are you trying to register a hotplug slot without a proper release function?\n"); | ||
| 417 | return -EINVAL; | ||
| 418 | } | ||
| 419 | 449 | ||
| 420 | slot->ops->owner = owner; | 450 | slot->ops->owner = owner; |
| 421 | slot->ops->mod_name = mod_name; | 451 | slot->ops->mod_name = mod_name; |
| 422 | 452 | ||
| 423 | mutex_lock(&pci_hp_mutex); | ||
| 424 | /* | 453 | /* |
| 425 | * No problems if we call this interface from both ACPI_PCI_SLOT | 454 | * No problems if we call this interface from both ACPI_PCI_SLOT |
| 426 | * driver and call it here again. If we've already created the | 455 | * driver and call it here again. If we've already created the |
| 427 | * pci_slot, the interface will simply bump the refcount. | 456 | * pci_slot, the interface will simply bump the refcount. |
| 428 | */ | 457 | */ |
| 429 | pci_slot = pci_create_slot(bus, devnr, name, slot); | 458 | pci_slot = pci_create_slot(bus, devnr, name, slot); |
| 430 | if (IS_ERR(pci_slot)) { | 459 | if (IS_ERR(pci_slot)) |
| 431 | result = PTR_ERR(pci_slot); | 460 | return PTR_ERR(pci_slot); |
| 432 | goto out; | ||
| 433 | } | ||
| 434 | 461 | ||
| 435 | slot->pci_slot = pci_slot; | 462 | slot->pci_slot = pci_slot; |
| 436 | pci_slot->hotplug = slot; | 463 | pci_slot->hotplug = slot; |
| 464 | return 0; | ||
| 465 | } | ||
| 466 | EXPORT_SYMBOL_GPL(__pci_hp_initialize); | ||
| 437 | 467 | ||
| 438 | list_add(&slot->slot_list, &pci_hotplug_slot_list); | 468 | /** |
| 469 | * pci_hp_add - publish hotplug slot to user space | ||
| 470 | * @slot: pointer to the &struct hotplug_slot to publish | ||
| 471 | * | ||
| 472 | * Make a hotplug slot's sysfs interface available and inform user space of its | ||
| 473 | * addition by sending a uevent. The hotplug driver must be prepared to handle | ||
| 474 | * all &struct hotplug_slot_ops callbacks from this point on. | ||
| 475 | * | ||
| 476 | * Returns 0 on success or a negative int on error. | ||
| 477 | */ | ||
| 478 | int pci_hp_add(struct hotplug_slot *slot) | ||
| 479 | { | ||
| 480 | struct pci_slot *pci_slot = slot->pci_slot; | ||
| 481 | int result; | ||
| 439 | 482 | ||
| 440 | result = fs_add_slot(pci_slot); | 483 | result = fs_add_slot(pci_slot); |
| 484 | if (result) | ||
| 485 | return result; | ||
| 486 | |||
| 441 | kobject_uevent(&pci_slot->kobj, KOBJ_ADD); | 487 | kobject_uevent(&pci_slot->kobj, KOBJ_ADD); |
| 442 | dbg("Added slot %s to the list\n", name); | 488 | mutex_lock(&pci_hp_mutex); |
| 443 | out: | 489 | list_add(&slot->slot_list, &pci_hotplug_slot_list); |
| 444 | mutex_unlock(&pci_hp_mutex); | 490 | mutex_unlock(&pci_hp_mutex); |
| 445 | return result; | 491 | dbg("Added slot %s to the list\n", hotplug_slot_name(slot)); |
| 492 | return 0; | ||
| 446 | } | 493 | } |
| 447 | EXPORT_SYMBOL_GPL(__pci_hp_register); | 494 | EXPORT_SYMBOL_GPL(pci_hp_add); |
| 448 | 495 | ||
| 449 | /** | 496 | /** |
| 450 | * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem | 497 | * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem |
| @@ -455,35 +502,62 @@ EXPORT_SYMBOL_GPL(__pci_hp_register); | |||
| 455 | * | 502 | * |
| 456 | * Returns 0 if successful, anything else for an error. | 503 | * Returns 0 if successful, anything else for an error. |
| 457 | */ | 504 | */ |
| 458 | int pci_hp_deregister(struct hotplug_slot *slot) | 505 | void pci_hp_deregister(struct hotplug_slot *slot) |
| 506 | { | ||
| 507 | pci_hp_del(slot); | ||
| 508 | pci_hp_destroy(slot); | ||
| 509 | } | ||
| 510 | EXPORT_SYMBOL_GPL(pci_hp_deregister); | ||
| 511 | |||
| 512 | /** | ||
| 513 | * pci_hp_del - unpublish hotplug slot from user space | ||
| 514 | * @slot: pointer to the &struct hotplug_slot to unpublish | ||
| 515 | * | ||
| 516 | * Remove a hotplug slot's sysfs interface. | ||
| 517 | * | ||
| 518 | * Returns 0 on success or a negative int on error. | ||
| 519 | */ | ||
| 520 | void pci_hp_del(struct hotplug_slot *slot) | ||
| 459 | { | 521 | { |
| 460 | struct hotplug_slot *temp; | 522 | struct hotplug_slot *temp; |
| 461 | struct pci_slot *pci_slot; | ||
| 462 | 523 | ||
| 463 | if (!slot) | 524 | if (WARN_ON(!slot)) |
| 464 | return -ENODEV; | 525 | return; |
| 465 | 526 | ||
| 466 | mutex_lock(&pci_hp_mutex); | 527 | mutex_lock(&pci_hp_mutex); |
| 467 | temp = get_slot_from_name(hotplug_slot_name(slot)); | 528 | temp = get_slot_from_name(hotplug_slot_name(slot)); |
| 468 | if (temp != slot) { | 529 | if (WARN_ON(temp != slot)) { |
| 469 | mutex_unlock(&pci_hp_mutex); | 530 | mutex_unlock(&pci_hp_mutex); |
| 470 | return -ENODEV; | 531 | return; |
| 471 | } | 532 | } |
| 472 | 533 | ||
| 473 | list_del(&slot->slot_list); | 534 | list_del(&slot->slot_list); |
| 474 | 535 | mutex_unlock(&pci_hp_mutex); | |
| 475 | pci_slot = slot->pci_slot; | ||
| 476 | fs_remove_slot(pci_slot); | ||
| 477 | dbg("Removed slot %s from the list\n", hotplug_slot_name(slot)); | 536 | dbg("Removed slot %s from the list\n", hotplug_slot_name(slot)); |
| 537 | fs_remove_slot(slot->pci_slot); | ||
| 538 | } | ||
| 539 | EXPORT_SYMBOL_GPL(pci_hp_del); | ||
| 478 | 540 | ||
| 479 | slot->release(slot); | 541 | /** |
| 542 | * pci_hp_destroy - remove hotplug slot from in-kernel use | ||
| 543 | * @slot: pointer to the &struct hotplug_slot to destroy | ||
| 544 | * | ||
| 545 | * Destroy a PCI slot used by a hotplug driver. Once this has been called, | ||
| 546 | * the driver may no longer invoke hotplug_slot_name() to get the slot's | ||
| 547 | * unique name. The driver no longer needs to handle a ->reset_slot callback | ||
| 548 | * from this point on. | ||
| 549 | * | ||
| 550 | * Returns 0 on success or a negative int on error. | ||
| 551 | */ | ||
| 552 | void pci_hp_destroy(struct hotplug_slot *slot) | ||
| 553 | { | ||
| 554 | struct pci_slot *pci_slot = slot->pci_slot; | ||
| 555 | |||
| 556 | slot->pci_slot = NULL; | ||
| 480 | pci_slot->hotplug = NULL; | 557 | pci_slot->hotplug = NULL; |
| 481 | pci_destroy_slot(pci_slot); | 558 | pci_destroy_slot(pci_slot); |
| 482 | mutex_unlock(&pci_hp_mutex); | ||
| 483 | |||
| 484 | return 0; | ||
| 485 | } | 559 | } |
| 486 | EXPORT_SYMBOL_GPL(pci_hp_deregister); | 560 | EXPORT_SYMBOL_GPL(pci_hp_destroy); |
| 487 | 561 | ||
| 488 | /** | 562 | /** |
| 489 | * pci_hp_change_slot_info - changes the slot's information structure in the core | 563 | * pci_hp_change_slot_info - changes the slot's information structure in the core |
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 5f892065585e..811cf83f956d 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include <linux/delay.h> | 21 | #include <linux/delay.h> |
| 22 | #include <linux/sched/signal.h> /* signal_pending() */ | 22 | #include <linux/sched/signal.h> /* signal_pending() */ |
| 23 | #include <linux/mutex.h> | 23 | #include <linux/mutex.h> |
| 24 | #include <linux/rwsem.h> | ||
| 24 | #include <linux/workqueue.h> | 25 | #include <linux/workqueue.h> |
| 25 | 26 | ||
| 26 | #include "../pcie/portdrv.h" | 27 | #include "../pcie/portdrv.h" |
| @@ -57,49 +58,111 @@ do { \ | |||
| 57 | dev_warn(&ctrl->pcie->device, format, ## arg) | 58 | dev_warn(&ctrl->pcie->device, format, ## arg) |
| 58 | 59 | ||
| 59 | #define SLOT_NAME_SIZE 10 | 60 | #define SLOT_NAME_SIZE 10 |
| 61 | |||
| 62 | /** | ||
| 63 | * struct slot - PCIe hotplug slot | ||
| 64 | * @state: current state machine position | ||
| 65 | * @ctrl: pointer to the slot's controller structure | ||
| 66 | * @hotplug_slot: pointer to the structure registered with the PCI hotplug core | ||
| 67 | * @work: work item to turn the slot on or off after 5 seconds in response to | ||
| 68 | * an Attention Button press | ||
| 69 | * @lock: protects reads and writes of @state; | ||
| 70 | * protects scheduling, execution and cancellation of @work | ||
| 71 | */ | ||
| 60 | struct slot { | 72 | struct slot { |
| 61 | u8 state; | 73 | u8 state; |
| 62 | struct controller *ctrl; | 74 | struct controller *ctrl; |
| 63 | struct hotplug_slot *hotplug_slot; | 75 | struct hotplug_slot *hotplug_slot; |
| 64 | struct delayed_work work; /* work for button event */ | 76 | struct delayed_work work; |
| 65 | struct mutex lock; | 77 | struct mutex lock; |
| 66 | struct mutex hotplug_lock; | ||
| 67 | struct workqueue_struct *wq; | ||
| 68 | }; | ||
| 69 | |||
| 70 | struct event_info { | ||
| 71 | u32 event_type; | ||
| 72 | struct slot *p_slot; | ||
| 73 | struct work_struct work; | ||
| 74 | }; | 78 | }; |
| 75 | 79 | ||
| 80 | /** | ||
| 81 | * struct controller - PCIe hotplug controller | ||
| 82 | * @ctrl_lock: serializes writes to the Slot Control register | ||
| 83 | * @pcie: pointer to the controller's PCIe port service device | ||
| 84 | * @reset_lock: prevents access to the Data Link Layer Link Active bit in the | ||
| 85 | * Link Status register and to the Presence Detect State bit in the Slot | ||
| 86 | * Status register during a slot reset which may cause them to flap | ||
| 87 | * @slot: pointer to the controller's slot structure | ||
| 88 | * @queue: wait queue to wake up on reception of a Command Completed event, | ||
| 89 | * used for synchronous writes to the Slot Control register | ||
| 90 | * @slot_cap: cached copy of the Slot Capabilities register | ||
| 91 | * @slot_ctrl: cached copy of the Slot Control register | ||
| 92 | * @poll_thread: thread to poll for slot events if no IRQ is available, | ||
| 93 | * enabled with pciehp_poll_mode module parameter | ||
| 94 | * @cmd_started: jiffies when the Slot Control register was last written; | ||
| 95 | * the next write is allowed 1 second later, absent a Command Completed | ||
| 96 | * interrupt (PCIe r4.0, sec 6.7.3.2) | ||
| 97 | * @cmd_busy: flag set on Slot Control register write, cleared by IRQ handler | ||
| 98 | * on reception of a Command Completed event | ||
| 99 | * @link_active_reporting: cached copy of Data Link Layer Link Active Reporting | ||
| 100 | * Capable bit in Link Capabilities register; if this bit is zero, the | ||
| 101 | * Data Link Layer Link Active bit in the Link Status register will never | ||
| 102 | * be set and the driver is thus confined to wait 1 second before assuming | ||
| 103 | * the link to a hotplugged device is up and accessing it | ||
| 104 | * @notification_enabled: whether the IRQ was requested successfully | ||
| 105 | * @power_fault_detected: whether a power fault was detected by the hardware | ||
| 106 | * that has not yet been cleared by the user | ||
| 107 | * @pending_events: used by the IRQ handler to save events retrieved from the | ||
| 108 | * Slot Status register for later consumption by the IRQ thread | ||
| 109 | * @request_result: result of last user request submitted to the IRQ thread | ||
| 110 | * @requester: wait queue to wake up on completion of user request, | ||
| 111 | * used for synchronous slot enable/disable request via sysfs | ||
| 112 | */ | ||
| 76 | struct controller { | 113 | struct controller { |
| 77 | struct mutex ctrl_lock; /* controller lock */ | 114 | struct mutex ctrl_lock; |
| 78 | struct pcie_device *pcie; /* PCI Express port service */ | 115 | struct pcie_device *pcie; |
| 116 | struct rw_semaphore reset_lock; | ||
| 79 | struct slot *slot; | 117 | struct slot *slot; |
| 80 | wait_queue_head_t queue; /* sleep & wake process */ | 118 | wait_queue_head_t queue; |
| 81 | u32 slot_cap; | 119 | u32 slot_cap; |
| 82 | u16 slot_ctrl; | 120 | u16 slot_ctrl; |
| 83 | struct timer_list poll_timer; | 121 | struct task_struct *poll_thread; |
| 84 | unsigned long cmd_started; /* jiffies */ | 122 | unsigned long cmd_started; /* jiffies */ |
| 85 | unsigned int cmd_busy:1; | 123 | unsigned int cmd_busy:1; |
| 86 | unsigned int link_active_reporting:1; | 124 | unsigned int link_active_reporting:1; |
| 87 | unsigned int notification_enabled:1; | 125 | unsigned int notification_enabled:1; |
| 88 | unsigned int power_fault_detected; | 126 | unsigned int power_fault_detected; |
| 127 | atomic_t pending_events; | ||
| 128 | int request_result; | ||
| 129 | wait_queue_head_t requester; | ||
| 89 | }; | 130 | }; |
| 90 | 131 | ||
| 91 | #define INT_PRESENCE_ON 1 | 132 | /** |
| 92 | #define INT_PRESENCE_OFF 2 | 133 | * DOC: Slot state |
| 93 | #define INT_POWER_FAULT 3 | 134 | * |
| 94 | #define INT_BUTTON_PRESS 4 | 135 | * @OFF_STATE: slot is powered off, no subordinate devices are enumerated |
| 95 | #define INT_LINK_UP 5 | 136 | * @BLINKINGON_STATE: slot will be powered on after the 5 second delay, |
| 96 | #define INT_LINK_DOWN 6 | 137 | * green led is blinking |
| 97 | 138 | * @BLINKINGOFF_STATE: slot will be powered off after the 5 second delay, | |
| 98 | #define STATIC_STATE 0 | 139 | * green led is blinking |
| 140 | * @POWERON_STATE: slot is currently powering on | ||
| 141 | * @POWEROFF_STATE: slot is currently powering off | ||
| 142 | * @ON_STATE: slot is powered on, subordinate devices have been enumerated | ||
| 143 | */ | ||
| 144 | #define OFF_STATE 0 | ||
| 99 | #define BLINKINGON_STATE 1 | 145 | #define BLINKINGON_STATE 1 |
| 100 | #define BLINKINGOFF_STATE 2 | 146 | #define BLINKINGOFF_STATE 2 |
| 101 | #define POWERON_STATE 3 | 147 | #define POWERON_STATE 3 |
| 102 | #define POWEROFF_STATE 4 | 148 | #define POWEROFF_STATE 4 |
| 149 | #define ON_STATE 5 | ||
| 150 | |||
| 151 | /** | ||
| 152 | * DOC: Flags to request an action from the IRQ thread | ||
| 153 | * | ||
| 154 | * These are stored together with events read from the Slot Status register, | ||
| 155 | * hence must be greater than its 16-bit width. | ||
| 156 | * | ||
| 157 | * %DISABLE_SLOT: Disable the slot in response to a user request via sysfs or | ||
| 158 | * an Attention Button press after the 5 second delay | ||
| 159 | * %RERUN_ISR: Used by the IRQ handler to inform the IRQ thread that the | ||
| 160 | * hotplug port was inaccessible when the interrupt occurred, requiring | ||
| 161 | * that the IRQ handler is rerun by the IRQ thread after it has made the | ||
| 162 | * hotplug port accessible by runtime resuming its parents to D0 | ||
| 163 | */ | ||
| 164 | #define DISABLE_SLOT (1 << 16) | ||
| 165 | #define RERUN_ISR (1 << 17) | ||
| 103 | 166 | ||
| 104 | #define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP) | 167 | #define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP) |
| 105 | #define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP) | 168 | #define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP) |
| @@ -113,15 +176,17 @@ struct controller { | |||
| 113 | 176 | ||
| 114 | int pciehp_sysfs_enable_slot(struct slot *slot); | 177 | int pciehp_sysfs_enable_slot(struct slot *slot); |
| 115 | int pciehp_sysfs_disable_slot(struct slot *slot); | 178 | int pciehp_sysfs_disable_slot(struct slot *slot); |
| 116 | void pciehp_queue_interrupt_event(struct slot *slot, u32 event_type); | 179 | void pciehp_request(struct controller *ctrl, int action); |
| 180 | void pciehp_handle_button_press(struct slot *slot); | ||
| 181 | void pciehp_handle_disable_request(struct slot *slot); | ||
| 182 | void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events); | ||
| 117 | int pciehp_configure_device(struct slot *p_slot); | 183 | int pciehp_configure_device(struct slot *p_slot); |
| 118 | int pciehp_unconfigure_device(struct slot *p_slot); | 184 | void pciehp_unconfigure_device(struct slot *p_slot); |
| 119 | void pciehp_queue_pushbutton_work(struct work_struct *work); | 185 | void pciehp_queue_pushbutton_work(struct work_struct *work); |
| 120 | struct controller *pcie_init(struct pcie_device *dev); | 186 | struct controller *pcie_init(struct pcie_device *dev); |
| 121 | int pcie_init_notification(struct controller *ctrl); | 187 | int pcie_init_notification(struct controller *ctrl); |
| 122 | int pciehp_enable_slot(struct slot *p_slot); | 188 | void pcie_shutdown_notification(struct controller *ctrl); |
| 123 | int pciehp_disable_slot(struct slot *p_slot); | 189 | void pcie_clear_hotplug_events(struct controller *ctrl); |
| 124 | void pcie_reenable_notification(struct controller *ctrl); | ||
| 125 | int pciehp_power_on_slot(struct slot *slot); | 190 | int pciehp_power_on_slot(struct slot *slot); |
| 126 | void pciehp_power_off_slot(struct slot *slot); | 191 | void pciehp_power_off_slot(struct slot *slot); |
| 127 | void pciehp_get_power_status(struct slot *slot, u8 *status); | 192 | void pciehp_get_power_status(struct slot *slot, u8 *status); |
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 44a6a63802d5..ec48c9433ae5 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c | |||
| @@ -26,11 +26,12 @@ | |||
| 26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
| 27 | #include <linux/time.h> | 27 | #include <linux/time.h> |
| 28 | 28 | ||
| 29 | #include "../pci.h" | ||
| 30 | |||
| 29 | /* Global variables */ | 31 | /* Global variables */ |
| 30 | bool pciehp_debug; | 32 | bool pciehp_debug; |
| 31 | bool pciehp_poll_mode; | 33 | bool pciehp_poll_mode; |
| 32 | int pciehp_poll_time; | 34 | int pciehp_poll_time; |
| 33 | static bool pciehp_force; | ||
| 34 | 35 | ||
| 35 | /* | 36 | /* |
| 36 | * not really modular, but the easiest way to keep compat with existing | 37 | * not really modular, but the easiest way to keep compat with existing |
| @@ -39,11 +40,9 @@ static bool pciehp_force; | |||
| 39 | module_param(pciehp_debug, bool, 0644); | 40 | module_param(pciehp_debug, bool, 0644); |
| 40 | module_param(pciehp_poll_mode, bool, 0644); | 41 | module_param(pciehp_poll_mode, bool, 0644); |
| 41 | module_param(pciehp_poll_time, int, 0644); | 42 | module_param(pciehp_poll_time, int, 0644); |
| 42 | module_param(pciehp_force, bool, 0644); | ||
| 43 | MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not"); | 43 | MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not"); |
| 44 | MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not"); | 44 | MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not"); |
| 45 | MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds"); | 45 | MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds"); |
| 46 | MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing"); | ||
| 47 | 46 | ||
| 48 | #define PCIE_MODULE_NAME "pciehp" | 47 | #define PCIE_MODULE_NAME "pciehp" |
| 49 | 48 | ||
| @@ -56,17 +55,6 @@ static int get_latch_status(struct hotplug_slot *slot, u8 *value); | |||
| 56 | static int get_adapter_status(struct hotplug_slot *slot, u8 *value); | 55 | static int get_adapter_status(struct hotplug_slot *slot, u8 *value); |
| 57 | static int reset_slot(struct hotplug_slot *slot, int probe); | 56 | static int reset_slot(struct hotplug_slot *slot, int probe); |
| 58 | 57 | ||
| 59 | /** | ||
| 60 | * release_slot - free up the memory used by a slot | ||
| 61 | * @hotplug_slot: slot to free | ||
| 62 | */ | ||
| 63 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
| 64 | { | ||
| 65 | kfree(hotplug_slot->ops); | ||
| 66 | kfree(hotplug_slot->info); | ||
| 67 | kfree(hotplug_slot); | ||
| 68 | } | ||
| 69 | |||
| 70 | static int init_slot(struct controller *ctrl) | 58 | static int init_slot(struct controller *ctrl) |
| 71 | { | 59 | { |
| 72 | struct slot *slot = ctrl->slot; | 60 | struct slot *slot = ctrl->slot; |
| @@ -107,15 +95,14 @@ static int init_slot(struct controller *ctrl) | |||
| 107 | /* register this slot with the hotplug pci core */ | 95 | /* register this slot with the hotplug pci core */ |
| 108 | hotplug->info = info; | 96 | hotplug->info = info; |
| 109 | hotplug->private = slot; | 97 | hotplug->private = slot; |
| 110 | hotplug->release = &release_slot; | ||
| 111 | hotplug->ops = ops; | 98 | hotplug->ops = ops; |
| 112 | slot->hotplug_slot = hotplug; | 99 | slot->hotplug_slot = hotplug; |
| 113 | snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); | 100 | snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); |
| 114 | 101 | ||
| 115 | retval = pci_hp_register(hotplug, | 102 | retval = pci_hp_initialize(hotplug, |
| 116 | ctrl->pcie->port->subordinate, 0, name); | 103 | ctrl->pcie->port->subordinate, 0, name); |
| 117 | if (retval) | 104 | if (retval) |
| 118 | ctrl_err(ctrl, "pci_hp_register failed: error %d\n", retval); | 105 | ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval); |
| 119 | out: | 106 | out: |
| 120 | if (retval) { | 107 | if (retval) { |
| 121 | kfree(ops); | 108 | kfree(ops); |
| @@ -127,7 +114,12 @@ out: | |||
| 127 | 114 | ||
| 128 | static void cleanup_slot(struct controller *ctrl) | 115 | static void cleanup_slot(struct controller *ctrl) |
| 129 | { | 116 | { |
| 130 | pci_hp_deregister(ctrl->slot->hotplug_slot); | 117 | struct hotplug_slot *hotplug_slot = ctrl->slot->hotplug_slot; |
| 118 | |||
| 119 | pci_hp_destroy(hotplug_slot); | ||
| 120 | kfree(hotplug_slot->ops); | ||
| 121 | kfree(hotplug_slot->info); | ||
| 122 | kfree(hotplug_slot); | ||
| 131 | } | 123 | } |
| 132 | 124 | ||
| 133 | /* | 125 | /* |
| @@ -136,8 +128,11 @@ static void cleanup_slot(struct controller *ctrl) | |||
| 136 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) | 128 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) |
| 137 | { | 129 | { |
| 138 | struct slot *slot = hotplug_slot->private; | 130 | struct slot *slot = hotplug_slot->private; |
| 131 | struct pci_dev *pdev = slot->ctrl->pcie->port; | ||
| 139 | 132 | ||
| 133 | pci_config_pm_runtime_get(pdev); | ||
| 140 | pciehp_set_attention_status(slot, status); | 134 | pciehp_set_attention_status(slot, status); |
| 135 | pci_config_pm_runtime_put(pdev); | ||
| 141 | return 0; | 136 | return 0; |
| 142 | } | 137 | } |
| 143 | 138 | ||
| @@ -160,8 +155,11 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) | |||
| 160 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | 155 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) |
| 161 | { | 156 | { |
| 162 | struct slot *slot = hotplug_slot->private; | 157 | struct slot *slot = hotplug_slot->private; |
| 158 | struct pci_dev *pdev = slot->ctrl->pcie->port; | ||
| 163 | 159 | ||
| 160 | pci_config_pm_runtime_get(pdev); | ||
| 164 | pciehp_get_power_status(slot, value); | 161 | pciehp_get_power_status(slot, value); |
| 162 | pci_config_pm_runtime_put(pdev); | ||
| 165 | return 0; | 163 | return 0; |
| 166 | } | 164 | } |
| 167 | 165 | ||
| @@ -176,16 +174,22 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) | |||
| 176 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) | 174 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) |
| 177 | { | 175 | { |
| 178 | struct slot *slot = hotplug_slot->private; | 176 | struct slot *slot = hotplug_slot->private; |
| 177 | struct pci_dev *pdev = slot->ctrl->pcie->port; | ||
| 179 | 178 | ||
| 179 | pci_config_pm_runtime_get(pdev); | ||
| 180 | pciehp_get_latch_status(slot, value); | 180 | pciehp_get_latch_status(slot, value); |
| 181 | pci_config_pm_runtime_put(pdev); | ||
| 181 | return 0; | 182 | return 0; |
| 182 | } | 183 | } |
| 183 | 184 | ||
| 184 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | 185 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) |
| 185 | { | 186 | { |
| 186 | struct slot *slot = hotplug_slot->private; | 187 | struct slot *slot = hotplug_slot->private; |
| 188 | struct pci_dev *pdev = slot->ctrl->pcie->port; | ||
| 187 | 189 | ||
| 190 | pci_config_pm_runtime_get(pdev); | ||
| 188 | pciehp_get_adapter_status(slot, value); | 191 | pciehp_get_adapter_status(slot, value); |
| 192 | pci_config_pm_runtime_put(pdev); | ||
| 189 | return 0; | 193 | return 0; |
| 190 | } | 194 | } |
| 191 | 195 | ||
| @@ -196,12 +200,40 @@ static int reset_slot(struct hotplug_slot *hotplug_slot, int probe) | |||
| 196 | return pciehp_reset_slot(slot, probe); | 200 | return pciehp_reset_slot(slot, probe); |
| 197 | } | 201 | } |
| 198 | 202 | ||
| 203 | /** | ||
| 204 | * pciehp_check_presence() - synthesize event if presence has changed | ||
| 205 | * | ||
| 206 | * On probe and resume, an explicit presence check is necessary to bring up an | ||
| 207 | * occupied slot or bring down an unoccupied slot. This can't be triggered by | ||
| 208 | * events in the Slot Status register, they may be stale and are therefore | ||
| 209 | * cleared. Secondly, sending an interrupt for "events that occur while | ||
| 210 | * interrupt generation is disabled [when] interrupt generation is subsequently | ||
| 211 | * enabled" is optional per PCIe r4.0, sec 6.7.3.4. | ||
| 212 | */ | ||
| 213 | static void pciehp_check_presence(struct controller *ctrl) | ||
| 214 | { | ||
| 215 | struct slot *slot = ctrl->slot; | ||
| 216 | u8 occupied; | ||
| 217 | |||
| 218 | down_read(&ctrl->reset_lock); | ||
| 219 | mutex_lock(&slot->lock); | ||
| 220 | |||
| 221 | pciehp_get_adapter_status(slot, &occupied); | ||
| 222 | if ((occupied && (slot->state == OFF_STATE || | ||
| 223 | slot->state == BLINKINGON_STATE)) || | ||
| 224 | (!occupied && (slot->state == ON_STATE || | ||
| 225 | slot->state == BLINKINGOFF_STATE))) | ||
| 226 | pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); | ||
| 227 | |||
| 228 | mutex_unlock(&slot->lock); | ||
| 229 | up_read(&ctrl->reset_lock); | ||
| 230 | } | ||
| 231 | |||
| 199 | static int pciehp_probe(struct pcie_device *dev) | 232 | static int pciehp_probe(struct pcie_device *dev) |
| 200 | { | 233 | { |
| 201 | int rc; | 234 | int rc; |
| 202 | struct controller *ctrl; | 235 | struct controller *ctrl; |
| 203 | struct slot *slot; | 236 | struct slot *slot; |
| 204 | u8 occupied, poweron; | ||
| 205 | 237 | ||
| 206 | /* If this is not a "hotplug" service, we have no business here. */ | 238 | /* If this is not a "hotplug" service, we have no business here. */ |
| 207 | if (dev->service != PCIE_PORT_SERVICE_HP) | 239 | if (dev->service != PCIE_PORT_SERVICE_HP) |
| @@ -238,21 +270,20 @@ static int pciehp_probe(struct pcie_device *dev) | |||
| 238 | goto err_out_free_ctrl_slot; | 270 | goto err_out_free_ctrl_slot; |
| 239 | } | 271 | } |
| 240 | 272 | ||
| 241 | /* Check if slot is occupied */ | 273 | /* Publish to user space */ |
| 242 | slot = ctrl->slot; | 274 | slot = ctrl->slot; |
| 243 | pciehp_get_adapter_status(slot, &occupied); | 275 | rc = pci_hp_add(slot->hotplug_slot); |
| 244 | pciehp_get_power_status(slot, &poweron); | 276 | if (rc) { |
| 245 | if (occupied && pciehp_force) { | 277 | ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc); |
| 246 | mutex_lock(&slot->hotplug_lock); | 278 | goto err_out_shutdown_notification; |
| 247 | pciehp_enable_slot(slot); | ||
| 248 | mutex_unlock(&slot->hotplug_lock); | ||
| 249 | } | 279 | } |
| 250 | /* If empty slot's power status is on, turn power off */ | 280 | |
| 251 | if (!occupied && poweron && POWER_CTRL(ctrl)) | 281 | pciehp_check_presence(ctrl); |
| 252 | pciehp_power_off_slot(slot); | ||
| 253 | 282 | ||
| 254 | return 0; | 283 | return 0; |
| 255 | 284 | ||
| 285 | err_out_shutdown_notification: | ||
| 286 | pcie_shutdown_notification(ctrl); | ||
| 256 | err_out_free_ctrl_slot: | 287 | err_out_free_ctrl_slot: |
| 257 | cleanup_slot(ctrl); | 288 | cleanup_slot(ctrl); |
| 258 | err_out_release_ctlr: | 289 | err_out_release_ctlr: |
| @@ -264,6 +295,8 @@ static void pciehp_remove(struct pcie_device *dev) | |||
| 264 | { | 295 | { |
| 265 | struct controller *ctrl = get_service_data(dev); | 296 | struct controller *ctrl = get_service_data(dev); |
| 266 | 297 | ||
| 298 | pci_hp_del(ctrl->slot->hotplug_slot); | ||
| 299 | pcie_shutdown_notification(ctrl); | ||
| 267 | cleanup_slot(ctrl); | 300 | cleanup_slot(ctrl); |
| 268 | pciehp_release_ctrl(ctrl); | 301 | pciehp_release_ctrl(ctrl); |
| 269 | } | 302 | } |
| @@ -274,27 +307,28 @@ static int pciehp_suspend(struct pcie_device *dev) | |||
| 274 | return 0; | 307 | return 0; |
| 275 | } | 308 | } |
| 276 | 309 | ||
| 277 | static int pciehp_resume(struct pcie_device *dev) | 310 | static int pciehp_resume_noirq(struct pcie_device *dev) |
| 278 | { | 311 | { |
| 279 | struct controller *ctrl; | 312 | struct controller *ctrl = get_service_data(dev); |
| 280 | struct slot *slot; | 313 | struct slot *slot = ctrl->slot; |
| 281 | u8 status; | ||
| 282 | 314 | ||
| 283 | ctrl = get_service_data(dev); | 315 | /* pci_restore_state() just wrote to the Slot Control register */ |
| 316 | ctrl->cmd_started = jiffies; | ||
| 317 | ctrl->cmd_busy = true; | ||
| 284 | 318 | ||
| 285 | /* reinitialize the chipset's event detection logic */ | 319 | /* clear spurious events from rediscovery of inserted card */ |
| 286 | pcie_reenable_notification(ctrl); | 320 | if (slot->state == ON_STATE || slot->state == BLINKINGOFF_STATE) |
| 321 | pcie_clear_hotplug_events(ctrl); | ||
| 287 | 322 | ||
| 288 | slot = ctrl->slot; | 323 | return 0; |
| 324 | } | ||
| 325 | |||
| 326 | static int pciehp_resume(struct pcie_device *dev) | ||
| 327 | { | ||
| 328 | struct controller *ctrl = get_service_data(dev); | ||
| 329 | |||
| 330 | pciehp_check_presence(ctrl); | ||
| 289 | 331 | ||
| 290 | /* Check if slot is occupied */ | ||
| 291 | pciehp_get_adapter_status(slot, &status); | ||
| 292 | mutex_lock(&slot->hotplug_lock); | ||
| 293 | if (status) | ||
| 294 | pciehp_enable_slot(slot); | ||
| 295 | else | ||
| 296 | pciehp_disable_slot(slot); | ||
| 297 | mutex_unlock(&slot->hotplug_lock); | ||
| 298 | return 0; | 332 | return 0; |
| 299 | } | 333 | } |
| 300 | #endif /* PM */ | 334 | #endif /* PM */ |
| @@ -309,6 +343,7 @@ static struct pcie_port_service_driver hpdriver_portdrv = { | |||
| 309 | 343 | ||
| 310 | #ifdef CONFIG_PM | 344 | #ifdef CONFIG_PM |
| 311 | .suspend = pciehp_suspend, | 345 | .suspend = pciehp_suspend, |
| 346 | .resume_noirq = pciehp_resume_noirq, | ||
| 312 | .resume = pciehp_resume, | 347 | .resume = pciehp_resume, |
| 313 | #endif /* PM */ | 348 | #endif /* PM */ |
| 314 | }; | 349 | }; |
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index c684faa43387..da7c72372ffc 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c | |||
| @@ -17,28 +17,11 @@ | |||
| 17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
| 18 | #include <linux/types.h> | 18 | #include <linux/types.h> |
| 19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
| 20 | #include <linux/pm_runtime.h> | ||
| 20 | #include <linux/pci.h> | 21 | #include <linux/pci.h> |
| 21 | #include "../pci.h" | 22 | #include "../pci.h" |
| 22 | #include "pciehp.h" | 23 | #include "pciehp.h" |
| 23 | 24 | ||
| 24 | static void interrupt_event_handler(struct work_struct *work); | ||
| 25 | |||
| 26 | void pciehp_queue_interrupt_event(struct slot *p_slot, u32 event_type) | ||
| 27 | { | ||
| 28 | struct event_info *info; | ||
| 29 | |||
| 30 | info = kmalloc(sizeof(*info), GFP_ATOMIC); | ||
| 31 | if (!info) { | ||
| 32 | ctrl_err(p_slot->ctrl, "dropped event %d (ENOMEM)\n", event_type); | ||
| 33 | return; | ||
| 34 | } | ||
| 35 | |||
| 36 | INIT_WORK(&info->work, interrupt_event_handler); | ||
| 37 | info->event_type = event_type; | ||
| 38 | info->p_slot = p_slot; | ||
| 39 | queue_work(p_slot->wq, &info->work); | ||
| 40 | } | ||
| 41 | |||
| 42 | /* The following routines constitute the bulk of the | 25 | /* The following routines constitute the bulk of the |
| 43 | hotplug controller logic | 26 | hotplug controller logic |
| 44 | */ | 27 | */ |
| @@ -119,14 +102,11 @@ err_exit: | |||
| 119 | * remove_board - Turns off slot and LEDs | 102 | * remove_board - Turns off slot and LEDs |
| 120 | * @p_slot: slot where board is being removed | 103 | * @p_slot: slot where board is being removed |
| 121 | */ | 104 | */ |
| 122 | static int remove_board(struct slot *p_slot) | 105 | static void remove_board(struct slot *p_slot) |
| 123 | { | 106 | { |
| 124 | int retval; | ||
| 125 | struct controller *ctrl = p_slot->ctrl; | 107 | struct controller *ctrl = p_slot->ctrl; |
| 126 | 108 | ||
| 127 | retval = pciehp_unconfigure_device(p_slot); | 109 | pciehp_unconfigure_device(p_slot); |
| 128 | if (retval) | ||
| 129 | return retval; | ||
| 130 | 110 | ||
| 131 | if (POWER_CTRL(ctrl)) { | 111 | if (POWER_CTRL(ctrl)) { |
| 132 | pciehp_power_off_slot(p_slot); | 112 | pciehp_power_off_slot(p_slot); |
| @@ -141,86 +121,30 @@ static int remove_board(struct slot *p_slot) | |||
| 141 | 121 | ||
| 142 | /* turn off Green LED */ | 122 | /* turn off Green LED */ |
| 143 | pciehp_green_led_off(p_slot); | 123 | pciehp_green_led_off(p_slot); |
| 144 | return 0; | ||
| 145 | } | 124 | } |
| 146 | 125 | ||
| 147 | struct power_work_info { | 126 | static int pciehp_enable_slot(struct slot *slot); |
| 148 | struct slot *p_slot; | 127 | static int pciehp_disable_slot(struct slot *slot); |
| 149 | struct work_struct work; | ||
| 150 | unsigned int req; | ||
| 151 | #define DISABLE_REQ 0 | ||
| 152 | #define ENABLE_REQ 1 | ||
| 153 | }; | ||
| 154 | |||
| 155 | /** | ||
| 156 | * pciehp_power_thread - handle pushbutton events | ||
| 157 | * @work: &struct work_struct describing work to be done | ||
| 158 | * | ||
| 159 | * Scheduled procedure to handle blocking stuff for the pushbuttons. | ||
| 160 | * Handles all pending events and exits. | ||
| 161 | */ | ||
| 162 | static void pciehp_power_thread(struct work_struct *work) | ||
| 163 | { | ||
| 164 | struct power_work_info *info = | ||
| 165 | container_of(work, struct power_work_info, work); | ||
| 166 | struct slot *p_slot = info->p_slot; | ||
| 167 | int ret; | ||
| 168 | |||
| 169 | switch (info->req) { | ||
| 170 | case DISABLE_REQ: | ||
| 171 | mutex_lock(&p_slot->hotplug_lock); | ||
| 172 | pciehp_disable_slot(p_slot); | ||
| 173 | mutex_unlock(&p_slot->hotplug_lock); | ||
| 174 | mutex_lock(&p_slot->lock); | ||
| 175 | p_slot->state = STATIC_STATE; | ||
| 176 | mutex_unlock(&p_slot->lock); | ||
| 177 | break; | ||
| 178 | case ENABLE_REQ: | ||
| 179 | mutex_lock(&p_slot->hotplug_lock); | ||
| 180 | ret = pciehp_enable_slot(p_slot); | ||
| 181 | mutex_unlock(&p_slot->hotplug_lock); | ||
| 182 | if (ret) | ||
| 183 | pciehp_green_led_off(p_slot); | ||
| 184 | mutex_lock(&p_slot->lock); | ||
| 185 | p_slot->state = STATIC_STATE; | ||
| 186 | mutex_unlock(&p_slot->lock); | ||
| 187 | break; | ||
| 188 | default: | ||
| 189 | break; | ||
| 190 | } | ||
| 191 | |||
| 192 | kfree(info); | ||
| 193 | } | ||
| 194 | 128 | ||
| 195 | static void pciehp_queue_power_work(struct slot *p_slot, int req) | 129 | void pciehp_request(struct controller *ctrl, int action) |
| 196 | { | 130 | { |
| 197 | struct power_work_info *info; | 131 | atomic_or(action, &ctrl->pending_events); |
| 198 | 132 | if (!pciehp_poll_mode) | |
| 199 | p_slot->state = (req == ENABLE_REQ) ? POWERON_STATE : POWEROFF_STATE; | 133 | irq_wake_thread(ctrl->pcie->irq, ctrl); |
| 200 | |||
| 201 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
| 202 | if (!info) { | ||
| 203 | ctrl_err(p_slot->ctrl, "no memory to queue %s request\n", | ||
| 204 | (req == ENABLE_REQ) ? "poweron" : "poweroff"); | ||
| 205 | return; | ||
| 206 | } | ||
| 207 | info->p_slot = p_slot; | ||
| 208 | INIT_WORK(&info->work, pciehp_power_thread); | ||
| 209 | info->req = req; | ||
| 210 | queue_work(p_slot->wq, &info->work); | ||
| 211 | } | 134 | } |
| 212 | 135 | ||
| 213 | void pciehp_queue_pushbutton_work(struct work_struct *work) | 136 | void pciehp_queue_pushbutton_work(struct work_struct *work) |
| 214 | { | 137 | { |
| 215 | struct slot *p_slot = container_of(work, struct slot, work.work); | 138 | struct slot *p_slot = container_of(work, struct slot, work.work); |
| 139 | struct controller *ctrl = p_slot->ctrl; | ||
| 216 | 140 | ||
| 217 | mutex_lock(&p_slot->lock); | 141 | mutex_lock(&p_slot->lock); |
| 218 | switch (p_slot->state) { | 142 | switch (p_slot->state) { |
| 219 | case BLINKINGOFF_STATE: | 143 | case BLINKINGOFF_STATE: |
| 220 | pciehp_queue_power_work(p_slot, DISABLE_REQ); | 144 | pciehp_request(ctrl, DISABLE_SLOT); |
| 221 | break; | 145 | break; |
| 222 | case BLINKINGON_STATE: | 146 | case BLINKINGON_STATE: |
| 223 | pciehp_queue_power_work(p_slot, ENABLE_REQ); | 147 | pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); |
| 224 | break; | 148 | break; |
| 225 | default: | 149 | default: |
| 226 | break; | 150 | break; |
| @@ -228,18 +152,15 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) | |||
| 228 | mutex_unlock(&p_slot->lock); | 152 | mutex_unlock(&p_slot->lock); |
| 229 | } | 153 | } |
| 230 | 154 | ||
| 231 | /* | 155 | void pciehp_handle_button_press(struct slot *p_slot) |
| 232 | * Note: This function must be called with slot->lock held | ||
| 233 | */ | ||
| 234 | static void handle_button_press_event(struct slot *p_slot) | ||
| 235 | { | 156 | { |
| 236 | struct controller *ctrl = p_slot->ctrl; | 157 | struct controller *ctrl = p_slot->ctrl; |
| 237 | u8 getstatus; | ||
| 238 | 158 | ||
| 159 | mutex_lock(&p_slot->lock); | ||
| 239 | switch (p_slot->state) { | 160 | switch (p_slot->state) { |
| 240 | case STATIC_STATE: | 161 | case OFF_STATE: |
| 241 | pciehp_get_power_status(p_slot, &getstatus); | 162 | case ON_STATE: |
| 242 | if (getstatus) { | 163 | if (p_slot->state == ON_STATE) { |
| 243 | p_slot->state = BLINKINGOFF_STATE; | 164 | p_slot->state = BLINKINGOFF_STATE; |
| 244 | ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n", | 165 | ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n", |
| 245 | slot_name(p_slot)); | 166 | slot_name(p_slot)); |
| @@ -251,7 +172,7 @@ static void handle_button_press_event(struct slot *p_slot) | |||
| 251 | /* blink green LED and turn off amber */ | 172 | /* blink green LED and turn off amber */ |
| 252 | pciehp_green_led_blink(p_slot); | 173 | pciehp_green_led_blink(p_slot); |
| 253 | pciehp_set_attention_status(p_slot, 0); | 174 | pciehp_set_attention_status(p_slot, 0); |
| 254 | queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); | 175 | schedule_delayed_work(&p_slot->work, 5 * HZ); |
| 255 | break; | 176 | break; |
| 256 | case BLINKINGOFF_STATE: | 177 | case BLINKINGOFF_STATE: |
| 257 | case BLINKINGON_STATE: | 178 | case BLINKINGON_STATE: |
| @@ -262,118 +183,104 @@ static void handle_button_press_event(struct slot *p_slot) | |||
| 262 | */ | 183 | */ |
| 263 | ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot)); | 184 | ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot)); |
| 264 | cancel_delayed_work(&p_slot->work); | 185 | cancel_delayed_work(&p_slot->work); |
| 265 | if (p_slot->state == BLINKINGOFF_STATE) | 186 | if (p_slot->state == BLINKINGOFF_STATE) { |
| 187 | p_slot->state = ON_STATE; | ||
| 266 | pciehp_green_led_on(p_slot); | 188 | pciehp_green_led_on(p_slot); |
| 267 | else | 189 | } else { |
| 190 | p_slot->state = OFF_STATE; | ||
| 268 | pciehp_green_led_off(p_slot); | 191 | pciehp_green_led_off(p_slot); |
| 192 | } | ||
| 269 | pciehp_set_attention_status(p_slot, 0); | 193 | pciehp_set_attention_status(p_slot, 0); |
| 270 | ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", | 194 | ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", |
| 271 | slot_name(p_slot)); | 195 | slot_name(p_slot)); |
| 272 | p_slot->state = STATIC_STATE; | ||
| 273 | break; | ||
| 274 | case POWEROFF_STATE: | ||
| 275 | case POWERON_STATE: | ||
| 276 | /* | ||
| 277 | * Ignore if the slot is on power-on or power-off state; | ||
| 278 | * this means that the previous attention button action | ||
| 279 | * to hot-add or hot-remove is undergoing | ||
| 280 | */ | ||
| 281 | ctrl_info(ctrl, "Slot(%s): Button ignored\n", | ||
| 282 | slot_name(p_slot)); | ||
| 283 | break; | 196 | break; |
| 284 | default: | 197 | default: |
| 285 | ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", | 198 | ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", |
| 286 | slot_name(p_slot), p_slot->state); | 199 | slot_name(p_slot), p_slot->state); |
| 287 | break; | 200 | break; |
| 288 | } | 201 | } |
| 202 | mutex_unlock(&p_slot->lock); | ||
| 289 | } | 203 | } |
| 290 | 204 | ||
| 291 | /* | 205 | void pciehp_handle_disable_request(struct slot *slot) |
| 292 | * Note: This function must be called with slot->lock held | ||
| 293 | */ | ||
| 294 | static void handle_link_event(struct slot *p_slot, u32 event) | ||
| 295 | { | 206 | { |
| 296 | struct controller *ctrl = p_slot->ctrl; | 207 | struct controller *ctrl = slot->ctrl; |
| 297 | 208 | ||
| 298 | switch (p_slot->state) { | 209 | mutex_lock(&slot->lock); |
| 210 | switch (slot->state) { | ||
| 299 | case BLINKINGON_STATE: | 211 | case BLINKINGON_STATE: |
| 300 | case BLINKINGOFF_STATE: | 212 | case BLINKINGOFF_STATE: |
| 301 | cancel_delayed_work(&p_slot->work); | 213 | cancel_delayed_work(&slot->work); |
| 302 | /* Fall through */ | ||
| 303 | case STATIC_STATE: | ||
| 304 | pciehp_queue_power_work(p_slot, event == INT_LINK_UP ? | ||
| 305 | ENABLE_REQ : DISABLE_REQ); | ||
| 306 | break; | ||
| 307 | case POWERON_STATE: | ||
| 308 | if (event == INT_LINK_UP) { | ||
| 309 | ctrl_info(ctrl, "Slot(%s): Link Up event ignored; already powering on\n", | ||
| 310 | slot_name(p_slot)); | ||
| 311 | } else { | ||
| 312 | ctrl_info(ctrl, "Slot(%s): Link Down event queued; currently getting powered on\n", | ||
| 313 | slot_name(p_slot)); | ||
| 314 | pciehp_queue_power_work(p_slot, DISABLE_REQ); | ||
| 315 | } | ||
| 316 | break; | ||
| 317 | case POWEROFF_STATE: | ||
| 318 | if (event == INT_LINK_UP) { | ||
| 319 | ctrl_info(ctrl, "Slot(%s): Link Up event queued; currently getting powered off\n", | ||
| 320 | slot_name(p_slot)); | ||
| 321 | pciehp_queue_power_work(p_slot, ENABLE_REQ); | ||
| 322 | } else { | ||
| 323 | ctrl_info(ctrl, "Slot(%s): Link Down event ignored; already powering off\n", | ||
| 324 | slot_name(p_slot)); | ||
| 325 | } | ||
| 326 | break; | ||
| 327 | default: | ||
| 328 | ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", | ||
| 329 | slot_name(p_slot), p_slot->state); | ||
| 330 | break; | 214 | break; |
| 331 | } | 215 | } |
| 216 | slot->state = POWEROFF_STATE; | ||
| 217 | mutex_unlock(&slot->lock); | ||
| 218 | |||
| 219 | ctrl->request_result = pciehp_disable_slot(slot); | ||
| 332 | } | 220 | } |
| 333 | 221 | ||
| 334 | static void interrupt_event_handler(struct work_struct *work) | 222 | void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) |
| 335 | { | 223 | { |
| 336 | struct event_info *info = container_of(work, struct event_info, work); | 224 | struct controller *ctrl = slot->ctrl; |
| 337 | struct slot *p_slot = info->p_slot; | 225 | bool link_active; |
| 338 | struct controller *ctrl = p_slot->ctrl; | 226 | u8 present; |
| 339 | 227 | ||
| 340 | mutex_lock(&p_slot->lock); | 228 | /* |
| 341 | switch (info->event_type) { | 229 | * If the slot is on and presence or link has changed, turn it off. |
| 342 | case INT_BUTTON_PRESS: | 230 | * Even if it's occupied again, we cannot assume the card is the same. |
| 343 | handle_button_press_event(p_slot); | 231 | */ |
| 344 | break; | 232 | mutex_lock(&slot->lock); |
| 345 | case INT_POWER_FAULT: | 233 | switch (slot->state) { |
| 346 | if (!POWER_CTRL(ctrl)) | 234 | case BLINKINGOFF_STATE: |
| 347 | break; | 235 | cancel_delayed_work(&slot->work); |
| 348 | pciehp_set_attention_status(p_slot, 1); | 236 | /* fall through */ |
| 349 | pciehp_green_led_off(p_slot); | 237 | case ON_STATE: |
| 350 | break; | 238 | slot->state = POWEROFF_STATE; |
| 351 | case INT_PRESENCE_ON: | 239 | mutex_unlock(&slot->lock); |
| 352 | pciehp_queue_power_work(p_slot, ENABLE_REQ); | 240 | if (events & PCI_EXP_SLTSTA_DLLSC) |
| 241 | ctrl_info(ctrl, "Slot(%s): Link Down\n", | ||
| 242 | slot_name(slot)); | ||
| 243 | if (events & PCI_EXP_SLTSTA_PDC) | ||
| 244 | ctrl_info(ctrl, "Slot(%s): Card not present\n", | ||
| 245 | slot_name(slot)); | ||
| 246 | pciehp_disable_slot(slot); | ||
| 353 | break; | 247 | break; |
| 354 | case INT_PRESENCE_OFF: | 248 | default: |
| 355 | /* | 249 | mutex_unlock(&slot->lock); |
| 356 | * Regardless of surprise capability, we need to | ||
| 357 | * definitely remove a card that has been pulled out! | ||
| 358 | */ | ||
| 359 | pciehp_queue_power_work(p_slot, DISABLE_REQ); | ||
| 360 | break; | 250 | break; |
| 361 | case INT_LINK_UP: | 251 | } |
| 362 | case INT_LINK_DOWN: | 252 | |
| 363 | handle_link_event(p_slot, info->event_type); | 253 | /* Turn the slot on if it's occupied or link is up */ |
| 254 | mutex_lock(&slot->lock); | ||
| 255 | pciehp_get_adapter_status(slot, &present); | ||
| 256 | link_active = pciehp_check_link_active(ctrl); | ||
| 257 | if (!present && !link_active) { | ||
| 258 | mutex_unlock(&slot->lock); | ||
| 259 | return; | ||
| 260 | } | ||
| 261 | |||
| 262 | switch (slot->state) { | ||
| 263 | case BLINKINGON_STATE: | ||
| 264 | cancel_delayed_work(&slot->work); | ||
| 265 | /* fall through */ | ||
| 266 | case OFF_STATE: | ||
| 267 | slot->state = POWERON_STATE; | ||
| 268 | mutex_unlock(&slot->lock); | ||
| 269 | if (present) | ||
| 270 | ctrl_info(ctrl, "Slot(%s): Card present\n", | ||
| 271 | slot_name(slot)); | ||
| 272 | if (link_active) | ||
| 273 | ctrl_info(ctrl, "Slot(%s): Link Up\n", | ||
| 274 | slot_name(slot)); | ||
| 275 | ctrl->request_result = pciehp_enable_slot(slot); | ||
| 364 | break; | 276 | break; |
| 365 | default: | 277 | default: |
| 278 | mutex_unlock(&slot->lock); | ||
| 366 | break; | 279 | break; |
| 367 | } | 280 | } |
| 368 | mutex_unlock(&p_slot->lock); | ||
| 369 | |||
| 370 | kfree(info); | ||
| 371 | } | 281 | } |
| 372 | 282 | ||
| 373 | /* | 283 | static int __pciehp_enable_slot(struct slot *p_slot) |
| 374 | * Note: This function must be called with slot->hotplug_lock held | ||
| 375 | */ | ||
| 376 | int pciehp_enable_slot(struct slot *p_slot) | ||
| 377 | { | 284 | { |
| 378 | u8 getstatus = 0; | 285 | u8 getstatus = 0; |
| 379 | struct controller *ctrl = p_slot->ctrl; | 286 | struct controller *ctrl = p_slot->ctrl; |
| @@ -404,17 +311,29 @@ int pciehp_enable_slot(struct slot *p_slot) | |||
| 404 | return board_added(p_slot); | 311 | return board_added(p_slot); |
| 405 | } | 312 | } |
| 406 | 313 | ||
| 407 | /* | 314 | static int pciehp_enable_slot(struct slot *slot) |
| 408 | * Note: This function must be called with slot->hotplug_lock held | 315 | { |
| 409 | */ | 316 | struct controller *ctrl = slot->ctrl; |
| 410 | int pciehp_disable_slot(struct slot *p_slot) | 317 | int ret; |
| 318 | |||
| 319 | pm_runtime_get_sync(&ctrl->pcie->port->dev); | ||
| 320 | ret = __pciehp_enable_slot(slot); | ||
| 321 | if (ret && ATTN_BUTTN(ctrl)) | ||
| 322 | pciehp_green_led_off(slot); /* may be blinking */ | ||
| 323 | pm_runtime_put(&ctrl->pcie->port->dev); | ||
| 324 | |||
| 325 | mutex_lock(&slot->lock); | ||
| 326 | slot->state = ret ? OFF_STATE : ON_STATE; | ||
| 327 | mutex_unlock(&slot->lock); | ||
| 328 | |||
| 329 | return ret; | ||
| 330 | } | ||
| 331 | |||
| 332 | static int __pciehp_disable_slot(struct slot *p_slot) | ||
| 411 | { | 333 | { |
| 412 | u8 getstatus = 0; | 334 | u8 getstatus = 0; |
| 413 | struct controller *ctrl = p_slot->ctrl; | 335 | struct controller *ctrl = p_slot->ctrl; |
| 414 | 336 | ||
| 415 | if (!p_slot->ctrl) | ||
| 416 | return 1; | ||
| 417 | |||
| 418 | if (POWER_CTRL(p_slot->ctrl)) { | 337 | if (POWER_CTRL(p_slot->ctrl)) { |
| 419 | pciehp_get_power_status(p_slot, &getstatus); | 338 | pciehp_get_power_status(p_slot, &getstatus); |
| 420 | if (!getstatus) { | 339 | if (!getstatus) { |
| @@ -424,32 +343,50 @@ int pciehp_disable_slot(struct slot *p_slot) | |||
| 424 | } | 343 | } |
| 425 | } | 344 | } |
| 426 | 345 | ||
| 427 | return remove_board(p_slot); | 346 | remove_board(p_slot); |
| 347 | return 0; | ||
| 348 | } | ||
| 349 | |||
| 350 | static int pciehp_disable_slot(struct slot *slot) | ||
| 351 | { | ||
| 352 | struct controller *ctrl = slot->ctrl; | ||
| 353 | int ret; | ||
| 354 | |||
| 355 | pm_runtime_get_sync(&ctrl->pcie->port->dev); | ||
| 356 | ret = __pciehp_disable_slot(slot); | ||
| 357 | pm_runtime_put(&ctrl->pcie->port->dev); | ||
| 358 | |||
| 359 | mutex_lock(&slot->lock); | ||
| 360 | slot->state = OFF_STATE; | ||
| 361 | mutex_unlock(&slot->lock); | ||
| 362 | |||
| 363 | return ret; | ||
| 428 | } | 364 | } |
| 429 | 365 | ||
| 430 | int pciehp_sysfs_enable_slot(struct slot *p_slot) | 366 | int pciehp_sysfs_enable_slot(struct slot *p_slot) |
| 431 | { | 367 | { |
| 432 | int retval = -ENODEV; | ||
| 433 | struct controller *ctrl = p_slot->ctrl; | 368 | struct controller *ctrl = p_slot->ctrl; |
| 434 | 369 | ||
| 435 | mutex_lock(&p_slot->lock); | 370 | mutex_lock(&p_slot->lock); |
| 436 | switch (p_slot->state) { | 371 | switch (p_slot->state) { |
| 437 | case BLINKINGON_STATE: | 372 | case BLINKINGON_STATE: |
| 438 | cancel_delayed_work(&p_slot->work); | 373 | case OFF_STATE: |
| 439 | case STATIC_STATE: | ||
| 440 | p_slot->state = POWERON_STATE; | ||
| 441 | mutex_unlock(&p_slot->lock); | 374 | mutex_unlock(&p_slot->lock); |
| 442 | mutex_lock(&p_slot->hotplug_lock); | 375 | /* |
| 443 | retval = pciehp_enable_slot(p_slot); | 376 | * The IRQ thread becomes a no-op if the user pulls out the |
| 444 | mutex_unlock(&p_slot->hotplug_lock); | 377 | * card before the thread wakes up, so initialize to -ENODEV. |
| 445 | mutex_lock(&p_slot->lock); | 378 | */ |
| 446 | p_slot->state = STATIC_STATE; | 379 | ctrl->request_result = -ENODEV; |
| 447 | break; | 380 | pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); |
| 381 | wait_event(ctrl->requester, | ||
| 382 | !atomic_read(&ctrl->pending_events)); | ||
| 383 | return ctrl->request_result; | ||
| 448 | case POWERON_STATE: | 384 | case POWERON_STATE: |
| 449 | ctrl_info(ctrl, "Slot(%s): Already in powering on state\n", | 385 | ctrl_info(ctrl, "Slot(%s): Already in powering on state\n", |
| 450 | slot_name(p_slot)); | 386 | slot_name(p_slot)); |
| 451 | break; | 387 | break; |
| 452 | case BLINKINGOFF_STATE: | 388 | case BLINKINGOFF_STATE: |
| 389 | case ON_STATE: | ||
| 453 | case POWEROFF_STATE: | 390 | case POWEROFF_STATE: |
| 454 | ctrl_info(ctrl, "Slot(%s): Already enabled\n", | 391 | ctrl_info(ctrl, "Slot(%s): Already enabled\n", |
| 455 | slot_name(p_slot)); | 392 | slot_name(p_slot)); |
| @@ -461,32 +398,28 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot) | |||
| 461 | } | 398 | } |
| 462 | mutex_unlock(&p_slot->lock); | 399 | mutex_unlock(&p_slot->lock); |
| 463 | 400 | ||
| 464 | return retval; | 401 | return -ENODEV; |
| 465 | } | 402 | } |
| 466 | 403 | ||
| 467 | int pciehp_sysfs_disable_slot(struct slot *p_slot) | 404 | int pciehp_sysfs_disable_slot(struct slot *p_slot) |
| 468 | { | 405 | { |
| 469 | int retval = -ENODEV; | ||
| 470 | struct controller *ctrl = p_slot->ctrl; | 406 | struct controller *ctrl = p_slot->ctrl; |
| 471 | 407 | ||
| 472 | mutex_lock(&p_slot->lock); | 408 | mutex_lock(&p_slot->lock); |
| 473 | switch (p_slot->state) { | 409 | switch (p_slot->state) { |
| 474 | case BLINKINGOFF_STATE: | 410 | case BLINKINGOFF_STATE: |
| 475 | cancel_delayed_work(&p_slot->work); | 411 | case ON_STATE: |
| 476 | case STATIC_STATE: | ||
| 477 | p_slot->state = POWEROFF_STATE; | ||
| 478 | mutex_unlock(&p_slot->lock); | 412 | mutex_unlock(&p_slot->lock); |
| 479 | mutex_lock(&p_slot->hotplug_lock); | 413 | pciehp_request(ctrl, DISABLE_SLOT); |
| 480 | retval = pciehp_disable_slot(p_slot); | 414 | wait_event(ctrl->requester, |
| 481 | mutex_unlock(&p_slot->hotplug_lock); | 415 | !atomic_read(&ctrl->pending_events)); |
| 482 | mutex_lock(&p_slot->lock); | 416 | return ctrl->request_result; |
| 483 | p_slot->state = STATIC_STATE; | ||
| 484 | break; | ||
| 485 | case POWEROFF_STATE: | 417 | case POWEROFF_STATE: |
| 486 | ctrl_info(ctrl, "Slot(%s): Already in powering off state\n", | 418 | ctrl_info(ctrl, "Slot(%s): Already in powering off state\n", |
| 487 | slot_name(p_slot)); | 419 | slot_name(p_slot)); |
| 488 | break; | 420 | break; |
| 489 | case BLINKINGON_STATE: | 421 | case BLINKINGON_STATE: |
| 422 | case OFF_STATE: | ||
| 490 | case POWERON_STATE: | 423 | case POWERON_STATE: |
| 491 | ctrl_info(ctrl, "Slot(%s): Already disabled\n", | 424 | ctrl_info(ctrl, "Slot(%s): Already disabled\n", |
| 492 | slot_name(p_slot)); | 425 | slot_name(p_slot)); |
| @@ -498,5 +431,5 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot) | |||
| 498 | } | 431 | } |
| 499 | mutex_unlock(&p_slot->lock); | 432 | mutex_unlock(&p_slot->lock); |
| 500 | 433 | ||
| 501 | return retval; | 434 | return -ENODEV; |
| 502 | } | 435 | } |
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 718b6073afad..5b15e76f3564 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
| @@ -17,8 +17,9 @@ | |||
| 17 | #include <linux/types.h> | 17 | #include <linux/types.h> |
| 18 | #include <linux/signal.h> | 18 | #include <linux/signal.h> |
| 19 | #include <linux/jiffies.h> | 19 | #include <linux/jiffies.h> |
| 20 | #include <linux/timer.h> | 20 | #include <linux/kthread.h> |
| 21 | #include <linux/pci.h> | 21 | #include <linux/pci.h> |
| 22 | #include <linux/pm_runtime.h> | ||
| 22 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
| 23 | #include <linux/time.h> | 24 | #include <linux/time.h> |
| 24 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
| @@ -31,47 +32,24 @@ static inline struct pci_dev *ctrl_dev(struct controller *ctrl) | |||
| 31 | return ctrl->pcie->port; | 32 | return ctrl->pcie->port; |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | static irqreturn_t pcie_isr(int irq, void *dev_id); | 35 | static irqreturn_t pciehp_isr(int irq, void *dev_id); |
| 35 | static void start_int_poll_timer(struct controller *ctrl, int sec); | 36 | static irqreturn_t pciehp_ist(int irq, void *dev_id); |
| 36 | 37 | static int pciehp_poll(void *data); | |
| 37 | /* This is the interrupt polling timeout function. */ | ||
| 38 | static void int_poll_timeout(struct timer_list *t) | ||
| 39 | { | ||
| 40 | struct controller *ctrl = from_timer(ctrl, t, poll_timer); | ||
| 41 | |||
| 42 | /* Poll for interrupt events. regs == NULL => polling */ | ||
| 43 | pcie_isr(0, ctrl); | ||
| 44 | |||
| 45 | if (!pciehp_poll_time) | ||
| 46 | pciehp_poll_time = 2; /* default polling interval is 2 sec */ | ||
| 47 | |||
| 48 | start_int_poll_timer(ctrl, pciehp_poll_time); | ||
| 49 | } | ||
| 50 | |||
| 51 | /* This function starts the interrupt polling timer. */ | ||
| 52 | static void start_int_poll_timer(struct controller *ctrl, int sec) | ||
| 53 | { | ||
| 54 | /* Clamp to sane value */ | ||
| 55 | if ((sec <= 0) || (sec > 60)) | ||
| 56 | sec = 2; | ||
| 57 | |||
| 58 | ctrl->poll_timer.expires = jiffies + sec * HZ; | ||
| 59 | add_timer(&ctrl->poll_timer); | ||
| 60 | } | ||
| 61 | 38 | ||
| 62 | static inline int pciehp_request_irq(struct controller *ctrl) | 39 | static inline int pciehp_request_irq(struct controller *ctrl) |
| 63 | { | 40 | { |
| 64 | int retval, irq = ctrl->pcie->irq; | 41 | int retval, irq = ctrl->pcie->irq; |
| 65 | 42 | ||
| 66 | /* Install interrupt polling timer. Start with 10 sec delay */ | ||
| 67 | if (pciehp_poll_mode) { | 43 | if (pciehp_poll_mode) { |
| 68 | timer_setup(&ctrl->poll_timer, int_poll_timeout, 0); | 44 | ctrl->poll_thread = kthread_run(&pciehp_poll, ctrl, |
| 69 | start_int_poll_timer(ctrl, 10); | 45 | "pciehp_poll-%s", |
| 70 | return 0; | 46 | slot_name(ctrl->slot)); |
| 47 | return PTR_ERR_OR_ZERO(ctrl->poll_thread); | ||
| 71 | } | 48 | } |
| 72 | 49 | ||
| 73 | /* Installs the interrupt handler */ | 50 | /* Installs the interrupt handler */ |
| 74 | retval = request_irq(irq, pcie_isr, IRQF_SHARED, MY_NAME, ctrl); | 51 | retval = request_threaded_irq(irq, pciehp_isr, pciehp_ist, |
| 52 | IRQF_SHARED, MY_NAME, ctrl); | ||
| 75 | if (retval) | 53 | if (retval) |
| 76 | ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n", | 54 | ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n", |
| 77 | irq); | 55 | irq); |
| @@ -81,7 +59,7 @@ static inline int pciehp_request_irq(struct controller *ctrl) | |||
| 81 | static inline void pciehp_free_irq(struct controller *ctrl) | 59 | static inline void pciehp_free_irq(struct controller *ctrl) |
| 82 | { | 60 | { |
| 83 | if (pciehp_poll_mode) | 61 | if (pciehp_poll_mode) |
| 84 | del_timer_sync(&ctrl->poll_timer); | 62 | kthread_stop(ctrl->poll_thread); |
| 85 | else | 63 | else |
| 86 | free_irq(ctrl->pcie->irq, ctrl); | 64 | free_irq(ctrl->pcie->irq, ctrl); |
| 87 | } | 65 | } |
| @@ -293,6 +271,11 @@ int pciehp_check_link_status(struct controller *ctrl) | |||
| 293 | found = pci_bus_check_dev(ctrl->pcie->port->subordinate, | 271 | found = pci_bus_check_dev(ctrl->pcie->port->subordinate, |
| 294 | PCI_DEVFN(0, 0)); | 272 | PCI_DEVFN(0, 0)); |
| 295 | 273 | ||
| 274 | /* ignore link or presence changes up to this point */ | ||
| 275 | if (found) | ||
| 276 | atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC), | ||
| 277 | &ctrl->pending_events); | ||
| 278 | |||
| 296 | pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); | 279 | pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); |
| 297 | ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); | 280 | ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); |
| 298 | if ((lnk_status & PCI_EXP_LNKSTA_LT) || | 281 | if ((lnk_status & PCI_EXP_LNKSTA_LT) || |
| @@ -339,7 +322,9 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, | |||
| 339 | struct pci_dev *pdev = ctrl_dev(slot->ctrl); | 322 | struct pci_dev *pdev = ctrl_dev(slot->ctrl); |
| 340 | u16 slot_ctrl; | 323 | u16 slot_ctrl; |
| 341 | 324 | ||
| 325 | pci_config_pm_runtime_get(pdev); | ||
| 342 | pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); | 326 | pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); |
| 327 | pci_config_pm_runtime_put(pdev); | ||
| 343 | *status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6; | 328 | *status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6; |
| 344 | return 0; | 329 | return 0; |
| 345 | } | 330 | } |
| @@ -350,7 +335,9 @@ void pciehp_get_attention_status(struct slot *slot, u8 *status) | |||
| 350 | struct pci_dev *pdev = ctrl_dev(ctrl); | 335 | struct pci_dev *pdev = ctrl_dev(ctrl); |
| 351 | u16 slot_ctrl; | 336 | u16 slot_ctrl; |
| 352 | 337 | ||
| 338 | pci_config_pm_runtime_get(pdev); | ||
| 353 | pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); | 339 | pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); |
| 340 | pci_config_pm_runtime_put(pdev); | ||
| 354 | ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__, | 341 | ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__, |
| 355 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); | 342 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); |
| 356 | 343 | ||
| @@ -425,9 +412,12 @@ int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot, | |||
| 425 | { | 412 | { |
| 426 | struct slot *slot = hotplug_slot->private; | 413 | struct slot *slot = hotplug_slot->private; |
| 427 | struct controller *ctrl = slot->ctrl; | 414 | struct controller *ctrl = slot->ctrl; |
| 415 | struct pci_dev *pdev = ctrl_dev(ctrl); | ||
| 428 | 416 | ||
| 417 | pci_config_pm_runtime_get(pdev); | ||
| 429 | pcie_write_cmd_nowait(ctrl, status << 6, | 418 | pcie_write_cmd_nowait(ctrl, status << 6, |
| 430 | PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC); | 419 | PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC); |
| 420 | pci_config_pm_runtime_put(pdev); | ||
| 431 | return 0; | 421 | return 0; |
| 432 | } | 422 | } |
| 433 | 423 | ||
| @@ -539,20 +529,35 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id) | |||
| 539 | { | 529 | { |
| 540 | struct controller *ctrl = (struct controller *)dev_id; | 530 | struct controller *ctrl = (struct controller *)dev_id; |
| 541 | struct pci_dev *pdev = ctrl_dev(ctrl); | 531 | struct pci_dev *pdev = ctrl_dev(ctrl); |
| 542 | struct pci_bus *subordinate = pdev->subordinate; | 532 | struct device *parent = pdev->dev.parent; |
| 543 | struct pci_dev *dev; | ||
| 544 | struct slot *slot = ctrl->slot; | ||
| 545 | u16 status, events; | 533 | u16 status, events; |
| 546 | u8 present; | ||
| 547 | bool link; | ||
| 548 | 534 | ||
| 549 | /* Interrupts cannot originate from a controller that's asleep */ | 535 | /* |
| 536 | * Interrupts only occur in D3hot or shallower (PCIe r4.0, sec 6.7.3.4). | ||
| 537 | */ | ||
| 550 | if (pdev->current_state == PCI_D3cold) | 538 | if (pdev->current_state == PCI_D3cold) |
| 551 | return IRQ_NONE; | 539 | return IRQ_NONE; |
| 552 | 540 | ||
| 541 | /* | ||
| 542 | * Keep the port accessible by holding a runtime PM ref on its parent. | ||
| 543 | * Defer resume of the parent to the IRQ thread if it's suspended. | ||
| 544 | * Mask the interrupt until then. | ||
| 545 | */ | ||
| 546 | if (parent) { | ||
| 547 | pm_runtime_get_noresume(parent); | ||
| 548 | if (!pm_runtime_active(parent)) { | ||
| 549 | pm_runtime_put(parent); | ||
| 550 | disable_irq_nosync(irq); | ||
| 551 | atomic_or(RERUN_ISR, &ctrl->pending_events); | ||
| 552 | return IRQ_WAKE_THREAD; | ||
| 553 | } | ||
| 554 | } | ||
| 555 | |||
| 553 | pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status); | 556 | pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status); |
| 554 | if (status == (u16) ~0) { | 557 | if (status == (u16) ~0) { |
| 555 | ctrl_info(ctrl, "%s: no response from device\n", __func__); | 558 | ctrl_info(ctrl, "%s: no response from device\n", __func__); |
| 559 | if (parent) | ||
| 560 | pm_runtime_put(parent); | ||
| 556 | return IRQ_NONE; | 561 | return IRQ_NONE; |
| 557 | } | 562 | } |
| 558 | 563 | ||
| @@ -571,86 +576,119 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id) | |||
| 571 | if (ctrl->power_fault_detected) | 576 | if (ctrl->power_fault_detected) |
| 572 | events &= ~PCI_EXP_SLTSTA_PFD; | 577 | events &= ~PCI_EXP_SLTSTA_PFD; |
| 573 | 578 | ||
| 574 | if (!events) | 579 | if (!events) { |
| 580 | if (parent) | ||
| 581 | pm_runtime_put(parent); | ||
| 575 | return IRQ_NONE; | 582 | return IRQ_NONE; |
| 576 | 583 | } | |
| 577 | /* Capture link status before clearing interrupts */ | ||
| 578 | if (events & PCI_EXP_SLTSTA_DLLSC) | ||
| 579 | link = pciehp_check_link_active(ctrl); | ||
| 580 | 584 | ||
| 581 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events); | 585 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events); |
| 582 | ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events); | 586 | ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events); |
| 587 | if (parent) | ||
| 588 | pm_runtime_put(parent); | ||
| 583 | 589 | ||
| 584 | /* Check Command Complete Interrupt Pending */ | 590 | /* |
| 591 | * Command Completed notifications are not deferred to the | ||
| 592 | * IRQ thread because it may be waiting for their arrival. | ||
| 593 | */ | ||
| 585 | if (events & PCI_EXP_SLTSTA_CC) { | 594 | if (events & PCI_EXP_SLTSTA_CC) { |
| 586 | ctrl->cmd_busy = 0; | 595 | ctrl->cmd_busy = 0; |
| 587 | smp_mb(); | 596 | smp_mb(); |
| 588 | wake_up(&ctrl->queue); | 597 | wake_up(&ctrl->queue); |
| 598 | |||
| 599 | if (events == PCI_EXP_SLTSTA_CC) | ||
| 600 | return IRQ_HANDLED; | ||
| 601 | |||
| 602 | events &= ~PCI_EXP_SLTSTA_CC; | ||
| 603 | } | ||
| 604 | |||
| 605 | if (pdev->ignore_hotplug) { | ||
| 606 | ctrl_dbg(ctrl, "ignoring hotplug event %#06x\n", events); | ||
| 607 | return IRQ_HANDLED; | ||
| 589 | } | 608 | } |
| 590 | 609 | ||
| 591 | if (subordinate) { | 610 | /* Save pending events for consumption by IRQ thread. */ |
| 592 | list_for_each_entry(dev, &subordinate->devices, bus_list) { | 611 | atomic_or(events, &ctrl->pending_events); |
| 593 | if (dev->ignore_hotplug) { | 612 | return IRQ_WAKE_THREAD; |
| 594 | ctrl_dbg(ctrl, "ignoring hotplug event %#06x (%s requested no hotplug)\n", | 613 | } |
| 595 | events, pci_name(dev)); | 614 | |
| 596 | return IRQ_HANDLED; | 615 | static irqreturn_t pciehp_ist(int irq, void *dev_id) |
| 597 | } | 616 | { |
| 617 | struct controller *ctrl = (struct controller *)dev_id; | ||
| 618 | struct pci_dev *pdev = ctrl_dev(ctrl); | ||
| 619 | struct slot *slot = ctrl->slot; | ||
| 620 | irqreturn_t ret; | ||
| 621 | u32 events; | ||
| 622 | |||
| 623 | pci_config_pm_runtime_get(pdev); | ||
| 624 | |||
| 625 | /* rerun pciehp_isr() if the port was inaccessible on interrupt */ | ||
| 626 | if (atomic_fetch_and(~RERUN_ISR, &ctrl->pending_events) & RERUN_ISR) { | ||
| 627 | ret = pciehp_isr(irq, dev_id); | ||
| 628 | enable_irq(irq); | ||
| 629 | if (ret != IRQ_WAKE_THREAD) { | ||
| 630 | pci_config_pm_runtime_put(pdev); | ||
| 631 | return ret; | ||
| 598 | } | 632 | } |
| 599 | } | 633 | } |
| 600 | 634 | ||
| 635 | synchronize_hardirq(irq); | ||
| 636 | events = atomic_xchg(&ctrl->pending_events, 0); | ||
| 637 | if (!events) { | ||
| 638 | pci_config_pm_runtime_put(pdev); | ||
| 639 | return IRQ_NONE; | ||
| 640 | } | ||
| 641 | |||
| 601 | /* Check Attention Button Pressed */ | 642 | /* Check Attention Button Pressed */ |
| 602 | if (events & PCI_EXP_SLTSTA_ABP) { | 643 | if (events & PCI_EXP_SLTSTA_ABP) { |
| 603 | ctrl_info(ctrl, "Slot(%s): Attention button pressed\n", | 644 | ctrl_info(ctrl, "Slot(%s): Attention button pressed\n", |
| 604 | slot_name(slot)); | 645 | slot_name(slot)); |
| 605 | pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS); | 646 | pciehp_handle_button_press(slot); |
| 606 | } | 647 | } |
| 607 | 648 | ||
| 608 | /* | 649 | /* |
| 609 | * Check Link Status Changed at higher precedence than Presence | 650 | * Disable requests have higher priority than Presence Detect Changed |
| 610 | * Detect Changed. The PDS value may be set to "card present" from | 651 | * or Data Link Layer State Changed events. |
| 611 | * out-of-band detection, which may be in conflict with a Link Down | ||
| 612 | * and cause the wrong event to queue. | ||
| 613 | */ | 652 | */ |
| 614 | if (events & PCI_EXP_SLTSTA_DLLSC) { | 653 | down_read(&ctrl->reset_lock); |
| 615 | ctrl_info(ctrl, "Slot(%s): Link %s\n", slot_name(slot), | 654 | if (events & DISABLE_SLOT) |
| 616 | link ? "Up" : "Down"); | 655 | pciehp_handle_disable_request(slot); |
| 617 | pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP : | 656 | else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC)) |
| 618 | INT_LINK_DOWN); | 657 | pciehp_handle_presence_or_link_change(slot, events); |
| 619 | } else if (events & PCI_EXP_SLTSTA_PDC) { | 658 | up_read(&ctrl->reset_lock); |
| 620 | present = !!(status & PCI_EXP_SLTSTA_PDS); | ||
| 621 | ctrl_info(ctrl, "Slot(%s): Card %spresent\n", slot_name(slot), | ||
| 622 | present ? "" : "not "); | ||
| 623 | pciehp_queue_interrupt_event(slot, present ? INT_PRESENCE_ON : | ||
| 624 | INT_PRESENCE_OFF); | ||
| 625 | } | ||
| 626 | 659 | ||
| 627 | /* Check Power Fault Detected */ | 660 | /* Check Power Fault Detected */ |
| 628 | if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { | 661 | if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { |
| 629 | ctrl->power_fault_detected = 1; | 662 | ctrl->power_fault_detected = 1; |
| 630 | ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot)); | 663 | ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot)); |
| 631 | pciehp_queue_interrupt_event(slot, INT_POWER_FAULT); | 664 | pciehp_set_attention_status(slot, 1); |
| 665 | pciehp_green_led_off(slot); | ||
| 632 | } | 666 | } |
| 633 | 667 | ||
| 668 | pci_config_pm_runtime_put(pdev); | ||
| 669 | wake_up(&ctrl->requester); | ||
| 634 | return IRQ_HANDLED; | 670 | return IRQ_HANDLED; |
| 635 | } | 671 | } |
| 636 | 672 | ||
| 637 | static irqreturn_t pcie_isr(int irq, void *dev_id) | 673 | static int pciehp_poll(void *data) |
| 638 | { | 674 | { |
| 639 | irqreturn_t rc, handled = IRQ_NONE; | 675 | struct controller *ctrl = data; |
| 640 | 676 | ||
| 641 | /* | 677 | schedule_timeout_idle(10 * HZ); /* start with 10 sec delay */ |
| 642 | * To guarantee that all interrupt events are serviced, we need to | 678 | |
| 643 | * re-inspect Slot Status register after clearing what is presumed | 679 | while (!kthread_should_stop()) { |
| 644 | * to be the last pending interrupt. | 680 | /* poll for interrupt events or user requests */ |
| 645 | */ | 681 | while (pciehp_isr(IRQ_NOTCONNECTED, ctrl) == IRQ_WAKE_THREAD || |
| 646 | do { | 682 | atomic_read(&ctrl->pending_events)) |
| 647 | rc = pciehp_isr(irq, dev_id); | 683 | pciehp_ist(IRQ_NOTCONNECTED, ctrl); |
| 648 | if (rc == IRQ_HANDLED) | 684 | |
| 649 | handled = IRQ_HANDLED; | 685 | if (pciehp_poll_time <= 0 || pciehp_poll_time > 60) |
| 650 | } while (rc == IRQ_HANDLED); | 686 | pciehp_poll_time = 2; /* clamp to sane value */ |
| 687 | |||
| 688 | schedule_timeout_idle(pciehp_poll_time * HZ); | ||
| 689 | } | ||
| 651 | 690 | ||
| 652 | /* Return IRQ_HANDLED if we handled one or more events */ | 691 | return 0; |
| 653 | return handled; | ||
| 654 | } | 692 | } |
| 655 | 693 | ||
| 656 | static void pcie_enable_notification(struct controller *ctrl) | 694 | static void pcie_enable_notification(struct controller *ctrl) |
| @@ -691,17 +729,6 @@ static void pcie_enable_notification(struct controller *ctrl) | |||
| 691 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd); | 729 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd); |
| 692 | } | 730 | } |
| 693 | 731 | ||
| 694 | void pcie_reenable_notification(struct controller *ctrl) | ||
| 695 | { | ||
| 696 | /* | ||
| 697 | * Clear both Presence and Data Link Layer Changed to make sure | ||
| 698 | * those events still fire after we have re-enabled them. | ||
| 699 | */ | ||
| 700 | pcie_capability_write_word(ctrl->pcie->port, PCI_EXP_SLTSTA, | ||
| 701 | PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); | ||
| 702 | pcie_enable_notification(ctrl); | ||
| 703 | } | ||
| 704 | |||
| 705 | static void pcie_disable_notification(struct controller *ctrl) | 732 | static void pcie_disable_notification(struct controller *ctrl) |
| 706 | { | 733 | { |
| 707 | u16 mask; | 734 | u16 mask; |
| @@ -715,6 +742,12 @@ static void pcie_disable_notification(struct controller *ctrl) | |||
| 715 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0); | 742 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0); |
| 716 | } | 743 | } |
| 717 | 744 | ||
| 745 | void pcie_clear_hotplug_events(struct controller *ctrl) | ||
| 746 | { | ||
| 747 | pcie_capability_write_word(ctrl_dev(ctrl), PCI_EXP_SLTSTA, | ||
| 748 | PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); | ||
| 749 | } | ||
| 750 | |||
| 718 | /* | 751 | /* |
| 719 | * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary | 752 | * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary |
| 720 | * bus reset of the bridge, but at the same time we want to ensure that it is | 753 | * bus reset of the bridge, but at the same time we want to ensure that it is |
| @@ -732,6 +765,8 @@ int pciehp_reset_slot(struct slot *slot, int probe) | |||
| 732 | if (probe) | 765 | if (probe) |
| 733 | return 0; | 766 | return 0; |
| 734 | 767 | ||
| 768 | down_write(&ctrl->reset_lock); | ||
| 769 | |||
| 735 | if (!ATTN_BUTTN(ctrl)) { | 770 | if (!ATTN_BUTTN(ctrl)) { |
| 736 | ctrl_mask |= PCI_EXP_SLTCTL_PDCE; | 771 | ctrl_mask |= PCI_EXP_SLTCTL_PDCE; |
| 737 | stat_mask |= PCI_EXP_SLTSTA_PDC; | 772 | stat_mask |= PCI_EXP_SLTSTA_PDC; |
| @@ -742,8 +777,6 @@ int pciehp_reset_slot(struct slot *slot, int probe) | |||
| 742 | pcie_write_cmd(ctrl, 0, ctrl_mask); | 777 | pcie_write_cmd(ctrl, 0, ctrl_mask); |
| 743 | ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, | 778 | ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, |
| 744 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0); | 779 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0); |
| 745 | if (pciehp_poll_mode) | ||
| 746 | del_timer_sync(&ctrl->poll_timer); | ||
| 747 | 780 | ||
| 748 | pci_reset_bridge_secondary_bus(ctrl->pcie->port); | 781 | pci_reset_bridge_secondary_bus(ctrl->pcie->port); |
| 749 | 782 | ||
| @@ -751,8 +784,8 @@ int pciehp_reset_slot(struct slot *slot, int probe) | |||
| 751 | pcie_write_cmd_nowait(ctrl, ctrl_mask, ctrl_mask); | 784 | pcie_write_cmd_nowait(ctrl, ctrl_mask, ctrl_mask); |
| 752 | ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, | 785 | ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, |
| 753 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask); | 786 | pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask); |
| 754 | if (pciehp_poll_mode) | 787 | |
| 755 | int_poll_timeout(&ctrl->poll_timer); | 788 | up_write(&ctrl->reset_lock); |
| 756 | return 0; | 789 | return 0; |
| 757 | } | 790 | } |
| 758 | 791 | ||
| @@ -765,7 +798,7 @@ int pcie_init_notification(struct controller *ctrl) | |||
| 765 | return 0; | 798 | return 0; |
| 766 | } | 799 | } |
| 767 | 800 | ||
| 768 | static void pcie_shutdown_notification(struct controller *ctrl) | 801 | void pcie_shutdown_notification(struct controller *ctrl) |
| 769 | { | 802 | { |
| 770 | if (ctrl->notification_enabled) { | 803 | if (ctrl->notification_enabled) { |
| 771 | pcie_disable_notification(ctrl); | 804 | pcie_disable_notification(ctrl); |
| @@ -776,32 +809,29 @@ static void pcie_shutdown_notification(struct controller *ctrl) | |||
| 776 | 809 | ||
| 777 | static int pcie_init_slot(struct controller *ctrl) | 810 | static int pcie_init_slot(struct controller *ctrl) |
| 778 | { | 811 | { |
| 812 | struct pci_bus *subordinate = ctrl_dev(ctrl)->subordinate; | ||
| 779 | struct slot *slot; | 813 | struct slot *slot; |
| 780 | 814 | ||
| 781 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); | 815 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); |
| 782 | if (!slot) | 816 | if (!slot) |
| 783 | return -ENOMEM; | 817 | return -ENOMEM; |
| 784 | 818 | ||
| 785 | slot->wq = alloc_ordered_workqueue("pciehp-%u", 0, PSN(ctrl)); | 819 | down_read(&pci_bus_sem); |
| 786 | if (!slot->wq) | 820 | slot->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE; |
| 787 | goto abort; | 821 | up_read(&pci_bus_sem); |
| 788 | 822 | ||
| 789 | slot->ctrl = ctrl; | 823 | slot->ctrl = ctrl; |
| 790 | mutex_init(&slot->lock); | 824 | mutex_init(&slot->lock); |
| 791 | mutex_init(&slot->hotplug_lock); | ||
| 792 | INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); | 825 | INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); |
| 793 | ctrl->slot = slot; | 826 | ctrl->slot = slot; |
| 794 | return 0; | 827 | return 0; |
| 795 | abort: | ||
| 796 | kfree(slot); | ||
| 797 | return -ENOMEM; | ||
| 798 | } | 828 | } |
| 799 | 829 | ||
| 800 | static void pcie_cleanup_slot(struct controller *ctrl) | 830 | static void pcie_cleanup_slot(struct controller *ctrl) |
| 801 | { | 831 | { |
| 802 | struct slot *slot = ctrl->slot; | 832 | struct slot *slot = ctrl->slot; |
| 803 | cancel_delayed_work(&slot->work); | 833 | |
| 804 | destroy_workqueue(slot->wq); | 834 | cancel_delayed_work_sync(&slot->work); |
| 805 | kfree(slot); | 835 | kfree(slot); |
| 806 | } | 836 | } |
| 807 | 837 | ||
| @@ -826,6 +856,7 @@ struct controller *pcie_init(struct pcie_device *dev) | |||
| 826 | { | 856 | { |
| 827 | struct controller *ctrl; | 857 | struct controller *ctrl; |
| 828 | u32 slot_cap, link_cap; | 858 | u32 slot_cap, link_cap; |
| 859 | u8 occupied, poweron; | ||
| 829 | struct pci_dev *pdev = dev->port; | 860 | struct pci_dev *pdev = dev->port; |
| 830 | 861 | ||
| 831 | ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); | 862 | ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); |
| @@ -847,6 +878,8 @@ struct controller *pcie_init(struct pcie_device *dev) | |||
| 847 | 878 | ||
| 848 | ctrl->slot_cap = slot_cap; | 879 | ctrl->slot_cap = slot_cap; |
| 849 | mutex_init(&ctrl->ctrl_lock); | 880 | mutex_init(&ctrl->ctrl_lock); |
| 881 | init_rwsem(&ctrl->reset_lock); | ||
| 882 | init_waitqueue_head(&ctrl->requester); | ||
| 850 | init_waitqueue_head(&ctrl->queue); | 883 | init_waitqueue_head(&ctrl->queue); |
| 851 | dbg_ctrl(ctrl); | 884 | dbg_ctrl(ctrl); |
| 852 | 885 | ||
| @@ -855,16 +888,11 @@ struct controller *pcie_init(struct pcie_device *dev) | |||
| 855 | if (link_cap & PCI_EXP_LNKCAP_DLLLARC) | 888 | if (link_cap & PCI_EXP_LNKCAP_DLLLARC) |
| 856 | ctrl->link_active_reporting = 1; | 889 | ctrl->link_active_reporting = 1; |
| 857 | 890 | ||
| 858 | /* | 891 | /* Clear all remaining event bits in Slot Status register. */ |
| 859 | * Clear all remaining event bits in Slot Status register except | ||
| 860 | * Presence Detect Changed. We want to make sure possible | ||
| 861 | * hotplug event is triggered when the interrupt is unmasked so | ||
| 862 | * that we don't lose that event. | ||
| 863 | */ | ||
| 864 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, | 892 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, |
| 865 | PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | | 893 | PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | |
| 866 | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC | | 894 | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC | |
| 867 | PCI_EXP_SLTSTA_DLLSC); | 895 | PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC); |
| 868 | 896 | ||
| 869 | ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c LLActRep%c%s\n", | 897 | ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c LLActRep%c%s\n", |
| 870 | (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, | 898 | (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, |
| @@ -883,6 +911,19 @@ struct controller *pcie_init(struct pcie_device *dev) | |||
| 883 | if (pcie_init_slot(ctrl)) | 911 | if (pcie_init_slot(ctrl)) |
| 884 | goto abort_ctrl; | 912 | goto abort_ctrl; |
| 885 | 913 | ||
| 914 | /* | ||
| 915 | * If empty slot's power status is on, turn power off. The IRQ isn't | ||
| 916 | * requested yet, so avoid triggering a notification with this command. | ||
| 917 | */ | ||
| 918 | if (POWER_CTRL(ctrl)) { | ||
| 919 | pciehp_get_adapter_status(ctrl->slot, &occupied); | ||
| 920 | pciehp_get_power_status(ctrl->slot, &poweron); | ||
| 921 | if (!occupied && poweron) { | ||
| 922 | pcie_disable_notification(ctrl); | ||
| 923 | pciehp_power_off_slot(ctrl->slot); | ||
| 924 | } | ||
| 925 | } | ||
| 926 | |||
| 886 | return ctrl; | 927 | return ctrl; |
| 887 | 928 | ||
| 888 | abort_ctrl: | 929 | abort_ctrl: |
| @@ -893,7 +934,6 @@ abort: | |||
| 893 | 934 | ||
| 894 | void pciehp_release_ctrl(struct controller *ctrl) | 935 | void pciehp_release_ctrl(struct controller *ctrl) |
| 895 | { | 936 | { |
| 896 | pcie_shutdown_notification(ctrl); | ||
| 897 | pcie_cleanup_slot(ctrl); | 937 | pcie_cleanup_slot(ctrl); |
| 898 | kfree(ctrl); | 938 | kfree(ctrl); |
| 899 | } | 939 | } |
diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 3f518dea856d..5c58c22e0c08 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c | |||
| @@ -62,9 +62,8 @@ int pciehp_configure_device(struct slot *p_slot) | |||
| 62 | return ret; | 62 | return ret; |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | int pciehp_unconfigure_device(struct slot *p_slot) | 65 | void pciehp_unconfigure_device(struct slot *p_slot) |
| 66 | { | 66 | { |
| 67 | int rc = 0; | ||
| 68 | u8 presence = 0; | 67 | u8 presence = 0; |
| 69 | struct pci_dev *dev, *temp; | 68 | struct pci_dev *dev, *temp; |
| 70 | struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; | 69 | struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; |
| @@ -107,5 +106,4 @@ int pciehp_unconfigure_device(struct slot *p_slot) | |||
| 107 | } | 106 | } |
| 108 | 107 | ||
| 109 | pci_unlock_rescan_remove(); | 108 | pci_unlock_rescan_remove(); |
| 110 | return rc; | ||
| 111 | } | 109 | } |
diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c deleted file mode 100644 index c19694a04d2c..000000000000 --- a/drivers/pci/hotplug/pcihp_skeleton.c +++ /dev/null | |||
| @@ -1,348 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * PCI Hot Plug Controller Skeleton Driver - 0.3 | ||
| 4 | * | ||
| 5 | * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) | ||
| 6 | * Copyright (C) 2001,2003 IBM Corp. | ||
| 7 | * | ||
| 8 | * All rights reserved. | ||
| 9 | * | ||
| 10 | * This driver is to be used as a skeleton driver to show how to interface | ||
| 11 | * with the pci hotplug core easily. | ||
| 12 | * | ||
| 13 | * Send feedback to <greg@kroah.com> | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/moduleparam.h> | ||
| 19 | #include <linux/kernel.h> | ||
| 20 | #include <linux/slab.h> | ||
| 21 | #include <linux/pci.h> | ||
| 22 | #include <linux/pci_hotplug.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | |||
| 25 | #define SLOT_NAME_SIZE 10 | ||
| 26 | struct slot { | ||
| 27 | u8 number; | ||
| 28 | struct hotplug_slot *hotplug_slot; | ||
| 29 | struct list_head slot_list; | ||
| 30 | char name[SLOT_NAME_SIZE]; | ||
| 31 | }; | ||
| 32 | |||
| 33 | static LIST_HEAD(slot_list); | ||
| 34 | |||
| 35 | #define MY_NAME "pcihp_skeleton" | ||
| 36 | |||
| 37 | #define dbg(format, arg...) \ | ||
| 38 | do { \ | ||
| 39 | if (debug) \ | ||
| 40 | printk(KERN_DEBUG "%s: " format "\n", \ | ||
| 41 | MY_NAME, ## arg); \ | ||
| 42 | } while (0) | ||
| 43 | #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg) | ||
| 44 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg) | ||
| 45 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg) | ||
| 46 | |||
| 47 | /* local variables */ | ||
| 48 | static bool debug; | ||
| 49 | static int num_slots; | ||
| 50 | |||
| 51 | #define DRIVER_VERSION "0.3" | ||
| 52 | #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>" | ||
| 53 | #define DRIVER_DESC "Hot Plug PCI Controller Skeleton Driver" | ||
| 54 | |||
| 55 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
| 56 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
| 57 | MODULE_LICENSE("GPL"); | ||
| 58 | module_param(debug, bool, 0644); | ||
| 59 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); | ||
| 60 | |||
| 61 | static int enable_slot(struct hotplug_slot *slot); | ||
| 62 | static int disable_slot(struct hotplug_slot *slot); | ||
| 63 | static int set_attention_status(struct hotplug_slot *slot, u8 value); | ||
| 64 | static int hardware_test(struct hotplug_slot *slot, u32 value); | ||
| 65 | static int get_power_status(struct hotplug_slot *slot, u8 *value); | ||
| 66 | static int get_attention_status(struct hotplug_slot *slot, u8 *value); | ||
| 67 | static int get_latch_status(struct hotplug_slot *slot, u8 *value); | ||
| 68 | static int get_adapter_status(struct hotplug_slot *slot, u8 *value); | ||
| 69 | |||
| 70 | static struct hotplug_slot_ops skel_hotplug_slot_ops = { | ||
| 71 | .enable_slot = enable_slot, | ||
| 72 | .disable_slot = disable_slot, | ||
| 73 | .set_attention_status = set_attention_status, | ||
| 74 | .hardware_test = hardware_test, | ||
| 75 | .get_power_status = get_power_status, | ||
| 76 | .get_attention_status = get_attention_status, | ||
| 77 | .get_latch_status = get_latch_status, | ||
| 78 | .get_adapter_status = get_adapter_status, | ||
| 79 | }; | ||
| 80 | |||
| 81 | static int enable_slot(struct hotplug_slot *hotplug_slot) | ||
| 82 | { | ||
| 83 | struct slot *slot = hotplug_slot->private; | ||
| 84 | int retval = 0; | ||
| 85 | |||
| 86 | dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); | ||
| 87 | |||
| 88 | /* | ||
| 89 | * Fill in code here to enable the specified slot | ||
| 90 | */ | ||
| 91 | |||
| 92 | return retval; | ||
| 93 | } | ||
| 94 | |||
| 95 | static int disable_slot(struct hotplug_slot *hotplug_slot) | ||
| 96 | { | ||
| 97 | struct slot *slot = hotplug_slot->private; | ||
| 98 | int retval = 0; | ||
| 99 | |||
| 100 | dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); | ||
| 101 | |||
| 102 | /* | ||
| 103 | * Fill in code here to disable the specified slot | ||
| 104 | */ | ||
| 105 | |||
| 106 | return retval; | ||
| 107 | } | ||
| 108 | |||
| 109 | static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) | ||
| 110 | { | ||
| 111 | struct slot *slot = hotplug_slot->private; | ||
| 112 | int retval = 0; | ||
| 113 | |||
| 114 | dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); | ||
| 115 | |||
| 116 | switch (status) { | ||
| 117 | case 0: | ||
| 118 | /* | ||
| 119 | * Fill in code here to turn light off | ||
| 120 | */ | ||
| 121 | break; | ||
| 122 | |||
| 123 | case 1: | ||
| 124 | default: | ||
| 125 | /* | ||
| 126 | * Fill in code here to turn light on | ||
| 127 | */ | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | |||
| 131 | return retval; | ||
| 132 | } | ||
| 133 | |||
| 134 | static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value) | ||
| 135 | { | ||
| 136 | struct slot *slot = hotplug_slot->private; | ||
| 137 | int retval = 0; | ||
| 138 | |||
| 139 | dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); | ||
| 140 | |||
| 141 | switch (value) { | ||
| 142 | case 0: | ||
| 143 | /* Specify a test here */ | ||
| 144 | break; | ||
| 145 | case 1: | ||
| 146 | /* Specify another test here */ | ||
| 147 | break; | ||
| 148 | } | ||
| 149 | |||
| 150 | return retval; | ||
| 151 | } | ||
| 152 | |||
| 153 | static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
| 154 | { | ||
| 155 | struct slot *slot = hotplug_slot->private; | ||
| 156 | int retval = 0; | ||
| 157 | |||
| 158 | dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); | ||
| 159 | |||
| 160 | /* | ||
| 161 | * Fill in logic to get the current power status of the specific | ||
| 162 | * slot and store it in the *value location. | ||
| 163 | */ | ||
| 164 | |||
| 165 | return retval; | ||
| 166 | } | ||
| 167 | |||
| 168 | static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
| 169 | { | ||
| 170 | struct slot *slot = hotplug_slot->private; | ||
| 171 | int retval = 0; | ||
| 172 | |||
| 173 | dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); | ||
| 174 | |||
| 175 | /* | ||
| 176 | * Fill in logic to get the current attention status of the specific | ||
| 177 | * slot and store it in the *value location. | ||
| 178 | */ | ||
| 179 | |||
| 180 | return retval; | ||
| 181 | } | ||
| 182 | |||
| 183 | static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
| 184 | { | ||
| 185 | struct slot *slot = hotplug_slot->private; | ||
| 186 | int retval = 0; | ||
| 187 | |||
| 188 | dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); | ||
| 189 | |||
| 190 | /* | ||
| 191 | * Fill in logic to get the current latch status of the specific | ||
| 192 | * slot and store it in the *value location. | ||
| 193 | */ | ||
| 194 | |||
| 195 | return retval; | ||
| 196 | } | ||
| 197 | |||
| 198 | static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
| 199 | { | ||
| 200 | struct slot *slot = hotplug_slot->private; | ||
| 201 | int retval = 0; | ||
| 202 | |||
| 203 | dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); | ||
| 204 | |||
| 205 | /* | ||
| 206 | * Fill in logic to get the current adapter status of the specific | ||
| 207 | * slot and store it in the *value location. | ||
| 208 | */ | ||
| 209 | |||
| 210 | return retval; | ||
| 211 | } | ||
| 212 | |||
| 213 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
| 214 | { | ||
| 215 | struct slot *slot = hotplug_slot->private; | ||
| 216 | |||
| 217 | dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); | ||
| 218 | kfree(slot->hotplug_slot->info); | ||
| 219 | kfree(slot->hotplug_slot); | ||
| 220 | kfree(slot); | ||
| 221 | } | ||
| 222 | |||
| 223 | static void make_slot_name(struct slot *slot) | ||
| 224 | { | ||
| 225 | /* | ||
| 226 | * Stupid way to make a filename out of the slot name. | ||
| 227 | * replace this if your hardware provides a better way to name slots. | ||
| 228 | */ | ||
| 229 | snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", slot->number); | ||
| 230 | } | ||
| 231 | |||
| 232 | /** | ||
| 233 | * init_slots - initialize 'struct slot' structures for each slot | ||
| 234 | * | ||
| 235 | */ | ||
| 236 | static int __init init_slots(void) | ||
| 237 | { | ||
| 238 | struct slot *slot; | ||
| 239 | struct hotplug_slot *hotplug_slot; | ||
| 240 | struct hotplug_slot_info *info; | ||
| 241 | int retval; | ||
| 242 | int i; | ||
| 243 | |||
| 244 | /* | ||
| 245 | * Create a structure for each slot, and register that slot | ||
| 246 | * with the pci_hotplug subsystem. | ||
| 247 | */ | ||
| 248 | for (i = 0; i < num_slots; ++i) { | ||
| 249 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); | ||
| 250 | if (!slot) { | ||
| 251 | retval = -ENOMEM; | ||
| 252 | goto error; | ||
| 253 | } | ||
| 254 | |||
| 255 | hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); | ||
| 256 | if (!hotplug_slot) { | ||
| 257 | retval = -ENOMEM; | ||
| 258 | goto error_slot; | ||
| 259 | } | ||
| 260 | slot->hotplug_slot = hotplug_slot; | ||
| 261 | |||
| 262 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
| 263 | if (!info) { | ||
| 264 | retval = -ENOMEM; | ||
| 265 | goto error_hpslot; | ||
| 266 | } | ||
| 267 | hotplug_slot->info = info; | ||
| 268 | |||
| 269 | slot->number = i; | ||
| 270 | |||
| 271 | hotplug_slot->name = slot->name; | ||
| 272 | hotplug_slot->private = slot; | ||
| 273 | hotplug_slot->release = &release_slot; | ||
| 274 | make_slot_name(slot); | ||
| 275 | hotplug_slot->ops = &skel_hotplug_slot_ops; | ||
| 276 | |||
| 277 | /* | ||
| 278 | * Initialize the slot info structure with some known | ||
| 279 | * good values. | ||
| 280 | */ | ||
| 281 | get_power_status(hotplug_slot, &info->power_status); | ||
| 282 | get_attention_status(hotplug_slot, &info->attention_status); | ||
| 283 | get_latch_status(hotplug_slot, &info->latch_status); | ||
| 284 | get_adapter_status(hotplug_slot, &info->adapter_status); | ||
| 285 | |||
| 286 | dbg("registering slot %d\n", i); | ||
| 287 | retval = pci_hp_register(slot->hotplug_slot); | ||
| 288 | if (retval) { | ||
| 289 | err("pci_hp_register failed with error %d\n", retval); | ||
| 290 | goto error_info; | ||
| 291 | } | ||
| 292 | |||
| 293 | /* add slot to our internal list */ | ||
| 294 | list_add(&slot->slot_list, &slot_list); | ||
| 295 | } | ||
| 296 | |||
| 297 | return 0; | ||
| 298 | error_info: | ||
| 299 | kfree(info); | ||
| 300 | error_hpslot: | ||
| 301 | kfree(hotplug_slot); | ||
| 302 | error_slot: | ||
| 303 | kfree(slot); | ||
| 304 | error: | ||
| 305 | return retval; | ||
| 306 | } | ||
| 307 | |||
| 308 | static void __exit cleanup_slots(void) | ||
| 309 | { | ||
| 310 | struct slot *slot, *next; | ||
| 311 | |||
| 312 | /* | ||
| 313 | * Unregister all of our slots with the pci_hotplug subsystem. | ||
| 314 | * Memory will be freed in release_slot() callback after slot's | ||
| 315 | * lifespan is finished. | ||
| 316 | */ | ||
| 317 | list_for_each_entry_safe(slot, next, &slot_list, slot_list) { | ||
| 318 | list_del(&slot->slot_list); | ||
| 319 | pci_hp_deregister(slot->hotplug_slot); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | static int __init pcihp_skel_init(void) | ||
| 324 | { | ||
| 325 | int retval; | ||
| 326 | |||
| 327 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | ||
| 328 | /* | ||
| 329 | * Do specific initialization stuff for your driver here | ||
| 330 | * like initializing your controller hardware (if any) and | ||
| 331 | * determining the number of slots you have in the system | ||
| 332 | * right now. | ||
| 333 | */ | ||
| 334 | num_slots = 5; | ||
| 335 | |||
| 336 | return init_slots(); | ||
| 337 | } | ||
| 338 | |||
| 339 | static void __exit pcihp_skel_exit(void) | ||
| 340 | { | ||
| 341 | /* | ||
| 342 | * Clean everything up. | ||
| 343 | */ | ||
| 344 | cleanup_slots(); | ||
| 345 | } | ||
| 346 | |||
| 347 | module_init(pcihp_skel_init); | ||
| 348 | module_exit(pcihp_skel_exit); | ||
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index 6c2e8d7307c6..3276a5e4c430 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c | |||
| @@ -538,9 +538,8 @@ static struct hotplug_slot_ops php_slot_ops = { | |||
| 538 | .disable_slot = pnv_php_disable_slot, | 538 | .disable_slot = pnv_php_disable_slot, |
| 539 | }; | 539 | }; |
| 540 | 540 | ||
| 541 | static void pnv_php_release(struct hotplug_slot *slot) | 541 | static void pnv_php_release(struct pnv_php_slot *php_slot) |
| 542 | { | 542 | { |
| 543 | struct pnv_php_slot *php_slot = slot->private; | ||
| 544 | unsigned long flags; | 543 | unsigned long flags; |
| 545 | 544 | ||
| 546 | /* Remove from global or child list */ | 545 | /* Remove from global or child list */ |
| @@ -596,7 +595,6 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn) | |||
| 596 | php_slot->power_state_check = false; | 595 | php_slot->power_state_check = false; |
| 597 | php_slot->slot.ops = &php_slot_ops; | 596 | php_slot->slot.ops = &php_slot_ops; |
| 598 | php_slot->slot.info = &php_slot->slot_info; | 597 | php_slot->slot.info = &php_slot->slot_info; |
| 599 | php_slot->slot.release = pnv_php_release; | ||
| 600 | php_slot->slot.private = php_slot; | 598 | php_slot->slot.private = php_slot; |
| 601 | 599 | ||
| 602 | INIT_LIST_HEAD(&php_slot->children); | 600 | INIT_LIST_HEAD(&php_slot->children); |
| @@ -924,6 +922,7 @@ static void pnv_php_unregister_one(struct device_node *dn) | |||
| 924 | 922 | ||
| 925 | php_slot->state = PNV_PHP_STATE_OFFLINE; | 923 | php_slot->state = PNV_PHP_STATE_OFFLINE; |
| 926 | pci_hp_deregister(&php_slot->slot); | 924 | pci_hp_deregister(&php_slot->slot); |
| 925 | pnv_php_release(php_slot); | ||
| 927 | pnv_php_put_slot(php_slot); | 926 | pnv_php_put_slot(php_slot); |
| 928 | } | 927 | } |
| 929 | 928 | ||
diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index fb5e0845429d..857c358b727b 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c | |||
| @@ -404,13 +404,13 @@ static void __exit cleanup_slots(void) | |||
| 404 | /* | 404 | /* |
| 405 | * Unregister all of our slots with the pci_hotplug subsystem, | 405 | * Unregister all of our slots with the pci_hotplug subsystem, |
| 406 | * and free up all memory that we had allocated. | 406 | * and free up all memory that we had allocated. |
| 407 | * memory will be freed in release_slot callback. | ||
| 408 | */ | 407 | */ |
| 409 | 408 | ||
| 410 | list_for_each_entry_safe(slot, next, &rpaphp_slot_head, | 409 | list_for_each_entry_safe(slot, next, &rpaphp_slot_head, |
| 411 | rpaphp_slot_list) { | 410 | rpaphp_slot_list) { |
| 412 | list_del(&slot->rpaphp_slot_list); | 411 | list_del(&slot->rpaphp_slot_list); |
| 413 | pci_hp_deregister(slot->hotplug_slot); | 412 | pci_hp_deregister(slot->hotplug_slot); |
| 413 | dealloc_slot_struct(slot); | ||
| 414 | } | 414 | } |
| 415 | return; | 415 | return; |
| 416 | } | 416 | } |
diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c index 3840a2075e6a..b916c8e4372d 100644 --- a/drivers/pci/hotplug/rpaphp_slot.c +++ b/drivers/pci/hotplug/rpaphp_slot.c | |||
| @@ -19,12 +19,6 @@ | |||
| 19 | #include "rpaphp.h" | 19 | #include "rpaphp.h" |
| 20 | 20 | ||
| 21 | /* free up the memory used by a slot */ | 21 | /* free up the memory used by a slot */ |
| 22 | static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot) | ||
| 23 | { | ||
| 24 | struct slot *slot = (struct slot *) hotplug_slot->private; | ||
| 25 | dealloc_slot_struct(slot); | ||
| 26 | } | ||
| 27 | |||
| 28 | void dealloc_slot_struct(struct slot *slot) | 22 | void dealloc_slot_struct(struct slot *slot) |
| 29 | { | 23 | { |
| 30 | kfree(slot->hotplug_slot->info); | 24 | kfree(slot->hotplug_slot->info); |
| @@ -56,7 +50,6 @@ struct slot *alloc_slot_struct(struct device_node *dn, | |||
| 56 | slot->power_domain = power_domain; | 50 | slot->power_domain = power_domain; |
| 57 | slot->hotplug_slot->private = slot; | 51 | slot->hotplug_slot->private = slot; |
| 58 | slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops; | 52 | slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops; |
| 59 | slot->hotplug_slot->release = &rpaphp_release_slot; | ||
| 60 | 53 | ||
| 61 | return (slot); | 54 | return (slot); |
| 62 | 55 | ||
| @@ -90,10 +83,8 @@ int rpaphp_deregister_slot(struct slot *slot) | |||
| 90 | __func__, slot->name); | 83 | __func__, slot->name); |
| 91 | 84 | ||
| 92 | list_del(&slot->rpaphp_slot_list); | 85 | list_del(&slot->rpaphp_slot_list); |
| 93 | 86 | pci_hp_deregister(php_slot); | |
| 94 | retval = pci_hp_deregister(php_slot); | 87 | dealloc_slot_struct(slot); |
| 95 | if (retval) | ||
| 96 | err("Problem unregistering a slot %s\n", slot->name); | ||
| 97 | 88 | ||
| 98 | dbg("%s - Exit: rc[%d]\n", __func__, retval); | 89 | dbg("%s - Exit: rc[%d]\n", __func__, retval); |
| 99 | return retval; | 90 | return retval; |
diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index ffdc2977395d..93b5341d282c 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c | |||
| @@ -130,15 +130,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | |||
| 130 | return 0; | 130 | return 0; |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
| 134 | { | ||
| 135 | struct slot *slot = hotplug_slot->private; | ||
| 136 | |||
| 137 | kfree(slot->hotplug_slot->info); | ||
| 138 | kfree(slot->hotplug_slot); | ||
| 139 | kfree(slot); | ||
| 140 | } | ||
| 141 | |||
| 142 | static struct hotplug_slot_ops s390_hotplug_slot_ops = { | 133 | static struct hotplug_slot_ops s390_hotplug_slot_ops = { |
| 143 | .enable_slot = enable_slot, | 134 | .enable_slot = enable_slot, |
| 144 | .disable_slot = disable_slot, | 135 | .disable_slot = disable_slot, |
| @@ -175,7 +166,6 @@ int zpci_init_slot(struct zpci_dev *zdev) | |||
| 175 | hotplug_slot->info = info; | 166 | hotplug_slot->info = info; |
| 176 | 167 | ||
| 177 | hotplug_slot->ops = &s390_hotplug_slot_ops; | 168 | hotplug_slot->ops = &s390_hotplug_slot_ops; |
| 178 | hotplug_slot->release = &release_slot; | ||
| 179 | 169 | ||
| 180 | get_power_status(hotplug_slot, &info->power_status); | 170 | get_power_status(hotplug_slot, &info->power_status); |
| 181 | get_adapter_status(hotplug_slot, &info->adapter_status); | 171 | get_adapter_status(hotplug_slot, &info->adapter_status); |
| @@ -209,5 +199,8 @@ void zpci_exit_slot(struct zpci_dev *zdev) | |||
| 209 | continue; | 199 | continue; |
| 210 | list_del(&slot->slot_list); | 200 | list_del(&slot->slot_list); |
| 211 | pci_hp_deregister(slot->hotplug_slot); | 201 | pci_hp_deregister(slot->hotplug_slot); |
| 202 | kfree(slot->hotplug_slot->info); | ||
| 203 | kfree(slot->hotplug_slot); | ||
| 204 | kfree(slot); | ||
| 212 | } | 205 | } |
| 213 | } | 206 | } |
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 78b6bdbb3a39..babd23409f61 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c | |||
| @@ -628,7 +628,6 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus) | |||
| 628 | goto alloc_err; | 628 | goto alloc_err; |
| 629 | } | 629 | } |
| 630 | bss_hotplug_slot->ops = &sn_hotplug_slot_ops; | 630 | bss_hotplug_slot->ops = &sn_hotplug_slot_ops; |
| 631 | bss_hotplug_slot->release = &sn_release_slot; | ||
| 632 | 631 | ||
| 633 | rc = pci_hp_register(bss_hotplug_slot, pci_bus, device, name); | 632 | rc = pci_hp_register(bss_hotplug_slot, pci_bus, device, name); |
| 634 | if (rc) | 633 | if (rc) |
| @@ -656,8 +655,10 @@ alloc_err: | |||
| 656 | sn_release_slot(bss_hotplug_slot); | 655 | sn_release_slot(bss_hotplug_slot); |
| 657 | 656 | ||
| 658 | /* destroy anything else on the list */ | 657 | /* destroy anything else on the list */ |
| 659 | while ((bss_hotplug_slot = sn_hp_destroy())) | 658 | while ((bss_hotplug_slot = sn_hp_destroy())) { |
| 660 | pci_hp_deregister(bss_hotplug_slot); | 659 | pci_hp_deregister(bss_hotplug_slot); |
| 660 | sn_release_slot(bss_hotplug_slot); | ||
| 661 | } | ||
| 661 | 662 | ||
| 662 | return rc; | 663 | return rc; |
| 663 | } | 664 | } |
| @@ -703,8 +704,10 @@ static void __exit sn_pci_hotplug_exit(void) | |||
| 703 | { | 704 | { |
| 704 | struct hotplug_slot *bss_hotplug_slot; | 705 | struct hotplug_slot *bss_hotplug_slot; |
| 705 | 706 | ||
| 706 | while ((bss_hotplug_slot = sn_hp_destroy())) | 707 | while ((bss_hotplug_slot = sn_hp_destroy())) { |
| 707 | pci_hp_deregister(bss_hotplug_slot); | 708 | pci_hp_deregister(bss_hotplug_slot); |
| 709 | sn_release_slot(bss_hotplug_slot); | ||
| 710 | } | ||
| 708 | 711 | ||
| 709 | if (!list_empty(&sn_hp_list)) | 712 | if (!list_empty(&sn_hp_list)) |
| 710 | printk(KERN_ERR "%s: internal list is not empty\n", __FILE__); | 713 | printk(KERN_ERR "%s: internal list is not empty\n", __FILE__); |
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index e91be287f292..97cee23f3d51 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c | |||
| @@ -61,22 +61,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { | |||
| 61 | .get_adapter_status = get_adapter_status, | 61 | .get_adapter_status = get_adapter_status, |
| 62 | }; | 62 | }; |
| 63 | 63 | ||
| 64 | /** | ||
| 65 | * release_slot - free up the memory used by a slot | ||
| 66 | * @hotplug_slot: slot to free | ||
| 67 | */ | ||
| 68 | static void release_slot(struct hotplug_slot *hotplug_slot) | ||
| 69 | { | ||
| 70 | struct slot *slot = hotplug_slot->private; | ||
| 71 | |||
| 72 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", | ||
| 73 | __func__, slot_name(slot)); | ||
| 74 | |||
| 75 | kfree(slot->hotplug_slot->info); | ||
| 76 | kfree(slot->hotplug_slot); | ||
| 77 | kfree(slot); | ||
| 78 | } | ||
| 79 | |||
| 80 | static int init_slots(struct controller *ctrl) | 64 | static int init_slots(struct controller *ctrl) |
| 81 | { | 65 | { |
| 82 | struct slot *slot; | 66 | struct slot *slot; |
| @@ -125,7 +109,6 @@ static int init_slots(struct controller *ctrl) | |||
| 125 | 109 | ||
| 126 | /* register this slot with the hotplug pci core */ | 110 | /* register this slot with the hotplug pci core */ |
| 127 | hotplug_slot->private = slot; | 111 | hotplug_slot->private = slot; |
| 128 | hotplug_slot->release = &release_slot; | ||
| 129 | snprintf(name, SLOT_NAME_SIZE, "%d", slot->number); | 112 | snprintf(name, SLOT_NAME_SIZE, "%d", slot->number); |
| 130 | hotplug_slot->ops = &shpchp_hotplug_slot_ops; | 113 | hotplug_slot->ops = &shpchp_hotplug_slot_ops; |
| 131 | 114 | ||
| @@ -171,6 +154,9 @@ void cleanup_slots(struct controller *ctrl) | |||
| 171 | cancel_delayed_work(&slot->work); | 154 | cancel_delayed_work(&slot->work); |
| 172 | destroy_workqueue(slot->wq); | 155 | destroy_workqueue(slot->wq); |
| 173 | pci_hp_deregister(slot->hotplug_slot); | 156 | pci_hp_deregister(slot->hotplug_slot); |
| 157 | kfree(slot->hotplug_slot->info); | ||
| 158 | kfree(slot->hotplug_slot); | ||
| 159 | kfree(slot); | ||
| 174 | } | 160 | } |
| 175 | } | 161 | } |
| 176 | 162 | ||
| @@ -270,11 +256,30 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | |||
| 270 | return 0; | 256 | return 0; |
| 271 | } | 257 | } |
| 272 | 258 | ||
| 259 | static bool shpc_capable(struct pci_dev *bridge) | ||
| 260 | { | ||
| 261 | /* | ||
| 262 | * It is assumed that AMD GOLAM chips support SHPC but they do not | ||
| 263 | * have SHPC capability. | ||
| 264 | */ | ||
| 265 | if (bridge->vendor == PCI_VENDOR_ID_AMD && | ||
| 266 | bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450) | ||
| 267 | return true; | ||
| 268 | |||
| 269 | if (pci_find_capability(bridge, PCI_CAP_ID_SHPC)) | ||
| 270 | return true; | ||
| 271 | |||
| 272 | return false; | ||
| 273 | } | ||
| 274 | |||
| 273 | static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | 275 | static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
| 274 | { | 276 | { |
| 275 | int rc; | 277 | int rc; |
| 276 | struct controller *ctrl; | 278 | struct controller *ctrl; |
| 277 | 279 | ||
| 280 | if (!shpc_capable(pdev)) | ||
| 281 | return -ENODEV; | ||
| 282 | |||
| 278 | if (acpi_get_hp_hw_control_from_firmware(pdev)) | 283 | if (acpi_get_hp_hw_control_from_firmware(pdev)) |
| 279 | return -ENODEV; | 284 | return -ENODEV; |
| 280 | 285 | ||
| @@ -303,6 +308,7 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
| 303 | if (rc) | 308 | if (rc) |
| 304 | goto err_cleanup_slots; | 309 | goto err_cleanup_slots; |
| 305 | 310 | ||
| 311 | pdev->shpc_managed = 1; | ||
| 306 | return 0; | 312 | return 0; |
| 307 | 313 | ||
| 308 | err_cleanup_slots: | 314 | err_cleanup_slots: |
| @@ -319,6 +325,7 @@ static void shpc_remove(struct pci_dev *dev) | |||
| 319 | { | 325 | { |
| 320 | struct controller *ctrl = pci_get_drvdata(dev); | 326 | struct controller *ctrl = pci_get_drvdata(dev); |
| 321 | 327 | ||
| 328 | dev->shpc_managed = 0; | ||
| 322 | shpchp_remove_ctrl_files(ctrl); | 329 | shpchp_remove_ctrl_files(ctrl); |
| 323 | ctrl->hpc_ops->release_ctlr(ctrl); | 330 | ctrl->hpc_ops->release_ctlr(ctrl); |
| 324 | kfree(ctrl); | 331 | kfree(ctrl); |
