summaryrefslogtreecommitdiffstats
path: root/hw/p7ioc.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/p7ioc.c')
-rw-r--r--hw/p7ioc.c677
1 files changed, 677 insertions, 0 deletions
diff --git a/hw/p7ioc.c b/hw/p7ioc.c
new file mode 100644
index 00000000..9aa6480e
--- /dev/null
+++ b/hw/p7ioc.c
@@ -0,0 +1,677 @@
+/* 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 = {
+ .set_tce_mem = NULL, /* No set_tce_mem for p7ioc, we use FMTC */
+ .get_diag_data = p7ioc_get_diag_data,
+ .reset = p7ioc_reset,
+};
+
+static int64_t p7ioc_rgc_get_xive(void *data, uint32_t isn,
+ uint16_t *server, uint8_t *prio)
+{
+ struct p7ioc *ioc = 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(void *data, uint32_t isn,
+ uint16_t server, uint8_t prio)
+{
+ struct p7ioc *ioc = 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, (0x0002000000000000 | 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] = 0xF00086E0F4FCFFFF;
+ severity[P7IOC_ERR_CLASS_RGA] = 0x0000010000000000;
+ severity[P7IOC_ERR_CLASS_INF] = 0x0FFF781F0B030000;
+ break;
+ case P7IOC_ERR_SRC_BI_UP:
+ severity[P7IOC_ERR_CLASS_GXE] = 0xF7FFFFFF7FFFFFFF;
+ severity[P7IOC_ERR_CLASS_INF] = 0x0800000080000000;
+ break;
+ case P7IOC_ERR_SRC_BI_DOWN:
+ severity[P7IOC_ERR_CLASS_GXE] = 0xDFFFF7F35F8000BF;
+ severity[P7IOC_ERR_CLASS_INF] = 0x2000080CA07FFF40;
+ break;
+ case P7IOC_ERR_SRC_CI_P0:
+ severity[P7IOC_ERR_CLASS_GXE] = 0xF5FF000000000000;
+ severity[P7IOC_ERR_CLASS_INF] = 0x0200FFFFFFFFFFFF;
+ severity[P7IOC_ERR_CLASS_MAL] = 0x0800000000000000;
+ break;
+ case P7IOC_ERR_SRC_CI_P1:
+ severity[P7IOC_ERR_CLASS_GXE] = 0xFFFF000000000000;
+ severity[P7IOC_ERR_CLASS_INF] = 0x0000FFFFFFFFFFFF;
+ 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] = 0x5B0B000000000000;
+ severity[P7IOC_ERR_CLASS_PHB] = 0xA4F4000000000000;
+ severity[P7IOC_ERR_CLASS_INF] = 0x0000FFFFFFFFFFFF;
+ break;
+ case P7IOC_ERR_SRC_MISC:
+ severity[P7IOC_ERR_CLASS_GXE] = 0x0000000310000000;
+ severity[P7IOC_ERR_CLASS_PLL] = 0x0000000001C00000;
+ severity[P7IOC_ERR_CLASS_INF] = 0x555FFFF0EE3FFFFF;
+ severity[P7IOC_ERR_CLASS_MAL] = 0xAAA0000C00000000;
+ break;
+ case P7IOC_ERR_SRC_I2C:
+ severity[P7IOC_ERR_CLASS_GXE] = 0x1100000000000000;
+ severity[P7IOC_ERR_CLASS_INF] = 0xEEFFFFFFFFFFFFFF;
+ 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] = 0xADB650CB808DD051;
+ severity[P7IOC_ERR_CLASS_ER] = 0x0000A0147F50092C;
+ severity[P7IOC_ERR_CLASS_INF] = 0x52490F2000222682;
+ 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 == 0xffffffffffffffff) {
+ 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 == 0xffffffffffffffff) {
+ 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(void *data, uint32_t isn)
+{
+ struct p7ioc *ioc = data;
+
+ printf("Got RGC interrupt 0x%04x\n", isn);
+
+ /* We will notify OS while getting error from GEM */
+ if (p7ioc_check_GEM(ioc))
+ opal_update_pending_evt(OPAL_EVENT_PCI_ERROR,
+ OPAL_EVENT_PCI_ERROR);
+}
+
+static const struct irq_source_ops p7ioc_rgc_irq_ops = {
+ .get_xive = p7ioc_rgc_get_xive,
+ .set_xive = p7ioc_rgc_set_xive,
+ .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_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, "interrupts", ioc->rgc_buid << 4);
+ 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);
+}
+
OpenPOWER on IntegriCloud