From 4051f5ebb11c6ef4b0d3eac2fbbd187c070656c5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 19 Apr 2018 13:05:49 +0300 Subject: PCI: ibmphp: Fix use-before-set in get_max_bus_speed() The "rc" variable is only initialized on the error path. The caller doesn't check the return but, if "rc" is non-zero, then this function is basically a no-op. Fixes: 3749c51ac6c1 ("PCI: Make current and maximum bus speeds part of the PCI core") Signed-off-by: Dan Carpenter Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/ibmphp_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index b81ca3fa0e84..1869b0411ce0 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -379,7 +379,7 @@ static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 *value) static int get_max_bus_speed(struct slot *slot) { - int rc; + int rc = 0; u8 mode = 0; enum pci_bus_speed speed; struct pci_bus *bus = slot->hotplug_slot->pci_slot->bus; -- cgit From d22b362184553899f7d6b6760899a77d3b2d7c1b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 3 May 2018 18:39:38 -0500 Subject: PCI: pciehp: Add quirk for Command Completed errata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several PCIe hotplug controllers have errata that mean they do not set the Command Completed bit unless writes to the Slot Command register change "Control" bits. Command Completed is never set for writes that only change software notification "Enable" bits. This results in timeouts like this: pciehp 0000:00:1c.0:pcie004: Timeout on hotplug command 0x1038 (issued 65284 msec ago) When this erratum is present, avoid these timeouts by marking commands "completed" immediately unless they change the "Control" bits. Here's the text of the Intel erratum CF118. We assume this applies to all Intel parts: CF118 PCIe Slot Status Register Command Completed bit not always updated on any configuration write to the Slot Control Register Problem: For PCIe root ports (devices 0 - 10) supporting hot-plug, the Slot Status Register (offset AAh) Command Completed (bit[4]) status is updated under the following condition: IOH will set Command Completed bit after delivering the new commands written in the Slot Controller register (offset A8h) to VPP. The IOH detects new commands written in Slot Control register by checking the change of value for Power Controller Control (bit[10]), Power Indicator Control (bits[9:8]), Attention Indicator Control (bits[7:6]), or Electromechanical Interlock Control (bit[11]) fields. Any other configuration writes to the Slot Control register without changing the values of these fields will not cause Command Completed bit to be set. The PCIe Base Specification Revision 2.0 or later describes the “Slot Control Register” in section 7.8.10, as follows (Reference section 7.8.10, Slot Control Register, Offset 18h). In hot-plug capable Downstream Ports, a write to the Slot Control register must cause a hot-plug command to be generated (see Section 6.7.3.2 for details on hot-plug commands). A write to the Slot Control register in a Downstream Port that is not hotplug capable must not cause a hot-plug command to be executed. The PCIe Spec intended that every write to the Slot Control Register is a command and expected a command complete status to abstract the VPP implementation specific nuances from the OS software. IOH PCIe Slot Control Register implementation is not fully conforming to the PCIe Specification in this respect. Implication: Software checking on the Command Completed status after writing to the Slot Control register may time out. Workaround: Software can read the Slot Control register and compare the existing and new values to determine if it should check the Command Completed status after writing to the Slot Control register. Per Sinan, the Qualcomm QDF2400 controller also does not set the Command Completed bit unless writes to the Slot Command register change "Control" bits. Link: http://www.intel.com/content/www/us/en/processors/xeon/xeon-e7-v2-spec-update.html Link: https://lkml.kernel.org/r/8770820b-85a0-172b-7230-3a44524e6c9f@molgen.mpg.de Reported-by: Paul Menzel # Lenovo X60 Tested-by: Paul Menzel # Lenovo X60 Signed-off-by: Sinan Kaya # Qcom quirk Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/hotplug/pciehp_hpc.c | 51 ++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 12 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 18a42f8f5dc5..74a7f7bde263 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -10,7 +10,6 @@ * All rights reserved. * * Send feedback to , - * */ #include @@ -147,25 +146,22 @@ static void pcie_wait_cmd(struct controller *ctrl) else rc = pcie_poll_cmd(ctrl, jiffies_to_msecs(timeout)); - /* - * Controllers with errata like Intel CF118 don't generate - * completion notifications unless the power/indicator/interlock - * control bits are changed. On such controllers, we'll emit this - * timeout message when we wait for completion of commands that - * don't change those bits, e.g., commands that merely enable - * interrupts. - */ if (!rc) ctrl_info(ctrl, "Timeout on hotplug command %#06x (issued %u msec ago)\n", ctrl->slot_ctrl, jiffies_to_msecs(jiffies - ctrl->cmd_started)); } +#define CC_ERRATUM_MASK (PCI_EXP_SLTCTL_PCC | \ + PCI_EXP_SLTCTL_PIC | \ + PCI_EXP_SLTCTL_AIC | \ + PCI_EXP_SLTCTL_EIC) + static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd, u16 mask, bool wait) { struct pci_dev *pdev = ctrl_dev(ctrl); - u16 slot_ctrl; + u16 slot_ctrl_orig, slot_ctrl; mutex_lock(&ctrl->ctrl_lock); @@ -180,6 +176,7 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd, goto out; } + slot_ctrl_orig = slot_ctrl; slot_ctrl &= ~mask; slot_ctrl |= (cmd & mask); ctrl->cmd_busy = 1; @@ -188,6 +185,17 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd, ctrl->cmd_started = jiffies; ctrl->slot_ctrl = slot_ctrl; + /* + * Controllers with the Intel CF118 and similar errata advertise + * Command Completed support, but they only set Command Completed + * if we change the "Control" bits for power, power indicator, + * attention indicator, or interlock. If we only change the + * "Enable" bits, they never set the Command Completed bit. + */ + if (pdev->broken_cmd_compl && + (slot_ctrl_orig & CC_ERRATUM_MASK) == (slot_ctrl & CC_ERRATUM_MASK)) + ctrl->cmd_busy = 0; + /* * Optionally wait for the hardware to be ready for a new command, * indicating completion of the above issued command. @@ -861,7 +869,7 @@ struct controller *pcie_init(struct pcie_device *dev) PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC); - 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\n", + 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", (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, FLAG(slot_cap, PCI_EXP_SLTCAP_ABP), FLAG(slot_cap, PCI_EXP_SLTCAP_PCP), @@ -872,7 +880,8 @@ struct controller *pcie_init(struct pcie_device *dev) FLAG(slot_cap, PCI_EXP_SLTCAP_HPS), FLAG(slot_cap, PCI_EXP_SLTCAP_EIP), FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS), - FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC)); + FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC), + pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : ""); if (pcie_init_slot(ctrl)) goto abort_ctrl; @@ -891,3 +900,21 @@ void pciehp_release_ctrl(struct controller *ctrl) pcie_cleanup_slot(ctrl); kfree(ctrl); } + +static void quirk_cmd_compl(struct pci_dev *pdev) +{ + u32 slot_cap; + + if (pci_is_pcie(pdev)) { + pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); + if (slot_cap & PCI_EXP_SLTCAP_HPC && + !(slot_cap & PCI_EXP_SLTCAP_NCCS)) + pdev->broken_cmd_compl = 1; + } +} +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, + PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0400, + PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_QCOM, 0x0401, + PCI_CLASS_BRIDGE_PCI, 8, quirk_cmd_compl); -- cgit From 5d9c6b8ae987c7207ee665be81c4ad04949ad15a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 23 May 2018 21:07:15 +0200 Subject: PCI: pnv_php: Add missing of_node_put() The device node iterators perform an of_node_get() on each iteration, so a jump out of the loop requires an of_node_put(). The semantic patch that fixes this problem is as follows (http://coccinelle.lip6.fr): // @@ expression root,e; local idexpression child; iterator name for_each_child_of_node; @@ for_each_child_of_node(root, child) { ... when != of_node_put(child) when != e = child + of_node_put(child); ? break; ... } ... when != child // Signed-off-by: Julia Lawall Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pnv_php.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index d44100687dfe..6c2e8d7307c6 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -220,12 +220,16 @@ static int pnv_php_populate_changeset(struct of_changeset *ocs, for_each_child_of_node(dn, child) { ret = of_changeset_attach_node(ocs, child); - if (ret) + if (ret) { + of_node_put(child); break; + } ret = pnv_php_populate_changeset(ocs, child); - if (ret) + if (ret) { + of_node_put(child); break; + } } return ret; -- cgit From 13c65840feab8109194f9490c9870587173cb29d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 23 May 2018 17:14:39 -0500 Subject: PCI: pciehp: Clear Presence Detect and Data Link Layer Status Changed on resume After a suspend/resume cycle the Presence Detect or Data Link Layer Status Changed bits might be set. If we don't clear them those events will not fire anymore and nothing happens for instance when a device is now hot-unplugged. Fix this by clearing those bits in a newly introduced function pcie_reenable_notification(). This should be fine because immediately after, we check if the adapter is still present by reading directly from the status register. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Cc: stable@vger.kernel.org --- drivers/pci/hotplug/pciehp.h | 2 +- drivers/pci/hotplug/pciehp_core.c | 2 +- drivers/pci/hotplug/pciehp_hpc.c | 13 ++++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 88e917c9120f..5f892065585e 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -121,7 +121,7 @@ struct controller *pcie_init(struct pcie_device *dev); int pcie_init_notification(struct controller *ctrl); int pciehp_enable_slot(struct slot *p_slot); int pciehp_disable_slot(struct slot *p_slot); -void pcie_enable_notification(struct controller *ctrl); +void pcie_reenable_notification(struct controller *ctrl); int pciehp_power_on_slot(struct slot *slot); void pciehp_power_off_slot(struct slot *slot); 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 332b723ff9e6..44a6a63802d5 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -283,7 +283,7 @@ static int pciehp_resume(struct pcie_device *dev) ctrl = get_service_data(dev); /* reinitialize the chipset's event detection logic */ - pcie_enable_notification(ctrl); + pcie_reenable_notification(ctrl); slot = ctrl->slot; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 74a7f7bde263..022e68f7c3ec 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -667,7 +667,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) return handled; } -void pcie_enable_notification(struct controller *ctrl) +static void pcie_enable_notification(struct controller *ctrl) { u16 cmd, mask; @@ -705,6 +705,17 @@ void pcie_enable_notification(struct controller *ctrl) pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd); } +void pcie_reenable_notification(struct controller *ctrl) +{ + /* + * Clear both Presence and Data Link Layer Changed to make sure + * those events still fire after we have re-enabled them. + */ + pcie_capability_write_word(ctrl->pcie->port, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); + pcie_enable_notification(ctrl); +} + static void pcie_disable_notification(struct controller *ctrl) { u16 mask; -- cgit From 82280f7af729125b89d47cb420cac0a761664b32 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 23 May 2018 17:29:38 -0500 Subject: PCI: shpchp: Convert SHPC to be builtin only We need to be able coordinate between SHPC and acpiphp to determine which driver handles hotplug of a given bridge. Because acpiphp is already bool, convert SHPC to be bool as well. Suggested-by: Bjorn Helgaas Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/hotplug/Kconfig | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index a8f21d051e0c..e9f78eb390d2 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -104,14 +104,11 @@ config HOTPLUG_PCI_CPCI_GENERIC When in doubt, say N. config HOTPLUG_PCI_SHPC - tristate "SHPC PCI Hotplug driver" + bool "SHPC PCI Hotplug driver" help Say Y here if you have a motherboard with a SHPC PCI Hotplug controller. - To compile this driver as a module, choose M here: the - module will be called shpchp. - When in doubt, say N. config HOTPLUG_PCI_POWERNV -- cgit From aa6be07c27447a0c07a5d2e7cbcfc1417ab280e2 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 24 May 2018 14:45:55 -0500 Subject: PCI: shpchp: Rely on previous _OSC results If _OSC exists, we evaluated it when adding the ACPI host bridge, and we requested SHPC control if the SHPC driver is present. Use the result of that _OSC evaluation instead of evaluating it again. Signed-off-by: Mika Westerberg [bhelgaas: split to separate patch] Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/acpi_pcihp.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index c9816166978e..65cc5042489a 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -69,16 +69,12 @@ static acpi_status acpi_run_oshp(acpi_handle handle) */ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags) { + const struct pci_host_bridge *host; + const struct acpi_pci_root *root; acpi_status status; acpi_handle chandle, handle; struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; - flags &= OSC_PCI_SHPC_NATIVE_HP_CONTROL; - if (!flags) { - err("Invalid flags %u specified!\n", flags); - return -EINVAL; - } - /* * Per PCI firmware specification, we should run the ACPI _OSC * method to get control of hotplug hardware before using it. If @@ -88,19 +84,14 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags) * OSHP within the scope of the hotplug controller and its parents, * up to the host bridge under which this controller exists. */ - handle = acpi_find_root_bridge_handle(pdev); - if (handle) { - acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); - dbg("Trying to get hotplug control for %s\n", - (char *)string.pointer); - status = acpi_pci_osc_control_set(handle, &flags, flags); - if (ACPI_SUCCESS(status)) - goto got_one; - if (status == AE_SUPPORT) - goto no_control; - kfree(string.pointer); - string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL }; - } + host = pci_find_host_bridge(pdev->bus); + if (host->native_shpc_hotplug) + return 0; + + /* If _OSC exists, we should not evaluate OSHP */ + root = acpi_pci_find_root(ACPI_HANDLE(&host->dev)); + if (root->osc_support_set) + goto no_control; handle = ACPI_HANDLE(&pdev->dev); if (!handle) { -- cgit From 6f77fa4941aac0fa721eef5fe61820a4c314fffb Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 23 May 2018 17:32:23 -0500 Subject: PCI: shpchp: Remove acpi_get_hp_hw_control_from_firmware() flags acpi_get_hp_hw_control_from_firmware() no longer uses the flags parameter, so remove it. Signed-off-by: Mika Westerberg [bhelgaas: split to separate patch] Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/hotplug/acpi_pcihp.c | 3 +-- drivers/pci/hotplug/shpchp.h | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 65cc5042489a..7cc50cfef9c6 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -63,11 +63,10 @@ static acpi_status acpi_run_oshp(acpi_handle handle) /** * acpi_get_hp_hw_control_from_firmware * @dev: the pci_dev of the bridge that has a hotplug controller - * @flags: requested control bits for _OSC * * Attempt to take hotplug control from firmware. */ -int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags) +int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) { const struct pci_host_bridge *host; const struct acpi_pci_root *root; diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index c55730b61c9a..71f44edaa94d 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -177,8 +177,7 @@ static inline const char *slot_name(struct slot *slot) #include static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev) { - u32 flags = OSC_PCI_SHPC_NATIVE_HP_CONTROL; - return acpi_get_hp_hw_control_from_firmware(dev, flags); + return acpi_get_hp_hw_control_from_firmware(dev); } #else #define get_hp_hw_control_from_firmware(dev) (0) -- cgit From 96a621e01a42dc53848e2e4915fd807ebc1fc82f Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 24 May 2018 15:10:21 -0500 Subject: PCI: shpchp: Remove get_hp_hw_control_from_firmware() wrapper get_hp_hw_control_from_firmware() is a trivial wrapper around acpi_get_hp_hw_control_from_firmware(), probably intended to be generic in case other firmware needed similar OS/platform negotiation. Remove get_hp_hw_control_from_firmware() and call acpi_get_hp_hw_control_from_firmware() directly. Add a stub for acpi_get_hp_hw_control_from_firmware() for the non-ACPI case. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/hotplug/shpchp.h | 10 ---------- drivers/pci/hotplug/shpchp_core.c | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 71f44edaa94d..9675ab757323 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -173,16 +173,6 @@ static inline const char *slot_name(struct slot *slot) return hotplug_slot_name(slot->hotplug_slot); } -#ifdef CONFIG_ACPI -#include -static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev) -{ - return acpi_get_hp_hw_control_from_firmware(dev); -} -#else -#define get_hp_hw_control_from_firmware(dev) (0) -#endif - struct ctrl_reg { volatile u32 base_offset; volatile u32 slot_avail1; diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 1f0f96908b5a..47decc9b3bb3 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -277,7 +277,7 @@ static int is_shpc_capable(struct pci_dev *dev) return 1; if (!pci_find_capability(dev, PCI_CAP_ID_SHPC)) return 0; - if (get_hp_hw_control_from_firmware(dev)) + if (acpi_get_hp_hw_control_from_firmware(dev)) return 0; return 1; } -- cgit From f2b775f5df661277d1b5662171f58c04adbdc97f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 24 May 2018 16:36:04 -0500 Subject: PCI: shpchp: Use dev_printk() for OSHP-related messages Use dev_printk() for messages related to requesting control of SHPC hotplug via the OSHP method. Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg Reviewed-by: Rafael J. Wysocki --- drivers/pci/hotplug/acpi_pcihp.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 7cc50cfef9c6..597d22aeefc1 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -96,7 +96,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) if (!handle) { /* * This hotplug controller was not listed in the ACPI name - * space at all. Try to get acpi handle of parent pci bus. + * space at all. Try to get ACPI handle of parent PCI bus. */ struct pci_bus *pbus; for (pbus = pdev->bus; pbus; pbus = pbus->parent) { @@ -108,8 +108,8 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) while (handle) { acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); - dbg("Trying to get hotplug control for %s\n", - (char *)string.pointer); + pci_info(pdev, "Requesting control of SHPC hotplug via OSHP (%s)\n", + (char *)string.pointer); status = acpi_run_oshp(handle); if (ACPI_SUCCESS(status)) goto got_one; @@ -121,13 +121,12 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) break; } no_control: - dbg("Cannot get control of hotplug hardware for pci %s\n", - pci_name(pdev)); + pci_info(pdev, "Cannot get control of SHPC hotplug\n"); kfree(string.pointer); return -ENODEV; got_one: - dbg("Gained control for hotplug HW for pci %s (%s)\n", - pci_name(pdev), (char *)string.pointer); + pci_info(pdev, "Gained control of SHPC hotplug (%s)\n", + (char *)string.pointer); kfree(string.pointer); return 0; } -- cgit From bed4e9cfab93a0f3d0144cb919820e6d5c40b8b1 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 30 May 2018 14:06:42 -0500 Subject: PCI: shpchp: Fix AMD POGO identification The fix for an AMD POGO erratum related to SHPC incorrectly identified the device. The workaround should be applied only for AMD POGO devices, but it was instead applied to: - all AMD bridges, and - all devices from any vendor with device ID 0x7458 Fixes: 53044f357448 ("[PATCH] PCI Hotplug: shpchp: AMD POGO errata fix") Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg Reviewed-by: Rafael J. Wysocki --- drivers/pci/hotplug/shpchp_ctrl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index bedda5bda910..1047b56e5730 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c @@ -585,13 +585,13 @@ static int shpchp_enable_slot (struct slot *p_slot) ctrl_dbg(ctrl, "%s: p_slot->pwr_save %x\n", __func__, p_slot->pwr_save); p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); - if (((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) || - (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458)) + if ((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD && + p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458) && p_slot->ctrl->num_slots == 1) { - /* handle amd pogo errata; this must be done before enable */ + /* handle AMD POGO errata; this must be done before enable */ amd_pogo_errata_save_misc_reg(p_slot); retval = board_added(p_slot); - /* handle amd pogo errata; this must be done after enable */ + /* handle AMD POGO errata; this must be done after enable */ amd_pogo_errata_restore_misc_reg(p_slot); } else retval = board_added(p_slot); -- cgit From 90cc0c3cc7092ea4c7871fdd5fb00a9ba62842e3 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 31 May 2018 11:42:11 -0500 Subject: PCI: shpchp: Add shpchp_is_native() In the same way we do for pciehp, add shpchp_is_native(), which returns true if the bridge should be handled by the native SHPC driver. Then convert the driver to use this function. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/acpi_pcihp.c | 4 ++-- drivers/pci/hotplug/shpchp.h | 1 - drivers/pci/hotplug/shpchp_core.c | 14 +------------- 3 files changed, 3 insertions(+), 16 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 597d22aeefc1..3979f89b250a 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -83,11 +83,11 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) * OSHP within the scope of the hotplug controller and its parents, * up to the host bridge under which this controller exists. */ - host = pci_find_host_bridge(pdev->bus); - if (host->native_shpc_hotplug) + if (shpchp_is_native(pdev)) return 0; /* If _OSC exists, we should not evaluate OSHP */ + host = pci_find_host_bridge(pdev->bus); root = acpi_pci_find_root(ACPI_HANDLE(&host->dev)); if (root->osc_support_set) goto no_control; diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 9675ab757323..516e4835019c 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -105,7 +105,6 @@ struct controller { }; /* Define AMD SHPC ID */ -#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450 #define PCI_DEVICE_ID_AMD_POGO_7458 0x7458 /* AMD PCI-X bridge registers */ diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 47decc9b3bb3..e91be287f292 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -270,24 +270,12 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static int is_shpc_capable(struct pci_dev *dev) -{ - if (dev->vendor == PCI_VENDOR_ID_AMD && - dev->device == PCI_DEVICE_ID_AMD_GOLAM_7450) - return 1; - if (!pci_find_capability(dev, PCI_CAP_ID_SHPC)) - return 0; - if (acpi_get_hp_hw_control_from_firmware(dev)) - return 0; - return 1; -} - static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int rc; struct controller *ctrl; - if (!is_shpc_capable(pdev)) + if (acpi_get_hp_hw_control_from_firmware(pdev)) return -ENODEV; ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); -- cgit From 84c8b58ed3addf17d3beb2e5037b001ffa65c5ef Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 29 May 2018 19:01:55 +0300 Subject: ACPI / hotplug / PCI: Don't scan bridges managed by native hotplug When acpiphp re-enumerates a PCI hierarchy because of an ACPI Notify() event, we should skip bridges managed by native hotplug (pciehp or shpchp). We don't want to scan below a native hotplug bridge until the hotplug controller generates a hot-add event. A typical scenario is a Root Port leading to a Thunderbolt host router that remains powered off until something is connected to it. See [1] for the lspci details. 1. Before something is connected, only the Root Port exists. It has PCI_EXP_SLTCAP_HPC set and pciehp is responsible for hotplug: 00:1b.0 Root Port (HotPlug+) 2. When a USB-C or Thunderbolt device is connected, the Switch in the Thunderbolt host router is powered up, the Root Port signals a hotplug add event and pciehp enumerates the Switch: 01:00.0 Switch Upstream Port to [bus 02-39] 02:00.0 Switch Downstream Port to [bus 03] (HotPlug-, to NHI) 02:01.0 Switch Downstream Port to [bus 04-38] (HotPlug+, to Thunderbolt connector) 02:02.0 Switch Downstream Port to [bus 39] (HotPlug-, to xHCI) The 02:00.0 and 02:02.0 Ports lead to Endpoints that are not powered up yet. The Ports have PCI_EXP_SLTCAP_HPC cleared, so pciehp doesn't handle hotplug for them and we assign minimal resources to them. The 02:01.0 Port has PCI_EXP_SLTCAP_HPC set, so pciehp handles native hotplug events for it. 3. The BIOS powers up the xHCI controller. If a Thunderbolt device was connected (not just a USB-C device), it also powers up the NHI. Then it sends an ACPI Notify() to the Root Port, and acpiphp enumerates the new device(s): 03:00.0 Thunderbolt Host Controller (NHI) Endpoint 39:00.0 xHCI Endpoint 4. If a Thunderbolt device was connected, the host router firmware uses the NHI to set up Thunderbolt tunnels and triggers a native hotplug event (via 02:01.0 in this example). Then pciehp enumerates the new Thunderbolt devices: 04:00.0 Switch Upstream Port to [bus 05-38] 05:01.0 Switch Downstream Port to [bus 06-09] (HotPlug-) 05:04.0 Switch Downstream Port to [bus 0a-38] (HotPlug+) In this example, 05:01.0 leads to another Switch and some NICs. This subtree is static, so 05:01.0 doesn't support hotplug and has PCI_EXP_SLTCAP_HPC cleared. In step 3, acpiphp previously enumerated everything below the Root Port, including things below the 02:01.0 Port. We don't want that because pciehp expects to manage hotplug below that Port, and firmware on the host router may be in the middle of configuring its Link so it may not be ready yet. To make this work better with the native PCIe (pciehp) and standard PCI (shpchp) hotplug drivers, we let them handle all slot management and resource allocation for hotplug bridges and restrict ACPI hotplug to non-hotplug bridges. [1] https://bugzilla.kernel.org/show_bug.cgi?id=199581#c5 Link: https://lkml.kernel.org/r/20180529160155.1738-1-mika.westerberg@linux.intel.com Signed-off-by: Mika Westerberg [bhelgaas: changelog, use hotplug_is_native() instead of dev->is_hotplug_bridge] Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko --- drivers/pci/hotplug/acpiphp_glue.c | 75 +++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 17 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index b45b375c0e6c..bc9e19642567 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -287,11 +287,12 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data, /* * Expose slots to user space for functions that have _EJ0 or _RMV or * are located in dock stations. Do not expose them for devices handled - * by the native PCIe hotplug (PCIeHP), becuase that code is supposed to - * expose slots to user space in those cases. + * by the native PCIe hotplug (PCIeHP) or standard PCI hotplug + * (SHPCHP), because that code is supposed to expose slots to user + * space in those cases. */ if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(adev)) - && !(pdev && pdev->is_hotplug_bridge && pciehp_is_native(pdev))) { + && !(pdev && hotplug_is_native(pdev))) { unsigned long long sun; int retval; @@ -430,6 +431,29 @@ static int acpiphp_rescan_slot(struct acpiphp_slot *slot) return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0)); } +static void acpiphp_native_scan_bridge(struct pci_dev *bridge) +{ + struct pci_bus *bus = bridge->subordinate; + struct pci_dev *dev; + int max; + + if (!bus) + return; + + max = bus->busn_res.start; + /* Scan already configured non-hotplug bridges */ + for_each_pci_bridge(dev, bus) { + if (!hotplug_is_native(dev)) + max = pci_scan_bridge(bus, dev, max, 0); + } + + /* Scan non-hotplug bridges that need to be reconfigured */ + for_each_pci_bridge(dev, bus) { + if (!hotplug_is_native(dev)) + max = pci_scan_bridge(bus, dev, max, 1); + } +} + /** * enable_slot - enable, configure a slot * @slot: slot to be enabled @@ -442,25 +466,42 @@ static void enable_slot(struct acpiphp_slot *slot) struct pci_dev *dev; struct pci_bus *bus = slot->bus; struct acpiphp_func *func; - int max, pass; - LIST_HEAD(add_list); - acpiphp_rescan_slot(slot); - max = acpiphp_max_busnr(bus); - for (pass = 0; pass < 2; pass++) { + if (bus->self && hotplug_is_native(bus->self)) { + /* + * If native hotplug is used, it will take care of hotplug + * slot management and resource allocation for hotplug + * bridges. However, ACPI hotplug may still be used for + * non-hotplug bridges to bring in additional devices such + * as a Thunderbolt host controller. + */ for_each_pci_bridge(dev, bus) { - if (PCI_SLOT(dev->devfn) != slot->device) - continue; - - max = pci_scan_bridge(bus, dev, max, pass); - if (pass && dev->subordinate) { - check_hotplug_bridge(slot, dev); - pcibios_resource_survey_bus(dev->subordinate); - __pci_bus_size_bridges(dev->subordinate, &add_list); + if (PCI_SLOT(dev->devfn) == slot->device) + acpiphp_native_scan_bridge(dev); + } + pci_assign_unassigned_bridge_resources(bus->self); + } else { + LIST_HEAD(add_list); + int max, pass; + + acpiphp_rescan_slot(slot); + max = acpiphp_max_busnr(bus); + for (pass = 0; pass < 2; pass++) { + for_each_pci_bridge(dev, bus) { + if (PCI_SLOT(dev->devfn) != slot->device) + continue; + + max = pci_scan_bridge(bus, dev, max, pass); + if (pass && dev->subordinate) { + check_hotplug_bridge(slot, dev); + pcibios_resource_survey_bus(dev->subordinate); + __pci_bus_size_bridges(dev->subordinate, + &add_list); + } } } + __pci_bus_assign_resources(bus, &add_list, NULL); } - __pci_bus_assign_resources(bus, &add_list, NULL); acpiphp_sanitize_bus(bus); pcie_bus_configure_settings(bus); -- cgit From 8f004f4a34fd129622567cbec381101cc5ff7f09 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 29 May 2018 19:02:23 +0300 Subject: ACPI / hotplug / PCI: Mark stale PCI devices disconnected Following PCIehp mark the unplugged PCI devices disconnected. This makes sure PCI core code leaves the now missing hardware registers alone. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko --- drivers/pci/hotplug/acpiphp_glue.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index bc9e19642567..b526565b0464 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -649,6 +649,11 @@ static void trim_stale_devices(struct pci_dev *dev) alive = pci_device_is_present(dev); if (!alive) { + pci_dev_set_disconnected(dev, NULL); + if (pci_has_subordinate(dev)) + pci_walk_bus(dev->subordinate, pci_dev_set_disconnected, + NULL); + pci_stop_and_remove_bus_device(dev); if (adev) acpi_bus_trim(adev); -- cgit From 9337a493623d59202a9d0e7c23fe15cf5bf7c0f8 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 24 May 2018 13:25:15 -0500 Subject: ACPI / hotplug / PCI: Drop unnecessary parentheses Remove unnecessary parentheses. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/hotplug/acpiphp_glue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci/hotplug') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index b526565b0464..3a17b290df5d 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -522,7 +522,7 @@ static void enable_slot(struct acpiphp_slot *slot) if (!dev) { /* Do not set SLOT_ENABLED flag if some funcs are not added. */ - slot->flags &= (~SLOT_ENABLED); + slot->flags &= ~SLOT_ENABLED; continue; } } @@ -551,7 +551,7 @@ static void disable_slot(struct acpiphp_slot *slot) list_for_each_entry(func, &slot->funcs, sibling) acpi_bus_trim(func_to_acpi_device(func)); - slot->flags &= (~SLOT_ENABLED); + slot->flags &= ~SLOT_ENABLED; } static bool slot_no_hotplug(struct acpiphp_slot *slot) -- cgit