Merge branch 'pci/hotplug'

- fix use-before-set error in ibmphp (Dan Carpenter)

  - fix pciehp timeouts caused by Command Completed errata (Bjorn Helgaas)

  - fix refcounting in pnv_php hotplug (Julia Lawall)

  - clear pciehp Presence Detect and Data Link Layer Status Changed on
    resume so we don't miss hotplug events (Mika Westerberg)

  - only request pciehp control if we support it, so platform can use ACPI
    hotplug otherwise (Mika Westerberg)

  - convert SHPC to be builtin only (Mika Westerberg)

  - request SHPC control via _OSC if we support it (Mika Westerberg)

  - simplify SHPC handoff from firmware (Mika Westerberg)

* pci/hotplug:
  PCI: Improve "partially hidden behind bridge" log message
  PCI: Improve pci_scan_bridge() and pci_scan_bridge_extend() doc
  PCI: Move resource distribution for single bridge outside loop
  PCI: Account for all bridges on bus when distributing bus numbers
  ACPI / hotplug / PCI: Drop unnecessary parentheses
  ACPI / hotplug / PCI: Mark stale PCI devices disconnected
  ACPI / hotplug / PCI: Don't scan bridges managed by native hotplug
  PCI: hotplug: Add hotplug_is_native()
  PCI: shpchp: Add shpchp_is_native()
  PCI: shpchp: Fix AMD POGO identification
  PCI: shpchp: Use dev_printk() for OSHP-related messages
  PCI: shpchp: Remove get_hp_hw_control_from_firmware() wrapper
  PCI: shpchp: Remove acpi_get_hp_hw_control_from_firmware() flags
  PCI: shpchp: Rely on previous _OSC results
  PCI: shpchp: Request SHPC control via _OSC when adding host bridge
  PCI: shpchp: Convert SHPC to be builtin only
  PCI: pciehp: Make pciehp_is_native() stricter
  PCI: pciehp: Rename host->native_hotplug to host->native_pcie_hotplug
  PCI: pciehp: Request control of native hotplug only if supported
  PCI: pciehp: Clear Presence Detect and Data Link Layer Status Changed on resume
  PCI: pnv_php: Add missing of_node_put()
  PCI: pciehp: Add quirk for Command Completed errata
  PCI: Add Qualcomm vendor ID
  PCI: ibmphp: Fix use-before-set in get_max_bus_speed()

# Conflicts:
#	drivers/acpi/pci_root.c
This commit is contained in:
Bjorn Helgaas 2018-06-06 16:10:10 -05:00
commit f64c146410
21 changed files with 287 additions and 171 deletions

View file

@ -473,12 +473,17 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm)
} }
control = OSC_PCI_EXPRESS_CAPABILITY_CONTROL control = OSC_PCI_EXPRESS_CAPABILITY_CONTROL
| OSC_PCI_EXPRESS_NATIVE_HP_CONTROL
| OSC_PCI_EXPRESS_PME_CONTROL; | OSC_PCI_EXPRESS_PME_CONTROL;
if (IS_ENABLED(CONFIG_PCIEASPM)) if (IS_ENABLED(CONFIG_PCIEASPM))
control |= OSC_PCI_EXPRESS_LTR_CONTROL; control |= OSC_PCI_EXPRESS_LTR_CONTROL;
if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
control |= OSC_PCI_EXPRESS_NATIVE_HP_CONTROL;
if (IS_ENABLED(CONFIG_HOTPLUG_PCI_SHPC))
control |= OSC_PCI_SHPC_NATIVE_HP_CONTROL;
if (pci_aer_available()) { if (pci_aer_available()) {
if (aer_acpi_firmware_first()) if (aer_acpi_firmware_first())
dev_info(&device->dev, dev_info(&device->dev,
@ -904,7 +909,9 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
host_bridge = to_pci_host_bridge(bus->bridge); host_bridge = to_pci_host_bridge(bus->bridge);
if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)) if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
host_bridge->native_hotplug = 0; host_bridge->native_pcie_hotplug = 0;
if (!(root->osc_control_set & OSC_PCI_SHPC_NATIVE_HP_CONTROL))
host_bridge->native_shpc_hotplug = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL)) if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL))
host_bridge->native_aer = 0; host_bridge->native_aer = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL)) if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL))

View file

@ -104,14 +104,11 @@ config HOTPLUG_PCI_CPCI_GENERIC
When in doubt, say N. When in doubt, say N.
config HOTPLUG_PCI_SHPC config HOTPLUG_PCI_SHPC
tristate "SHPC PCI Hotplug driver" bool "SHPC PCI Hotplug driver"
help help
Say Y here if you have a motherboard with a SHPC PCI Hotplug Say Y here if you have a motherboard with a SHPC PCI Hotplug
controller. controller.
To compile this driver as a module, choose M here: the
module will be called shpchp.
When in doubt, say N. When in doubt, say N.
config HOTPLUG_PCI_POWERNV config HOTPLUG_PCI_POWERNV

View file

@ -63,22 +63,17 @@ static acpi_status acpi_run_oshp(acpi_handle handle)
/** /**
* acpi_get_hp_hw_control_from_firmware * acpi_get_hp_hw_control_from_firmware
* @dev: the pci_dev of the bridge that has a hotplug controller * @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. * 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;
acpi_status status; acpi_status status;
acpi_handle chandle, handle; acpi_handle chandle, handle;
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; 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 * Per PCI firmware specification, we should run the ACPI _OSC
* method to get control of hotplug hardware before using it. If * method to get control of hotplug hardware before using it. If
@ -88,25 +83,20 @@ 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, * OSHP within the scope of the hotplug controller and its parents,
* up to the host bridge under which this controller exists. * up to the host bridge under which this controller exists.
*/ */
handle = acpi_find_root_bridge_handle(pdev); if (shpchp_is_native(pdev))
if (handle) { return 0;
acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
dbg("Trying to get hotplug control for %s\n", /* If _OSC exists, we should not evaluate OSHP */
(char *)string.pointer); host = pci_find_host_bridge(pdev->bus);
status = acpi_pci_osc_control_set(handle, &flags, flags); root = acpi_pci_find_root(ACPI_HANDLE(&host->dev));
if (ACPI_SUCCESS(status)) if (root->osc_support_set)
goto got_one; goto no_control;
if (status == AE_SUPPORT)
goto no_control;
kfree(string.pointer);
string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
}
handle = ACPI_HANDLE(&pdev->dev); handle = ACPI_HANDLE(&pdev->dev);
if (!handle) { if (!handle) {
/* /*
* This hotplug controller was not listed in the ACPI name * 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; struct pci_bus *pbus;
for (pbus = pdev->bus; pbus; pbus = pbus->parent) { for (pbus = pdev->bus; pbus; pbus = pbus->parent) {
@ -118,8 +108,8 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
while (handle) { while (handle) {
acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
dbg("Trying to get hotplug control for %s\n", pci_info(pdev, "Requesting control of SHPC hotplug via OSHP (%s)\n",
(char *)string.pointer); (char *)string.pointer);
status = acpi_run_oshp(handle); status = acpi_run_oshp(handle);
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
goto got_one; goto got_one;
@ -131,13 +121,12 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
break; break;
} }
no_control: no_control:
dbg("Cannot get control of hotplug hardware for pci %s\n", pci_info(pdev, "Cannot get control of SHPC hotplug\n");
pci_name(pdev));
kfree(string.pointer); kfree(string.pointer);
return -ENODEV; return -ENODEV;
got_one: got_one:
dbg("Gained control for hotplug HW for pci %s (%s)\n", pci_info(pdev, "Gained control of SHPC hotplug (%s)\n",
pci_name(pdev), (char *)string.pointer); (char *)string.pointer);
kfree(string.pointer); kfree(string.pointer);
return 0; return 0;
} }

View file

@ -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 * 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 * are located in dock stations. Do not expose them for devices handled
* by the native PCIe hotplug (PCIeHP), becuase that code is supposed to * by the native PCIe hotplug (PCIeHP) or standard PCI hotplug
* expose slots to user space in those cases. * (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)) 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; unsigned long long sun;
int retval; 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)); 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 * enable_slot - enable, configure a slot
* @slot: slot to be enabled * @slot: slot to be enabled
@ -442,25 +466,42 @@ static void enable_slot(struct acpiphp_slot *slot)
struct pci_dev *dev; struct pci_dev *dev;
struct pci_bus *bus = slot->bus; struct pci_bus *bus = slot->bus;
struct acpiphp_func *func; struct acpiphp_func *func;
int max, pass;
LIST_HEAD(add_list);
acpiphp_rescan_slot(slot); if (bus->self && hotplug_is_native(bus->self)) {
max = acpiphp_max_busnr(bus); /*
for (pass = 0; pass < 2; pass++) { * 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) { for_each_pci_bridge(dev, bus) {
if (PCI_SLOT(dev->devfn) != slot->device) if (PCI_SLOT(dev->devfn) == slot->device)
continue; acpiphp_native_scan_bridge(dev);
}
pci_assign_unassigned_bridge_resources(bus->self);
} else {
LIST_HEAD(add_list);
int max, pass;
max = pci_scan_bridge(bus, dev, max, pass); acpiphp_rescan_slot(slot);
if (pass && dev->subordinate) { max = acpiphp_max_busnr(bus);
check_hotplug_bridge(slot, dev); for (pass = 0; pass < 2; pass++) {
pcibios_resource_survey_bus(dev->subordinate); for_each_pci_bridge(dev, bus) {
__pci_bus_size_bridges(dev->subordinate, &add_list); 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); acpiphp_sanitize_bus(bus);
pcie_bus_configure_settings(bus); pcie_bus_configure_settings(bus);
@ -481,7 +522,7 @@ static void enable_slot(struct acpiphp_slot *slot)
if (!dev) { if (!dev) {
/* Do not set SLOT_ENABLED flag if some funcs /* Do not set SLOT_ENABLED flag if some funcs
are not added. */ are not added. */
slot->flags &= (~SLOT_ENABLED); slot->flags &= ~SLOT_ENABLED;
continue; continue;
} }
} }
@ -510,7 +551,7 @@ static void disable_slot(struct acpiphp_slot *slot)
list_for_each_entry(func, &slot->funcs, sibling) list_for_each_entry(func, &slot->funcs, sibling)
acpi_bus_trim(func_to_acpi_device(func)); 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) static bool slot_no_hotplug(struct acpiphp_slot *slot)
@ -608,6 +649,11 @@ static void trim_stale_devices(struct pci_dev *dev)
alive = pci_device_is_present(dev); alive = pci_device_is_present(dev);
if (!alive) { 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); pci_stop_and_remove_bus_device(dev);
if (adev) if (adev)
acpi_bus_trim(adev); acpi_bus_trim(adev);

View file

@ -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) static int get_max_bus_speed(struct slot *slot)
{ {
int rc; int rc = 0;
u8 mode = 0; u8 mode = 0;
enum pci_bus_speed speed; enum pci_bus_speed speed;
struct pci_bus *bus = slot->hotplug_slot->pci_slot->bus; struct pci_bus *bus = slot->hotplug_slot->pci_slot->bus;

View file

@ -121,7 +121,7 @@ struct controller *pcie_init(struct pcie_device *dev);
int pcie_init_notification(struct controller *ctrl); int pcie_init_notification(struct controller *ctrl);
int pciehp_enable_slot(struct slot *p_slot); int pciehp_enable_slot(struct slot *p_slot);
int pciehp_disable_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); int pciehp_power_on_slot(struct slot *slot);
void pciehp_power_off_slot(struct slot *slot); void pciehp_power_off_slot(struct slot *slot);
void pciehp_get_power_status(struct slot *slot, u8 *status); void pciehp_get_power_status(struct slot *slot, u8 *status);

View file

@ -283,7 +283,7 @@ static int pciehp_resume(struct pcie_device *dev)
ctrl = get_service_data(dev); ctrl = get_service_data(dev);
/* reinitialize the chipset's event detection logic */ /* reinitialize the chipset's event detection logic */
pcie_enable_notification(ctrl); pcie_reenable_notification(ctrl);
slot = ctrl->slot; slot = ctrl->slot;

View file

@ -10,7 +10,6 @@
* All rights reserved. * All rights reserved.
* *
* Send feedback to <greg@kroah.com>,<kristen.c.accardi@intel.com> * Send feedback to <greg@kroah.com>,<kristen.c.accardi@intel.com>
*
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
@ -147,25 +146,22 @@ static void pcie_wait_cmd(struct controller *ctrl)
else else
rc = pcie_poll_cmd(ctrl, jiffies_to_msecs(timeout)); 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) if (!rc)
ctrl_info(ctrl, "Timeout on hotplug command %#06x (issued %u msec ago)\n", ctrl_info(ctrl, "Timeout on hotplug command %#06x (issued %u msec ago)\n",
ctrl->slot_ctrl, ctrl->slot_ctrl,
jiffies_to_msecs(jiffies - ctrl->cmd_started)); 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, static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd,
u16 mask, bool wait) u16 mask, bool wait)
{ {
struct pci_dev *pdev = ctrl_dev(ctrl); struct pci_dev *pdev = ctrl_dev(ctrl);
u16 slot_ctrl; u16 slot_ctrl_orig, slot_ctrl;
mutex_lock(&ctrl->ctrl_lock); mutex_lock(&ctrl->ctrl_lock);
@ -180,6 +176,7 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd,
goto out; goto out;
} }
slot_ctrl_orig = slot_ctrl;
slot_ctrl &= ~mask; slot_ctrl &= ~mask;
slot_ctrl |= (cmd & mask); slot_ctrl |= (cmd & mask);
ctrl->cmd_busy = 1; ctrl->cmd_busy = 1;
@ -188,6 +185,17 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd,
ctrl->cmd_started = jiffies; ctrl->cmd_started = jiffies;
ctrl->slot_ctrl = slot_ctrl; 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, * Optionally wait for the hardware to be ready for a new command,
* indicating completion of the above issued command. * indicating completion of the above issued command.
@ -645,7 +653,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
return handled; return handled;
} }
void pcie_enable_notification(struct controller *ctrl) static void pcie_enable_notification(struct controller *ctrl)
{ {
u16 cmd, mask; u16 cmd, mask;
@ -683,6 +691,17 @@ void pcie_enable_notification(struct controller *ctrl)
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd); 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) static void pcie_disable_notification(struct controller *ctrl)
{ {
u16 mask; u16 mask;
@ -847,7 +866,7 @@ struct controller *pcie_init(struct pcie_device *dev)
PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC |
PCI_EXP_SLTSTA_DLLSC); 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, (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
FLAG(slot_cap, PCI_EXP_SLTCAP_ABP), FLAG(slot_cap, PCI_EXP_SLTCAP_ABP),
FLAG(slot_cap, PCI_EXP_SLTCAP_PCP), FLAG(slot_cap, PCI_EXP_SLTCAP_PCP),
@ -858,7 +877,8 @@ struct controller *pcie_init(struct pcie_device *dev)
FLAG(slot_cap, PCI_EXP_SLTCAP_HPS), FLAG(slot_cap, PCI_EXP_SLTCAP_HPS),
FLAG(slot_cap, PCI_EXP_SLTCAP_EIP), FLAG(slot_cap, PCI_EXP_SLTCAP_EIP),
FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS), 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)) if (pcie_init_slot(ctrl))
goto abort_ctrl; goto abort_ctrl;
@ -877,3 +897,21 @@ void pciehp_release_ctrl(struct controller *ctrl)
pcie_cleanup_slot(ctrl); pcie_cleanup_slot(ctrl);
kfree(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);

View file

@ -220,12 +220,16 @@ static int pnv_php_populate_changeset(struct of_changeset *ocs,
for_each_child_of_node(dn, child) { for_each_child_of_node(dn, child) {
ret = of_changeset_attach_node(ocs, child); ret = of_changeset_attach_node(ocs, child);
if (ret) if (ret) {
of_node_put(child);
break; break;
}
ret = pnv_php_populate_changeset(ocs, child); ret = pnv_php_populate_changeset(ocs, child);
if (ret) if (ret) {
of_node_put(child);
break; break;
}
} }
return ret; return ret;

View file

@ -105,7 +105,6 @@ struct controller {
}; };
/* Define AMD SHPC ID */ /* Define AMD SHPC ID */
#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450
#define PCI_DEVICE_ID_AMD_POGO_7458 0x7458 #define PCI_DEVICE_ID_AMD_POGO_7458 0x7458
/* AMD PCI-X bridge registers */ /* AMD PCI-X bridge registers */
@ -173,17 +172,6 @@ static inline const char *slot_name(struct slot *slot)
return hotplug_slot_name(slot->hotplug_slot); return hotplug_slot_name(slot->hotplug_slot);
} }
#ifdef CONFIG_ACPI
#include <linux/pci-acpi.h>
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);
}
#else
#define get_hp_hw_control_from_firmware(dev) (0)
#endif
struct ctrl_reg { struct ctrl_reg {
volatile u32 base_offset; volatile u32 base_offset;
volatile u32 slot_avail1; volatile u32 slot_avail1;

View file

@ -270,24 +270,12 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0; 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 (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) static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{ {
int rc; int rc;
struct controller *ctrl; struct controller *ctrl;
if (!is_shpc_capable(pdev)) if (acpi_get_hp_hw_control_from_firmware(pdev))
return -ENODEV; return -ENODEV;
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);

View file

@ -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); 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); p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) || 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->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458)
&& p_slot->ctrl->num_slots == 1) { && 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); amd_pogo_errata_save_misc_reg(p_slot);
retval = board_added(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); amd_pogo_errata_restore_misc_reg(p_slot);
} else } else
retval = board_added(p_slot); retval = board_added(p_slot);

View file

@ -370,26 +370,57 @@ EXPORT_SYMBOL_GPL(pci_get_hp_params);
/** /**
* pciehp_is_native - Check whether a hotplug port is handled by the OS * pciehp_is_native - Check whether a hotplug port is handled by the OS
* @pdev: Hotplug port to check * @bridge: Hotplug port to check
* *
* Walk up from @pdev to the host bridge, obtain its cached _OSC Control Field * Returns true if the given @bridge is handled by the native PCIe hotplug
* and return the value of the "PCI Express Native Hot Plug control" bit. * driver.
* On failure to obtain the _OSC Control Field return %false.
*/ */
bool pciehp_is_native(struct pci_dev *pdev) bool pciehp_is_native(struct pci_dev *bridge)
{ {
struct acpi_pci_root *root; const struct pci_host_bridge *host;
acpi_handle handle; u32 slot_cap;
handle = acpi_find_root_bridge_handle(pdev); if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
if (!handle)
return false; return false;
root = acpi_pci_find_root(handle); pcie_capability_read_dword(bridge, PCI_EXP_SLTCAP, &slot_cap);
if (!root) if (!(slot_cap & PCI_EXP_SLTCAP_HPC))
return false; return false;
return root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL; if (pcie_ports_native)
return true;
host = pci_find_host_bridge(bridge->bus);
return host->native_pcie_hotplug;
}
/**
* shpchp_is_native - Check whether a hotplug port is handled by the OS
* @bridge: Hotplug port to check
*
* Returns true if the given @bridge is handled by the native SHPC hotplug
* driver.
*/
bool shpchp_is_native(struct pci_dev *bridge)
{
const struct pci_host_bridge *host;
if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_SHPC))
return false;
/*
* It is assumed that AMD GOLAM chips support SHPC but they do not
* have SHPC capability.
*/
if (bridge->vendor == PCI_VENDOR_ID_AMD &&
bridge->device == PCI_DEVICE_ID_AMD_GOLAM_7450)
return true;
if (!pci_find_capability(bridge, PCI_CAP_ID_SHPC))
return false;
host = pci_find_host_bridge(bridge->bus);
return host->native_shpc_hotplug;
} }
/** /**

View file

@ -11,8 +11,6 @@
#include <linux/compiler.h> #include <linux/compiler.h>
extern bool pcie_ports_native;
/* Service Type */ /* Service Type */
#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ #define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */
#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) #define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT)

View file

@ -205,7 +205,7 @@ static int get_port_device_capability(struct pci_dev *dev)
int services = 0; int services = 0;
if (dev->is_hotplug_bridge && if (dev->is_hotplug_bridge &&
(pcie_ports_native || host->native_hotplug)) { (pcie_ports_native || host->native_pcie_hotplug)) {
services |= PCIE_PORT_SERVICE_HP; services |= PCIE_PORT_SERVICE_HP;
/* /*

View file

@ -552,7 +552,8 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
* OS from interfering. * OS from interfering.
*/ */
bridge->native_aer = 1; bridge->native_aer = 1;
bridge->native_hotplug = 1; bridge->native_pcie_hotplug = 1;
bridge->native_shpc_hotplug = 1;
bridge->native_pme = 1; bridge->native_pme = 1;
bridge->native_ltr = 1; bridge->native_ltr = 1;
@ -1048,6 +1049,8 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
* already configured by the BIOS and after we are done with all of * already configured by the BIOS and after we are done with all of
* them, we proceed to assigning numbers to the remaining buses in * them, we proceed to assigning numbers to the remaining buses in
* order to avoid overlaps between old and new bus numbers. * order to avoid overlaps between old and new bus numbers.
*
* Return: New subordinate number covering all buses behind this bridge.
*/ */
static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
int max, unsigned int available_buses, int max, unsigned int available_buses,
@ -1238,20 +1241,15 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
(is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"), (is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"),
pci_domain_nr(bus), child->number); pci_domain_nr(bus), child->number);
/* Has only triggered on CardBus, fixup is in yenta_socket */ /* Check that all devices are accessible */
while (bus->parent) { while (bus->parent) {
if ((child->busn_res.end > bus->busn_res.end) || if ((child->busn_res.end > bus->busn_res.end) ||
(child->number > bus->busn_res.end) || (child->number > bus->busn_res.end) ||
(child->number < bus->number) || (child->number < bus->number) ||
(child->busn_res.end < bus->number)) { (child->busn_res.end < bus->number)) {
dev_info(&child->dev, "%pR %s hidden behind%s bridge %s %pR\n", dev_info(&dev->dev, "devices behind bridge are unusable because %pR cannot be assigned for them\n",
&child->busn_res, &child->busn_res);
(bus->number > child->busn_res.end && break;
bus->busn_res.end < child->number) ?
"wholly" : "partially",
bus->self->transparent ? " transparent" : "",
dev_name(&bus->dev),
&bus->busn_res);
} }
bus = bus->parent; bus = bus->parent;
} }
@ -1280,6 +1278,8 @@ out:
* already configured by the BIOS and after we are done with all of * already configured by the BIOS and after we are done with all of
* them, we proceed to assigning numbers to the remaining buses in * them, we proceed to assigning numbers to the remaining buses in
* order to avoid overlaps between old and new bus numbers. * order to avoid overlaps between old and new bus numbers.
*
* Return: New subordinate number covering all buses behind this bridge.
*/ */
int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
{ {
@ -2695,7 +2695,14 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
for_each_pci_bridge(dev, bus) { for_each_pci_bridge(dev, bus) {
cmax = max; cmax = max;
max = pci_scan_bridge_extend(bus, dev, max, 0, 0); max = pci_scan_bridge_extend(bus, dev, max, 0, 0);
used_buses += cmax - max;
/*
* Reserve one bus for each bridge now to avoid extending
* hotplug bridges too much during the second scan below.
*/
used_buses++;
if (cmax - max > 1)
used_buses += cmax - max - 1;
} }
/* Scan bridges that need to be reconfigured */ /* Scan bridges that need to be reconfigured */
@ -2718,12 +2725,14 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
* bridges if any. * bridges if any.
*/ */
buses = available_buses / hotplug_bridges; buses = available_buses / hotplug_bridges;
buses = min(buses, available_buses - used_buses); buses = min(buses, available_buses - used_buses + 1);
} }
cmax = max; cmax = max;
max = pci_scan_bridge_extend(bus, dev, cmax, buses, 1); max = pci_scan_bridge_extend(bus, dev, cmax, buses, 1);
used_buses += max - cmax; /* One bus is already accounted so don't add it again */
if (max - cmax > 1)
used_buses += max - cmax - 1;
} }
/* /*

View file

@ -4361,8 +4361,8 @@ static const struct pci_dev_acs_enabled {
{ PCI_VENDOR_ID_INTEL, 0x15b7, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, 0x15b7, pci_quirk_mf_endpoint_acs },
{ PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs },
/* QCOM QDF2xxx root ports */ /* QCOM QDF2xxx root ports */
{ 0x17cb, 0x400, pci_quirk_qcom_rp_acs }, { PCI_VENDOR_ID_QCOM, 0x0400, pci_quirk_qcom_rp_acs },
{ 0x17cb, 0x401, pci_quirk_qcom_rp_acs }, { PCI_VENDOR_ID_QCOM, 0x0401, pci_quirk_qcom_rp_acs },
/* Intel PCH root ports */ /* Intel PCH root ports */
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs }, { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs },

View file

@ -1942,57 +1942,57 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
remaining_mmio_pref -= resource_size(res); remaining_mmio_pref -= resource_size(res);
} }
/*
* There is only one bridge on the bus so it gets all available
* resources which it can then distribute to the possible
* hotplug bridges below.
*/
if (hotplug_bridges + normal_bridges == 1) {
dev = list_first_entry(&bus->devices, struct pci_dev, bus_list);
if (dev->subordinate) {
pci_bus_distribute_available_resources(dev->subordinate,
add_list, available_io, available_mmio,
available_mmio_pref);
}
return;
}
/* /*
* Go over devices on this bus and distribute the remaining * Go over devices on this bus and distribute the remaining
* resource space between hotplug bridges. * resource space between hotplug bridges.
*/ */
for_each_pci_bridge(dev, bus) { for_each_pci_bridge(dev, bus) {
resource_size_t align, io, mmio, mmio_pref;
struct pci_bus *b; struct pci_bus *b;
b = dev->subordinate; b = dev->subordinate;
if (!b) if (!b || !dev->is_hotplug_bridge)
continue; continue;
if (!hotplug_bridges && normal_bridges == 1) { /*
/* * Distribute available extra resources equally between
* There is only one bridge on the bus (upstream * hotplug-capable downstream ports taking alignment into
* port) so it gets all available resources * account.
* which it can then distribute to the possible *
* hotplug bridges below. * Here hotplug_bridges is always != 0.
*/ */
pci_bus_distribute_available_resources(b, add_list, align = pci_resource_alignment(bridge, io_res);
available_io, available_mmio, io = div64_ul(available_io, hotplug_bridges);
available_mmio_pref); io = min(ALIGN(io, align), remaining_io);
} else if (dev->is_hotplug_bridge) { remaining_io -= io;
resource_size_t align, io, mmio, mmio_pref;
/* align = pci_resource_alignment(bridge, mmio_res);
* Distribute available extra resources equally mmio = div64_ul(available_mmio, hotplug_bridges);
* between hotplug-capable downstream ports mmio = min(ALIGN(mmio, align), remaining_mmio);
* taking alignment into account. remaining_mmio -= mmio;
*
* Here hotplug_bridges is always != 0.
*/
align = pci_resource_alignment(bridge, io_res);
io = div64_ul(available_io, hotplug_bridges);
io = min(ALIGN(io, align), remaining_io);
remaining_io -= io;
align = pci_resource_alignment(bridge, mmio_res); align = pci_resource_alignment(bridge, mmio_pref_res);
mmio = div64_ul(available_mmio, hotplug_bridges); mmio_pref = div64_ul(available_mmio_pref, hotplug_bridges);
mmio = min(ALIGN(mmio, align), remaining_mmio); mmio_pref = min(ALIGN(mmio_pref, align), remaining_mmio_pref);
remaining_mmio -= mmio; remaining_mmio_pref -= mmio_pref;
align = pci_resource_alignment(bridge, mmio_pref_res); pci_bus_distribute_available_resources(b, add_list, io, mmio,
mmio_pref = div64_ul(available_mmio_pref, mmio_pref);
hotplug_bridges);
mmio_pref = min(ALIGN(mmio_pref, align),
remaining_mmio_pref);
remaining_mmio_pref -= mmio_pref;
pci_bus_distribute_available_resources(b, add_list, io,
mmio, mmio_pref);
}
} }
} }

View file

@ -407,6 +407,9 @@ struct pci_dev {
struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */
#ifdef CONFIG_HOTPLUG_PCI_PCIE
unsigned int broken_cmd_compl:1; /* No compl for some cmds */
#endif
#ifdef CONFIG_PCIE_PTM #ifdef CONFIG_PCIE_PTM
unsigned int ptm_root:1; unsigned int ptm_root:1;
unsigned int ptm_enabled:1; unsigned int ptm_enabled:1;
@ -472,7 +475,8 @@ struct pci_host_bridge {
unsigned int ignore_reset_delay:1; /* For entire hierarchy */ unsigned int ignore_reset_delay:1; /* For entire hierarchy */
unsigned int no_ext_tags:1; /* No Extended Tags */ unsigned int no_ext_tags:1; /* No Extended Tags */
unsigned int native_aer:1; /* OS may use PCIe AER */ unsigned int native_aer:1; /* OS may use PCIe AER */
unsigned int native_hotplug:1; /* OS may use PCIe hotplug */ unsigned int native_pcie_hotplug:1; /* OS may use PCIe hotplug */
unsigned int native_shpc_hotplug:1; /* OS may use SHPC hotplug */
unsigned int native_pme:1; /* OS may use PCIe PME */ unsigned int native_pme:1; /* OS may use PCIe PME */
unsigned int native_ltr:1; /* OS may use PCIe LTR */ unsigned int native_ltr:1; /* OS may use PCIe LTR */
/* Resource alignment requirements */ /* Resource alignment requirements */
@ -1451,8 +1455,10 @@ static inline int pci_irqd_intx_xlate(struct irq_domain *d,
#ifdef CONFIG_PCIEPORTBUS #ifdef CONFIG_PCIEPORTBUS
extern bool pcie_ports_disabled; extern bool pcie_ports_disabled;
extern bool pcie_ports_native;
#else #else
#define pcie_ports_disabled true #define pcie_ports_disabled true
#define pcie_ports_native false
#endif #endif
#ifdef CONFIG_PCIEASPM #ifdef CONFIG_PCIEASPM

View file

@ -162,8 +162,9 @@ struct hotplug_params {
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
#include <linux/acpi.h> #include <linux/acpi.h>
int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp); int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp);
bool pciehp_is_native(struct pci_dev *pdev); bool pciehp_is_native(struct pci_dev *bridge);
int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags); int acpi_get_hp_hw_control_from_firmware(struct pci_dev *bridge);
bool shpchp_is_native(struct pci_dev *bridge);
int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle); int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle);
int acpi_pci_detect_ejectable(acpi_handle handle); int acpi_pci_detect_ejectable(acpi_handle handle);
#else #else
@ -172,6 +173,17 @@ static inline int pci_get_hp_params(struct pci_dev *dev,
{ {
return -ENODEV; return -ENODEV;
} }
static inline bool pciehp_is_native(struct pci_dev *pdev) { return true; }
static inline int acpi_get_hp_hw_control_from_firmware(struct pci_dev *bridge)
{
return 0;
}
static inline bool pciehp_is_native(struct pci_dev *bridge) { return true; }
static inline bool shpchp_is_native(struct pci_dev *bridge) { return true; }
#endif #endif
static inline bool hotplug_is_native(struct pci_dev *bridge)
{
return pciehp_is_native(bridge) || shpchp_is_native(bridge);
}
#endif #endif

View file

@ -561,6 +561,7 @@
#define PCI_DEVICE_ID_AMD_OPUS_7443 0x7443 #define PCI_DEVICE_ID_AMD_OPUS_7443 0x7443
#define PCI_DEVICE_ID_AMD_VIPER_7443 0x7443 #define PCI_DEVICE_ID_AMD_VIPER_7443 0x7443
#define PCI_DEVICE_ID_AMD_OPUS_7445 0x7445 #define PCI_DEVICE_ID_AMD_OPUS_7445 0x7445
#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450
#define PCI_DEVICE_ID_AMD_8111_PCI 0x7460 #define PCI_DEVICE_ID_AMD_8111_PCI 0x7460
#define PCI_DEVICE_ID_AMD_8111_LPC 0x7468 #define PCI_DEVICE_ID_AMD_8111_LPC 0x7468
#define PCI_DEVICE_ID_AMD_8111_IDE 0x7469 #define PCI_DEVICE_ID_AMD_8111_IDE 0x7469
@ -2387,6 +2388,8 @@
#define PCI_VENDOR_ID_LENOVO 0x17aa #define PCI_VENDOR_ID_LENOVO 0x17aa
#define PCI_VENDOR_ID_QCOM 0x17cb
#define PCI_VENDOR_ID_CDNS 0x17cd #define PCI_VENDOR_ID_CDNS 0x17cd
#define PCI_VENDOR_ID_ARECA 0x17d3 #define PCI_VENDOR_ID_ARECA 0x17d3