blob: 8a07047290150d420ebaeb8641e443d7be7fc479 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/*
* Service Processor serial console handling code
*
* Copyright 2013-2019 IBM Corp.
*/
#include <io.h>
#include <psi.h>
#include <fsp.h>
#include <opal.h>
#include <interrupts.h>
#include <cpu.h>
#include <dio-p9.h>
#include <trace.h>
#include <xscom.h>
#include <chip.h>
#include <lpc.h>
#include <i2c.h>
#include <timebase.h>
#include <platform.h>
#include <errorlog.h>
#include <xive.h>
#include <sbe-p9.h>
#include <phys-map.h>
#include <occ.h>
static LIST_HEAD(psis);
static u64 psi_link_timer;
static u64 psi_link_timeout;
static bool psi_link_poll_active;
static void psi_activate_phb(struct psi *psi);
struct lock psi_lock = LOCK_UNLOCKED;
DEFINE_LOG_ENTRY(OPAL_RC_PSI_TIMEOUT, OPAL_PLATFORM_ERR_EVT, OPAL_PSI,
OPAL_PLATFORM_FIRMWARE,
OPAL_UNRECOVERABLE_ERR_LOSS_OF_FUNCTION, OPAL_NA);
void psi_set_link_polling(bool active)
{
printf("PSI: %sing link polling\n",
active ? "start" : "stopp");
psi_link_poll_active = active;
}
void psi_disable_link(struct psi *psi)
{
lock(&psi_lock);
/*
* Note: This can be called with the link already down but
* not detected as such yet by this layer since psi_check_link_active()
* operates locklessly and thus won't update the PSI structure. This
* is a non-issue, the only consequence is the messages in the log
* mentioning first the link having gone down then being disabled.
*/
if (psi->active) {
u64 reg;
psi->active = false;
/* Mask errors in SEMR */
reg = in_be64(psi->regs + PSIHB_SEMR);
reg &= ((0xfffull << 36) | (0xfffull << 20));
out_be64(psi->regs + PSIHB_SEMR, reg);
printf("PSI: SEMR set to %llx\n", reg);
/* Reset all the error bits in PSIHB_CR and
* disable FSP interrupts
*/
reg = in_be64(psi->regs + PSIHB_CR);
reg &= ~(0x7ffull << 20);
reg &= ~PSIHB_CR_PSI_LINK_ENABLE; /* flip link enable */
/*
* Ensure no commands/spurious interrupts reach
* the processor, by flipping the command enable.
*/
reg &= ~PSIHB_CR_FSP_CMD_ENABLE;
reg &= ~PSIHB_CR_FSP_IRQ_ENABLE;
reg &= ~PSIHB_CR_FSP_IRQ; /* Clear interrupt state too */
printf("PSI[0x%03x]: Disabling link!\n", psi->chip_id);
out_be64(psi->regs + PSIHB_CR, reg);
printf("PSI: PSIHB_CR (error bits) set to %llx\n",
in_be64(psi->regs + PSIHB_CR));
psi_set_link_polling(true);
}
unlock(&psi_lock);
}
/*
* Resetting the FSP is a multi step sequence:
* 1. Read the PSIHBCR
* 2. Set the PSIHBCR[6] -- write register back.
* 3. Read PSIHBCR again
* 4. Reset PSIHBCR[6] -- write register back.
*/
void psi_reset_fsp(struct psi *psi)
{
lock(&psi_lock);
if (psi->active) {
u64 reg;
printf("PSI: Driving FSP reset via PSI\n");
reg = in_be64(psi->regs + PSIHB_CR);
reg &= ~(0xfffull << 20); /* Reset error bits */
reg |= PSIHB_CR_FSP_RESET; /* FSP reset trigger start */
out_be64(psi->regs + PSIHB_CR, reg);
printf("PSI[0x%03x]: FSP reset start PSIHBCR set to %llx\n",
psi->chip_id, in_be64(psi->regs + PSIHB_CR));
reg = in_be64(psi->regs + PSIHB_CR);
reg &= ~PSIHB_CR_FSP_RESET; /* Clear FSP reset bit */
out_be64(psi->regs + PSIHB_CR, reg); /* Complete reset */
printf("PSI[0x%03x]: FSP reset complete. PSIHBCR set to %llx\n",
psi->chip_id, in_be64(psi->regs + PSIHB_CR));
}
unlock(&psi_lock);
/* Now bring down the PSI link too... */
psi_disable_link(psi);
}
bool psi_check_link_active(struct psi *psi)
{
u64 val = in_be64(psi->regs + PSIHB_CR);
/*
* Unlocked, used during fsp_poke_msg so we really want
* to avoid fancy link re-entrancy and deadlocks here
*/
if (!psi->active)
return false;
return (val & PSIHB_CR_PSI_LINK_ENABLE) &&
(val & PSIHB_CR_FSP_LINK_ACTIVE);
}
struct psi *psi_find_link(uint32_t chip_id)
{
struct psi *psi;
list_for_each(&psis, psi, list) {
if (psi->chip_id == chip_id)
return psi;
}
return NULL;
}
#define PSI_LINK_CHECK_INTERVAL 10 /* Interval in secs */
#define PSI_LINK_RECOVERY_TIMEOUT 1800 /* 30 minutes */
static void psi_link_poll(void *data __unused)
{
struct psi *psi;
u64 now;
if (!psi_link_poll_active)
return;
now = mftb();
if (psi_link_timer == 0 ||
(tb_compare(now, psi_link_timer) == TB_AAFTERB) ||
(tb_compare(now, psi_link_timer) == TB_AEQUALB)) {
lock(&psi_lock);
list_for_each(&psis, psi, list) {
u64 val;
if (psi->active)
continue;
val = in_be64(psi->regs + PSIHB_CR);
printf("PSI[0x%03x]: Poll CR=0x%016llx\n",
psi->chip_id, val);
if ((val & PSIHB_CR_PSI_LINK_ENABLE) &&
(val & PSIHB_CR_FSP_LINK_ACTIVE)) {
printf("PSI[0x%03x]: Found active link!\n",
psi->chip_id);
psi_link_timeout = 0;
psi->active = true;
psi_activate_phb(psi);
psi_set_link_polling(false);
unlock(&psi_lock);
if (platform.psi && platform.psi->link_established)
platform.psi->link_established();
return;
}
}
if (!psi_link_timeout)
psi_link_timeout =
now + secs_to_tb(PSI_LINK_RECOVERY_TIMEOUT);
if (tb_compare(now, psi_link_timeout) == TB_AAFTERB) {
log_simple_error(&e_info(OPAL_RC_PSI_TIMEOUT),
"PSI: Link timeout -- loss of FSP\n");
/* Reset the link timeout and continue looking */
psi_link_timeout = 0;
}
/* Poll every 10 seconds */
psi_link_timer = now + secs_to_tb(PSI_LINK_CHECK_INTERVAL);
unlock(&psi_lock);
}
}
void psi_enable_fsp_interrupt(struct psi *psi)
{
/* Enable FSP interrupts in the GXHB */
lock(&psi_lock);
out_be64(psi->regs + PSIHB_CR,
in_be64(psi->regs + PSIHB_CR) | PSIHB_CR_FSP_IRQ_ENABLE);
unlock(&psi_lock);
}
/* Multiple bits can be set on errors */
static void decode_psihb_error(u64 val)
{
if (val & PSIHB_CR_PSI_ERROR)
printf("PSI: PSI Reported Error\n");
if (val & PSIHB_CR_PSI_LINK_INACTIVE)
printf("PSI: PSI Link Inactive Transition\n");
if (val & PSIHB_CR_FSP_ACK_TIMEOUT)
printf("PSI: FSP Ack Timeout\n");
if (val & PSIHB_CR_MMIO_LOAD_TIMEOUT)
printf("PSI: MMIO Load Timeout\n");
if (val & PSIHB_CR_MMIO_LENGTH_ERROR)
printf("PSI: MMIO Length Error\n");
if (val & PSIHB_CR_MMIO_ADDRESS_ERROR)
printf("PSI: MMIO Address Error\n");
if (val & PSIHB_CR_MMIO_TYPE_ERROR)
printf("PSI: MMIO Type Error\n");
if (val & PSIHB_CR_UE)
printf("PSI: UE Detected\n");
if (val & PSIHB_CR_PARITY_ERROR)
printf("PSI: Internal Parity Error\n");
if (val & PSIHB_CR_SYNC_ERR_ALERT1)
printf("PSI: Sync Error Alert1\n");
if (val & PSIHB_CR_SYNC_ERR_ALERT2)
printf("PSI: Sync Error Alert2\n");
if (val & PSIHB_CR_FSP_COMMAND_ERROR)
printf("PSI: FSP Command Error\n");
}
static void handle_psi_interrupt(struct psi *psi, u64 val)
{
printf("PSI[0x%03x]: PSI mgmnt interrupt CR=0x%016llx\n",
psi->chip_id, val);
if (val & (0xfffull << 20)) {
decode_psihb_error(val);
psi_disable_link(psi);
} else if (val & (0x1full << 11))
printf("PSI: FSP error detected\n");
}
static void psi_spurious_fsp_irq(struct psi *psi)
{
u64 reg, bit;
prlog(PR_NOTICE, "PSI: Spurious interrupt, attempting clear\n");
if (proc_gen == proc_gen_p9) {
reg = PSIHB_XSCOM_P9_HBCSR_CLR;
bit = PSIHB_XSCOM_P9_HBSCR_FSP_IRQ;
} else if (proc_gen == proc_gen_p8) {
reg = PSIHB_XSCOM_P8_HBCSR_CLR;
bit = PSIHB_XSCOM_P8_HBSCR_FSP_IRQ;
} else {
assert(false);
}
xscom_write(psi->chip_id, psi->xscom_base + reg, bit);
}
bool psi_poll_fsp_interrupt(struct psi *psi)
{
return !!(in_be64(psi->regs + PSIHB_CR) & PSIHB_CR_FSP_IRQ);
}
static void psihb_interrupt(struct irq_source *is, uint32_t isn __unused)
{
struct psi *psi = is->data;
u64 val;
val = in_be64(psi->regs + PSIHB_CR);
if (psi_link_poll_active) {
printf("PSI[0x%03x]: PSI interrupt CR=0x%016llx (A=%d)\n",
psi->chip_id, val, psi->active);
}
/* Handle PSI interrupts first in case it's a link down */
if (val & PSIHB_CR_PSI_IRQ) {
handle_psi_interrupt(psi, val);
/*
* If the link went down, re-read PSIHB_CR as
* the FSP interrupt might have been cleared.
*/
if (!psi->active)
val = in_be64(psi->regs + PSIHB_CR);
}
/*
* We avoid forwarding FSP interrupts if the link isn't
* active. They should be masked anyway but it looks
* like the CR bit can remain set.
*/
if (val & PSIHB_CR_FSP_IRQ) {
/*
* We have a case a flood with FSP mailbox interrupts
* when the link is down, see if we manage to clear
* the condition
*/
if (!psi->active)
psi_spurious_fsp_irq(psi);
else {
if (platform.psi && platform.psi->fsp_interrupt)
platform.psi->fsp_interrupt();
}
}
if (platform.psi && platform.psi->psihb_interrupt)
platform.psi->psihb_interrupt();
}
static const uint32_t psi_p8_irq_to_xivr[P8_IRQ_PSI_IRQ_COUNT] = {
[P8_IRQ_PSI_FSP] = PSIHB_XIVR_FSP,
[P8_IRQ_PSI_OCC] = PSIHB_XIVR_OCC,
[P8_IRQ_PSI_FSI] = PSIHB_XIVR_FSI,
[P8_IRQ_PSI_LPC] = PSIHB_XIVR_LPC,
[P8_IRQ_PSI_LOCAL_ERR] = PSIHB_XIVR_LOCAL_ERR,
[P8_IRQ_PSI_EXTERNAL]= PSIHB_XIVR_HOST_ERR,
};
static void psi_cleanup_irq(struct psi *psi)
{
uint32_t irq;
uint64_t xivr, xivr_p;
for (irq = 0; irq < P8_IRQ_PSI_IRQ_COUNT; irq++) {
prlog(PR_DEBUG, "PSI[0x%03x]: Cleaning up IRQ %d\n",
psi->chip_id, irq);
xivr_p = psi_p8_irq_to_xivr[irq];
xivr = in_be64(psi->regs + xivr_p);
xivr |= (0xffull << 32);
out_be64(psi->regs + xivr_p, xivr);
time_wait_ms_nopoll(10);
xivr = in_be64(psi->regs + xivr_p);
if (xivr & PPC_BIT(39)) {
printf(" Need EOI !\n");
icp_send_eoi(psi->interrupt + irq);
}
}
}
/* Called on a fast reset, make sure we aren't stuck with
* an accepted and never EOId PSI interrupt
*/
void psi_irq_reset(void)
{
struct psi *psi;
printf("PSI: Hot reset!\n");
assert(proc_gen == proc_gen_p8);
list_for_each(&psis, psi, list) {
psi_cleanup_irq(psi);
}
}
static int64_t psi_p8_set_xive(struct irq_source *is, uint32_t isn,
uint16_t server, uint8_t priority)
{
struct psi *psi = is->data;
uint64_t xivr_p, xivr;
uint32_t irq_idx = isn & 7;
if (irq_idx >= P8_IRQ_PSI_IRQ_COUNT)
return OPAL_PARAMETER;
xivr_p = psi_p8_irq_to_xivr[irq_idx];
/* Populate the XIVR */
xivr = (uint64_t)server << 40;
xivr |= (uint64_t)priority << 32;
xivr |= (uint64_t)(isn & 7) << 29;
out_be64(psi->regs + xivr_p, xivr);
return OPAL_SUCCESS;
}
static int64_t psi_p8_get_xive(struct irq_source *is, uint32_t isn __unused,
uint16_t *server, uint8_t *priority)
{
struct psi *psi = is->data;
uint64_t xivr_p, xivr;
uint32_t irq_idx = isn & 7;
if (irq_idx >= P8_IRQ_PSI_IRQ_COUNT)
return OPAL_PARAMETER;
xivr_p = psi_p8_irq_to_xivr[irq_idx];
/* Read & decode the XIVR */
xivr = in_be64(psi->regs + xivr_p);
*server = (xivr >> 40) & 0xffff;
*priority = (xivr >> 32) & 0xff;
return OPAL_SUCCESS;
}
static void psihb_p8_interrupt(struct irq_source *is, uint32_t isn)
{
struct psi *psi = is->data;
uint32_t idx = isn - psi->interrupt;
switch (idx) {
case P8_IRQ_PSI_FSP:
psihb_interrupt(is, isn);
break;
case P8_IRQ_PSI_OCC:
occ_p8_interrupt(psi->chip_id);
break;
case P8_IRQ_PSI_FSI:
printf("PSI: FSI irq received\n");
break;
case P8_IRQ_PSI_LPC:
lpc_interrupt(psi->chip_id);
/*
* i2c interrupts are ORed with the LPC ones on
* Murano DD2.1 and Venice DD2.0
*/
p8_i2c_interrupt(psi->chip_id);
break;
case P8_IRQ_PSI_LOCAL_ERR:
prd_psi_interrupt(psi->chip_id);
break;
case P8_IRQ_PSI_EXTERNAL:
if (platform.external_irq)
platform.external_irq(psi->chip_id);
break;
}
/*
* TODO: Per Vicente Chung, CRESPs don't generate interrupts,
* and are just informational. Need to define the policy
* to handle them.
*/
}
static uint64_t psi_p8_irq_attributes(struct irq_source *is, uint32_t isn)
{
struct psi *psi = is->data;
uint32_t idx = isn - psi->interrupt;
uint64_t attr;
if (psi->no_lpc_irqs && idx == P8_IRQ_PSI_LPC)
return IRQ_ATTR_TARGET_LINUX;
/* Only direct external interrupts to OPAL if we have a handler */
if (idx == P8_IRQ_PSI_EXTERNAL && !platform.external_irq)
return IRQ_ATTR_TARGET_LINUX;
attr = IRQ_ATTR_TARGET_OPAL | IRQ_ATTR_TYPE_LSI;
if (idx == P8_IRQ_PSI_EXTERNAL || idx == P8_IRQ_PSI_LPC ||
idx == P8_IRQ_PSI_FSP)
attr |= IRQ_ATTR_TARGET_FREQUENT;
return attr;
}
static char *psi_p8_irq_name(struct irq_source *is, uint32_t isn)
{
struct psi *psi = is->data;
uint32_t idx = isn - psi->interrupt;
char tmp[30];
static const char *names[P8_IRQ_PSI_IRQ_COUNT] = {
"fsp",
"occ",
"fsi",
"lpchc",
"local_err",
"external",
};
if (idx >= P8_IRQ_PSI_IRQ_COUNT)
return NULL;
snprintf(tmp, sizeof(tmp), "psi#%x:%s",
psi->chip_id, names[idx]);
return strdup(tmp);
}
static const struct irq_source_ops psi_p8_irq_ops = {
.get_xive = psi_p8_get_xive,
.set_xive = psi_p8_set_xive,
.interrupt = psihb_p8_interrupt,
.attributes = psi_p8_irq_attributes,
.name = psi_p8_irq_name,
};
static const char *psi_p9_irq_names[P9_PSI_NUM_IRQS] = {
"fsp",
"occ",
"fsi",
"lpchc",
"local_err",
"global_err",
"external",
"lpc_serirq_mux0", /* Have a callback to get name ? */
"lpc_serirq_mux1", /* Have a callback to get name ? */
"lpc_serirq_mux2", /* Have a callback to get name ? */
"lpc_serirq_mux3", /* Have a callback to get name ? */
"i2c",
"dio",
"psu"
};
static void psi_p9_mask_unhandled_irq(struct irq_source *is, uint32_t isn)
{
struct psi *psi = is->data;
int idx = isn - psi->interrupt;
const char *name;
if (idx < ARRAY_SIZE(psi_p9_irq_names))
name = psi_p9_irq_names[idx];
else
name = "unknown!";
prerror("PSI[0x%03x]: Masking unhandled LSI %d (%s)\n",
psi->chip_id, idx, name);
/*
* All the PSI interrupts are LSIs and will be constantly re-fired
* unless the underlying interrupt condition is cleared. If we don't
* have a handler for the interrupt then it needs to be masked to
* prevent the IRQ from locking up the thread which handles it.
*/
xive_source_mask(is, isn);
}
static void psihb_p9_interrupt(struct irq_source *is, uint32_t isn)
{
struct psi *psi = is->data;
uint32_t idx = isn - psi->interrupt;
switch (idx) {
case P9_PSI_IRQ_PSI:
psihb_interrupt(is, isn);
break;
case P9_PSI_IRQ_OCC:
occ_p9_interrupt(psi->chip_id);
break;
case P9_PSI_IRQ_LPCHC:
lpc_interrupt(psi->chip_id);
break;
case P9_PSI_IRQ_LOCAL_ERR:
prd_psi_interrupt(psi->chip_id);
break;
case P9_PSI_IRQ_EXTERNAL:
if (platform.external_irq)
platform.external_irq(psi->chip_id);
else
psi_p9_mask_unhandled_irq(is, isn);
break;
case P9_PSI_IRQ_LPC_SIRQ0:
case P9_PSI_IRQ_LPC_SIRQ1:
case P9_PSI_IRQ_LPC_SIRQ2:
case P9_PSI_IRQ_LPC_SIRQ3:
lpc_serirq(psi->chip_id, idx - P9_PSI_IRQ_LPC_SIRQ0);
break;
case P9_PSI_IRQ_SBE_I2C:
p8_i2c_interrupt(psi->chip_id);
break;
case P9_PSI_IRQ_DIO:
printf("PSI: DIO irq received\n");
dio_interrupt_handler(psi->chip_id);
break;
case P9_PSI_IRQ_PSU:
p9_sbe_interrupt(psi->chip_id);
break;
default:
psi_p9_mask_unhandled_irq(is, isn);
}
}
static uint64_t psi_p9_irq_attributes(struct irq_source *is __unused,
uint32_t isn)
{
struct psi *psi = is->data;
unsigned int idx = isn & 0xf;
bool is_lpc_serirq;
is_lpc_serirq =
(idx == P9_PSI_IRQ_LPC_SIRQ0 ||
idx == P9_PSI_IRQ_LPC_SIRQ1 ||
idx == P9_PSI_IRQ_LPC_SIRQ2 ||
idx == P9_PSI_IRQ_LPC_SIRQ3);
/* If LPC interrupts are disabled, route them to Linux
* (who will not request them since they aren't referenced
* in the device tree)
*/
if (is_lpc_serirq && psi->no_lpc_irqs)
return IRQ_ATTR_TARGET_LINUX;
/* For serirq, check the LPC layer for policy */
if (is_lpc_serirq)
return lpc_get_irq_policy(psi->chip_id, idx - P9_PSI_IRQ_LPC_SIRQ0);
/* Only direct external interrupts to OPAL if we have a handler */
if (idx == P9_PSI_IRQ_EXTERNAL && !platform.external_irq)
return IRQ_ATTR_TARGET_LINUX | IRQ_ATTR_TYPE_LSI;
return IRQ_ATTR_TARGET_OPAL | IRQ_ATTR_TYPE_LSI;
}
static char *psi_p9_irq_name(struct irq_source *is, uint32_t isn)
{
struct psi *psi = is->data;
uint32_t idx = isn - psi->interrupt;
char tmp[30];
if (idx >= ARRAY_SIZE(psi_p9_irq_names))
return NULL;
snprintf(tmp, sizeof(tmp), "psi#%x:%s",
psi->chip_id, psi_p9_irq_names[idx]);
return strdup(tmp);
}
static const struct irq_source_ops psi_p9_irq_ops = {
.interrupt = psihb_p9_interrupt,
.attributes = psi_p9_irq_attributes,
.name = psi_p9_irq_name,
};
static void psi_init_p8_interrupts(struct psi *psi)
{
uint32_t irq;
uint64_t xivr_p;
/* On P8 we get a block of 8, set up the base/mask
* and mask all the sources for now
*/
out_be64(psi->regs + PSIHB_IRSN,
SETFIELD(PSIHB_IRSN_COMP, 0ul, psi->interrupt) |
SETFIELD(PSIHB_IRSN_MASK, 0ul, 0x7fff8ul) |
PSIHB_IRSN_DOWNSTREAM_EN |
PSIHB_IRSN_UPSTREAM_EN);
for (irq = 0; irq < P8_IRQ_PSI_IRQ_COUNT; irq++) {
xivr_p = psi_p8_irq_to_xivr[irq];
out_be64(psi->regs + xivr_p, (0xffull << 32) | (irq << 29));
}
/*
* Register the IRQ sources FSP, OCC, FSI, LPC
* and Local Error. Host Error is actually the
* external interrupt and the policy for that comes
* from the platform
*/
register_irq_source(&psi_p8_irq_ops, psi,
psi->interrupt, P8_IRQ_PSI_IRQ_COUNT);
}
static void psi_init_p9_interrupts(struct psi *psi)
{
struct proc_chip *chip;
u64 val;
/* Grab chip */
chip = get_chip(psi->chip_id);
if (!chip)
return;
/* Configure the CI BAR */
phys_map_get(chip->id, PSIHB_ESB, 0, &val, NULL);
val |= PSIHB_ESB_CI_VALID;
out_be64(psi->regs + PSIHB_ESB_CI_BASE, val);
val = in_be64(psi->regs + PSIHB_ESB_CI_BASE);
psi->esb_mmio = (void *)(val & ~PSIHB_ESB_CI_VALID);
prlog(PR_DEBUG, "PSI[0x%03x]: ESB MMIO at @%p\n",
psi->chip_id, psi->esb_mmio);
/* Register sources */
prlog(PR_DEBUG,
"PSI[0x%03x]: Interrupts sources registered for P9 DD2.x\n",
psi->chip_id);
xive_register_hw_source(psi->interrupt, P9_PSI_NUM_IRQS,
12, psi->esb_mmio, XIVE_SRC_LSI,
psi, &psi_p9_irq_ops);
/* Setup interrupt offset */
val = xive_get_notify_base(psi->interrupt);
val <<= 32;
out_be64(psi->regs + PSIHB_IVT_OFFSET, val);
/* Grab and configure the notification port */
val = xive_get_notify_port(psi->chip_id, XIVE_HW_SRC_PSI);
val |= PSIHB_ESB_NOTIF_VALID;
out_be64(psi->regs + PSIHB_ESB_NOTIF_ADDR, val);
/* Reset irq handling and switch to ESB mode */
out_be64(psi->regs + PSIHB_INTERRUPT_CONTROL, PSIHB_IRQ_RESET);
out_be64(psi->regs + PSIHB_INTERRUPT_CONTROL, 0);
}
static void psi_init_interrupts(struct psi *psi)
{
/* Configure the interrupt BUID and mask it */
switch (proc_gen) {
case proc_gen_p8:
psi_init_p8_interrupts(psi);
break;
case proc_gen_p9:
psi_init_p9_interrupts(psi);
break;
default:
/* Unknown: just no interrupts */
prerror("PSI: Unknown interrupt type\n");
}
}
static void psi_activate_phb(struct psi *psi)
{
u64 reg;
/*
* Disable interrupt emission in the control register,
* it will be re-enabled later, after the mailbox one
* will have been enabled.
*/
reg = in_be64(psi->regs + PSIHB_CR);
reg &= ~PSIHB_CR_FSP_IRQ_ENABLE;
out_be64(psi->regs + PSIHB_CR, reg);
/* Enable interrupts in the mask register. We enable everything
* except for bit "FSP command error detected" which the doc
* (P7 BookIV) says should be masked for normal ops. It also
* seems to be masked under OPAL.
*/
reg = 0x0000010000100000ull;
out_be64(psi->regs + PSIHB_SEMR, reg);
#if 0
/* Dump the GXHB registers */
printf(" PSIHB_BBAR : %llx\n",
in_be64(psi->regs + PSIHB_BBAR));
printf(" PSIHB_FSPBAR : %llx\n",
in_be64(psi->regs + PSIHB_FSPBAR));
printf(" PSIHB_FSPMMR : %llx\n",
in_be64(psi->regs + PSIHB_FSPMMR));
printf(" PSIHB_TAR : %llx\n",
in_be64(psi->regs + PSIHB_TAR));
printf(" PSIHB_CR : %llx\n",
in_be64(psi->regs + PSIHB_CR));
printf(" PSIHB_SEMR : %llx\n",
in_be64(psi->regs + PSIHB_SEMR));
printf(" PSIHB_XIVR : %llx\n",
in_be64(psi->regs + PSIHB_XIVR));
#endif
}
static void psi_create_p9_int_map(struct psi *psi, struct dt_node *np)
{
__be32 map[P9_PSI_NUM_IRQS][4];
int i;
for (i = 0; i < P9_PSI_NUM_IRQS; i++) {
map[i][0] = cpu_to_be32(i);
map[i][1] = cpu_to_be32(get_ics_phandle());
map[i][2] = cpu_to_be32(psi->interrupt + i);
map[i][3] = cpu_to_be32(1);
}
dt_add_property(np, "interrupt-map", map, sizeof(map));
dt_add_property_cells(np, "#address-cells", 0);
dt_add_property_cells(np, "#interrupt-cells", 1);
}
static void psi_create_mm_dtnode(struct psi *psi)
{
struct dt_node *np;
uint64_t addr = (uint64_t)psi->regs;
np = dt_new_addr(dt_root, "psi", addr);
if (!np)
return;
/* Hard wire size to 4G */
dt_add_property_u64s(np, "reg", addr, 0x100000000ull);
switch (proc_gen) {
case proc_gen_p8:
dt_add_property_strings(np, "compatible", "ibm,psi",
"ibm,power8-psi");
break;
case proc_gen_p9:
dt_add_property_strings(np, "compatible", "ibm,psi",
"ibm,power9-psi");
psi_create_p9_int_map(psi, np);
break;
default:
assert(0);
break;
}
dt_add_property_cells(np, "interrupt-parent", get_ics_phandle());
dt_add_property_cells(np, "interrupts", psi->interrupt, 1);
dt_add_property_cells(np, "ibm,chip-id", psi->chip_id);
psi->node = np;
}
static struct psi *alloc_psi(struct proc_chip *chip, uint64_t base)
{
struct psi *psi;
psi = zalloc(sizeof(struct psi));
if (!psi) {
prerror("PSI: Could not allocate memory\n");
return NULL;
}
psi->xscom_base = base;
psi->chip_id = chip->id;
return psi;
}
static struct psi *psi_probe_p8(struct proc_chip *chip, u64 base)
{
struct psi *psi = NULL;
uint64_t rc, val;
rc = xscom_read(chip->id, base + PSIHB_XSCOM_P8_BASE, &val);
if (rc) {
prerror("PSI[0x%03x]: Error %llx reading PSIHB BAR\n",
chip->id, rc);
return NULL;
}
if (val & PSIHB_XSCOM_P8_HBBAR_EN) {
psi = alloc_psi(chip, base);
if (!psi)
return NULL;
psi->regs = (void *)(val & ~PSIHB_XSCOM_P8_HBBAR_EN);
psi->interrupt = get_psi_interrupt(chip->id);
} else
printf("PSI[0x%03x]: Working chip not found\n", chip->id);
return psi;
}
static struct psi *psi_probe_p9(struct proc_chip *chip, u64 base)
{
struct psi *psi = NULL;
uint64_t addr;
phys_map_get(chip->id, PSIHB_REG, 0, &addr, NULL);
xscom_write(chip->id, base + PSIHB_XSCOM_P9_BASE,
addr | PSIHB_XSCOM_P9_HBBAR_EN);
psi = alloc_psi(chip, base);
if (!psi)
return NULL;
psi->regs = (void *)addr;
psi->interrupt = xive_alloc_hw_irqs(chip->id, P9_PSI_NUM_IRQS, 16);
return psi;
}
static bool psi_init_psihb(struct dt_node *psihb)
{
uint32_t chip_id = dt_get_chip_id(psihb);
struct proc_chip *chip = get_chip(chip_id);
struct psi *psi = NULL;
u64 base, val;
if (!chip) {
prerror("PSI: Can't find chip!\n");
return false;
}
base = dt_get_address(psihb, 0, NULL);
if (dt_node_is_compatible(psihb, "ibm,power8-psihb-x"))
psi = psi_probe_p8(chip, base);
else if (dt_node_is_compatible(psihb, "ibm,power9-psihb-x"))
psi = psi_probe_p9(chip, base);
else {
prerror("PSI: Unknown processor type\n");
return false;
}
if (!psi)
return false;
list_add(&psis, &psi->list);
val = in_be64(psi->regs + PSIHB_CR);
if (val & PSIHB_CR_FSP_LINK_ACTIVE) {
lock(&psi_lock);
psi->active = true;
unlock(&psi_lock);
}
chip->psi = psi;
if (dt_has_node_property(psihb, "no-lpc-interrupts", NULL))
psi->no_lpc_irqs = true;
psi_activate_phb(psi);
psi_init_interrupts(psi);
psi_create_mm_dtnode(psi);
prlog(PR_INFO, "PSI[0x%03x]: Found PSI bridge [active=%d]\n",
psi->chip_id, psi->active);
return true;
}
void psi_fsp_link_in_use(struct psi *psi __unused)
{
static bool poller_created = false;
/* Do this once only */
if (!poller_created) {
poller_created = true;
opal_add_poller(psi_link_poll, NULL);
}
}
struct psi *psi_find_functional_chip(void)
{
return list_top(&psis, struct psi, list);
}
void psi_init(void)
{
struct dt_node *np;
dt_for_each_compatible(dt_root, np, "ibm,psihb-x")
psi_init_psihb(np);
}