diff options
author | Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com> | 2010-04-15 13:21:27 +0900 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-05-11 12:01:38 -0700 |
commit | 89713422a768458a0d375f0c2f3586cd5ccde6a1 (patch) | |
tree | c446440123602cdb5320617ac7a8c2dbf514ff41 /drivers/pci/pcie | |
parent | 517cae3829ae8cc3033c24f60e64eb251b2f0d14 (diff) | |
download | linux-stable-89713422a768458a0d375f0c2f3586cd5ccde6a1.tar.gz linux-stable-89713422a768458a0d375f0c2f3586cd5ccde6a1.tar.bz2 linux-stable-89713422a768458a0d375f0c2f3586cd5ccde6a1.zip |
PCI: aerdrv: introduce default_downstream_reset_link
I noticed that when I inject a fatal error to an endpoint via
aer-inject, aer_root_reset() is called as reset_link for a
downstream port at upstream of the endpoint:
pcieport 0000:00:06.0: AER: Uncorrected (Fatal) error received: id=5401
:
pcieport 0000:52:02.0: Root Port link has been reset
It externally appears to be working, but internally issues some
accesses to PCI_ERR_ROOT_COMMAND/STATUS registers that is for
root port so not available on downstream port.
This patch introduces default_downstream_reset_link that is
a version of aer_root_reset() with no accesses to root port's
register. It is used for downstream ports that has no reset_link
function its specific.
This patch also updates related description in pcieaer-howto.txt.
Some minor fixes are included.
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Reviewed-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv.c | 23 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv.h | 1 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv_core.c | 78 |
3 files changed, 63 insertions, 39 deletions
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index cbc7cc77b2c3..a225d58c1ac8 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -341,7 +341,6 @@ static int __devinit aer_probe(struct pcie_device *dev) **/ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) { - u16 p2p_ctrl; u32 reg32; int pos; @@ -352,27 +351,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); - /* 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 - * propogate 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); + aer_do_secondary_bus_reset(dev); dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n"); /* Clear Root Error Status */ diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index d0f8291c5ca0..7aaae2d2bd67 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -114,6 +114,7 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig, } extern struct bus_type pcie_port_bus_type; +extern void aer_do_secondary_bus_reset(struct pci_dev *dev); extern int aer_init(struct pcie_device *dev); extern void aer_isr(struct work_struct *work); extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 8fb14aeb74dd..ce42cac99dd3 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -373,6 +373,53 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, 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_downstream_reset_link - default reset function for Downstream Port + * @dev: pointer to downstream port's pci_dev data structure + * + * Invoked when performing link reset at Downstream Port w/ no aer driver. + */ +static pci_ers_result_t default_downstream_reset_link(struct pci_dev *dev) +{ + aer_do_secondary_bus_reset(dev); + dev_printk(KERN_DEBUG, &dev->dev, + "Downstream Port link has been reset\n"); + return PCI_ERS_RESULT_RECOVERED; +} + static int find_aer_service_iter(struct device *device, void *data) { struct pcie_port_service_driver *service_driver, **drv; @@ -406,31 +453,28 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev, pci_ers_result_t status; struct pcie_port_service_driver *driver; - if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) + if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) { + /* Reset this port for all subordinates */ udev = dev; - else + } else { + /* Reset the upstream component (likely downstream port) */ udev = dev->bus->self; + } /* Use the aer driver of the component firstly */ driver = find_aer_service(udev); - /* - * If it hasn't the driver and is downstream port, use the root port's - */ - if (!driver || !driver->reset_link) { - if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM && - aerdev->device.driver && - to_service_driver(aerdev->device.driver)->reset_link) { - driver = to_service_driver(aerdev->device.driver); - } else { - dev_printk(KERN_DEBUG, &dev->dev, - "no link-reset support at upstream device %s\n", - pci_name(udev)); - return PCI_ERS_RESULT_DISCONNECT; - } + if (driver && driver->reset_link) { + status = driver->reset_link(udev); + } else if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) { + status = default_downstream_reset_link(udev); + } else { + dev_printk(KERN_DEBUG, &dev->dev, + "no link-reset support at upstream device %s\n", + pci_name(udev)); + return PCI_ERS_RESULT_DISCONNECT; } - status = driver->reset_link(udev); if (status != PCI_ERS_RESULT_RECOVERED) { dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream device %s failed\n", |