X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=arch%2Fpowerpc%2Fplatforms%2Fpseries%2Feeh_pseries.c;h=8752f79a6af828fb3333c12b84630b6912a576bc;hb=5375871d432ae9fc581014ac117b96aaee3cd0c7;hp=39567b262deacb57fdd52869c74626ab358c9b4f;hpb=eb594a4754e71e41c048e0f1559bb6f13dab7070;p=firefly-linux-kernel-4.4.55.git diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 39567b262dea..8752f79a6af8 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -56,6 +56,15 @@ static int ibm_get_config_addr_info2; static int ibm_configure_bridge; static int ibm_configure_pe; +/* + * Buffer for reporting slot-error-detail rtas calls. Its here + * in BSS, and not dynamically alloced, so that it ends up in + * RMO where RTAS can access it. + */ +static unsigned char slot_errbuf[RTAS_ERROR_LOG_MAX]; +static DEFINE_SPINLOCK(slot_errbuf_lock); +static int eeh_error_buf_size; + /** * pseries_eeh_init - EEH platform dependent initialization * @@ -107,6 +116,19 @@ static int pseries_eeh_init(void) return -EINVAL; } + /* Initialize error log lock and size */ + spin_lock_init(&slot_errbuf_lock); + eeh_error_buf_size = rtas_token("rtas-error-log-max"); + if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) { + pr_warning("%s: unknown EEH error log size\n", + __func__); + eeh_error_buf_size = 1024; + } else if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) { + pr_warning("%s: EEH error log size %d exceeds the maximal %d\n", + __func__, eeh_error_buf_size, RTAS_ERROR_LOG_MAX); + eeh_error_buf_size = RTAS_ERROR_LOG_MAX; + } + return 0; } @@ -122,11 +144,11 @@ static int pseries_eeh_init(void) static int pseries_eeh_set_option(struct device_node *dn, int option) { int ret = 0; - struct pci_dn *pdn; + struct eeh_dev *edev; const u32 *reg; int config_addr; - pdn = PCI_DN(dn); + edev = of_node_to_eeh_dev(dn); /* * When we're enabling or disabling EEH functioality on @@ -143,9 +165,9 @@ static int pseries_eeh_set_option(struct device_node *dn, int option) case EEH_OPT_THAW_MMIO: case EEH_OPT_THAW_DMA: - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; + config_addr = edev->config_addr; + if (edev->pe_config_addr) + config_addr = edev->pe_config_addr; break; default: @@ -155,8 +177,8 @@ static int pseries_eeh_set_option(struct device_node *dn, int option) } ret = rtas_call(ibm_set_eeh_option, 4, 1, NULL, - config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), option); + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), option); return ret; } @@ -176,11 +198,11 @@ static int pseries_eeh_set_option(struct device_node *dn, int option) */ static int pseries_eeh_get_pe_addr(struct device_node *dn) { - struct pci_dn *pdn; + struct eeh_dev *edev; int ret = 0; int rets[3]; - pdn = PCI_DN(dn); + edev = of_node_to_eeh_dev(dn); if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) { /* @@ -189,15 +211,15 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn) * meaningless. */ ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, - pdn->eeh_config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), 1); + edev->config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), 1); if (ret || (rets[0] == 0)) return 0; /* Retrieve the associated PE config address */ ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, - pdn->eeh_config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), 0); + edev->config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), 0); if (ret) { pr_warning("%s: Failed to get PE address for %s\n", __func__, dn->full_name); @@ -209,8 +231,8 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn) if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { ret = rtas_call(ibm_get_config_addr_info, 4, 2, rets, - pdn->eeh_config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), 0); + edev->config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), 0); if (ret) { pr_warning("%s: Failed to get PE address for %s\n", __func__, dn->full_name); @@ -238,28 +260,28 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn) */ static int pseries_eeh_get_state(struct device_node *dn, int *state) { - struct pci_dn *pdn; + struct eeh_dev *edev; 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; + edev = of_node_to_eeh_dev(dn); + config_addr = edev->config_addr; + if (edev->pe_config_addr) + config_addr = edev->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)); + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->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)); + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid)); } else { return EEH_STATE_NOT_SUPPORT; } @@ -318,7 +340,30 @@ static int pseries_eeh_get_state(struct device_node *dn, int *state) */ static int pseries_eeh_reset(struct device_node *dn, int option) { - return 0; + struct eeh_dev *edev; + int config_addr; + int ret; + + /* Figure out PE address */ + edev = of_node_to_eeh_dev(dn); + config_addr = edev->config_addr; + if (edev->pe_config_addr) + config_addr = edev->pe_config_addr; + + /* Reset PE through RTAS call */ + ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), option); + + /* If fundamental-reset not supported, try hot-reset */ + if (option == EEH_RESET_FUNDAMENTAL && + ret == -8) { + ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), EEH_RESET_HOT); + } + + return ret; } /** @@ -331,7 +376,52 @@ static int pseries_eeh_reset(struct device_node *dn, int option) */ static int pseries_eeh_wait_state(struct device_node *dn, int max_wait) { - return 0; + int ret; + int mwait; + + /* + * According to PAPR, the state of PE might be temporarily + * unavailable. Under the circumstance, we have to wait + * for indicated time determined by firmware. The maximal + * wait time is 5 minutes, which is acquired from the original + * EEH implementation. Also, the original implementation + * also defined the minimal wait time as 1 second. + */ +#define EEH_STATE_MIN_WAIT_TIME (1000) +#define EEH_STATE_MAX_WAIT_TIME (300 * 1000) + + while (1) { + ret = pseries_eeh_get_state(dn, &mwait); + + /* + * If the PE's state is temporarily unavailable, + * we have to wait for the specified time. Otherwise, + * the PE's state will be returned immediately. + */ + if (ret != EEH_STATE_UNAVAILABLE) + return ret; + + if (max_wait <= 0) { + pr_warning("%s: Timeout when getting PE's state (%d)\n", + __func__, max_wait); + return EEH_STATE_NOT_SUPPORT; + } + + if (mwait <= 0) { + pr_warning("%s: Firmware returned bad wait value %d\n", + __func__, mwait); + mwait = EEH_STATE_MIN_WAIT_TIME; + } else if (mwait > EEH_STATE_MAX_WAIT_TIME) { + pr_warning("%s: Firmware returned too long wait value %d\n", + __func__, mwait); + mwait = EEH_STATE_MAX_WAIT_TIME; + } + + max_wait -= mwait; + msleep(mwait); + } + + return EEH_STATE_NOT_SUPPORT; } /** @@ -347,7 +437,30 @@ static int pseries_eeh_wait_state(struct device_node *dn, int max_wait) */ static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_log, unsigned long len) { - return 0; + struct eeh_dev *edev; + int config_addr; + unsigned long flags; + int ret; + + edev = of_node_to_eeh_dev(dn); + spin_lock_irqsave(&slot_errbuf_lock, flags); + memset(slot_errbuf, 0, eeh_error_buf_size); + + /* Figure out the PE address */ + config_addr = edev->config_addr; + if (edev->pe_config_addr) + config_addr = edev->pe_config_addr; + + ret = rtas_call(ibm_slot_error_detail, 8, 1, NULL, config_addr, + BUID_HI(edev->phb->buid), BUID_LO(edev->phb->buid), + virt_to_phys(drv_log), len, + virt_to_phys(slot_errbuf), eeh_error_buf_size, + severity); + if (!ret) + log_error(slot_errbuf, ERR_TYPE_RTAS_LOG, 0); + spin_unlock_irqrestore(&slot_errbuf_lock, flags); + + return ret; } /** @@ -360,7 +473,70 @@ static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_l */ static int pseries_eeh_configure_bridge(struct device_node *dn) { - return 0; + struct eeh_dev *edev; + int config_addr; + int ret; + + /* Figure out the PE address */ + edev = of_node_to_eeh_dev(dn); + config_addr = edev->config_addr; + if (edev->pe_config_addr) + config_addr = edev->pe_config_addr; + + /* Use new configure-pe function, if supported */ + if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE) { + ret = rtas_call(ibm_configure_pe, 3, 1, NULL, + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid)); + } else if (ibm_configure_bridge != RTAS_UNKNOWN_SERVICE) { + ret = rtas_call(ibm_configure_bridge, 3, 1, NULL, + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid)); + } else { + return -EFAULT; + } + + if (ret) + pr_warning("%s: Unable to configure bridge %d for %s\n", + __func__, ret, dn->full_name); + + return ret; +} + +/** + * pseries_eeh_read_config - Read PCI config space + * @dn: device node + * @where: PCI address + * @size: size to read + * @val: return value + * + * Read config space from the speicifed device + */ +static int pseries_eeh_read_config(struct device_node *dn, int where, int size, u32 *val) +{ + struct pci_dn *pdn; + + pdn = PCI_DN(dn); + + return rtas_read_config(pdn, where, size, val); +} + +/** + * pseries_eeh_write_config - Write PCI config space + * @dn: device node + * @where: PCI address + * @size: size to write + * @val: value to be written + * + * Write config space to the specified device + */ +static int pseries_eeh_write_config(struct device_node *dn, int where, int size, u32 val) +{ + struct pci_dn *pdn; + + pdn = PCI_DN(dn); + + return rtas_write_config(pdn, where, size, val); } static struct eeh_ops pseries_eeh_ops = { @@ -372,7 +548,9 @@ static struct eeh_ops pseries_eeh_ops = { .reset = pseries_eeh_reset, .wait_state = pseries_eeh_wait_state, .get_log = pseries_eeh_get_log, - .configure_bridge = pseries_eeh_configure_bridge + .configure_bridge = pseries_eeh_configure_bridge, + .read_config = pseries_eeh_read_config, + .write_config = pseries_eeh_write_config }; /**