mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-23 20:53:53 +08:00
Merge branch 'pci/aw-reset-v5' into next
* pci/aw-reset-v5: PCI: Add pci_probe_reset_slot() and pci_probe_reset_bus() PCI: Remove aer_do_secondary_bus_reset() PCI: Tune secondary bus reset timing PCI: Wake-up devices before saving config space for reset PCI: Add pci_reset_slot() and pci_reset_bus() PCI: Split out pci_dev lock/unlock and save/restore PCI: Add slot reset option to pci_dev_reset() PCI: pciehp: Add reset_slot() method PCI: Add hotplug_slot_ops.reset_slot() PCI: Add pci_reset_bridge_secondary_bus()
This commit is contained in:
commit
7d8c4a2c5a
@ -155,6 +155,7 @@ void pciehp_green_led_off(struct slot *slot);
|
|||||||
void pciehp_green_led_blink(struct slot *slot);
|
void pciehp_green_led_blink(struct slot *slot);
|
||||||
int pciehp_check_link_status(struct controller *ctrl);
|
int pciehp_check_link_status(struct controller *ctrl);
|
||||||
void pciehp_release_ctrl(struct controller *ctrl);
|
void pciehp_release_ctrl(struct controller *ctrl);
|
||||||
|
int pciehp_reset_slot(struct slot *slot, int probe);
|
||||||
|
|
||||||
static inline const char *slot_name(struct slot *slot)
|
static inline const char *slot_name(struct slot *slot)
|
||||||
{
|
{
|
||||||
|
@ -69,6 +69,7 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
|
|||||||
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
|
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
|
||||||
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
|
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
|
||||||
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
|
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
|
||||||
|
static int reset_slot (struct hotplug_slot *slot, int probe);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* release_slot - free up the memory used by a slot
|
* release_slot - free up the memory used by a slot
|
||||||
@ -111,6 +112,7 @@ static int init_slot(struct controller *ctrl)
|
|||||||
ops->disable_slot = disable_slot;
|
ops->disable_slot = disable_slot;
|
||||||
ops->get_power_status = get_power_status;
|
ops->get_power_status = get_power_status;
|
||||||
ops->get_adapter_status = get_adapter_status;
|
ops->get_adapter_status = get_adapter_status;
|
||||||
|
ops->reset_slot = reset_slot;
|
||||||
if (MRL_SENS(ctrl))
|
if (MRL_SENS(ctrl))
|
||||||
ops->get_latch_status = get_latch_status;
|
ops->get_latch_status = get_latch_status;
|
||||||
if (ATTN_LED(ctrl)) {
|
if (ATTN_LED(ctrl)) {
|
||||||
@ -223,6 +225,16 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
|||||||
return pciehp_get_adapter_status(slot, value);
|
return pciehp_get_adapter_status(slot, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
|
||||||
|
{
|
||||||
|
struct slot *slot = hotplug_slot->private;
|
||||||
|
|
||||||
|
ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
|
||||||
|
__func__, slot_name(slot));
|
||||||
|
|
||||||
|
return pciehp_reset_slot(slot, probe);
|
||||||
|
}
|
||||||
|
|
||||||
static int pciehp_probe(struct pcie_device *dev)
|
static int pciehp_probe(struct pcie_device *dev)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -749,6 +749,37 @@ static void pcie_disable_notification(struct controller *ctrl)
|
|||||||
ctrl_warn(ctrl, "Cannot disable software notification\n");
|
ctrl_warn(ctrl, "Cannot disable software notification\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
|
||||||
|
* bus reset of the bridge, but if the slot supports surprise removal we need
|
||||||
|
* to disable presence detection around the bus reset and clear any spurious
|
||||||
|
* events after.
|
||||||
|
*/
|
||||||
|
int pciehp_reset_slot(struct slot *slot, int probe)
|
||||||
|
{
|
||||||
|
struct controller *ctrl = slot->ctrl;
|
||||||
|
|
||||||
|
if (probe)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (HP_SUPR_RM(ctrl)) {
|
||||||
|
pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_PDCE);
|
||||||
|
if (pciehp_poll_mode)
|
||||||
|
del_timer_sync(&ctrl->poll_timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_reset_bridge_secondary_bus(ctrl->pcie->port);
|
||||||
|
|
||||||
|
if (HP_SUPR_RM(ctrl)) {
|
||||||
|
pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC);
|
||||||
|
pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE);
|
||||||
|
if (pciehp_poll_mode)
|
||||||
|
int_poll_timeout(ctrl->poll_timer.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int pcie_init_notification(struct controller *ctrl)
|
int pcie_init_notification(struct controller *ctrl)
|
||||||
{
|
{
|
||||||
if (pciehp_request_irq(ctrl))
|
if (pciehp_request_irq(ctrl))
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/pci_hotplug.h>
|
||||||
#include <asm-generic/pci-bridge.h>
|
#include <asm-generic/pci-bridge.h>
|
||||||
#include <asm/setup.h>
|
#include <asm/setup.h>
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
@ -3288,9 +3289,42 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
|
/**
|
||||||
|
* pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge.
|
||||||
|
* @dev: Bridge device
|
||||||
|
*
|
||||||
|
* Use the bridge control register to assert reset on the secondary bus.
|
||||||
|
* Devices on the secondary bus are left in power-on state.
|
||||||
|
*/
|
||||||
|
void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
u16 ctrl;
|
u16 ctrl;
|
||||||
|
|
||||||
|
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
|
||||||
|
ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
|
||||||
|
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
|
||||||
|
/*
|
||||||
|
* PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms. Double
|
||||||
|
* this to 2ms to ensure that we meet the minium requirement.
|
||||||
|
*/
|
||||||
|
msleep(2);
|
||||||
|
|
||||||
|
ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
|
||||||
|
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trhfa for conventional PCI is 2^25 clock cycles.
|
||||||
|
* Assuming a minimum 33MHz clock this results in a 1s
|
||||||
|
* delay before we can consider subordinate devices to
|
||||||
|
* be re-initialized. PCIe has some ways to shorten this,
|
||||||
|
* but we don't make use of them yet.
|
||||||
|
*/
|
||||||
|
ssleep(1);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
|
||||||
|
|
||||||
|
static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
|
||||||
|
{
|
||||||
struct pci_dev *pdev;
|
struct pci_dev *pdev;
|
||||||
|
|
||||||
if (pci_is_root_bus(dev->bus) || dev->subordinate || !dev->bus->self)
|
if (pci_is_root_bus(dev->bus) || dev->subordinate || !dev->bus->self)
|
||||||
@ -3303,18 +3337,40 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
|
|||||||
if (probe)
|
if (probe)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pci_read_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, &ctrl);
|
pci_reset_bridge_secondary_bus(dev->bus->self);
|
||||||
ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
|
|
||||||
pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
|
|
||||||
msleep(100);
|
|
||||||
|
|
||||||
ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
|
|
||||||
pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
|
|
||||||
msleep(100);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, int probe)
|
||||||
|
{
|
||||||
|
int rc = -ENOTTY;
|
||||||
|
|
||||||
|
if (!hotplug || !try_module_get(hotplug->ops->owner))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (hotplug->ops->reset_slot)
|
||||||
|
rc = hotplug->ops->reset_slot(hotplug, probe);
|
||||||
|
|
||||||
|
module_put(hotplug->ops->owner);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev;
|
||||||
|
|
||||||
|
if (dev->subordinate || !dev->slot)
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
list_for_each_entry(pdev, &dev->bus->devices, bus_list)
|
||||||
|
if (pdev != dev && pdev->slot == dev->slot)
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
|
||||||
|
}
|
||||||
|
|
||||||
static int __pci_dev_reset(struct pci_dev *dev, int probe)
|
static int __pci_dev_reset(struct pci_dev *dev, int probe)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
@ -3337,27 +3393,65 @@ static int __pci_dev_reset(struct pci_dev *dev, int probe)
|
|||||||
if (rc != -ENOTTY)
|
if (rc != -ENOTTY)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
rc = pci_dev_reset_slot_function(dev, probe);
|
||||||
|
if (rc != -ENOTTY)
|
||||||
|
goto done;
|
||||||
|
|
||||||
rc = pci_parent_bus_reset(dev, probe);
|
rc = pci_parent_bus_reset(dev, probe);
|
||||||
done:
|
done:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pci_dev_lock(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
pci_cfg_access_lock(dev);
|
||||||
|
/* block PM suspend, driver probe, etc. */
|
||||||
|
device_lock(&dev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pci_dev_unlock(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
device_unlock(&dev->dev);
|
||||||
|
pci_cfg_access_unlock(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pci_dev_save_and_disable(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Wake-up device prior to save. PM registers default to D0 after
|
||||||
|
* reset and a simple register restore doesn't reliably return
|
||||||
|
* to a non-D0 state anyway.
|
||||||
|
*/
|
||||||
|
pci_set_power_state(dev, PCI_D0);
|
||||||
|
|
||||||
|
pci_save_state(dev);
|
||||||
|
/*
|
||||||
|
* Disable the device by clearing the Command register, except for
|
||||||
|
* INTx-disable which is set. This not only disables MMIO and I/O port
|
||||||
|
* BARs, but also prevents the device from being Bus Master, preventing
|
||||||
|
* DMA from the device including MSI/MSI-X interrupts. For PCI 2.3
|
||||||
|
* compliant devices, INTx-disable prevents legacy interrupts.
|
||||||
|
*/
|
||||||
|
pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pci_dev_restore(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
pci_restore_state(dev);
|
||||||
|
}
|
||||||
|
|
||||||
static int pci_dev_reset(struct pci_dev *dev, int probe)
|
static int pci_dev_reset(struct pci_dev *dev, int probe)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (!probe) {
|
if (!probe)
|
||||||
pci_cfg_access_lock(dev);
|
pci_dev_lock(dev);
|
||||||
/* block PM suspend, driver probe, etc. */
|
|
||||||
device_lock(&dev->dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = __pci_dev_reset(dev, probe);
|
rc = __pci_dev_reset(dev, probe);
|
||||||
|
|
||||||
if (!probe) {
|
if (!probe)
|
||||||
device_unlock(&dev->dev);
|
pci_dev_unlock(dev);
|
||||||
pci_cfg_access_unlock(dev);
|
|
||||||
}
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -3448,22 +3542,249 @@ int pci_reset_function(struct pci_dev *dev)
|
|||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
pci_save_state(dev);
|
pci_dev_save_and_disable(dev);
|
||||||
|
|
||||||
/*
|
|
||||||
* both INTx and MSI are disabled after the Interrupt Disable bit
|
|
||||||
* is set and the Bus Master bit is cleared.
|
|
||||||
*/
|
|
||||||
pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
|
|
||||||
|
|
||||||
rc = pci_dev_reset(dev, 0);
|
rc = pci_dev_reset(dev, 0);
|
||||||
|
|
||||||
pci_restore_state(dev);
|
pci_dev_restore(dev);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pci_reset_function);
|
EXPORT_SYMBOL_GPL(pci_reset_function);
|
||||||
|
|
||||||
|
/* Lock devices from the top of the tree down */
|
||||||
|
static void pci_bus_lock(struct pci_bus *bus)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||||
|
pci_dev_lock(dev);
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_bus_lock(dev->subordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlock devices from the bottom of the tree up */
|
||||||
|
static void pci_bus_unlock(struct pci_bus *bus)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_bus_unlock(dev->subordinate);
|
||||||
|
pci_dev_unlock(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock devices from the top of the tree down */
|
||||||
|
static void pci_slot_lock(struct pci_slot *slot)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
|
||||||
|
if (!dev->slot || dev->slot != slot)
|
||||||
|
continue;
|
||||||
|
pci_dev_lock(dev);
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_bus_lock(dev->subordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlock devices from the bottom of the tree up */
|
||||||
|
static void pci_slot_unlock(struct pci_slot *slot)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
|
||||||
|
if (!dev->slot || dev->slot != slot)
|
||||||
|
continue;
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_bus_unlock(dev->subordinate);
|
||||||
|
pci_dev_unlock(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save and disable devices from the top of the tree down */
|
||||||
|
static void pci_bus_save_and_disable(struct pci_bus *bus)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||||
|
pci_dev_save_and_disable(dev);
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_bus_save_and_disable(dev->subordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore devices from top of the tree down - parent bridges need to be
|
||||||
|
* restored before we can get to subordinate devices.
|
||||||
|
*/
|
||||||
|
static void pci_bus_restore(struct pci_bus *bus)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||||
|
pci_dev_restore(dev);
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_bus_restore(dev->subordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save and disable devices from the top of the tree down */
|
||||||
|
static void pci_slot_save_and_disable(struct pci_slot *slot)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
|
||||||
|
if (!dev->slot || dev->slot != slot)
|
||||||
|
continue;
|
||||||
|
pci_dev_save_and_disable(dev);
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_bus_save_and_disable(dev->subordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore devices from top of the tree down - parent bridges need to be
|
||||||
|
* restored before we can get to subordinate devices.
|
||||||
|
*/
|
||||||
|
static void pci_slot_restore(struct pci_slot *slot)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
|
||||||
|
if (!dev->slot || dev->slot != slot)
|
||||||
|
continue;
|
||||||
|
pci_dev_restore(dev);
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_bus_restore(dev->subordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pci_slot_reset(struct pci_slot *slot, int probe)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!slot)
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
if (!probe)
|
||||||
|
pci_slot_lock(slot);
|
||||||
|
|
||||||
|
might_sleep();
|
||||||
|
|
||||||
|
rc = pci_reset_hotplug_slot(slot->hotplug, probe);
|
||||||
|
|
||||||
|
if (!probe)
|
||||||
|
pci_slot_unlock(slot);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_probe_reset_slot - probe whether a PCI slot can be reset
|
||||||
|
* @slot: PCI slot to probe
|
||||||
|
*
|
||||||
|
* Return 0 if slot can be reset, negative if a slot reset is not supported.
|
||||||
|
*/
|
||||||
|
int pci_probe_reset_slot(struct pci_slot *slot)
|
||||||
|
{
|
||||||
|
return pci_slot_reset(slot, 1);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_probe_reset_slot);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_reset_slot - reset a PCI slot
|
||||||
|
* @slot: PCI slot to reset
|
||||||
|
*
|
||||||
|
* A PCI bus may host multiple slots, each slot may support a reset mechanism
|
||||||
|
* independent of other slots. For instance, some slots may support slot power
|
||||||
|
* control. In the case of a 1:1 bus to slot architecture, this function may
|
||||||
|
* wrap the bus reset to avoid spurious slot related events such as hotplug.
|
||||||
|
* Generally a slot reset should be attempted before a bus reset. All of the
|
||||||
|
* function of the slot and any subordinate buses behind the slot are reset
|
||||||
|
* through this function. PCI config space of all devices in the slot and
|
||||||
|
* behind the slot is saved before and restored after reset.
|
||||||
|
*
|
||||||
|
* Return 0 on success, non-zero on error.
|
||||||
|
*/
|
||||||
|
int pci_reset_slot(struct pci_slot *slot)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = pci_slot_reset(slot, 1);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
pci_slot_save_and_disable(slot);
|
||||||
|
|
||||||
|
rc = pci_slot_reset(slot, 0);
|
||||||
|
|
||||||
|
pci_slot_restore(slot);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_reset_slot);
|
||||||
|
|
||||||
|
static int pci_bus_reset(struct pci_bus *bus, int probe)
|
||||||
|
{
|
||||||
|
if (!bus->self)
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
if (probe)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pci_bus_lock(bus);
|
||||||
|
|
||||||
|
might_sleep();
|
||||||
|
|
||||||
|
pci_reset_bridge_secondary_bus(bus->self);
|
||||||
|
|
||||||
|
pci_bus_unlock(bus);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_probe_reset_bus - probe whether a PCI bus can be reset
|
||||||
|
* @bus: PCI bus to probe
|
||||||
|
*
|
||||||
|
* Return 0 if bus can be reset, negative if a bus reset is not supported.
|
||||||
|
*/
|
||||||
|
int pci_probe_reset_bus(struct pci_bus *bus)
|
||||||
|
{
|
||||||
|
return pci_bus_reset(bus, 1);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_probe_reset_bus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_reset_bus - reset a PCI bus
|
||||||
|
* @bus: top level PCI bus to reset
|
||||||
|
*
|
||||||
|
* Do a bus reset on the given bus and any subordinate buses, saving
|
||||||
|
* and restoring state of all devices.
|
||||||
|
*
|
||||||
|
* Return 0 on success, non-zero on error.
|
||||||
|
*/
|
||||||
|
int pci_reset_bus(struct pci_bus *bus)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = pci_bus_reset(bus, 1);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
pci_bus_save_and_disable(bus);
|
||||||
|
|
||||||
|
rc = pci_bus_reset(bus, 0);
|
||||||
|
|
||||||
|
pci_bus_restore(bus);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_reset_bus);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
|
* pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
|
||||||
* @dev: PCI device to query
|
* @dev: PCI device to query
|
||||||
|
@ -352,7 +352,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
|||||||
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
||||||
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
|
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
|
||||||
|
|
||||||
aer_do_secondary_bus_reset(dev);
|
pci_reset_bridge_secondary_bus(dev);
|
||||||
dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
|
dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
|
||||||
|
|
||||||
/* Clear Root Error Status */
|
/* Clear Root Error Status */
|
||||||
|
@ -106,7 +106,6 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern struct bus_type pcie_port_bus_type;
|
extern struct bus_type pcie_port_bus_type;
|
||||||
void aer_do_secondary_bus_reset(struct pci_dev *dev);
|
|
||||||
int aer_init(struct pcie_device *dev);
|
int aer_init(struct pcie_device *dev);
|
||||||
void aer_isr(struct work_struct *work);
|
void aer_isr(struct work_struct *work);
|
||||||
void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
|
void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
|
||||||
|
@ -366,39 +366,6 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
|
|||||||
return result_data.result;
|
return result_data.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* aer_do_secondary_bus_reset - perform secondary bus reset
|
|
||||||
* @dev: pointer to bridge's pci_dev data structure
|
|
||||||
*
|
|
||||||
* Invoked when performing link reset at Root Port or Downstream Port.
|
|
||||||
*/
|
|
||||||
void aer_do_secondary_bus_reset(struct pci_dev *dev)
|
|
||||||
{
|
|
||||||
u16 p2p_ctrl;
|
|
||||||
|
|
||||||
/* Assert Secondary Bus Reset */
|
|
||||||
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
|
|
||||||
p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
|
|
||||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* we should send hot reset message for 2ms to allow it time to
|
|
||||||
* propagate to all downstream ports
|
|
||||||
*/
|
|
||||||
msleep(2);
|
|
||||||
|
|
||||||
/* De-assert Secondary Bus Reset */
|
|
||||||
p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
|
|
||||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* System software must wait for at least 100ms from the end
|
|
||||||
* of a reset of one or more device before it is permitted
|
|
||||||
* to issue Configuration Requests to those devices.
|
|
||||||
*/
|
|
||||||
msleep(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* default_reset_link - default reset function
|
* default_reset_link - default reset function
|
||||||
* @dev: pointer to pci_dev data structure
|
* @dev: pointer to pci_dev data structure
|
||||||
@ -408,7 +375,7 @@ void aer_do_secondary_bus_reset(struct pci_dev *dev)
|
|||||||
*/
|
*/
|
||||||
static pci_ers_result_t default_reset_link(struct pci_dev *dev)
|
static pci_ers_result_t default_reset_link(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
aer_do_secondary_bus_reset(dev);
|
pci_reset_bridge_secondary_bus(dev);
|
||||||
dev_printk(KERN_DEBUG, &dev->dev, "downstream link has been reset\n");
|
dev_printk(KERN_DEBUG, &dev->dev, "downstream link has been reset\n");
|
||||||
return PCI_ERS_RESULT_RECOVERED;
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
}
|
}
|
||||||
|
@ -925,6 +925,11 @@ int pcie_set_mps(struct pci_dev *dev, int mps);
|
|||||||
int __pci_reset_function(struct pci_dev *dev);
|
int __pci_reset_function(struct pci_dev *dev);
|
||||||
int __pci_reset_function_locked(struct pci_dev *dev);
|
int __pci_reset_function_locked(struct pci_dev *dev);
|
||||||
int pci_reset_function(struct pci_dev *dev);
|
int pci_reset_function(struct pci_dev *dev);
|
||||||
|
int pci_probe_reset_slot(struct pci_slot *slot);
|
||||||
|
int pci_reset_slot(struct pci_slot *slot);
|
||||||
|
int pci_probe_reset_bus(struct pci_bus *bus);
|
||||||
|
int pci_reset_bus(struct pci_bus *bus);
|
||||||
|
void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
|
||||||
void pci_update_resource(struct pci_dev *dev, int resno);
|
void pci_update_resource(struct pci_dev *dev, int resno);
|
||||||
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
|
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
|
||||||
int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
|
int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
|
||||||
|
@ -63,6 +63,9 @@ enum pcie_link_width {
|
|||||||
* @get_adapter_status: Called to get see if an adapter is present in the slot or not.
|
* @get_adapter_status: Called to get see if an adapter is present in the slot or not.
|
||||||
* If this field is NULL, the value passed in the struct hotplug_slot_info
|
* If this field is NULL, the value passed in the struct hotplug_slot_info
|
||||||
* will be used when this value is requested by a user.
|
* will be used when this value is requested by a user.
|
||||||
|
* @reset_slot: Optional interface to allow override of a bus reset for the
|
||||||
|
* slot for cases where a secondary bus reset can result in spurious
|
||||||
|
* hotplug events or where a slot can be reset independent of the bus.
|
||||||
*
|
*
|
||||||
* The table of function pointers that is passed to the hotplug pci core by a
|
* The table of function pointers that is passed to the hotplug pci core by a
|
||||||
* hotplug pci driver. These functions are called by the hotplug pci core when
|
* hotplug pci driver. These functions are called by the hotplug pci core when
|
||||||
@ -80,6 +83,7 @@ struct hotplug_slot_ops {
|
|||||||
int (*get_attention_status) (struct hotplug_slot *slot, u8 *value);
|
int (*get_attention_status) (struct hotplug_slot *slot, u8 *value);
|
||||||
int (*get_latch_status) (struct hotplug_slot *slot, u8 *value);
|
int (*get_latch_status) (struct hotplug_slot *slot, u8 *value);
|
||||||
int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value);
|
int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value);
|
||||||
|
int (*reset_slot) (struct hotplug_slot *slot, int probe);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user