blob: 5c5b7c69bdc512284c9a1f0f4ec0c5d5a4a2b6fc [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.
*/
#define pr_fmt(fmt) "LPC: " fmt
#include <skiboot.h>
#include <xscom.h>
#include <io.h>
#include <lock.h>
#include <chip.h>
#include <lpc.h>
#include <timebase.h>
#include <errorlog.h>
#include <opal-api.h>
#include <platform.h>
#include <psi.h>
//#define DBG_IRQ(fmt...) prerror(fmt)
#define DBG_IRQ(fmt...) do { } while(0)
DEFINE_LOG_ENTRY(OPAL_RC_LPC_READ, OPAL_PLATFORM_ERR_EVT, OPAL_LPC,
OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL,
OPAL_NA);
DEFINE_LOG_ENTRY(OPAL_RC_LPC_WRITE, OPAL_PLATFORM_ERR_EVT, OPAL_LPC,
OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL,
OPAL_NA);
DEFINE_LOG_ENTRY(OPAL_RC_LPC_SYNC, OPAL_PLATFORM_ERR_EVT, OPAL_LPC,
OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL,
OPAL_NA);
/* Used exclusively in manufacturing mode */
DEFINE_LOG_ENTRY(OPAL_RC_LPC_SYNC_PERF, OPAL_PLATFORM_ERR_EVT, OPAL_LPC,
OPAL_MISC_SUBSYSTEM, OPAL_UNRECOVERABLE_ERR_DEGRADE_PERF,
OPAL_NA);
#define ECCB_CTL 0 /* b0020 -> b00200 */
#define ECCB_STAT 2 /* b0022 -> b00210 */
#define ECCB_DATA 3 /* b0023 -> b00218 */
#define ECCB_CTL_MAGIC 0xd000000000000000ul
#define ECCB_CTL_DATASZ PPC_BITMASK(4,7)
#define ECCB_CTL_READ PPC_BIT(15)
#define ECCB_CTL_ADDRLEN PPC_BITMASK(23,25)
#define ECCB_ADDRLEN_4B 0x4
#define ECCB_CTL_ADDR PPC_BITMASK(32,63)
#define ECCB_STAT_PIB_ERR PPC_BITMASK(0,5)
#define ECCB_STAT_RD_DATA PPC_BITMASK(6,37)
#define ECCB_STAT_BUSY PPC_BIT(44)
#define ECCB_STAT_ERRORS1 PPC_BITMASK(45,51)
#define ECCB_STAT_OP_DONE PPC_BIT(52)
#define ECCB_STAT_ERRORS2 PPC_BITMASK(53,55)
#define ECCB_STAT_ERR_MASK (ECCB_STAT_PIB_ERR | \
ECCB_STAT_ERRORS1 | \
ECCB_STAT_ERRORS2)
#define ECCB_TIMEOUT 1000000
/* OPB Master LS registers */
#define OPB_MASTER_LS_IRQ_STAT 0x50
#define OPB_MASTER_LS_IRQ_MASK 0x54
#define OPB_MASTER_LS_IRQ_POL 0x58
#define OPB_MASTER_IRQ_LPC 0x00000800
/* LPC HC registers */
#define LPC_HC_FW_SEG_IDSEL 0x24
#define LPC_HC_FW_RD_ACC_SIZE 0x28
#define LPC_HC_FW_RD_1B 0x00000000
#define LPC_HC_FW_RD_2B 0x01000000
#define LPC_HC_FW_RD_4B 0x02000000
#define LPC_HC_FW_RD_16B 0x04000000
#define LPC_HC_FW_RD_128B 0x07000000
#define LPC_HC_IRQSER_CTRL 0x30
#define LPC_HC_IRQSER_EN 0x80000000
#define LPC_HC_IRQSER_QMODE 0x40000000
#define LPC_HC_IRQSER_START_MASK 0x03000000
#define LPC_HC_IRQSER_START_4CLK 0x00000000
#define LPC_HC_IRQSER_START_6CLK 0x01000000
#define LPC_HC_IRQSER_START_8CLK 0x02000000
#define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */
#define LPC_HC_IRQSTAT 0x38
#define LPC_HC_IRQ_SERIRQ0 0x80000000u /* all bits down to ... */
#define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */
#define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000
#define LPC_HC_IRQ_LRESET 0x00000400
#define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080
#define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040
#define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020
#define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010
#define LPC_HC_IRQ_TARG_TAR_ERR 0x00000008
#define LPC_HC_IRQ_BM_TAR_ERR 0x00000004
#define LPC_HC_IRQ_BM0_REQ 0x00000002
#define LPC_HC_IRQ_BM1_REQ 0x00000001
#define LPC_HC_IRQ_BASE_IRQS ( \
LPC_HC_IRQ_LRESET | \
LPC_HC_IRQ_SYNC_ABNORM_ERR | \
LPC_HC_IRQ_SYNC_NORESP_ERR | \
LPC_HC_IRQ_SYNC_NORM_ERR | \
LPC_HC_IRQ_SYNC_TIMEOUT_ERR | \
LPC_HC_IRQ_TARG_TAR_ERR | \
LPC_HC_IRQ_BM_TAR_ERR)
#define LPC_HC_ERROR_ADDRESS 0x40
#define LPC_NUM_SERIRQ 17
struct lpcm {
uint32_t chip_id;
uint32_t xbase;
void *mbase;
struct lock lock;
uint8_t fw_idsel;
uint8_t fw_rdsz;
struct list_head clients;
bool has_serirq;
uint8_t sirq_routes[LPC_NUM_SERIRQ];
uint32_t sirq_rmasks[4];
};
#define LPC_BUS_DEGRADED_PERF_THRESHOLD 5
struct lpc_client_entry {
struct list_node node;
const struct lpc_client *clt;
};
/* Default LPC bus */
static int32_t lpc_default_chip_id = -1;
static bool lpc_irqs_ready;
/*
* These are expected to be the same on all chips and should probably
* be read (or configured) dynamically. This is how things are configured
* today on Tuletta.
*/
static uint32_t lpc_io_opb_base = 0xd0010000;
static uint32_t lpc_mem_opb_base = 0xe0000000;
static uint32_t lpc_fw_opb_base = 0xf0000000;
static uint32_t lpc_reg_opb_base = 0xc0012000;
static uint32_t opb_master_reg_base = 0xc0010000;
static int64_t opb_mmio_write(struct lpcm *lpc, uint32_t addr, uint32_t data,
uint32_t sz)
{
switch (sz) {
case 1:
out_8(lpc->mbase + addr, data);
return OPAL_SUCCESS;
case 2:
out_be16(lpc->mbase + addr, data);
return OPAL_SUCCESS;
case 4:
out_be32(lpc->mbase + addr, data);
return OPAL_SUCCESS;
}
prerror("LPC: Invalid data size %d\n", sz);
return OPAL_PARAMETER;
}
static int64_t opb_write(struct lpcm *lpc, uint32_t addr, uint32_t data,
uint32_t sz)
{
uint64_t ctl = ECCB_CTL_MAGIC, stat;
int64_t rc, tout;
uint64_t data_reg;
if (lpc->mbase)
return opb_mmio_write(lpc, addr, data, sz);
switch(sz) {
case 1:
data_reg = ((uint64_t)data) << 56;
break;
case 2:
data_reg = ((uint64_t)data) << 48;
break;
case 4:
data_reg = ((uint64_t)data) << 32;
break;
default:
prerror("Invalid data size %d\n", sz);
return OPAL_PARAMETER;
}
rc = xscom_write(lpc->chip_id, lpc->xbase + ECCB_DATA, data_reg);
if (rc) {
log_simple_error(&e_info(OPAL_RC_LPC_WRITE),
"LPC: XSCOM write to ECCB DATA error %lld\n", rc);
return rc;
}
ctl = SETFIELD(ECCB_CTL_DATASZ, ctl, sz);
ctl = SETFIELD(ECCB_CTL_ADDRLEN, ctl, ECCB_ADDRLEN_4B);
ctl = SETFIELD(ECCB_CTL_ADDR, ctl, addr);
rc = xscom_write(lpc->chip_id, lpc->xbase + ECCB_CTL, ctl);
if (rc) {
log_simple_error(&e_info(OPAL_RC_LPC_WRITE),
"LPC: XSCOM write to ECCB CTL error %lld\n", rc);
return rc;
}
for (tout = 0; tout < ECCB_TIMEOUT; tout++) {
rc = xscom_read(lpc->chip_id, lpc->xbase + ECCB_STAT,
&stat);
if (rc) {
log_simple_error(&e_info(OPAL_RC_LPC_WRITE),
"LPC: XSCOM read from ECCB STAT err %lld\n",
rc);
return rc;
}
if (stat & ECCB_STAT_OP_DONE) {
if (stat & ECCB_STAT_ERR_MASK) {
log_simple_error(&e_info(OPAL_RC_LPC_WRITE),
"LPC: Error status: 0x%llx\n", stat);
return OPAL_HARDWARE;
}
return OPAL_SUCCESS;
}
time_wait_nopoll(100);
}
log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: Write timeout !\n");
return OPAL_HARDWARE;
}
static int64_t opb_mmio_read(struct lpcm *lpc, uint32_t addr, uint32_t *data,
uint32_t sz)
{
switch (sz) {
case 1:
*data = in_8(lpc->mbase + addr);
return OPAL_SUCCESS;
case 2:
*data = in_be16(lpc->mbase + addr);
return OPAL_SUCCESS;
case 4:
*data = in_be32(lpc->mbase + addr);
return OPAL_SUCCESS;
}
prerror("LPC: Invalid data size %d\n", sz);
return OPAL_PARAMETER;
}
static int64_t opb_read(struct lpcm *lpc, uint32_t addr, uint32_t *data,
uint32_t sz)
{
uint64_t ctl = ECCB_CTL_MAGIC | ECCB_CTL_READ, stat;
int64_t rc, tout;
if (lpc->mbase)
return opb_mmio_read(lpc, addr, data, sz);
if (sz != 1 && sz != 2 && sz != 4) {
prerror("Invalid data size %d\n", sz);
return OPAL_PARAMETER;
}
ctl = SETFIELD(ECCB_CTL_DATASZ, ctl, sz);
ctl = SETFIELD(ECCB_CTL_ADDRLEN, ctl, ECCB_ADDRLEN_4B);
ctl = SETFIELD(ECCB_CTL_ADDR, ctl, addr);
rc = xscom_write(lpc->chip_id, lpc->xbase + ECCB_CTL, ctl);
if (rc) {
log_simple_error(&e_info(OPAL_RC_LPC_READ),
"LPC: XSCOM write to ECCB CTL error %lld\n", rc);
return rc;
}
for (tout = 0; tout < ECCB_TIMEOUT; tout++) {
rc = xscom_read(lpc->chip_id, lpc->xbase + ECCB_STAT,
&stat);
if (rc) {
log_simple_error(&e_info(OPAL_RC_LPC_READ),
"LPC: XSCOM read from ECCB STAT err %lld\n",
rc);
return rc;
}
if (stat & ECCB_STAT_OP_DONE) {
uint32_t rdata = GETFIELD(ECCB_STAT_RD_DATA, stat);
if (stat & ECCB_STAT_ERR_MASK) {
log_simple_error(&e_info(OPAL_RC_LPC_READ),
"LPC: Error status: 0x%llx\n", stat);
return OPAL_HARDWARE;
}
switch(sz) {
case 1:
*data = rdata >> 24;
break;
case 2:
*data = rdata >> 16;
break;
default:
*data = rdata;
break;
}
return 0;
}
time_wait_nopoll(100);
}
log_simple_error(&e_info(OPAL_RC_LPC_READ), "LPC: Read timeout !\n");
return OPAL_HARDWARE;
}
static int64_t lpc_set_fw_idsel(struct lpcm *lpc, uint8_t idsel)
{
uint32_t val;
int64_t rc;
if (idsel == lpc->fw_idsel)
return OPAL_SUCCESS;
if (idsel > 0xf)
return OPAL_PARAMETER;
rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_FW_SEG_IDSEL,
&val, 4);
if (rc) {
prerror("Failed to read HC_FW_SEG_IDSEL register !\n");
return rc;
}
val = (val & 0xfffffff0) | idsel;
rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_FW_SEG_IDSEL,
val, 4);
if (rc) {
prerror("Failed to write HC_FW_SEG_IDSEL register !\n");
return rc;
}
lpc->fw_idsel = idsel;
return OPAL_SUCCESS;
}
static int64_t lpc_set_fw_rdsz(struct lpcm *lpc, uint8_t rdsz)
{
uint32_t val;
int64_t rc;
if (rdsz == lpc->fw_rdsz)
return OPAL_SUCCESS;
switch(rdsz) {
case 1:
val = LPC_HC_FW_RD_1B;
break;
case 2:
val = LPC_HC_FW_RD_2B;
break;
case 4:
val = LPC_HC_FW_RD_4B;
break;
default:
/*
* The HW supports 16 and 128 via a buffer/cache
* but I have never exprimented with it and am not
* sure it works the way we expect so let's leave it
* at that for now
*/
return OPAL_PARAMETER;
}
rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_FW_RD_ACC_SIZE,
val, 4);
if (rc) {
prerror("Failed to write LPC_HC_FW_RD_ACC_SIZE !\n");
return rc;
}
lpc->fw_rdsz = rdsz;
return OPAL_SUCCESS;
}
static int64_t lpc_opb_prepare(struct lpcm *lpc,
enum OpalLPCAddressType addr_type,
uint32_t addr, uint32_t sz,
uint32_t *opb_base, bool is_write)
{
uint32_t top = addr + sz;
uint8_t fw_idsel;
int64_t rc;
/* Address wraparound */
if (top < addr)
return OPAL_PARAMETER;
/*
* Bound check access and get the OPB base address for
* the window corresponding to the access type
*/
switch(addr_type) {
case OPAL_LPC_IO:
/* IO space is 64K */
if (top > 0x10000)
return OPAL_PARAMETER;
/* And only supports byte accesses */
if (sz != 1)
return OPAL_PARAMETER;
*opb_base = lpc_io_opb_base;
break;
case OPAL_LPC_MEM:
/* MEM space is 256M */
if (top > 0x10000000)
return OPAL_PARAMETER;
/* And only supports byte accesses */
if (sz != 1)
return OPAL_PARAMETER;
*opb_base = lpc_mem_opb_base;
break;
case OPAL_LPC_FW:
/*
* FW space is in segments of 256M controlled
* by IDSEL, make sure we don't cross segments
*/
*opb_base = lpc_fw_opb_base;
fw_idsel = (addr >> 28);
if (((top - 1) >> 28) != fw_idsel)
return OPAL_PARAMETER;
/* Set segment */
rc = lpc_set_fw_idsel(lpc, fw_idsel);
if (rc)
return rc;
/* Set read access size */
if (!is_write) {
rc = lpc_set_fw_rdsz(lpc, sz);
if (rc)
return rc;
}
break;
default:
return OPAL_PARAMETER;
}
return OPAL_SUCCESS;
}
static int64_t __lpc_write(struct lpcm *lpc, enum OpalLPCAddressType addr_type,
uint32_t addr, uint32_t data, uint32_t sz)
{
uint32_t opb_base;
int64_t rc;
lock(&lpc->lock);
/*
* Convert to an OPB access and handle LPC HC configuration
* for FW accesses (IDSEL)
*/
rc = lpc_opb_prepare(lpc, addr_type, addr, sz, &opb_base, true);
if (rc)
goto bail;
/* Perform OPB access */
rc = opb_write(lpc, opb_base + addr, data, sz);
/* XXX Add LPC error handling/recovery */
bail:
unlock(&lpc->lock);
return rc;
}
int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t data, uint32_t sz)
{
struct proc_chip *chip;
if (lpc_default_chip_id < 0)
return OPAL_PARAMETER;
chip = get_chip(lpc_default_chip_id);
if (!chip || !chip->lpc)
return OPAL_PARAMETER;
return __lpc_write(chip->lpc, addr_type, addr, data, sz);
}
/*
* The "OPAL" variant add the emulation of 2 and 4 byte accesses using
* byte accesses for IO and MEM space in order to be compatible with
* existing Linux expectations
*/
static int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type,
uint32_t addr, uint32_t data, uint32_t sz)
{
struct proc_chip *chip;
int64_t rc;
chip = get_chip(chip_id);
if (!chip || !chip->lpc)
return OPAL_PARAMETER;
if (addr_type == OPAL_LPC_FW || sz == 1)
return __lpc_write(chip->lpc, addr_type, addr, data, sz);
while(sz--) {
rc = __lpc_write(chip->lpc, addr_type, addr, data & 0xff, 1);
if (rc)
return rc;
addr++;
data >>= 8;
}
return OPAL_SUCCESS;
}
static int64_t __lpc_read(struct lpcm *lpc, enum OpalLPCAddressType addr_type,
uint32_t addr, uint32_t *data, uint32_t sz)
{
uint32_t opb_base;
int64_t rc;
lock(&lpc->lock);
/*
* Convert to an OPB access and handle LPC HC configuration
* for FW accesses (IDSEL and read size)
*/
rc = lpc_opb_prepare(lpc, addr_type, addr, sz, &opb_base, false);
if (rc)
goto bail;
/* Perform OPB access */
rc = opb_read(lpc, opb_base + addr, data, sz);
/* XXX Add LPC error handling/recovery */
bail:
unlock(&lpc->lock);
return rc;
}
int64_t lpc_read(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t *data, uint32_t sz)
{
struct proc_chip *chip;
if (lpc_default_chip_id < 0)
return OPAL_PARAMETER;
chip = get_chip(lpc_default_chip_id);
if (!chip || !chip->lpc)
return OPAL_PARAMETER;
return __lpc_read(chip->lpc, addr_type, addr, data, sz);
}
/*
* The "OPAL" variant add the emulation of 2 and 4 byte accesses using
* byte accesses for IO and MEM space in order to be compatible with
* existing Linux expectations
*/
static int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type,
uint32_t addr, uint32_t *data, uint32_t sz)
{
struct proc_chip *chip;
int64_t rc;
chip = get_chip(chip_id);
if (!chip || !chip->lpc)
return OPAL_PARAMETER;
if (addr_type == OPAL_LPC_FW || sz == 1)
return __lpc_read(chip->lpc, addr_type, addr, data, sz);
*data = 0;
while(sz--) {
uint32_t byte;
rc = __lpc_read(chip->lpc, addr_type, addr, &byte, 1);
if (rc)
return rc;
*data = *data | (byte << (8 * sz));
addr++;
}
return OPAL_SUCCESS;
}
bool lpc_present(void)
{
return lpc_default_chip_id >= 0;
}
/* Called with LPC lock held */
static void lpc_setup_serirq(struct lpcm *lpc)
{
struct lpc_client_entry *ent;
uint32_t mask = LPC_HC_IRQ_BASE_IRQS;
int rc;
if (!lpc_irqs_ready)
return;
/* Collect serirq enable bits */
list_for_each(&lpc->clients, ent, node)
mask |= ent->clt->interrupts & LPC_HC_IRQ_SERIRQ_ALL;
rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, mask, 4);
if (rc) {
prerror("Failed to update irq mask\n");
return;
}
DBG_IRQ("LPC: IRQ mask set to 0x%08x\n", mask);
/* Enable the LPC interrupt in the OPB Master */
opb_write(lpc, opb_master_reg_base + OPB_MASTER_LS_IRQ_POL, 0, 4);
rc = opb_write(lpc, opb_master_reg_base + OPB_MASTER_LS_IRQ_MASK,
OPB_MASTER_IRQ_LPC, 4);
if (rc)
prerror("Failed to enable IRQs in OPB\n");
/* Check whether we should enable serirq */
if (mask & LPC_HC_IRQ_SERIRQ_ALL) {
rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL,
LPC_HC_IRQSER_EN | LPC_HC_IRQSER_START_4CLK, 4);
DBG_IRQ("LPC: SerIRQ enabled\n");
} else {
rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL,
0, 4);
DBG_IRQ("LPC: SerIRQ disabled\n");
}
if (rc)
prerror("Failed to configure SerIRQ\n");
{
u32 val;
rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, &val, 4);
if (rc)
prerror("Failed to readback mask");
else
DBG_IRQ("LPC: MASK READBACK=%x\n", val);
rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL,
&val, 4);
if (rc)
prerror("Failed to readback ctrl");
else
DBG_IRQ("LPC: CTRL READBACK=%x\n", val);
}
}
static void __lpc_route_serirq(struct lpcm *lpc, uint32_t sirq,
uint32_t psi_idx)
{
uint32_t reg, shift, val;
int64_t rc;
lpc->sirq_routes[sirq] = psi_idx;
/* We may not be ready yet ... */
if (!lpc->has_serirq)
return;
if (sirq < 14) {
reg = 0xc;
shift = 4 + (sirq << 1);
} else {
reg = 0x8;
shift = 8 + ((sirq - 14) << 1);
}
shift = 30-shift;
rc = opb_read(lpc, opb_master_reg_base + reg, &val, 4);
if (rc)
return;
val = val & ~(3 << shift);
val |= (psi_idx & 3) << shift;
opb_write(lpc, opb_master_reg_base + reg, val, 4);
}
void lpc_route_serirq(uint32_t chip_id, uint32_t sirq, uint32_t psi_idx)
{
struct proc_chip *chip;
struct lpcm *lpc;
uint32_t psi_old;
if (sirq >= LPC_NUM_SERIRQ) {
prerror("LPC[%03x]: Routing request for invalid SerIRQ %d\n",
chip_id, sirq);
return;
}
chip = get_chip(chip_id);
if (!chip || !chip->lpc)
return;
lpc = chip->lpc;
lock(&lpc->lock);
psi_old = lpc->sirq_routes[sirq];
lpc->sirq_rmasks[psi_old] &= ~(LPC_HC_IRQ_SERIRQ0 >> sirq);
lpc->sirq_rmasks[psi_idx] |= (LPC_HC_IRQ_SERIRQ0 >> sirq);
__lpc_route_serirq(lpc, sirq, psi_idx);
unlock(&lpc->lock);
}
static void lpc_init_interrupts_one(struct proc_chip *chip)
{
struct lpcm *lpc = chip->lpc;
int i, rc;
lock(&lpc->lock);
/* First mask them all */
rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4);
if (rc) {
prerror("LPC: Failed to init interrutps\n");
goto bail;
}
switch(chip->type) {
case PROC_CHIP_P8_MURANO:
case PROC_CHIP_P8_VENICE:
/* On Murano/Venice, there is no SerIRQ, only enable error
* interrupts
*/
rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK,
LPC_HC_IRQ_BASE_IRQS, 4);
if (rc) {
prerror("LPC: Failed to set interrupt mask\n");
goto bail;
}
opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, 0, 4);
break;
case PROC_CHIP_P8_NAPLES:
/* On Naples, we support LPC interrupts, enable them based
* on what clients requests. This will setup the mask and
* enable processing
*/
lpc->has_serirq = true;
lpc_setup_serirq(lpc);
break;
case PROC_CHIP_P9_NIMBUS:
case PROC_CHIP_P9_CUMULUS:
/* On P9, we additionall setup the routing */
lpc->has_serirq = true;
for (i = 0; i < LPC_NUM_SERIRQ; i++) {
uint32_t pin = lpc->sirq_routes[i];
__lpc_route_serirq(lpc, i, pin);
lpc->sirq_rmasks[pin] |= LPC_HC_IRQ_SERIRQ0 >> i;
}
lpc_setup_serirq(lpc);
break;
default:
;
}
bail:
unlock(&lpc->lock);
}
void lpc_init_interrupts(void)
{
struct proc_chip *chip;
lpc_irqs_ready = true;
for_each_chip(chip) {
if (chip->lpc)
lpc_init_interrupts_one(chip);
}
}
static void lpc_dispatch_reset(struct lpcm *lpc)
{
struct lpc_client_entry *ent;
/* XXX We are going to hit this repeatedly while reset is
* asserted which might be sub-optimal. We should instead
* detect assertion and start a poller that will wait for
* de-assertion. We could notify clients of LPC being
* on/off rather than just reset
*/
prerror("LPC: Got LPC reset on chip 0x%x !\n", lpc->chip_id);
/* Collect serirq enable bits */
list_for_each(&lpc->clients, ent, node) {
if (!ent->clt->reset)
continue;
unlock(&lpc->lock);
ent->clt->reset(lpc->chip_id);
lock(&lpc->lock);
}
/* Reconfigure serial interrupts */
if (lpc->has_serirq)
lpc_setup_serirq(lpc);
}
static void lpc_dispatch_err_irqs(struct lpcm *lpc, uint32_t irqs)
{
const char *sync_err = "Unknown LPC error";
static int lpc_bus_err_count;
struct opal_err_info *info;
uint32_t err_addr;
int rc;
/* Write back to clear error interrupts, we clear SerIRQ later
* as they are handled as level interrupts
*/
rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSTAT,
LPC_HC_IRQ_BASE_IRQS, 4);
if (rc)
prerror("Failed to clear IRQ error latches !\n");
if (irqs & LPC_HC_IRQ_LRESET)
lpc_dispatch_reset(lpc);
if (irqs & LPC_HC_IRQ_SYNC_ABNORM_ERR)
sync_err = "Got SYNC abnormal error.";
if (irqs & LPC_HC_IRQ_SYNC_NORESP_ERR)
sync_err = "Got SYNC no-response error.";
if (irqs & LPC_HC_IRQ_SYNC_NORM_ERR)
sync_err = "Got SYNC normal error.";
if (irqs & LPC_HC_IRQ_SYNC_TIMEOUT_ERR)
sync_err = "Got SYNC timeout error.";
if (irqs & LPC_HC_IRQ_TARG_TAR_ERR)
sync_err = "Got abnormal TAR error.";
if (irqs & LPC_HC_IRQ_BM_TAR_ERR)
sync_err = "Got bus master TAR error.";
rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_ERROR_ADDRESS,
&err_addr, 4);
lpc_bus_err_count++;
if (manufacturing_mode && (lpc_bus_err_count > LPC_BUS_DEGRADED_PERF_THRESHOLD))
info = &e_info(OPAL_RC_LPC_SYNC_PERF);
else
info = &e_info(OPAL_RC_LPC_SYNC);
if (rc)
log_simple_error(info, "LPC[%03x]: %s "
"Error reading error address register\n",
lpc->chip_id, sync_err);
else
log_simple_error(info, "LPC[%03x]: %s Error address reg: "
"0x%08x\n",
lpc->chip_id, sync_err, err_addr);
}
static void lpc_dispatch_ser_irqs(struct lpcm *lpc, uint32_t irqs,
bool clear_latch)
{
struct lpc_client_entry *ent;
uint32_t cirqs;
int rc;
irqs &= LPC_HC_IRQ_SERIRQ_ALL;
/* Collect serirq enable bits */
list_for_each(&lpc->clients, ent, node) {
if (!ent->clt->interrupt)
continue;
cirqs = ent->clt->interrupts & irqs;
if (cirqs) {
unlock(&lpc->lock);
ent->clt->interrupt(lpc->chip_id, cirqs);
lock(&lpc->lock);
}
}
/* Our SerIRQ are level sensitive, we clear the latch after
* we call the handler.
*/
if (!clear_latch)
return;
rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSTAT, irqs, 4);
if (rc)
prerror("Failed to clear SerIRQ latches !\n");
}
void lpc_interrupt(uint32_t chip_id)
{
struct proc_chip *chip = get_chip(chip_id);
struct lpcm *lpc;
uint32_t irqs, opb_irqs;
int rc;
/* No initialized LPC controller on that chip */
if (!chip || !chip->lpc)
return;
lpc = chip->lpc;
lock(&lpc->lock);
/* Grab OPB Master LS interrupt status */
rc = opb_read(lpc, opb_master_reg_base + OPB_MASTER_LS_IRQ_STAT,
&opb_irqs, 4);
if (rc) {
prerror("Failed to read OPB IRQ state\n");
unlock(&lpc->lock);
return;
}
DBG_IRQ("LPC: OPB IRQ on chip 0x%x, oirqs=0x%08x\n", chip_id, opb_irqs);
/* Check if it's an LPC interrupt */
if (!(opb_irqs & OPB_MASTER_IRQ_LPC)) {
/* Something we don't support ? Ack it anyway... */
goto bail;
}
/* Handle the lpc interrupt source (errors etc...) */
rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_IRQSTAT, &irqs, 4);
if (rc) {
prerror("Failed to read LPC IRQ state\n");
goto bail;
}
DBG_IRQ("LPC: LPC IRQ on chip 0x%x, irqs=0x%08x\n", chip_id, irqs);
/* Handle error interrupts */
if (irqs & LPC_HC_IRQ_BASE_IRQS)
lpc_dispatch_err_irqs(lpc, irqs);
/* Handle SerIRQ interrupts */
if (irqs & LPC_HC_IRQ_SERIRQ_ALL)
lpc_dispatch_ser_irqs(lpc, irqs, true);
bail:
/* Ack it at the OPB level */
opb_write(lpc, opb_master_reg_base + OPB_MASTER_LS_IRQ_STAT,
opb_irqs, 4);
unlock(&lpc->lock);
}
void lpc_serirq(uint32_t chip_id, uint32_t index)
{
struct proc_chip *chip = get_chip(chip_id);
struct lpcm *lpc;
uint32_t irqs, rmask;
int rc;
/* No initialized LPC controller on that chip */
if (!chip || !chip->lpc)
return;
lpc = chip->lpc;
lock(&lpc->lock);
/* Handle the lpc interrupt source (errors etc...) */
rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_IRQSTAT, &irqs, 4);
if (rc) {
prerror("LPC: Failed to read LPC IRQ state\n");
goto bail;
}
rmask = lpc->sirq_rmasks[index];
DBG_IRQ("LPC: IRQ on chip 0x%x, irqs=0x%08x rmask=0x%08x\n",
chip_id, irqs, rmask);
irqs &= rmask;
/* Handle SerIRQ interrupts */
if (irqs)
lpc_dispatch_ser_irqs(lpc, irqs, true);
bail:
unlock(&lpc->lock);
}
void lpc_all_interrupts(uint32_t chip_id)
{
struct proc_chip *chip = get_chip(chip_id);
struct lpcm *lpc;
/* No initialized LPC controller on that chip */
if (!chip || !chip->lpc)
return;
lpc = chip->lpc;
/* Dispatch all */
lock(&lpc->lock);
lpc_dispatch_ser_irqs(lpc, LPC_HC_IRQ_SERIRQ_ALL, false);
unlock(&lpc->lock);
}
static void lpc_init_chip_p8(struct dt_node *xn)
{
uint32_t gcid = dt_get_chip_id(xn);
struct proc_chip *chip;
struct lpcm *lpc;
chip = get_chip(gcid);
assert(chip);
lpc = zalloc(sizeof(struct lpcm));
assert(lpc);
lpc->chip_id = gcid;
lpc->xbase = dt_get_address(xn, 0, NULL);
lpc->fw_idsel = 0xff;
lpc->fw_rdsz = 0xff;
list_head_init(&lpc->clients);
init_lock(&lpc->lock);
if (lpc_default_chip_id < 0 ||
dt_has_node_property(xn, "primary", NULL)) {
lpc_default_chip_id = gcid;
}
/* Mask all interrupts for now */
opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4);
printf("LPC[%03x]: Initialized, access via XSCOM @0x%x\n",
gcid, lpc->xbase);
dt_add_property(xn, "interrupt-controller", NULL, 0);
dt_add_property_cells(xn, "#interrupt-cells", 1);
assert(dt_prop_get_u32(xn, "#address-cells") == 2);
chip->lpc = lpc;
}
static void lpc_parse_interrupt_map(struct lpcm *lpc, struct dt_node *lpc_node)
{
const u32 *imap;
size_t imap_size;
imap = dt_prop_get_def_size(lpc_node, "interrupt-map", NULL, &imap_size);
if (!imap)
return;
imap_size >>= 2;
if (imap_size % 5) {
prerror("LPC[%03x]: Odd format for LPC interrupt-map !\n",
lpc->chip_id);
return;
}
while(imap_size >= 5) {
uint32_t sirq = be32_to_cpu(imap[2]);
uint32_t pirq = be32_to_cpu(imap[4]);
if (sirq >= LPC_NUM_SERIRQ) {
prerror("LPC[%03x]: LPC irq %d out of range in"
" interrupt-map\n", lpc->chip_id, sirq);
} else if (pirq < P9_PSI_IRQ_LPC_SIRQ0 ||
pirq > P9_PSI_IRQ_LPC_SIRQ3) {
prerror("LPC[%03x]: PSI irq %d out of range in"
" interrupt-map\n", lpc->chip_id, pirq);
} else {
uint32_t pin = pirq - P9_PSI_IRQ_LPC_SIRQ0;
lpc->sirq_routes[sirq] = pin;
prlog(PR_INFO, "LPC[%03x]: SerIRQ %d routed to PSI input %d\n",
lpc->chip_id, sirq, pin);
}
imap += 5;
imap_size -= 5;
}
}
static void lpc_init_chip_p9(struct dt_node *opb_node)
{
uint32_t gcid = dt_get_chip_id(opb_node);
struct dt_node *lpc_node;
struct proc_chip *chip;
struct lpcm *lpc;
u64 addr;
u32 val;
chip = get_chip(gcid);
assert(chip);
/* Grab OPB base address */
addr = dt_prop_get_cell(opb_node, "ranges", 1);
addr <<= 32;
addr |= dt_prop_get_cell(opb_node, "ranges", 2);
/* Find the "lpc" child node */
lpc_node = dt_find_compatible_node(opb_node, NULL, "ibm,power9-lpc");
if (!lpc_node)
return;
lpc = zalloc(sizeof(struct lpcm));
assert(lpc);
lpc->chip_id = gcid;
lpc->mbase = (void *)addr;
lpc->fw_idsel = 0xff;
lpc->fw_rdsz = 0xff;
list_head_init(&lpc->clients);
init_lock(&lpc->lock);
if (lpc_default_chip_id < 0 ||
dt_has_node_property(opb_node, "primary", NULL)) {
lpc_default_chip_id = gcid;
}
/* Parse interrupt map if any to setup initial routing */
lpc_parse_interrupt_map(lpc, lpc_node);
/* Mask all interrupts for now */
opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4);
/* Default with routing to PSI SerIRQ 0, this will be updated
* later when interrupts are initialized.
*/
opb_read(lpc, opb_master_reg_base + 8, &val, 4);
val &= 0xff03ffff;
opb_write(lpc, opb_master_reg_base + 8, val, 4);
opb_read(lpc, opb_master_reg_base + 0xc, &val, 4);
val &= 0xf0000000;
opb_write(lpc, opb_master_reg_base + 0xc, val, 4);
printf("LPC[%03x]: Initialized, access via MMIO @%p\n",
gcid, lpc->mbase);
chip->lpc = lpc;
}
void lpc_init(void)
{
struct dt_node *xn;
bool has_lpc = false;
dt_for_each_compatible(dt_root, xn, "ibm,power8-lpc") {
lpc_init_chip_p8(xn);
has_lpc = true;
}
dt_for_each_compatible(dt_root, xn, "ibm,power9-lpcm-opb") {
lpc_init_chip_p9(xn);
has_lpc = true;
}
if (lpc_default_chip_id >= 0)
printf("LPC: Default bus on chip 0x%x\n", lpc_default_chip_id);
if (has_lpc) {
opal_register(OPAL_LPC_WRITE, opal_lpc_write, 5);
opal_register(OPAL_LPC_READ, opal_lpc_read, 5);
}
}
void lpc_used_by_console(void)
{
struct proc_chip *chip;
xscom_used_by_console();
for_each_chip(chip) {
struct lpcm *lpc = chip->lpc;
if (lpc) {
lpc->lock.in_con_path = true;
lock(&lpc->lock);
unlock(&lpc->lock);
}
}
}
bool lpc_ok(void)
{
struct proc_chip *chip;
if (lpc_default_chip_id < 0)
return false;
if (!xscom_ok())
return false;
chip = get_chip(lpc_default_chip_id);
if (!chip->lpc)
return false;
return !lock_held_by_me(&chip->lpc->lock);
}
void lpc_register_client(uint32_t chip_id,
const struct lpc_client *clt)
{
struct lpc_client_entry *ent;
struct proc_chip *chip;
struct lpcm *lpc;
chip = get_chip(chip_id);
assert(chip);
lpc = chip->lpc;
if (!lpc) {
prerror("LPC: Attempt to register client on bad chip 0x%x\n",
chip_id);
return;
}
ent = malloc(sizeof(*ent));
assert(ent);
ent->clt = clt;
lock(&lpc->lock);
list_add(&lpc->clients, &ent->node);
if (lpc->has_serirq)
lpc_setup_serirq(lpc);
unlock(&lpc->lock);
}