Skip to content

Commit

Permalink
lpc: Introduce generic probe capability
Browse files Browse the repository at this point in the history
[ Upstream commit 5684204 ]

Introduce generic read and write probe functions that allow detection of
valid addresses by way of synchronous testing for the SYNC no-response
state. If the no-response state is detected the probe functions will
return an error to the caller, who can do with it what they wish.

In the process, rip out the naive mechanism for muting the equivalent
asynchronous error logging (regretfully introduced recently by yours
truly).

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
  • Loading branch information
amboar authored and stewartsmith committed Oct 31, 2018
1 parent dacc40a commit 494221e
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 58 deletions.
200 changes: 146 additions & 54 deletions hw/lpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ enum {
LPC_ROUTE_LINUX
};

struct lpc_error_entry {
int64_t rc;
const char *description;
};

struct lpcm {
uint32_t chip_id;
uint32_t xbase;
Expand Down Expand Up @@ -453,13 +458,85 @@ static int64_t lpc_opb_prepare(struct lpcm *lpc,
return OPAL_SUCCESS;
}

#define LPC_ERROR_IDX(x) (__builtin_ffs(x) - 1 - 2)
#define LPC_ERROR(_sts, _rc, _description) \
[LPC_ERROR_IDX(_sts)] = { _rc, _description }
static const struct lpc_error_entry lpc_error_table[] = {
LPC_ERROR(LPC_HC_IRQ_BM_TAR_ERR, OPAL_WRONG_STATE, "Got bus master TAR error."),
LPC_ERROR(LPC_HC_IRQ_TARG_TAR_ERR, OPAL_WRONG_STATE, "Got abnormal TAR error."),
LPC_ERROR(LPC_HC_IRQ_SYNC_TIMEOUT_ERR, OPAL_TIMEOUT, "Got SYNC timeout error."),
LPC_ERROR(LPC_HC_IRQ_SYNC_NORM_ERR, OPAL_WRONG_STATE, "Got SYNC normal error."),
LPC_ERROR(LPC_HC_IRQ_SYNC_NORESP_ERR, OPAL_HARDWARE, "Got SYNC no-response error."),
LPC_ERROR(LPC_HC_IRQ_SYNC_ABNORM_ERR, OPAL_WRONG_STATE, "Got SYNC abnormal error."),
};

static int64_t lpc_probe_prepare(struct lpcm *lpc)
{
const uint32_t irqmask_addr = lpc_reg_opb_base + LPC_HC_IRQMASK;
uint32_t irqmask;
int rc;

rc = opb_read(lpc, irqmask_addr, &irqmask, 4);
if (rc)
return rc;

irqmask &= ~LPC_HC_IRQ_SYNC_NORESP_ERR;
return opb_write(lpc, irqmask_addr, irqmask, 4);
}

static int64_t lpc_probe_test(struct lpcm *lpc)
{
const uint32_t irqmask_addr = lpc_reg_opb_base + LPC_HC_IRQMASK;
const uint32_t irqstat_addr = lpc_reg_opb_base + LPC_HC_IRQSTAT;
uint32_t irqmask, irqstat;
int64_t idx;
int rc;

rc = opb_read(lpc, irqstat_addr, &irqstat, 4);
if (rc)
return rc;

rc = opb_write(lpc, irqstat_addr, LPC_HC_IRQ_SYNC_NORESP_ERR, 4);
if (rc)
return rc;

rc = opb_read(lpc, irqmask_addr, &irqmask, 4);
if (rc)
return rc;

irqmask |= LPC_HC_IRQ_SYNC_NORESP_ERR;
rc = opb_write(lpc, irqmask_addr, irqmask, 4);
if (rc)
return rc;

if (!(irqstat & LPC_HC_IRQ_BASE_IRQS))
return OPAL_SUCCESS;

/* Ensure we can perform a valid lookup in the error table */
idx = LPC_ERROR_IDX(irqstat);
if (idx < 0 || idx > ARRAY_SIZE(lpc_error_table)) {
prerror("LPC bus error translation failed with status 0x%x\n",
irqstat);
return OPAL_PARAMETER;
}

rc = lpc_error_table[idx].rc;
return rc;
}

static int64_t __lpc_write(struct lpcm *lpc, enum OpalLPCAddressType addr_type,
uint32_t addr, uint32_t data, uint32_t sz)
uint32_t addr, uint32_t data, uint32_t sz,
bool probe)
{
uint32_t opb_base;
int64_t rc;

lock(&lpc->lock);
if (probe) {
rc = lpc_probe_prepare(lpc);
if (rc)
goto bail;
}

/*
* Convert to an OPB access and handle LPC HC configuration
Expand All @@ -471,15 +548,19 @@ static int64_t __lpc_write(struct lpcm *lpc, enum OpalLPCAddressType addr_type,

/* Perform OPB access */
rc = opb_write(lpc, opb_base + addr, data, sz);
if (rc)
goto bail;

/* XXX Add LPC error handling/recovery */
if (probe)
rc = lpc_probe_test(lpc);
bail:
unlock(&lpc->lock);
return rc;
}

int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t data, uint32_t sz)
static int64_t __lpc_write_sanity(enum OpalLPCAddressType addr_type,
uint32_t addr, uint32_t data, uint32_t sz,
bool probe)
{
struct proc_chip *chip;

Expand All @@ -488,7 +569,19 @@ int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr,
chip = get_chip(lpc_default_chip_id);
if (!chip || !chip->lpc)
return OPAL_PARAMETER;
return __lpc_write(chip->lpc, addr_type, addr, data, sz);
return __lpc_write(chip->lpc, addr_type, addr, data, sz, probe);
}

int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t data, uint32_t sz)
{
return __lpc_write_sanity(addr_type, addr, data, sz, false);
}

int64_t lpc_probe_write(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t data, uint32_t sz)
{
return __lpc_write_sanity(addr_type, addr, data, sz, true);
}

/*
Expand All @@ -507,9 +600,9 @@ static int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_typ
return OPAL_PARAMETER;

if (addr_type == OPAL_LPC_FW || sz == 1)
return __lpc_write(chip->lpc, addr_type, addr, data, sz);
return __lpc_write(chip->lpc, addr_type, addr, data, sz, false);
while(sz--) {
rc = __lpc_write(chip->lpc, addr_type, addr, data & 0xff, 1);
rc = __lpc_write(chip->lpc, addr_type, addr, data & 0xff, 1, false);
if (rc)
return rc;
addr++;
Expand All @@ -519,12 +612,18 @@ static int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_typ
}

static int64_t __lpc_read(struct lpcm *lpc, enum OpalLPCAddressType addr_type,
uint32_t addr, uint32_t *data, uint32_t sz)
uint32_t addr, uint32_t *data, uint32_t sz,
bool probe)
{
uint32_t opb_base;
int64_t rc;

lock(&lpc->lock);
if (probe) {
rc = lpc_probe_prepare(lpc);
if (rc)
goto bail;
}

/*
* Convert to an OPB access and handle LPC HC configuration
Expand All @@ -536,15 +635,19 @@ static int64_t __lpc_read(struct lpcm *lpc, enum OpalLPCAddressType addr_type,

/* Perform OPB access */
rc = opb_read(lpc, opb_base + addr, data, sz);
if (rc)
goto bail;

/* XXX Add LPC error handling/recovery */
if (probe)
rc = lpc_probe_test(lpc);
bail:
unlock(&lpc->lock);
return rc;
}

int64_t lpc_read(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t *data, uint32_t sz)
static int64_t __lpc_read_sanity(enum OpalLPCAddressType addr_type,
uint32_t addr, uint32_t *data, uint32_t sz,
bool probe)
{
struct proc_chip *chip;

Expand All @@ -553,7 +656,19 @@ int64_t lpc_read(enum OpalLPCAddressType addr_type, uint32_t addr,
chip = get_chip(lpc_default_chip_id);
if (!chip || !chip->lpc)
return OPAL_PARAMETER;
return __lpc_read(chip->lpc, addr_type, addr, data, sz);
return __lpc_read(chip->lpc, addr_type, addr, data, sz, probe);
}

int64_t lpc_read(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t *data, uint32_t sz)
{
return __lpc_read_sanity(addr_type, addr, data, sz, false);
}

int64_t lpc_probe_read(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t *data, uint32_t sz)
{
return __lpc_read_sanity(addr_type, addr, data, sz, true);
}

/*
Expand All @@ -572,12 +687,12 @@ static int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type
return OPAL_PARAMETER;

if (addr_type == OPAL_LPC_FW || sz == 1)
return __lpc_read(chip->lpc, addr_type, addr, data, sz);
return __lpc_read(chip->lpc, addr_type, addr, data, sz, false);
*data = 0;
while(sz--) {
uint32_t byte;

rc = __lpc_read(chip->lpc, addr_type, addr, &byte, 1);
rc = __lpc_read(chip->lpc, addr_type, addr, &byte, 1, false);
if (rc)
return rc;
*data = *data | (byte << (8 * sz));
Expand Down Expand Up @@ -891,26 +1006,13 @@ static void lpc_dispatch_reset(struct lpcm *lpc)
lpc_setup_serirq(lpc);
}

uint32_t lpc_irq_err_mask;

void lpc_irq_err_mask_sync_no_response(void)
{
lpc_irq_err_mask |= LPC_HC_IRQ_SYNC_NORESP_ERR;
lwsync();
}

static void lpc_irq_err_mask_reset(void)
{
lpc_irq_err_mask = 0;
lwsync();
}

static void lpc_dispatch_err_irqs(struct lpcm *lpc, uint32_t irqs)
{
const char *sync_err = "Unknown LPC error";
const struct lpc_error_entry *err;
static int lpc_bus_err_count;
struct opal_err_info *info;
uint32_t err_addr;
uint32_t addr;
int64_t idx;
int rc;

/* Write back to clear error interrupts, we clear SerIRQ later
Expand All @@ -921,46 +1023,36 @@ static void lpc_dispatch_err_irqs(struct lpcm *lpc, uint32_t irqs)
if (rc)
prerror("Failed to clear IRQ error latches !\n");

if (irqs & LPC_HC_IRQ_LRESET)
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) {
if (lpc_irq_err_mask & LPC_HC_IRQ_SYNC_NORESP_ERR)
goto done;
return;
}

sync_err = "Got SYNC no-response error.";
/* Ensure we can perform a valid lookup in the error table */
idx = LPC_ERROR_IDX(irqs);
if (idx < 0 || idx > ARRAY_SIZE(lpc_error_table)) {
prerror("LPC bus error translation failed with status 0x%x\n",
irqs);
return;
}
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);

/* Find and report the error */
err = &lpc_error_table[idx];
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);


rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_ERROR_ADDRESS, &addr, 4);
if (rc)
log_simple_error(info, "LPC[%03x]: %s "
"Error reading error address register\n",
lpc->chip_id, sync_err);
lpc->chip_id, err->description);
else
log_simple_error(info, "LPC[%03x]: %s Error address reg: "
"0x%08x\n",
lpc->chip_id, sync_err, err_addr);

done:
lpc_irq_err_mask_reset();
lpc->chip_id, err->description, addr);
}

static void lpc_dispatch_ser_irqs(struct lpcm *lpc, uint32_t irqs,
Expand Down
14 changes: 10 additions & 4 deletions include/lpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,21 @@ extern unsigned int lpc_get_irq_policy(uint32_t chip_id, uint32_t psi_idx);
/* Clear SerIRQ latch on P9 DD1 */
extern void lpc_p9_sirq_eoi(uint32_t chip_id, uint32_t index);

/* Default bus accessors */
/* Default bus accessors that perform error logging */
extern int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t data, uint32_t sz);
extern int64_t lpc_read(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t *data, uint32_t sz);

/*
* LPC bus accessors that return errors as required but do not log the failure.
* Useful if the caller wants to test the presence of a device on the LPC bus.
*/
extern int64_t lpc_probe_write(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t data, uint32_t sz);
extern int64_t lpc_probe_read(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t *data, uint32_t sz);

/* Mark LPC bus as used by console */
extern void lpc_used_by_console(void);

Expand Down Expand Up @@ -168,7 +177,4 @@ static inline uint32_t lpc_inl(uint32_t addr)
return (rc == OPAL_SUCCESS) ? le32_to_cpu(d32) : 0xffffffff;
}

/* LPC IRQ error masking - required for some corner cases */
extern void lpc_irq_err_mask_sync_no_response(void);

#endif /* __LPC_H */

0 comments on commit 494221e

Please sign in to comment.