Commit b09803b5 authored by Oza Pawandeep's avatar Oza Pawandeep Committed by Bjorn Helgaas

PCI/DPC: Use the generic pcie_do_fatal_recovery() path

Our goal is to handle ERR_FATAL errors similarly, whether they are reported
via AER or via DPC.  A previous commit changed AER so it handles ERR_FATAL
by calling driver .remove() methods and resetting the Link.  DPC already
does that (although the Link reset is done automatically by hardware and
happens before we call the driver .remove() methods).

Restructure the DPC code so it calls the same pcie_do_fatal_recovery()
interface used by AER.  This makes it clearer that we want to use the same
path.

Implement the .reset_link() method used by pcie_do_fatal_recovery().  For
DPC, the actual reset is done automatically by hardware, so we really only
have to wait for the Link to be inactive, then release the Port from DPC.
Signed-off-by: default avatarOza Pawandeep <poza@codeaurora.org>
[bhelgaas: changelog, DPC_FATAL is not a bitfield, can be sequential]
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent 0b91439d
...@@ -73,29 +73,30 @@ static void dpc_wait_link_inactive(struct dpc_dev *dpc) ...@@ -73,29 +73,30 @@ static void dpc_wait_link_inactive(struct dpc_dev *dpc)
pcie_wait_for_link(pdev, false); pcie_wait_for_link(pdev, false);
} }
static void dpc_work(struct work_struct *work) static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
{ {
struct dpc_dev *dpc = container_of(work, struct dpc_dev, work); struct dpc_dev *dpc;
struct pci_dev *dev, *temp, *pdev = dpc->dev->port; struct pcie_device *pciedev;
struct pci_bus *parent = pdev->subordinate; struct device *devdpc;
u16 cap = dpc->cap_pos, ctl; u16 cap, ctl;
pci_lock_rescan_remove(); /*
list_for_each_entry_safe_reverse(dev, temp, &parent->devices, * DPC disables the Link automatically in hardware, so it has
bus_list) { * already been reset by the time we get here.
pci_dev_get(dev); */
pci_dev_set_disconnected(dev, NULL); devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC);
if (pci_has_subordinate(dev)) pciedev = to_pcie_device(devdpc);
pci_walk_bus(dev->subordinate, dpc = get_service_data(pciedev);
pci_dev_set_disconnected, NULL); cap = dpc->cap_pos;
pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev); /*
} * Wait until the Link is inactive, then clear DPC Trigger Status
pci_unlock_rescan_remove(); * to allow the Port to leave DPC.
*/
dpc_wait_link_inactive(dpc); dpc_wait_link_inactive(dpc);
if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc)) if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc))
return; return PCI_ERS_RESULT_DISCONNECT;
if (dpc->rp_extensions && dpc->rp_pio_status) { if (dpc->rp_extensions && dpc->rp_pio_status) {
pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS,
dpc->rp_pio_status); dpc->rp_pio_status);
...@@ -108,6 +109,17 @@ static void dpc_work(struct work_struct *work) ...@@ -108,6 +109,17 @@ static void dpc_work(struct work_struct *work)
pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
ctl | PCI_EXP_DPC_CTL_INT_EN); ctl | PCI_EXP_DPC_CTL_INT_EN);
return PCI_ERS_RESULT_RECOVERED;
}
static void dpc_work(struct work_struct *work)
{
struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
struct pci_dev *pdev = dpc->dev->port;
/* We configure DPC so it only triggers on ERR_FATAL */
pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_DPC);
} }
static void dpc_process_rp_pio_error(struct dpc_dev *dpc) static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
...@@ -288,6 +300,7 @@ static struct pcie_port_service_driver dpcdriver = { ...@@ -288,6 +300,7 @@ static struct pcie_port_service_driver dpcdriver = {
.service = PCIE_PORT_SERVICE_DPC, .service = PCIE_PORT_SERVICE_DPC,
.probe = dpc_probe, .probe = dpc_probe,
.remove = dpc_remove, .remove = dpc_remove,
.reset_link = dpc_reset_link,
}; };
static int __init dpc_service_init(void) static int __init dpc_service_init(void)
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define AER_NONFATAL 0 #define AER_NONFATAL 0
#define AER_FATAL 1 #define AER_FATAL 1
#define AER_CORRECTABLE 2 #define AER_CORRECTABLE 2
#define DPC_FATAL 3
struct pci_dev; struct pci_dev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment