1
0
Fork 0

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
hifive-unleashed-5.1
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
| OSC_PCI_EXPRESS_NATIVE_HP_CONTROL
| OSC_PCI_EXPRESS_PME_CONTROL;
if (IS_ENABLED(CONFIG_PCIEASPM))
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 (aer_acpi_firmware_first())
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);
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))
host_bridge->native_aer = 0;
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.
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

View File

@ -63,22 +63,17 @@ 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;
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,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,
* 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 };
}
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;
handle = ACPI_HANDLE(&pdev->dev);
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) {
@ -118,8 +108,8 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
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;
@ -131,13 +121,12 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
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;
}

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
* 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;
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;
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);
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);
@ -481,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;
}
}
@ -510,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)
@ -608,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);

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)
{
int rc;
int rc = 0;
u8 mode = 0;
enum pci_bus_speed speed;
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 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);

View File

@ -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;

View File

@ -10,7 +10,6 @@
* All rights reserved.
*
* Send feedback to <greg@kroah.com>,<kristen.c.accardi@intel.com>
*
*/
#include <linux/kernel.h>
@ -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.
@ -645,7 +653,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;
@ -683,6 +691,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;
@ -847,7 +866,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),
@ -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_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;
@ -877,3 +897,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);

View File

@ -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;

View File

@ -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 */
@ -173,17 +172,6 @@ static inline const char *slot_name(struct slot *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 {
volatile u32 base_offset;
volatile u32 slot_avail1;

View File

@ -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 (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);

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);
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);

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
* @pdev: Hotplug port to check
* @bridge: Hotplug port to check
*
* Walk up from @pdev to the host bridge, obtain its cached _OSC Control Field
* and return the value of the "PCI Express Native Hot Plug control" bit.
* On failure to obtain the _OSC Control Field return %false.
* Returns true if the given @bridge is handled by the native PCIe hotplug
* driver.
*/
bool pciehp_is_native(struct pci_dev *pdev)
bool pciehp_is_native(struct pci_dev *bridge)
{
struct acpi_pci_root *root;
acpi_handle handle;
const struct pci_host_bridge *host;
u32 slot_cap;
handle = acpi_find_root_bridge_handle(pdev);
if (!handle)
if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
return false;
root = acpi_pci_find_root(handle);
if (!root)
pcie_capability_read_dword(bridge, PCI_EXP_SLTCAP, &slot_cap);
if (!(slot_cap & PCI_EXP_SLTCAP_HPC))
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>
extern bool pcie_ports_native;
/* Service Type */
#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */
#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;
if (dev->is_hotplug_bridge &&
(pcie_ports_native || host->native_hotplug)) {
(pcie_ports_native || host->native_pcie_hotplug)) {
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.
*/
bridge->native_aer = 1;
bridge->native_hotplug = 1;
bridge->native_pcie_hotplug = 1;
bridge->native_shpc_hotplug = 1;
bridge->native_pme = 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
* them, we proceed to assigning numbers to the remaining buses in
* 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,
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"),
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) {
if ((child->busn_res.end > bus->busn_res.end) ||
(child->number > bus->busn_res.end) ||
(child->number < bus->number) ||
(child->busn_res.end < bus->number)) {
dev_info(&child->dev, "%pR %s hidden behind%s bridge %s %pR\n",
&child->busn_res,
(bus->number > child->busn_res.end &&
bus->busn_res.end < child->number) ?
"wholly" : "partially",
bus->self->transparent ? " transparent" : "",
dev_name(&bus->dev),
&bus->busn_res);
dev_info(&dev->dev, "devices behind bridge are unusable because %pR cannot be assigned for them\n",
&child->busn_res);
break;
}
bus = bus->parent;
}
@ -1280,6 +1278,8 @@ out:
* already configured by the BIOS and after we are done with all of
* them, we proceed to assigning numbers to the remaining buses in
* 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)
{
@ -2695,7 +2695,14 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
for_each_pci_bridge(dev, bus) {
cmax = max;
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 */
@ -2718,12 +2725,14 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
* bridges if any.
*/
buses = available_buses / hotplug_bridges;
buses = min(buses, available_buses - used_buses);
buses = min(buses, available_buses - used_buses + 1);
}
cmax = max;
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, 0x15b8, pci_quirk_mf_endpoint_acs },
/* QCOM QDF2xxx root ports */
{ 0x17cb, 0x400, pci_quirk_qcom_rp_acs },
{ 0x17cb, 0x401, pci_quirk_qcom_rp_acs },
{ PCI_VENDOR_ID_QCOM, 0x0400, pci_quirk_qcom_rp_acs },
{ PCI_VENDOR_ID_QCOM, 0x0401, pci_quirk_qcom_rp_acs },
/* 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_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);
}
/*
* 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
* resource space between hotplug bridges.
*/
for_each_pci_bridge(dev, bus) {
resource_size_t align, io, mmio, mmio_pref;
struct pci_bus *b;
b = dev->subordinate;
if (!b)
if (!b || !dev->is_hotplug_bridge)
continue;
if (!hotplug_bridges && normal_bridges == 1) {
/*
* There is only one bridge on the bus (upstream
* port) so it gets all available resources
* which it can then distribute to the possible
* hotplug bridges below.
*/
pci_bus_distribute_available_resources(b, add_list,
available_io, available_mmio,
available_mmio_pref);
} else if (dev->is_hotplug_bridge) {
resource_size_t align, io, mmio, mmio_pref;
/*
* Distribute available extra resources equally between
* hotplug-capable downstream ports taking alignment into
* account.
*
* 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;
/*
* Distribute available extra resources equally
* between hotplug-capable downstream ports
* taking alignment into account.
*
* 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);
mmio = div64_ul(available_mmio, hotplug_bridges);
mmio = min(ALIGN(mmio, align), remaining_mmio);
remaining_mmio -= mmio;
align = pci_resource_alignment(bridge, mmio_res);
mmio = div64_ul(available_mmio, hotplug_bridges);
mmio = min(ALIGN(mmio, align), remaining_mmio);
remaining_mmio -= mmio;
align = pci_resource_alignment(bridge, mmio_pref_res);
mmio_pref = div64_ul(available_mmio_pref, hotplug_bridges);
mmio_pref = min(ALIGN(mmio_pref, align), remaining_mmio_pref);
remaining_mmio_pref -= mmio_pref;
align = pci_resource_alignment(bridge, mmio_pref_res);
mmio_pref = div64_ul(available_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);
}
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_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
unsigned int ptm_root: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 no_ext_tags:1; /* No Extended Tags */
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_ltr:1; /* OS may use PCIe LTR */
/* Resource alignment requirements */
@ -1451,8 +1455,10 @@ static inline int pci_irqd_intx_xlate(struct irq_domain *d,
#ifdef CONFIG_PCIEPORTBUS
extern bool pcie_ports_disabled;
extern bool pcie_ports_native;
#else
#define pcie_ports_disabled true
#define pcie_ports_native false
#endif
#ifdef CONFIG_PCIEASPM

View File

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

View File

@ -561,6 +561,7 @@
#define PCI_DEVICE_ID_AMD_OPUS_7443 0x7443
#define PCI_DEVICE_ID_AMD_VIPER_7443 0x7443
#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_LPC 0x7468
#define PCI_DEVICE_ID_AMD_8111_IDE 0x7469
@ -2387,6 +2388,8 @@
#define PCI_VENDOR_ID_LENOVO 0x17aa
#define PCI_VENDOR_ID_QCOM 0x17cb
#define PCI_VENDOR_ID_CDNS 0x17cd
#define PCI_VENDOR_ID_ARECA 0x17d3