Commit eb594a47 authored by Gavin Shan's avatar Gavin Shan Committed by Benjamin Herrenschmidt

powerpc/eeh: pseries platform PE state retrieval

On pSeries platform, there're 2 dedicated RTAS calls introduced to
retrieve the corresponding PE's state: ibm,read-slot-reset-state and
ibm,read-slot-reset-state2.

The patch implements the retrieval of PE's state according to the
given PE address. Besides, the implementation has been abstracted by
struct eeh_ops::get_state so that EEH core components could support
multiple platforms in future.
Signed-off-by: default avatarGavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent c8c29b38
...@@ -42,6 +42,14 @@ struct device_node; ...@@ -42,6 +42,14 @@ struct device_node;
#define EEH_OPT_ENABLE 1 /* EEH enable */ #define EEH_OPT_ENABLE 1 /* EEH enable */
#define EEH_OPT_THAW_MMIO 2 /* MMIO enable */ #define EEH_OPT_THAW_MMIO 2 /* MMIO enable */
#define EEH_OPT_THAW_DMA 3 /* DMA enable */ #define EEH_OPT_THAW_DMA 3 /* DMA enable */
#define EEH_STATE_UNAVAILABLE (1 << 0) /* State unavailable */
#define EEH_STATE_NOT_SUPPORT (1 << 1) /* EEH not supported */
#define EEH_STATE_RESET_ACTIVE (1 << 2) /* Active reset */
#define EEH_STATE_MMIO_ACTIVE (1 << 3) /* Active MMIO */
#define EEH_STATE_DMA_ACTIVE (1 << 4) /* Active DMA */
#define EEH_STATE_MMIO_ENABLED (1 << 5) /* MMIO enabled */
#define EEH_STATE_DMA_ENABLED (1 << 6) /* DMA enabled */
struct eeh_ops { struct eeh_ops {
char *name; char *name;
int (*init)(void); int (*init)(void);
......
...@@ -88,8 +88,6 @@ ...@@ -88,8 +88,6 @@
/* RTAS tokens */ /* RTAS tokens */
static int ibm_set_slot_reset; static int ibm_set_slot_reset;
static int ibm_read_slot_reset_state;
static int ibm_read_slot_reset_state2;
static int ibm_slot_error_detail; static int ibm_slot_error_detail;
static int ibm_configure_bridge; static int ibm_configure_bridge;
static int ibm_configure_pe; static int ibm_configure_pe;
...@@ -288,37 +286,6 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity) ...@@ -288,37 +286,6 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity)
eeh_rtas_slot_error_detail(pdn, severity, pci_regs_buf, loglen); eeh_rtas_slot_error_detail(pdn, severity, pci_regs_buf, loglen);
} }
/**
* eeh_read_slot_reset_state - Read the reset state of a device node's slot
* @dn: device node to read
* @rets: array to return results in
*
* Read the reset state of a device node's slot through platform dependent
* function call.
*/
static int eeh_read_slot_reset_state(struct pci_dn *pdn, int rets[])
{
int token, outputs;
int config_addr;
if (ibm_read_slot_reset_state2 != RTAS_UNKNOWN_SERVICE) {
token = ibm_read_slot_reset_state2;
outputs = 4;
} else {
token = ibm_read_slot_reset_state;
rets[2] = 0; /* fake PE Unavailable info */
outputs = 3;
}
/* Use PE configuration address, if present */
config_addr = pdn->eeh_config_addr;
if (pdn->eeh_pe_config_addr)
config_addr = pdn->eeh_pe_config_addr;
return rtas_call(token, 3, outputs, rets, config_addr,
BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid));
}
/** /**
* eeh_wait_for_slot_status - Returns error status of slot * eeh_wait_for_slot_status - Returns error status of slot
* @pdn: pci device node * @pdn: pci device node
...@@ -335,21 +302,15 @@ static int eeh_read_slot_reset_state(struct pci_dn *pdn, int rets[]) ...@@ -335,21 +302,15 @@ static int eeh_read_slot_reset_state(struct pci_dn *pdn, int rets[])
int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs) int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs)
{ {
int rc; int rc;
int rets[3];
int mwait; int mwait;
while (1) { while (1) {
rc = eeh_read_slot_reset_state(pdn, rets); rc = eeh_ops->get_state(pdn->node, &mwait);
if (rc) return rc; if (rc != EEH_STATE_UNAVAILABLE)
if (rets[1] == 0) return -1; /* EEH is not supported */ return rc;
if (rets[0] != 5) return rets[0]; /* return actual status */
if (rets[2] == 0) return -1; /* permanently unavailable */
if (max_wait_msecs <= 0) break; if (max_wait_msecs <= 0) break;
mwait = rets[2];
if (mwait <= 0) { if (mwait <= 0) {
printk(KERN_WARNING "EEH: Firmware returned bad wait value=%d\n", printk(KERN_WARNING "EEH: Firmware returned bad wait value=%d\n",
mwait); mwait);
...@@ -522,7 +483,6 @@ void eeh_clear_slot(struct device_node *dn, int mode_flag) ...@@ -522,7 +483,6 @@ void eeh_clear_slot(struct device_node *dn, int mode_flag)
int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
{ {
int ret; int ret;
int rets[3];
unsigned long flags; unsigned long flags;
struct pci_dn *pdn; struct pci_dn *pdn;
int rc = 0; int rc = 0;
...@@ -584,40 +544,18 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) ...@@ -584,40 +544,18 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
* function zero of a multi-function device. * function zero of a multi-function device.
* In any case they must share a common PHB. * In any case they must share a common PHB.
*/ */
ret = eeh_read_slot_reset_state(pdn, rets); ret = eeh_ops->get_state(pdn->node, NULL);
/* If the call to firmware failed, punt */
if (ret != 0) {
printk(KERN_WARNING "EEH: eeh_read_slot_reset_state() failed; rc=%d dn=%s\n",
ret, dn->full_name);
false_positives++;
pdn->eeh_false_positives ++;
rc = 0;
goto dn_unlock;
}
/* Note that config-io to empty slots may fail; /* Note that config-io to empty slots may fail;
* they are empty when they don't have children. * they are empty when they don't have children.
* We will punt with the following conditions: Failure to get
* PE's state, EEH not support and Permanently unavailable
* state, PE is in good state.
*/ */
if ((rets[0] == 5) && (rets[2] == 0) && (dn->child == NULL)) { if ((ret < 0) ||
false_positives++; (ret == EEH_STATE_NOT_SUPPORT) ||
pdn->eeh_false_positives ++; (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
rc = 0; (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
goto dn_unlock;
}
/* If EEH is not supported on this device, punt. */
if (rets[1] != 1) {
printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n",
ret, dn->full_name);
false_positives++;
pdn->eeh_false_positives ++;
rc = 0;
goto dn_unlock;
}
/* If not the kind of error we know about, punt. */
if (rets[0] != 1 && rets[0] != 2 && rets[0] != 4 && rets[0] != 5) {
false_positives++; false_positives++;
pdn->eeh_false_positives ++; pdn->eeh_false_positives ++;
rc = 0; rc = 0;
...@@ -703,7 +641,8 @@ int eeh_pci_enable(struct pci_dn *pdn, int function) ...@@ -703,7 +641,8 @@ int eeh_pci_enable(struct pci_dn *pdn, int function)
function, rc, pdn->node->full_name); function, rc, pdn->node->full_name);
rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC);
if ((rc == 4) && (function == EEH_OPT_THAW_MMIO)) if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
(function == EEH_OPT_THAW_MMIO))
return 0; return 0;
return rc; return rc;
...@@ -900,7 +839,7 @@ int eeh_reset_pe(struct pci_dn *pdn) ...@@ -900,7 +839,7 @@ int eeh_reset_pe(struct pci_dn *pdn)
eeh_reset_pe_once(pdn); eeh_reset_pe_once(pdn);
rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC);
if (rc == 0) if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
return 0; return 0;
if (rc < 0) { if (rc < 0) {
...@@ -1057,7 +996,6 @@ void eeh_configure_bridge(struct pci_dn *pdn) ...@@ -1057,7 +996,6 @@ void eeh_configure_bridge(struct pci_dn *pdn)
*/ */
static void *eeh_early_enable(struct device_node *dn, void *data) static void *eeh_early_enable(struct device_node *dn, void *data)
{ {
unsigned int rets[3];
int ret; int ret;
const u32 *class_code = of_get_property(dn, "class-code", NULL); const u32 *class_code = of_get_property(dn, "class-code", NULL);
const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL); const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL);
...@@ -1109,8 +1047,8 @@ static void *eeh_early_enable(struct device_node *dn, void *data) ...@@ -1109,8 +1047,8 @@ static void *eeh_early_enable(struct device_node *dn, void *data)
* where EEH is not supported. Verify support * where EEH is not supported. Verify support
* explicitly. * explicitly.
*/ */
ret = eeh_read_slot_reset_state(pdn, rets); ret = eeh_ops->get_state(pdn->node, NULL);
if ((ret == 0) && (rets[1] == 1)) if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT)
enable = 1; enable = 1;
} }
...@@ -1232,8 +1170,6 @@ void __init eeh_init(void) ...@@ -1232,8 +1170,6 @@ void __init eeh_init(void)
return; return;
ibm_set_slot_reset = rtas_token("ibm,set-slot-reset"); ibm_set_slot_reset = rtas_token("ibm,set-slot-reset");
ibm_read_slot_reset_state2 = rtas_token("ibm,read-slot-reset-state2");
ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state");
ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); ibm_slot_error_detail = rtas_token("ibm,slot-error-detail");
ibm_configure_bridge = rtas_token("ibm,configure-bridge"); ibm_configure_bridge = rtas_token("ibm,configure-bridge");
ibm_configure_pe = rtas_token("ibm,configure-pe"); ibm_configure_pe = rtas_token("ibm,configure-pe");
......
...@@ -397,7 +397,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) ...@@ -397,7 +397,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
/* Get the current PCI slot state. This can take a long time, /* Get the current PCI slot state. This can take a long time,
* sometimes over 3 seconds for certain systems. */ * sometimes over 3 seconds for certain systems. */
rc = eeh_wait_for_slot_status (frozen_pdn, MAX_WAIT_FOR_RECOVERY*1000); rc = eeh_wait_for_slot_status (frozen_pdn, MAX_WAIT_FOR_RECOVERY*1000);
if (rc < 0) { if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
printk(KERN_WARNING "EEH: Permanent failure\n"); printk(KERN_WARNING "EEH: Permanent failure\n");
goto hard_fail; goto hard_fail;
} }
......
...@@ -238,7 +238,75 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn) ...@@ -238,7 +238,75 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn)
*/ */
static int pseries_eeh_get_state(struct device_node *dn, int *state) static int pseries_eeh_get_state(struct device_node *dn, int *state)
{ {
return 0; struct pci_dn *pdn;
int config_addr;
int ret;
int rets[4];
int result;
/* Figure out PE config address if possible */
pdn = PCI_DN(dn);
config_addr = pdn->eeh_config_addr;
if (pdn->eeh_pe_config_addr)
config_addr = pdn->eeh_pe_config_addr;
if (ibm_read_slot_reset_state2 != RTAS_UNKNOWN_SERVICE) {
ret = rtas_call(ibm_read_slot_reset_state2, 3, 4, rets,
config_addr, BUID_HI(pdn->phb->buid),
BUID_LO(pdn->phb->buid));
} else if (ibm_read_slot_reset_state != RTAS_UNKNOWN_SERVICE) {
/* Fake PE unavailable info */
rets[2] = 0;
ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets,
config_addr, BUID_HI(pdn->phb->buid),
BUID_LO(pdn->phb->buid));
} else {
return EEH_STATE_NOT_SUPPORT;
}
if (ret)
return ret;
/* Parse the result out */
result = 0;
if (rets[1]) {
switch(rets[0]) {
case 0:
result &= ~EEH_STATE_RESET_ACTIVE;
result |= EEH_STATE_MMIO_ACTIVE;
result |= EEH_STATE_DMA_ACTIVE;
break;
case 1:
result |= EEH_STATE_RESET_ACTIVE;
result |= EEH_STATE_MMIO_ACTIVE;
result |= EEH_STATE_DMA_ACTIVE;
break;
case 2:
result &= ~EEH_STATE_RESET_ACTIVE;
result &= ~EEH_STATE_MMIO_ACTIVE;
result &= ~EEH_STATE_DMA_ACTIVE;
break;
case 4:
result &= ~EEH_STATE_RESET_ACTIVE;
result &= ~EEH_STATE_MMIO_ACTIVE;
result &= ~EEH_STATE_DMA_ACTIVE;
result |= EEH_STATE_MMIO_ENABLED;
break;
case 5:
if (rets[2]) {
if (state) *state = rets[2];
result = EEH_STATE_UNAVAILABLE;
} else {
result = EEH_STATE_NOT_SUPPORT;
}
default:
result = EEH_STATE_NOT_SUPPORT;
}
} else {
result = EEH_STATE_NOT_SUPPORT;
}
return result;
} }
/** /**
......
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