blob: fc035d6bbd07683198dce76a9c843dc0d55165ae [file] [log] [blame]
/* 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);
}