|
@@ -149,8 +149,8 @@ static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
|
|
|
* callback returns something other than NULL, or no more PEs
|
|
|
* to be traversed.
|
|
|
*/
|
|
|
-static void *eeh_pe_traverse(struct eeh_pe *root,
|
|
|
- eeh_traverse_func fn, void *flag)
|
|
|
+void *eeh_pe_traverse(struct eeh_pe *root,
|
|
|
+ eeh_traverse_func fn, void *flag)
|
|
|
{
|
|
|
struct eeh_pe *pe;
|
|
|
void *ret;
|
|
@@ -176,7 +176,7 @@ void *eeh_pe_dev_traverse(struct eeh_pe *root,
|
|
|
eeh_traverse_func fn, void *flag)
|
|
|
{
|
|
|
struct eeh_pe *pe;
|
|
|
- struct eeh_dev *edev;
|
|
|
+ struct eeh_dev *edev, *tmp;
|
|
|
void *ret;
|
|
|
|
|
|
if (!root) {
|
|
@@ -186,7 +186,7 @@ void *eeh_pe_dev_traverse(struct eeh_pe *root,
|
|
|
|
|
|
/* Traverse root PE */
|
|
|
for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
|
|
|
- eeh_pe_for_each_dev(pe, edev) {
|
|
|
+ eeh_pe_for_each_dev(pe, edev, tmp) {
|
|
|
ret = fn(edev, flag);
|
|
|
if (ret)
|
|
|
return ret;
|
|
@@ -333,7 +333,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
|
|
|
while (parent) {
|
|
|
if (!(parent->type & EEH_PE_INVALID))
|
|
|
break;
|
|
|
- parent->type &= ~EEH_PE_INVALID;
|
|
|
+ parent->type &= ~(EEH_PE_INVALID | EEH_PE_KEEP);
|
|
|
parent = parent->parent;
|
|
|
}
|
|
|
pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
|
|
@@ -397,21 +397,20 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
|
|
|
/**
|
|
|
* eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
|
|
|
* @edev: EEH device
|
|
|
- * @purge_pe: remove PE or not
|
|
|
*
|
|
|
* The PE hierarchy tree might be changed when doing PCI hotplug.
|
|
|
* Also, the PCI devices or buses could be removed from the system
|
|
|
* during EEH recovery. So we have to call the function remove the
|
|
|
* corresponding PE accordingly if necessary.
|
|
|
*/
|
|
|
-int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
|
|
|
+int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
|
|
|
{
|
|
|
struct eeh_pe *pe, *parent, *child;
|
|
|
int cnt;
|
|
|
|
|
|
if (!edev->pe) {
|
|
|
- pr_warning("%s: No PE found for EEH device %s\n",
|
|
|
- __func__, edev->dn->full_name);
|
|
|
+ pr_debug("%s: No PE found for EEH device %s\n",
|
|
|
+ __func__, edev->dn->full_name);
|
|
|
return -EEXIST;
|
|
|
}
|
|
|
|
|
@@ -431,7 +430,7 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
|
|
|
if (pe->type & EEH_PE_PHB)
|
|
|
break;
|
|
|
|
|
|
- if (purge_pe) {
|
|
|
+ if (!(pe->state & EEH_PE_KEEP)) {
|
|
|
if (list_empty(&pe->edevs) &&
|
|
|
list_empty(&pe->child_list)) {
|
|
|
list_del(&pe->child);
|
|
@@ -502,7 +501,7 @@ static void *__eeh_pe_state_mark(void *data, void *flag)
|
|
|
{
|
|
|
struct eeh_pe *pe = (struct eeh_pe *)data;
|
|
|
int state = *((int *)flag);
|
|
|
- struct eeh_dev *tmp;
|
|
|
+ struct eeh_dev *edev, *tmp;
|
|
|
struct pci_dev *pdev;
|
|
|
|
|
|
/*
|
|
@@ -512,8 +511,8 @@ static void *__eeh_pe_state_mark(void *data, void *flag)
|
|
|
* the PCI device driver.
|
|
|
*/
|
|
|
pe->state |= state;
|
|
|
- eeh_pe_for_each_dev(pe, tmp) {
|
|
|
- pdev = eeh_dev_to_pci_dev(tmp);
|
|
|
+ eeh_pe_for_each_dev(pe, edev, tmp) {
|
|
|
+ pdev = eeh_dev_to_pci_dev(edev);
|
|
|
if (pdev)
|
|
|
pdev->error_state = pci_channel_io_frozen;
|
|
|
}
|
|
@@ -579,7 +578,7 @@ void eeh_pe_state_clear(struct eeh_pe *pe, int state)
|
|
|
* blocked on normal path during the stage. So we need utilize
|
|
|
* eeh operations, which is always permitted.
|
|
|
*/
|
|
|
-static void eeh_bridge_check_link(struct pci_dev *pdev,
|
|
|
+static void eeh_bridge_check_link(struct eeh_dev *edev,
|
|
|
struct device_node *dn)
|
|
|
{
|
|
|
int cap;
|
|
@@ -590,16 +589,17 @@ static void eeh_bridge_check_link(struct pci_dev *pdev,
|
|
|
* We only check root port and downstream ports of
|
|
|
* PCIe switches
|
|
|
*/
|
|
|
- if (!pci_is_pcie(pdev) ||
|
|
|
- (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
|
|
|
- pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
|
|
|
+ if (!(edev->mode & (EEH_DEV_ROOT_PORT | EEH_DEV_DS_PORT)))
|
|
|
return;
|
|
|
|
|
|
- pr_debug("%s: Check PCIe link for %s ...\n",
|
|
|
- __func__, pci_name(pdev));
|
|
|
+ pr_debug("%s: Check PCIe link for %04x:%02x:%02x.%01x ...\n",
|
|
|
+ __func__, edev->phb->global_number,
|
|
|
+ edev->config_addr >> 8,
|
|
|
+ PCI_SLOT(edev->config_addr & 0xFF),
|
|
|
+ PCI_FUNC(edev->config_addr & 0xFF));
|
|
|
|
|
|
/* Check slot status */
|
|
|
- cap = pdev->pcie_cap;
|
|
|
+ cap = edev->pcie_cap;
|
|
|
eeh_ops->read_config(dn, cap + PCI_EXP_SLTSTA, 2, &val);
|
|
|
if (!(val & PCI_EXP_SLTSTA_PDS)) {
|
|
|
pr_debug(" No card in the slot (0x%04x) !\n", val);
|
|
@@ -653,8 +653,7 @@ static void eeh_bridge_check_link(struct pci_dev *pdev,
|
|
|
#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
|
|
|
#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
|
|
|
|
|
|
-static void eeh_restore_bridge_bars(struct pci_dev *pdev,
|
|
|
- struct eeh_dev *edev,
|
|
|
+static void eeh_restore_bridge_bars(struct eeh_dev *edev,
|
|
|
struct device_node *dn)
|
|
|
{
|
|
|
int i;
|
|
@@ -680,7 +679,7 @@ static void eeh_restore_bridge_bars(struct pci_dev *pdev,
|
|
|
eeh_ops->write_config(dn, PCI_COMMAND, 4, edev->config_space[1]);
|
|
|
|
|
|
/* Check the PCIe link is ready */
|
|
|
- eeh_bridge_check_link(pdev, dn);
|
|
|
+ eeh_bridge_check_link(edev, dn);
|
|
|
}
|
|
|
|
|
|
static void eeh_restore_device_bars(struct eeh_dev *edev,
|
|
@@ -729,19 +728,12 @@ static void eeh_restore_device_bars(struct eeh_dev *edev,
|
|
|
*/
|
|
|
static void *eeh_restore_one_device_bars(void *data, void *flag)
|
|
|
{
|
|
|
- struct pci_dev *pdev = NULL;
|
|
|
struct eeh_dev *edev = (struct eeh_dev *)data;
|
|
|
struct device_node *dn = eeh_dev_to_of_node(edev);
|
|
|
|
|
|
- /* Trace the PCI bridge */
|
|
|
- if (eeh_probe_mode_dev()) {
|
|
|
- pdev = eeh_dev_to_pci_dev(edev);
|
|
|
- if (pdev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
|
|
|
- pdev = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- if (pdev)
|
|
|
- eeh_restore_bridge_bars(pdev, edev, dn);
|
|
|
+ /* Do special restore for bridges */
|
|
|
+ if (edev->mode & EEH_DEV_BRIDGE)
|
|
|
+ eeh_restore_bridge_bars(edev, dn);
|
|
|
else
|
|
|
eeh_restore_device_bars(edev, dn);
|
|
|
|