| /* Copyright 2013-2014 IBM Corp. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <skiboot.h> |
| #include <p7ioc.h> |
| #include <p7ioc-regs.h> |
| #include <cec.h> |
| #include <opal.h> |
| #include <io.h> |
| #include <vpd.h> |
| #include <interrupts.h> |
| #include <ccan/str/str.h> |
| |
| /* |
| * Determine the base address of LEM registers according to |
| * the indicated error source. |
| */ |
| static void *p7ioc_LEM_base(struct p7ioc *ioc, uint32_t err_src) |
| { |
| uint32_t index; |
| void *base = NULL; |
| |
| switch (err_src) { |
| case P7IOC_ERR_SRC_RGC: |
| base = ioc->regs + P7IOC_RGC_LEM_BASE; |
| break; |
| case P7IOC_ERR_SRC_BI_UP: |
| base = ioc->regs + P7IOC_BI_UP_LEM_BASE; |
| break; |
| case P7IOC_ERR_SRC_BI_DOWN: |
| base = ioc->regs + P7IOC_BI_DOWN_LEM_BASE; |
| break; |
| case P7IOC_ERR_SRC_CI_P0: |
| case P7IOC_ERR_SRC_CI_P1: |
| case P7IOC_ERR_SRC_CI_P2: |
| case P7IOC_ERR_SRC_CI_P3: |
| case P7IOC_ERR_SRC_CI_P4: |
| case P7IOC_ERR_SRC_CI_P5: |
| case P7IOC_ERR_SRC_CI_P6: |
| case P7IOC_ERR_SRC_CI_P7: |
| index = err_src - P7IOC_ERR_SRC_CI_P0; |
| base = ioc->regs + P7IOC_CI_PORTn_LEM_BASE(index); |
| break; |
| case P7IOC_ERR_SRC_PHB0: |
| case P7IOC_ERR_SRC_PHB1: |
| case P7IOC_ERR_SRC_PHB2: |
| case P7IOC_ERR_SRC_PHB3: |
| case P7IOC_ERR_SRC_PHB4: |
| case P7IOC_ERR_SRC_PHB5: |
| index = err_src - P7IOC_ERR_SRC_PHB0; |
| base = ioc->regs + P7IOC_PHBn_LEM_BASE(index); |
| break; |
| case P7IOC_ERR_SRC_MISC: |
| base = ioc->regs + P7IOC_MISC_LEM_BASE; |
| break; |
| case P7IOC_ERR_SRC_I2C: |
| base = ioc->regs + P7IOC_I2C_LEM_BASE; |
| break; |
| default: |
| prerror("%s: Unknown error source %d\n", |
| __func__, err_src); |
| } |
| |
| return base; |
| } |
| |
| static void p7ioc_get_diag_common(struct p7ioc *ioc, |
| void *base, |
| struct OpalIoP7IOCErrorData *data) |
| { |
| /* GEM */ |
| data->gemXfir = in_be64(ioc->regs + P7IOC_GEM_XFIR); |
| data->gemRfir = in_be64(ioc->regs + P7IOC_GEM_RFIR); |
| data->gemRirqfir = in_be64(ioc->regs + P7IOC_GEM_RIRQFIR); |
| data->gemMask = in_be64(ioc->regs + P7IOC_GEM_MASK); |
| data->gemRwof = in_be64(ioc->regs + P7IOC_GEM_RWOF); |
| |
| /* LEM */ |
| data->lemFir = in_be64(base + P7IOC_LEM_FIR_OFFSET); |
| data->lemErrMask = in_be64(base + P7IOC_LEM_ERR_MASK_OFFSET); |
| data->lemAction0 = in_be64(base + P7IOC_LEM_ACTION_0_OFFSET); |
| data->lemAction1 = in_be64(base + P7IOC_LEM_ACTION_1_OFFSET); |
| data->lemWof = in_be64(base + P7IOC_LEM_WOF_OFFSET); |
| } |
| |
| static int64_t p7ioc_get_diag_data(struct io_hub *hub, |
| void *diag_buffer, |
| uint64_t diag_buffer_len) |
| { |
| struct p7ioc *ioc = iohub_to_p7ioc(hub); |
| struct OpalIoP7IOCErrorData *data = diag_buffer; |
| void *base; |
| |
| /* Make sure we have enough buffer */ |
| if (diag_buffer_len < sizeof(struct OpalIoP7IOCErrorData)) |
| return OPAL_PARAMETER; |
| |
| /* We need do nothing if there're no pending errors */ |
| if (!p7ioc_err_pending(ioc)) |
| return OPAL_CLOSED; |
| |
| /* |
| * We needn't collect diag-data for CI Port{2, ..., 7} |
| * and PHB{0, ..., 5} since their errors (except GXE) |
| * have been cached to the specific PHB. |
| */ |
| base = p7ioc_LEM_base(ioc, ioc->err.err_src); |
| if (!base) { |
| p7ioc_set_err_pending(ioc, false); |
| return OPAL_INTERNAL_ERROR; |
| } |
| |
| switch (ioc->err.err_src) { |
| case P7IOC_ERR_SRC_RGC: |
| data->type = OPAL_P7IOC_DIAG_TYPE_RGC; |
| p7ioc_get_diag_common(ioc, base, data); |
| |
| data->rgc.rgcStatus = in_be64(ioc->regs + 0x3E1C10); |
| data->rgc.rgcLdcp = in_be64(ioc->regs + 0x3E1C18); |
| |
| break; |
| case P7IOC_ERR_SRC_BI_UP: |
| data->type = OPAL_P7IOC_DIAG_TYPE_BI; |
| data->bi.biDownbound = 0; |
| p7ioc_get_diag_common(ioc, base, data); |
| |
| data->bi.biLdcp0 = in_be64(ioc->regs + 0x3C0100); |
| data->bi.biLdcp1 = in_be64(ioc->regs + 0x3C0108); |
| data->bi.biLdcp2 = in_be64(ioc->regs + 0x3C0110); |
| data->bi.biFenceStatus = in_be64(ioc->regs + 0x3C0130); |
| |
| break; |
| case P7IOC_ERR_SRC_BI_DOWN: |
| data->type = OPAL_P7IOC_DIAG_TYPE_BI; |
| data->bi.biDownbound = 1; |
| p7ioc_get_diag_common(ioc, base, data); |
| |
| data->bi.biLdcp0 = in_be64(ioc->regs + 0x3C0118); |
| data->bi.biLdcp1 = in_be64(ioc->regs + 0x3C0120); |
| data->bi.biLdcp2 = in_be64(ioc->regs + 0x3C0128); |
| data->bi.biFenceStatus = in_be64(ioc->regs + 0x3C0130); |
| |
| break; |
| case P7IOC_ERR_SRC_CI_P0: |
| case P7IOC_ERR_SRC_CI_P1: |
| data->type = OPAL_P7IOC_DIAG_TYPE_CI; |
| data->ci.ciPort = ioc->err.err_src - P7IOC_ERR_SRC_CI_P0; |
| p7ioc_get_diag_common(ioc, base, data); |
| |
| data->ci.ciPortStatus = in_be64(base + 0x008); |
| data->ci.ciPortLdcp = in_be64(base + 0x010); |
| break; |
| case P7IOC_ERR_SRC_MISC: |
| data->type = OPAL_P7IOC_DIAG_TYPE_MISC; |
| p7ioc_get_diag_common(ioc, base, data); |
| break; |
| case P7IOC_ERR_SRC_I2C: |
| data->type = OPAL_P7IOC_DIAG_TYPE_I2C; |
| p7ioc_get_diag_common(ioc, base, data); |
| break; |
| default: |
| p7ioc_set_err_pending(ioc, false); |
| return OPAL_CLOSED; |
| } |
| |
| /* For errors of MAL class, we need mask it */ |
| if (ioc->err.err_class == P7IOC_ERR_CLASS_MAL) |
| out_be64(base + P7IOC_LEM_ERR_MASK_OR_OFFSET, |
| PPC_BIT(63 - ioc->err.err_bit)); |
| p7ioc_set_err_pending(ioc, false); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| static const struct io_hub_ops p7ioc_hub_ops = { |
| .get_diag_data = p7ioc_get_diag_data, |
| .reset = p7ioc_reset, |
| }; |
| |
| static int64_t p7ioc_rgc_get_xive(struct irq_source *is, uint32_t isn, |
| uint16_t *server, uint8_t *prio) |
| { |
| struct p7ioc *ioc = is->data; |
| uint32_t irq = (isn & 0xf); |
| uint32_t fbuid = P7_IRQ_FBUID(isn); |
| uint64_t xive; |
| |
| if (fbuid != ioc->rgc_buid) |
| return OPAL_PARAMETER; |
| |
| xive = ioc->xive_cache[irq]; |
| *server = GETFIELD(IODA_XIVT_SERVER, xive); |
| *prio = GETFIELD(IODA_XIVT_PRIORITY, xive); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| static int64_t p7ioc_rgc_set_xive(struct irq_source *is, uint32_t isn, |
| uint16_t server, uint8_t prio) |
| { |
| struct p7ioc *ioc = is->data; |
| uint32_t irq = (isn & 0xf); |
| uint32_t fbuid = P7_IRQ_FBUID(isn); |
| uint64_t xive; |
| uint64_t m_server, m_prio; |
| |
| if (fbuid != ioc->rgc_buid) |
| return OPAL_PARAMETER; |
| |
| xive = SETFIELD(IODA_XIVT_SERVER, 0ull, server); |
| xive = SETFIELD(IODA_XIVT_PRIORITY, xive, prio); |
| ioc->xive_cache[irq] = xive; |
| |
| /* Now we mangle the server and priority */ |
| if (prio == 0xff) { |
| m_server = 0; |
| m_prio = 0xff; |
| } else { |
| m_server = server >> 3; |
| m_prio = (prio >> 3) | ((server & 7) << 5); |
| } |
| |
| /* Update the XIVE. Don't care HRT entry on P7IOC */ |
| out_be64(ioc->regs + 0x3e1820, (0x0002000000000000UL | irq)); |
| xive = in_be64(ioc->regs + 0x3e1830); |
| xive = SETFIELD(IODA_XIVT_SERVER, xive, m_server); |
| xive = SETFIELD(IODA_XIVT_PRIORITY, xive, m_prio); |
| out_be64(ioc->regs + 0x3e1830, xive); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| /* |
| * The function is used to figure out the error class and error |
| * bit according to LEM WOF. |
| * |
| * The bits of WOF register have been classified according to |
| * the error severity. Of course, we should process those errors |
| * with higher priority. For example, there have 2 errors (GXE, INF) |
| * pending, we should process GXE, and INF is meaningless in face |
| * of GXE. |
| */ |
| static bool p7ioc_err_bit(struct p7ioc *ioc, uint64_t wof) |
| { |
| uint64_t val, severity[P7IOC_ERR_CLASS_LAST]; |
| int32_t class, bit, err_bit = -1; |
| |
| /* Clear severity array */ |
| memset(severity, 0, sizeof(uint64_t) * P7IOC_ERR_CLASS_LAST); |
| |
| /* |
| * The severity array has fixed values. However, it depends |
| * on the damage settings for individual components. We're |
| * using fixed values based on the assumption that damage settings |
| * are fixed for now. If we change it some day, we also need |
| * change the severity array accordingly. Anyway, it's something |
| * to improve in future so that we can figure out the severity |
| * array from hardware registers. |
| */ |
| switch (ioc->err.err_src) { |
| case P7IOC_ERR_SRC_EI: |
| /* EI won't create interrupt yet */ |
| break; |
| case P7IOC_ERR_SRC_RGC: |
| severity[P7IOC_ERR_CLASS_GXE] = 0xF00086E0F4FCFFFFUL; |
| severity[P7IOC_ERR_CLASS_RGA] = 0x0000010000000000UL; |
| severity[P7IOC_ERR_CLASS_INF] = 0x0FFF781F0B030000UL; |
| break; |
| case P7IOC_ERR_SRC_BI_UP: |
| severity[P7IOC_ERR_CLASS_GXE] = 0xF7FFFFFF7FFFFFFFUL; |
| severity[P7IOC_ERR_CLASS_INF] = 0x0800000080000000UL; |
| break; |
| case P7IOC_ERR_SRC_BI_DOWN: |
| severity[P7IOC_ERR_CLASS_GXE] = 0xDFFFF7F35F8000BFUL; |
| severity[P7IOC_ERR_CLASS_INF] = 0x2000080CA07FFF40UL; |
| break; |
| case P7IOC_ERR_SRC_CI_P0: |
| severity[P7IOC_ERR_CLASS_GXE] = 0xF5FF000000000000UL; |
| severity[P7IOC_ERR_CLASS_INF] = 0x0200FFFFFFFFFFFFUL; |
| severity[P7IOC_ERR_CLASS_MAL] = 0x0800000000000000UL; |
| break; |
| case P7IOC_ERR_SRC_CI_P1: |
| severity[P7IOC_ERR_CLASS_GXE] = 0xFFFF000000000000UL; |
| severity[P7IOC_ERR_CLASS_INF] = 0x0000FFFFFFFFFFFFUL; |
| break; |
| case P7IOC_ERR_SRC_CI_P2: |
| case P7IOC_ERR_SRC_CI_P3: |
| case P7IOC_ERR_SRC_CI_P4: |
| case P7IOC_ERR_SRC_CI_P5: |
| case P7IOC_ERR_SRC_CI_P6: |
| case P7IOC_ERR_SRC_CI_P7: |
| severity[P7IOC_ERR_CLASS_GXE] = 0x5B0B000000000000UL; |
| severity[P7IOC_ERR_CLASS_PHB] = 0xA4F4000000000000UL; |
| severity[P7IOC_ERR_CLASS_INF] = 0x0000FFFFFFFFFFFFUL; |
| break; |
| case P7IOC_ERR_SRC_MISC: |
| severity[P7IOC_ERR_CLASS_GXE] = 0x0000000310000000UL; |
| severity[P7IOC_ERR_CLASS_PLL] = 0x0000000001C00000UL; |
| severity[P7IOC_ERR_CLASS_INF] = 0x555FFFF0EE3FFFFFUL; |
| severity[P7IOC_ERR_CLASS_MAL] = 0xAAA0000C00000000UL; |
| break; |
| case P7IOC_ERR_SRC_I2C: |
| severity[P7IOC_ERR_CLASS_GXE] = 0x1100000000000000UL; |
| severity[P7IOC_ERR_CLASS_INF] = 0xEEFFFFFFFFFFFFFFUL; |
| break; |
| case P7IOC_ERR_SRC_PHB0: |
| case P7IOC_ERR_SRC_PHB1: |
| case P7IOC_ERR_SRC_PHB2: |
| case P7IOC_ERR_SRC_PHB3: |
| case P7IOC_ERR_SRC_PHB4: |
| case P7IOC_ERR_SRC_PHB5: |
| severity[P7IOC_ERR_CLASS_PHB] = 0xADB650CB808DD051UL; |
| severity[P7IOC_ERR_CLASS_ER] = 0x0000A0147F50092CUL; |
| severity[P7IOC_ERR_CLASS_INF] = 0x52490F2000222682UL; |
| break; |
| } |
| |
| /* |
| * The error class (ERR_CLASS) has been defined based on |
| * their severity. The priority of those errors out of same |
| * class should be defined based on the position of corresponding |
| * bit in LEM (Local Error Macro) register. |
| */ |
| for (class = P7IOC_ERR_CLASS_NONE + 1; |
| err_bit < 0 && class < P7IOC_ERR_CLASS_LAST; |
| class++) { |
| val = wof & severity[class]; |
| if (!val) continue; |
| |
| for (bit = 0; bit < 64; bit++) { |
| if (val & PPC_BIT(bit)) { |
| err_bit = 63 - bit; |
| break; |
| } |
| } |
| } |
| |
| /* If we don't find the error bit, we needn't go on. */ |
| if (err_bit < 0) |
| return false; |
| |
| ioc->err.err_class = class - 1; |
| ioc->err.err_bit = err_bit; |
| return true; |
| } |
| |
| /* |
| * Check LEM to determine the detailed error information. |
| * The function is expected to be called while OS calls |
| * to OPAL API opal_pci_next_error(). Eventually, the errors |
| * from CI Port{2, ..., 7} or PHB{0, ..., 5} would be cached |
| * to the specific PHB, the left errors would be cached to |
| * the IOC. |
| */ |
| bool p7ioc_check_LEM(struct p7ioc *ioc, |
| uint16_t *pci_error_type, |
| uint16_t *severity) |
| { |
| void *base; |
| uint64_t fir, wof, mask; |
| struct p7ioc_phb *p; |
| int32_t index; |
| bool ret; |
| |
| /* Make sure we have error pending on IOC */ |
| if (!p7ioc_err_pending(ioc)) |
| return false; |
| |
| /* |
| * The IOC probably has been put to fatal error |
| * state (GXE) because of failure on reading on |
| * GEM FIR. |
| */ |
| if (ioc->err.err_src == P7IOC_ERR_SRC_NONE && |
| ioc->err.err_class != P7IOC_ERR_CLASS_NONE) |
| goto err; |
| |
| /* |
| * Get the base address of LEM registers according |
| * to the error source. If we failed to get that, |
| * the error pending flag would be cleared. |
| */ |
| base = p7ioc_LEM_base(ioc, ioc->err.err_src); |
| if (!base) { |
| p7ioc_set_err_pending(ioc, false); |
| return false; |
| } |
| |
| /* IOC would be broken upon broken FIR */ |
| fir = in_be64(base + P7IOC_LEM_FIR_OFFSET); |
| if (fir == 0xffffffffffffffffUL) { |
| ioc->err.err_src = P7IOC_ERR_SRC_NONE; |
| ioc->err.err_class = P7IOC_ERR_CLASS_GXE; |
| goto err; |
| } |
| |
| /* Read on ERR_MASK and WOF. However, we needn't do for PHBn */ |
| wof = in_be64(base + P7IOC_LEM_WOF_OFFSET); |
| if (ioc->err.err_src >= P7IOC_ERR_SRC_PHB0 && |
| ioc->err.err_src <= P7IOC_ERR_SRC_PHB5) { |
| mask = 0x0ull; |
| } else { |
| mask = in_be64(base + P7IOC_LEM_ERR_MASK_OFFSET); |
| in_be64(base + P7IOC_LEM_ACTION_0_OFFSET); |
| in_be64(base + P7IOC_LEM_ACTION_1_OFFSET); |
| } |
| |
| /* |
| * We need process those unmasked error first. If we're |
| * failing to get the error bit, we needn't proceed. |
| */ |
| if (wof & ~mask) |
| wof &= ~mask; |
| if (!wof) { |
| p7ioc_set_err_pending(ioc, false); |
| return false; |
| } |
| |
| if (!p7ioc_err_bit(ioc, wof)) { |
| p7ioc_set_err_pending(ioc, false); |
| return false; |
| } |
| |
| err: |
| /* |
| * We run into here because of valid error. Those errors |
| * from CI Port{2, ..., 7} and PHB{0, ..., 5} will be cached |
| * to the specific PHB. However, we will cache the global |
| * errors (e.g. GXE) to IOC directly. For the left errors, |
| * they will be cached to IOC. |
| */ |
| if (((ioc->err.err_src >= P7IOC_ERR_SRC_CI_P2 && |
| ioc->err.err_src <= P7IOC_ERR_SRC_CI_P7) || |
| (ioc->err.err_src >= P7IOC_ERR_SRC_PHB0 && |
| ioc->err.err_src <= P7IOC_ERR_SRC_PHB5)) && |
| ioc->err.err_class != P7IOC_ERR_CLASS_GXE) { |
| index = (ioc->err.err_src >= P7IOC_ERR_SRC_PHB0 && |
| ioc->err.err_src <= P7IOC_ERR_SRC_PHB5) ? |
| (ioc->err.err_src - P7IOC_ERR_SRC_PHB0) : |
| (ioc->err.err_src - P7IOC_ERR_SRC_CI_P2); |
| p = &ioc->phbs[index]; |
| |
| if (p7ioc_phb_enabled(ioc, index)) { |
| p->err.err_src = ioc->err.err_src; |
| p->err.err_class = ioc->err.err_class; |
| p->err.err_bit = ioc->err.err_bit; |
| p7ioc_phb_set_err_pending(p, true); |
| p7ioc_set_err_pending(ioc, false); |
| |
| return false; |
| } |
| } |
| |
| /* |
| * Map the internal error class to that OS can recognize. |
| * Errors from PHB or the associated CI port would be |
| * GXE, PHB-fatal, ER, or INF. For the case, GXE will be |
| * cached to IOC and the left classes will be cached to |
| * the specific PHB. |
| */ |
| switch (ioc->err.err_class) { |
| case P7IOC_ERR_CLASS_GXE: |
| case P7IOC_ERR_CLASS_PLL: |
| case P7IOC_ERR_CLASS_RGA: |
| *pci_error_type = OPAL_EEH_IOC_ERROR; |
| *severity = OPAL_EEH_SEV_IOC_DEAD; |
| ret = true; |
| break; |
| case P7IOC_ERR_CLASS_INF: |
| case P7IOC_ERR_CLASS_MAL: |
| *pci_error_type = OPAL_EEH_IOC_ERROR; |
| *severity = OPAL_EEH_SEV_INF; |
| ret = false; |
| break; |
| default: |
| p7ioc_set_err_pending(ioc, false); |
| ret = false; |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * Check GEM to see if there has any problematic components. |
| * The function is expected to be called in RGC interrupt |
| * handler. Also, it's notable that failure on reading on |
| * XFIR will cause GXE directly. |
| */ |
| static bool p7ioc_check_GEM(struct p7ioc *ioc) |
| { |
| uint64_t xfir, rwof; |
| |
| /* |
| * Recov_5: Read GEM Xfir |
| * Recov_6: go to GXE recovery? |
| */ |
| xfir = in_be64(ioc->regs + P7IOC_GEM_XFIR); |
| if (xfir == 0xffffffffffffffffUL) { |
| ioc->err.err_src = P7IOC_ERR_SRC_NONE; |
| ioc->err.err_class = P7IOC_ERR_CLASS_GXE; |
| p7ioc_set_err_pending(ioc, true); |
| return true; |
| } |
| |
| /* |
| * Recov_7: Read GEM Rfir |
| * Recov_8: Read GEM RIRQfir |
| * Recov_9: Read GEM RWOF |
| * Recov_10: Read Fence Shadow |
| * Recov_11: Read Fence Shadow WOF |
| */ |
| in_be64(ioc->regs + P7IOC_GEM_RFIR); |
| in_be64(ioc->regs + P7IOC_GEM_RIRQFIR); |
| rwof = in_be64(ioc->regs + P7IOC_GEM_RWOF); |
| in_be64(ioc->regs + P7IOC_CHIP_FENCE_SHADOW); |
| in_be64(ioc->regs + P7IOC_CHIP_FENCE_WOF); |
| |
| /* |
| * Check GEM RWOF to see which component has been |
| * put into problematic state. |
| */ |
| ioc->err.err_src = P7IOC_ERR_SRC_NONE; |
| if (rwof & PPC_BIT(1)) ioc->err.err_src = P7IOC_ERR_SRC_RGC; |
| else if (rwof & PPC_BIT(2)) ioc->err.err_src = P7IOC_ERR_SRC_BI_UP; |
| else if (rwof & PPC_BIT(3)) ioc->err.err_src = P7IOC_ERR_SRC_BI_DOWN; |
| else if (rwof & PPC_BIT(4)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P0; |
| else if (rwof & PPC_BIT(5)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P1; |
| else if (rwof & PPC_BIT(6)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P2; |
| else if (rwof & PPC_BIT(7)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P3; |
| else if (rwof & PPC_BIT(8)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P4; |
| else if (rwof & PPC_BIT(9)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P5; |
| else if (rwof & PPC_BIT(10)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P6; |
| else if (rwof & PPC_BIT(11)) ioc->err.err_src = P7IOC_ERR_SRC_CI_P7; |
| else if (rwof & PPC_BIT(16)) ioc->err.err_src = P7IOC_ERR_SRC_PHB0; |
| else if (rwof & PPC_BIT(17)) ioc->err.err_src = P7IOC_ERR_SRC_PHB1; |
| else if (rwof & PPC_BIT(18)) ioc->err.err_src = P7IOC_ERR_SRC_PHB2; |
| else if (rwof & PPC_BIT(19)) ioc->err.err_src = P7IOC_ERR_SRC_PHB3; |
| else if (rwof & PPC_BIT(20)) ioc->err.err_src = P7IOC_ERR_SRC_PHB4; |
| else if (rwof & PPC_BIT(21)) ioc->err.err_src = P7IOC_ERR_SRC_PHB5; |
| else if (rwof & PPC_BIT(24)) ioc->err.err_src = P7IOC_ERR_SRC_MISC; |
| else if (rwof & PPC_BIT(25)) ioc->err.err_src = P7IOC_ERR_SRC_I2C; |
| |
| /* |
| * If we detect any problematic components, the OS is |
| * expected to poll that for more details through OPAL |
| * interface. |
| */ |
| if (ioc->err.err_src != P7IOC_ERR_SRC_NONE) { |
| p7ioc_set_err_pending(ioc, true); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static void p7ioc_rgc_interrupt(struct irq_source *is, uint32_t isn) |
| { |
| struct p7ioc *ioc = is->data; |
| |
| printf("Got RGC interrupt 0x%04x\n", isn); |
| |
| /* We will notify OS while getting error from GEM */ |
| if (p7ioc_check_GEM(ioc)) |
| /* This is a bit hacky but works - we raise the event |
| on a downstream phb as the OS needs to call |
| opal_pci_next_error for all phbs to ensure all events |
| are cleared anyway. */ |
| opal_pci_eeh_set_evt(ioc->phbs[0].phb.opal_id); |
| } |
| |
| static uint64_t p7ioc_rgc_irq_attributes(struct irq_source *is __unused, |
| uint32_t isn __unused) |
| { |
| return IRQ_ATTR_TARGET_OPAL | IRQ_ATTR_TARGET_RARE; |
| } |
| |
| static const struct irq_source_ops p7ioc_rgc_irq_ops = { |
| .get_xive = p7ioc_rgc_get_xive, |
| .set_xive = p7ioc_rgc_set_xive, |
| .attributes = p7ioc_rgc_irq_attributes, |
| .interrupt = p7ioc_rgc_interrupt, |
| }; |
| |
| static void p7ioc_create_hub(struct dt_node *np) |
| { |
| struct p7ioc *ioc; |
| unsigned int i, id; |
| u64 bar1, bar2; |
| u32 pdt; |
| char *path; |
| |
| /* Use the BUID extension as ID and add it to device-tree */ |
| id = dt_prop_get_u32(np, "ibm,buid-ext"); |
| path = dt_get_path(np); |
| printf("P7IOC: Found at %s ID 0x%x\n", path, id); |
| free(path); |
| |
| /* Load VPD LID */ |
| vpd_preload(np); |
| vpd_iohub_load(np); |
| |
| ioc = zalloc(sizeof(struct p7ioc)); |
| if (!ioc) |
| return; |
| ioc->hub.hub_id = id; |
| ioc->hub.ops = &p7ioc_hub_ops; |
| ioc->dt_node = np; |
| |
| bar1 = dt_prop_get_u64(np, "ibm,gx-bar-1"); |
| bar2 = dt_prop_get_u64(np, "ibm,gx-bar-2"); |
| |
| ioc->regs = (void *)bar1; |
| |
| ioc->mmio1_win_start = bar1; |
| ioc->mmio1_win_size = MWIN1_SIZE; |
| ioc->mmio2_win_start = bar2; |
| ioc->mmio2_win_size = MWIN2_SIZE; |
| |
| ioc->buid_base = id << 9; |
| ioc->rgc_buid = ioc->buid_base + RGC_BUID_OFFSET; |
| |
| /* Add some DT properties */ |
| dt_add_property_cells(np, "ibm,opal-hubid", 0, id); |
| |
| /* XXX Fixme: how many RGC interrupts ? */ |
| dt_add_property_cells(np, "interrupt-parent", get_ics_phandle()); |
| dt_add_property_cells(np, "interrupts", ioc->rgc_buid << 4, 1); |
| dt_add_property_cells(np, "interrupt-base", ioc->rgc_buid << 4); |
| |
| /* XXX What about ibm,opal-mmio-real ? */ |
| |
| /* Clear the RGC XIVE cache */ |
| for (i = 0; i < 16; i++) |
| ioc->xive_cache[i] = SETFIELD(IODA_XIVT_PRIORITY, 0ull, 0xff); |
| |
| /* |
| * Register RGC interrupts |
| * |
| * For now I assume only 0 is... to verify with Greg or HW guys, |
| * we support all 16 |
| */ |
| register_irq_source(&p7ioc_rgc_irq_ops, ioc, ioc->rgc_buid << 4, 1); |
| |
| /* Check for presence detect from HDAT, we use only BR1 on P7IOC */ |
| pdt = dt_prop_get_u32_def(np, "ibm,br1-presence-detect", 0xffffffff); |
| if (pdt != 0xffffffff) |
| printf("P7IOC: Presence detect from HDAT : 0x%02x\n", pdt); |
| else { |
| } |
| ioc->phb_pdt = pdt & 0xff; |
| |
| /* Setup PHB structures (no HW access yet) */ |
| for (i = 0; i < P7IOC_NUM_PHBS; i++) { |
| if (p7ioc_phb_enabled(ioc, i)) |
| p7ioc_phb_setup(ioc, i); |
| else |
| ioc->phbs[i].state = P7IOC_PHB_STATE_OFF; |
| } |
| |
| /* Now, we do the bulk of the inits */ |
| p7ioc_inits(ioc); |
| |
| printf("P7IOC: Initialization complete\n"); |
| |
| cec_register(&ioc->hub); |
| } |
| |
| void probe_p7ioc(void) |
| { |
| struct dt_node *np; |
| |
| dt_for_each_compatible(dt_root, np, "ibm,p7ioc") |
| p7ioc_create_hub(np); |
| } |
| |
| |
| |