static void find_capp_checkstop_reason(int flat_chip_id, struct OpalHMIEvent *hmi_evt, uint64_t *out_flags) { struct capp_info info; struct phb *phb; uint64_t capp_fir; uint64_t capp_fir_mask; uint64_t capp_fir_action0; uint64_t capp_fir_action1; uint64_t reg; int64_t rc; /* Find the CAPP on the chip associated with the HMI. */ for_each_phb(phb) { /* get the CAPP info */ rc = capp_get_info(flat_chip_id, phb, &info); if (rc == OPAL_PARAMETER) continue; if (xscom_read(flat_chip_id, info.capp_fir_reg, &capp_fir) || xscom_read(flat_chip_id, info.capp_fir_mask_reg, &capp_fir_mask) || xscom_read(flat_chip_id, info.capp_fir_action0_reg, &capp_fir_action0) || xscom_read(flat_chip_id, info.capp_fir_action1_reg, &capp_fir_action1)) { prerror("CAPP: Couldn't read CAPP#%d (PHB:#%x) FIR registers by XSCOM!\n", info.capp_index, info.phb_index); continue; } if (!(capp_fir & ~capp_fir_mask)) continue; prlog(PR_DEBUG, "CAPP#%d (PHB:#%x): FIR 0x%016llx mask 0x%016llx\n", info.capp_index, info.phb_index, capp_fir, capp_fir_mask); prlog(PR_DEBUG, "CAPP#%d (PHB:#%x): ACTION0 0x%016llx, ACTION1 0x%016llx\n", info.capp_index, info.phb_index, capp_fir_action0, capp_fir_action1); /* * If this bit is set (=1) a Recoverable Error has been * detected */ xscom_read(flat_chip_id, info.capp_err_status_ctrl_reg, ®); if ((reg & PPC_BIT(0)) != 0) { phb_lock(phb); phb->ops->set_capp_recovery(phb); phb_unlock(phb); hmi_evt->severity = OpalHMI_SEV_NO_ERROR; hmi_evt->type = OpalHMI_ERROR_CAPP_RECOVERY; queue_hmi_event(hmi_evt, 1, out_flags); return; } } }
static void show_all_regs(struct npu2 *npu, int brick_index) { int i, stack, stack_min, stack_max; uint64_t fir_val, mask_val, fir_addr, mask_addr; struct npu2_dev *dev; npu2_scom_dump_t scom_reg; if (brick_index != -1) { stack_min = stack_max = NPU2_STACK_STCK_0 + brick_index / 2; } else { stack_min = NPU2_STACK_STCK_0; stack_max = NPU2_STACK_STCK_2; /* Avoid dumping unused stacks for opencapi on Lagrange */ if (npu->total_devices == 2) stack_min = stack_max = NPU2_STACK_STCK_1; } /* NPU FIRs */ for (i = 0; i < NPU2_TOTAL_FIR_REGISTERS; i++) { fir_addr = NPU2_FIR_REGISTER_0 + i * NPU2_FIR_OFFSET; mask_addr = fir_addr + NPU2_FIR_MASK_OFFSET; xscom_read(npu->chip_id, fir_addr, &fir_val); xscom_read(npu->chip_id, mask_addr, &mask_val); prlog(PR_ERR, "NPU[%d] FIR%d = 0x%016llx (mask 0x%016llx => 0x%016llx)\n", npu->chip_id, i, fir_val, mask_val, fir_val & ~mask_val); } /* NPU global, per-stack registers */ for (i = 0; i < ARRAY_SIZE(npu2_scom_dump_global); i++) { for (stack = stack_min; stack <= stack_max; stack++) print_one_npu_reg(npu, &npu2_scom_dump_global[i], stack); } /* * NPU global registers, stack independent * We have only one for now, so dump it directly */ scom_reg.name = "XTS.REG.ERR_HOLD"; scom_reg.block = NPU2_BLOCK_XTS; scom_reg.offset = 0; print_one_npu_reg(npu, &scom_reg, NPU2_STACK_MISC); /* nvlink- or opencapi-specific registers */ for (i = 0; i < npu->total_devices; i++) { dev = &npu->devices[i]; if (brick_index == -1 || dev->brick_index == brick_index) { if (dev->type == NPU2_DEV_TYPE_NVLINK) show_nvlink_regs(npu, dev->brick_index); else if (dev->type == NPU2_DEV_TYPE_OPENCAPI) show_opencapi_regs(npu, dev->brick_index); } } }
void p8_sbe_init_timer(void) { struct dt_node *np; int64_t rc; uint32_t tick_us; np = dt_find_compatible_node(dt_root, NULL, "ibm,power8-sbe-timer"); if (!np) return; sbe_timer_chip = dt_get_chip_id(np); tick_us = dt_prop_get_u32(np, "tick-time-us"); sbe_timer_inc = usecs_to_tb(tick_us); sbe_timer_target = ~0ull; rc = xscom_read(sbe_timer_chip, 0xE0006, &sbe_last_gen); if (rc) { prerror("SLW: Error %lld reading tmr gen count\n", rc); return; } sbe_last_gen_stamp = mftb(); prlog(PR_INFO, "SLW: Timer facility on chip %d, resolution %dus\n", sbe_timer_chip, tick_us); sbe_has_timer = true; }
static int64_t opb_write(struct proc_chip *chip, uint32_t addr, uint32_t data, uint32_t sz) { uint64_t ctl = ECCB_CTL_MAGIC, stat; int64_t rc, tout; uint64_t data_reg; 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("LPC: Invalid data size %d\n", sz); return OPAL_PARAMETER; } rc = xscom_write(chip->id, chip->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(chip->id, chip->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(chip->id, chip->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(100); } log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: Write timeout !\n"); return OPAL_HARDWARE; }
static int read_core_fir(uint32_t chip_id, uint32_t core_id, uint64_t *core_fir) { int rc; switch (proc_gen) { case proc_gen_p8: rc = xscom_read(chip_id, XSCOM_ADDR_P8_EX(core_id, P8_CORE_FIR), core_fir); break; case proc_gen_p9: rc = xscom_read(chip_id, XSCOM_ADDR_P9_EC(core_id, P9_CORE_FIR), core_fir); break; default: rc = OPAL_HARDWARE; } return rc; }
/* same as above, but for direct access registers */ static void print_one_reg(int chip_id, int brick_index, uint64_t reg_addr, const char *reg_name) { uint64_t val; xscom_read(chip_id, reg_addr, &val); prlog(PR_ERR, "NPU[%d] %s brick %d 0x%llx = 0x%016llx\n", chip_id, reg_name, brick_index, reg_addr, val); }
static uint64_t __unused dl_read(struct npu_dev *npu_dev, uint32_t addr) { uint64_t val; xscom_write(npu_dev->npu->chip_id, npu_dev->xscom + NX_DL_REG_ADDR, addr); xscom_read(npu_dev->npu->chip_id, npu_dev->xscom + NX_DL_REG_DATA, &val); return val; }
uint64_t npu2_scom_read(uint64_t gcid, uint64_t scom_base, uint64_t reg, uint64_t size) { uint64_t val; npu2_scom_set_addr(gcid, scom_base, reg, size); xscom_read(gcid, scom_base + NPU2_MISC_SCOM_IND_SCOM_DATA, &val); return val; }
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); }
static uint16_t phy_read(struct npu_dev *npu_dev, uint64_t addr) { uint64_t val; if (pl_use_scom) xscom_read(npu_dev->npu->chip_id, npu_dev->pl_xscom_base + addr, &val); else val = in_be16((void *) npu_dev->pl_base + PL_MMIO_ADDR(addr)); return val & 0xffff; }
static int read_core_wof(uint32_t chip_id, uint32_t core_id, uint64_t *core_wof) { int rc; switch (proc_gen) { case proc_gen_p9: rc = xscom_read(chip_id, XSCOM_ADDR_P9_EC(core_id, P9_CORE_WOF), core_wof); break; default: rc = OPAL_HARDWARE; } return rc; }
static bool read_pba_bar(struct proc_chip *chip, unsigned int bar_no, uint64_t *base, uint64_t *size) { uint64_t bar, mask; int rc; rc = xscom_read(chip->id, pba_bar0 + bar_no, &bar); if (rc) { prerror("SLW: Error %d reading PBA BAR%d on chip %d\n", rc, bar_no, chip->id); return false; } rc = xscom_read(chip->id, pba_barmask0 + bar_no, &mask); if (rc) { prerror("SLW: Error %d reading PBA BAR MASK%d on chip %d\n", rc, bar_no, chip->id); return false; } prlog(PR_DEBUG, " PBA BAR%d : 0x%016llx\n", bar_no, bar); prlog(PR_DEBUG, " PBA MASK%d: 0x%016llx\n", bar_no, mask); if (mask == PBA_MASK_ALL_BITS) { /* * This could happen if all HOMER users are not enabled during * early system bringup. Skip using the PBA BAR. */ mask = 0; bar = 0; prerror(" PBA MASK%d uninitalized skipping BAR\n", bar_no); } *base = bar & 0x0ffffffffffffffful; *size = (mask | 0xfffff) + 1; return (*base) != 0; }
void fsi_recovery() { int32_t rc = SUCCESS; /* Clear out OPB error */ uint64_t scom_data = 0; scom_data = 0x8000000000000000; /*0=Unit Reset*/ rc |= xscom_write( OPB_REG_RES, scom_data ); rc |= xscom_write( OPB_REG_STAT, scom_data ); /* Check if we have any errors left */ rc |= xscom_read( OPB_REG_STAT, &scom_data ); TRACFCOMP( "PIB2OPB Status after cleanup = %08X%08X (rc=%d)", (uint32_t)(scom_data >> 32), (uint32_t)scom_data, rc ); }
static int __ipoll_update_mask(uint32_t proc, bool set, uint64_t bits) { uint64_t mask; int rc; rc = xscom_read(proc, PRD_IPOLL_REG_MASK, &mask); if (rc) return rc; if (set) mask |= bits; else mask &= ~bits; return xscom_write(proc, PRD_IPOLL_REG_MASK, mask); }
static int ipoll_record_and_mask_pending(uint32_t proc) { uint64_t status; int rc; lock(&ipoll_lock); rc = xscom_read(proc, PRD_IPOLL_REG_STATUS, &status); if (!rc) __ipoll_update_mask(proc, true, status); unlock(&ipoll_lock); if (!rc) ipoll_status[proc] |= (status & PRD_IPOLL_MASK); return rc; }
static void decode_malfunction(struct OpalHMIEvent *hmi_evt, uint64_t *out_flags) { int i; uint64_t malf_alert, flags; flags = 0; if (!setup_scom_addresses()) { prerror("Failed to setup scom addresses\n"); /* Send an unknown HMI event. */ hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_UNKNOWN; hmi_evt->u.xstop_error.xstop_reason = 0; queue_hmi_event(hmi_evt, false, out_flags); return; } xscom_read(this_cpu()->chip_id, malf_alert_scom, &malf_alert); if (!malf_alert) return; for (i = 0; i < 64; i++) { if (malf_alert & PPC_BIT(i)) { xscom_write(this_cpu()->chip_id, malf_alert_scom, ~PPC_BIT(i)); find_capp_checkstop_reason(i, hmi_evt, &flags); find_nx_checkstop_reason(i, hmi_evt, &flags); find_npu_checkstop_reason(i, hmi_evt, &flags); } } find_core_checkstop_reason(hmi_evt, &flags); /* * If we fail to find checkstop reason, send an unknown HMI event. */ if (!(flags & OPAL_HMI_FLAGS_NEW_EVENT)) { hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_UNKNOWN; hmi_evt->u.xstop_error.xstop_reason = 0; queue_hmi_event(hmi_evt, false, &flags); } *out_flags |= flags; }
static void print_chip_info(uint32_t chip_id) { uint64_t f000f, cfam_id; const char *name; char uname_buf[64]; int rc; rc = xscom_read(chip_id, 0xf000f, &f000f); if (rc) return; cfam_id = f000f >> 44; switch(cfam_id & 0xff) { case 0xf9: name = "P7 processor"; break; case 0xe8: name = "P7+ processor"; break; case 0xef: name = "P8E (Murano) processor"; break; case 0xea: name = "P8 (Venice) processor"; break; case 0xd3: name = "P8NVL (Naples) processor"; break; case 0xe9: name = "Centaur memory buffer"; break; default: snprintf(uname_buf, sizeof(uname_buf), "Unknown ID 0x%02lx", cfam_id & 0xff); name = uname_buf; } printf("%08x | DD%lx.%lx | %s\n", chip_id, (cfam_id >> 16) & 0xf, (cfam_id >> 8) & 0xf, name); }
static int populate_ipoll_msg(struct opal_prd_msg *msg, uint32_t proc) { uint64_t ipoll_mask; int rc; lock(&ipoll_lock); rc = xscom_read(proc, PRD_IPOLL_REG_MASK, &ipoll_mask); unlock(&ipoll_lock); if (rc) { prlog(PR_ERR, "PRD: Unable to read ipoll status (chip %d)!\n", proc); return -1; } msg->attn.proc = proc; msg->attn.ipoll_status = ipoll_status[proc]; msg->attn.ipoll_mask = ipoll_mask; return 0; }
static int nx_cfg_umac_status_ctrl(u32 gcid, u64 xcfg) { u64 uctrl; int rc; #define CRB_ENABLE 1 rc = xscom_read(gcid, xcfg, &uctrl); if (rc) return rc; uctrl = SETFIELD(NX_P9_UMAC_STATUS_CTRL_CRB_ENABLE, uctrl, CRB_ENABLE); rc = xscom_write(gcid, xcfg, uctrl); if (rc) prerror("NX%d: ERROR: Setting UMAC Status Control failure %d\n", gcid, rc); else prlog(PR_DEBUG, "NX%d: Setting UMAC Status Control 0x%016lx\n", gcid, (unsigned long)uctrl); return rc; }
static void p8_sbe_dump_timer_ffdc(void) { uint64_t i, val; int64_t rc; static const uint32_t dump_regs[] = { 0xe0000, 0xe0001, 0xe0002, 0xe0003, 0xe0004, 0xe0005, 0xe0006, 0xe0007, 0xe0008, 0xe0009, 0xe000a, 0xe000b, 0xe000c, 0xe000d, 0xe000e, 0xe000f, 0xe0010, 0xe0011, 0xe0012, 0xe0013, 0xe0014, 0xe0015, 0xe0016, 0xe0017, 0xe0018, 0xe0019, 0x5001c, 0x50038, 0x50039, 0x5003a, 0x5003b }; /** * @fwts-label SLWRegisterDump * @fwts-advice An error condition occurred in sleep/winkle * engines timer state machine. Dumping debug information to * root-cause. OPAL/skiboot may be stuck on some operation that * requires SLW timer state machine (e.g. core powersaving) */ prlog(PR_DEBUG, "SLW: Register state:\n"); for (i = 0; i < ARRAY_SIZE(dump_regs); i++) { uint32_t reg = dump_regs[i]; rc = xscom_read(sbe_timer_chip, reg, &val); if (rc) { prlog(PR_DEBUG, "SLW: XSCOM error %lld reading" " reg 0x%x\n", rc, reg); break; } prlog(PR_DEBUG, "SLW: %5x = %016llx\n", reg, val); } }
/** * @brief Poll for completion of a FSI operation, return data on read */ int32_t poll_for_complete( uint32_t * o_val ) { int32_t rc = SUCCESS; enum { MAX_OPB_TIMEOUT_NS = 10*NS_PER_MSEC }; /*=10ms */ *o_val = 0; uint64_t read_data = 0; uint64_t elapsed_time_ns = 0; do { rc = xscom_read( OPB_REG_STAT, &read_data ); if ( SUCCESS != rc ) { fsi_recovery(); /* Try to recover the engine. */ return rc; } /* Check for completion. Note: not checking for FSI errors. */ if ( (read_data & OPB_STAT_BUSY) == 0 ) break; /* Not busy */ sleep( 10000 ); /* sleep for 10,000 ns */ elapsed_time_ns += 10000; } while ( elapsed_time_ns <= MAX_OPB_TIMEOUT_NS ); if ( MAX_OPB_TIMEOUT_NS < elapsed_time_ns ) { TRAC_ERR( "[poll_for_complete] FSI request timed out." ); return FAIL; } *o_val = (uint32_t)read_data; /* Data in the bottom half. */ return rc; }
int main(int argc, char *argv[]) { uint64_t val, addr = -1ull; uint32_t def_chip, chip_id = 0xffffffff; bool list_chips = false; bool no_work = false; int rc; while(1) { static struct option long_opts[] = { {"chip", required_argument, NULL, 'c'}, {"list-chips", no_argument, NULL, 'l'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, }; int c, oidx = 0; c = getopt_long(argc, argv, "-c:hlv", long_opts, &oidx); if (c == EOF) break; switch(c) { case 1: addr = strtoull(optarg, NULL, 16); break; case 'c': chip_id = strtoul(optarg, NULL, 0); break; case 'h': print_usage(0); break; case 'l': list_chips = true; break; case 'v': printf("xscom utils version %s\n", version); exit(0); default: exit(1); } } if (addr == -1ull) no_work = true; if (no_work && !list_chips) { fprintf(stderr, "Invalid or missing address\n"); print_usage(1); } def_chip = xscom_init(); if (def_chip == 0xffffffff) { fprintf(stderr, "No valid XSCOM chip found\n"); exit(1); } if (list_chips) { printf("Chip ID | Rev | Chip type\n"); printf("---------|-------|--------\n"); xscom_for_each_chip(print_chip_info); } if (no_work) return 0; if (chip_id == 0xffffffff) chip_id = def_chip; rc = xscom_read(chip_id, addr, &val); if (rc) { fprintf(stderr,"Error %d reading XSCOM\n", rc); exit(1); } printf("%016" PRIx64 "\n", val); return 0; }
static void find_nx_checkstop_reason(int flat_chip_id, struct OpalHMIEvent *hmi_evt, uint64_t *out_flags) { uint64_t nx_status; uint64_t nx_dma_fir; uint64_t nx_pbi_fir_val; int i; /* Get NX status register value. */ if (xscom_read(flat_chip_id, nx_status_reg, &nx_status) != 0) { prerror("XSCOM error reading NX_STATUS_REG\n"); return; } /* Check if NX has driven an HMI interrupt. */ if (!(nx_status & NX_HMI_ACTIVE)) return; /* Initialize HMI event */ hmi_evt->severity = OpalHMI_SEV_FATAL; hmi_evt->type = OpalHMI_ERROR_MALFUNC_ALERT; hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_NX; hmi_evt->u.xstop_error.u.chip_id = flat_chip_id; /* Get DMA & Engine FIR data register value. */ if (xscom_read(flat_chip_id, nx_dma_engine_fir, &nx_dma_fir) != 0) { prerror("XSCOM error reading NX_DMA_ENGINE_FIR\n"); return; } /* Get PowerBus Interface FIR data register value. */ if (xscom_read(flat_chip_id, nx_pbi_fir, &nx_pbi_fir_val) != 0) { prerror("XSCOM error reading NX_PBI_FIR\n"); return; } /* Find NX checkstop reason and populate HMI event with error info. */ for (i = 0; i < ARRAY_SIZE(nx_dma_xstop_bits); i++) if (nx_dma_fir & PPC_BIT(nx_dma_xstop_bits[i].bit)) hmi_evt->u.xstop_error.xstop_reason |= nx_dma_xstop_bits[i].reason; for (i = 0; i < ARRAY_SIZE(nx_pbi_xstop_bits); i++) if (nx_pbi_fir_val & PPC_BIT(nx_pbi_xstop_bits[i].bit)) hmi_evt->u.xstop_error.xstop_reason |= nx_pbi_xstop_bits[i].reason; /* * Set NXDMAENGFIR[38] to signal PRD that service action is required. * Without this inject, PRD will not be able to do NX unit checkstop * error analysis. NXDMAENGFIR[38] is a spare bit and used to report * a software initiated attention. * * The behavior of this bit and all FIR bits are documented in * RAS spreadsheet. */ xscom_write(flat_chip_id, nx_dma_engine_fir, PPC_BIT(38)); /* Send an HMI event. */ queue_hmi_event(hmi_evt, 0, out_flags); }
static void find_npu2_checkstop_reason(int flat_chip_id, struct OpalHMIEvent *hmi_evt, uint64_t *out_flags) { struct phb *phb; int i; bool npu2_hmi_verbose = false, found = false; uint64_t npu2_fir; uint64_t npu2_fir_mask; uint64_t npu2_fir_action0; uint64_t npu2_fir_action1; uint64_t npu2_fir_addr; uint64_t npu2_fir_mask_addr; uint64_t npu2_fir_action0_addr; uint64_t npu2_fir_action1_addr; uint64_t fatal_errors; int total_errors = 0; const char *loc; /* Find the NPU on the chip associated with the HMI. */ for_each_phb(phb) { /* NOTE: if a chip ever has >1 NPU this will need adjusting */ if (phb_is_npu2(phb->dt_node) && (dt_get_chip_id(phb->dt_node) == flat_chip_id)) { found = true; break; } } /* If we didn't find a NPU on the chip, it's not our checkstop. */ if (!found) return; npu2_fir_addr = NPU2_FIR_REGISTER_0; npu2_fir_mask_addr = NPU2_FIR_REGISTER_0 + NPU2_FIR_MASK_OFFSET; npu2_fir_action0_addr = NPU2_FIR_REGISTER_0 + NPU2_FIR_ACTION0_OFFSET; npu2_fir_action1_addr = NPU2_FIR_REGISTER_0 + NPU2_FIR_ACTION1_OFFSET; for (i = 0; i < NPU2_TOTAL_FIR_REGISTERS; i++) { /* Read all the registers necessary to find a checkstop condition. */ if (xscom_read(flat_chip_id, npu2_fir_addr, &npu2_fir) || xscom_read(flat_chip_id, npu2_fir_mask_addr, &npu2_fir_mask) || xscom_read(flat_chip_id, npu2_fir_action0_addr, &npu2_fir_action0) || xscom_read(flat_chip_id, npu2_fir_action1_addr, &npu2_fir_action1)) { prerror("HMI: Couldn't read NPU FIR register%d with XSCOM\n", i); continue; } fatal_errors = npu2_fir & ~npu2_fir_mask & npu2_fir_action0 & npu2_fir_action1; if (fatal_errors) { loc = chip_loc_code(flat_chip_id); if (!loc) loc = "Not Available"; prlog(PR_ERR, "NPU: [Loc: %s] P:%d FIR#%d FIR 0x%016llx mask 0x%016llx\n", loc, flat_chip_id, i, npu2_fir, npu2_fir_mask); prlog(PR_ERR, "NPU: [Loc: %s] P:%d ACTION0 0x%016llx, ACTION1 0x%016llx\n", loc, flat_chip_id, npu2_fir_action0, npu2_fir_action1); total_errors++; } /* Can't do a fence yet, we are just logging fir information for now */ npu2_fir_addr += NPU2_FIR_OFFSET; npu2_fir_mask_addr += NPU2_FIR_OFFSET; npu2_fir_action0_addr += NPU2_FIR_OFFSET; npu2_fir_action1_addr += NPU2_FIR_OFFSET; } if (!total_errors) return; npu2_hmi_verbose = nvram_query_eq_safe("npu2-hmi-verbose", "true"); /* Force this for now until we sort out something better */ npu2_hmi_verbose = true; if (npu2_hmi_verbose) { npu2_dump_scoms(flat_chip_id); prlog(PR_ERR, " _________________________ \n"); prlog(PR_ERR, "< It's Debug time! >\n"); prlog(PR_ERR, " ------------------------- \n"); prlog(PR_ERR, " \\ ,__, \n"); prlog(PR_ERR, " \\ (oo)____ \n"); prlog(PR_ERR, " (__) )\\ \n"); prlog(PR_ERR, " ||--|| * \n"); } /* Set up the HMI event */ hmi_evt->severity = OpalHMI_SEV_WARNING; hmi_evt->type = OpalHMI_ERROR_MALFUNC_ALERT; hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_NPU; hmi_evt->u.xstop_error.u.chip_id = flat_chip_id; /* Marking the event as recoverable so that we don't crash */ queue_hmi_event(hmi_evt, 1, out_flags); }
int nx_cfg_rx_fifo(struct dt_node *node, const char *compat, const char *priority, u32 gcid, u32 pid, u32 tid, u64 umac_bar, u64 umac_notify) { u64 cfg; int rc, size; uint64_t fifo; u32 lpid = 0xfff; /* All 1's for 12 bits in UMAC notify match reg */ #define MATCH_ENABLE 1 fifo = (uint64_t) local_alloc(gcid, RX_FIFO_SIZE, RX_FIFO_SIZE); assert(fifo); /* * When configuring the address of the Rx FIFO into the Receive FIFO * BAR, we should _NOT_ shift the address into bits 8:53. Instead we * should copy the address as is and VAS/NX will extract relevant bits. */ /* * Section 5.21 of P9 NX Workbook Version 2.42 shows Receive FIFO BAR * 54:56 represents FIFO size * 000 = 1KB, 8 CRBs * 001 = 2KB, 16 CRBs * 010 = 4KB, 32 CRBs * 011 = 8KB, 64 CRBs * 100 = 16KB, 128 CRBs * 101 = 32KB, 256 CRBs * 110 = 111 reserved */ size = RX_FIFO_SIZE / 1024; cfg = SETFIELD(NX_P9_RX_FIFO_BAR_SIZE, fifo, ilog2(size)); rc = xscom_write(gcid, umac_bar, cfg); if (rc) { prerror("NX%d: ERROR: Setting UMAC FIFO bar failure %d\n", gcid, rc); return rc; } else prlog(PR_DEBUG, "NX%d: Setting UMAC FIFO bar 0x%016lx\n", gcid, (unsigned long)cfg); rc = xscom_read(gcid, umac_notify, &cfg); if (rc) return rc; /* * VAS issues asb_notify with the unique ID to identify the target * co-processor/engine. Logical partition ID (lpid), process ID (pid), * and thread ID (tid) combination is used to define the unique ID * in the system. Export these values in device-tree such that the * driver configure RxFIFO with VAS. Set these values in RxFIFO notify * match register for each engine which compares the ID with each * request. * To define unique indentification, 0xfff (1's for 12 bits), * co-processor type, and counter within coprocessor type are used * for lpid, pid, and tid respectively. */ cfg = SETFIELD(NX_P9_RX_FIFO_NOTIFY_MATCH_LPID, cfg, lpid); cfg = SETFIELD(NX_P9_RX_FIFO_NOTIFY_MATCH_PID, cfg, pid); cfg = SETFIELD(NX_P9_RX_FIFO_NOTIFY_MATCH_TID, cfg, tid); cfg = SETFIELD(NX_P9_RX_FIFO_NOTIFY_MATCH_MATCH_ENABLE, cfg, MATCH_ENABLE); rc = xscom_write(gcid, umac_notify, cfg); if (rc) { prerror("NX%d: ERROR: Setting UMAC notify match failure %d\n", gcid, rc); return rc; } else prlog(PR_DEBUG, "NX%d: Setting UMAC notify match 0x%016lx\n", gcid, (unsigned long)cfg); dt_add_property_string(node, "compatible", compat); dt_add_property_string(node, "priority", priority); dt_add_property_u64(node, "rx-fifo-address", fifo); dt_add_property_cells(node, "rx-fifo-size", RX_FIFO_SIZE); dt_add_property_cells(node, "lpid", lpid); dt_add_property_cells(node, "pid", pid); dt_add_property_cells(node, "tid", tid); return 0; }
static void find_npu_checkstop_reason(int flat_chip_id, struct OpalHMIEvent *hmi_evt, uint64_t *out_flags) { struct phb *phb; struct npu *p = NULL; uint64_t npu_fir; uint64_t npu_fir_mask; uint64_t npu_fir_action0; uint64_t npu_fir_action1; uint64_t fatal_errors; /* Only check for NPU errors if the chip has a NPU */ if (PVR_TYPE(mfspr(SPR_PVR)) != PVR_TYPE_P8NVL) return find_npu2_checkstop_reason(flat_chip_id, hmi_evt, out_flags); /* Find the NPU on the chip associated with the HMI. */ for_each_phb(phb) { /* NOTE: if a chip ever has >1 NPU this will need adjusting */ if (dt_node_is_compatible(phb->dt_node, "ibm,power8-npu-pciex") && (dt_get_chip_id(phb->dt_node) == flat_chip_id)) { p = phb_to_npu(phb); break; } } /* If we didn't find a NPU on the chip, it's not our checkstop. */ if (p == NULL) return; /* Read all the registers necessary to find a checkstop condition. */ if (xscom_read(flat_chip_id, p->at_xscom + NX_FIR, &npu_fir) || xscom_read(flat_chip_id, p->at_xscom + NX_FIR_MASK, &npu_fir_mask) || xscom_read(flat_chip_id, p->at_xscom + NX_FIR_ACTION0, &npu_fir_action0) || xscom_read(flat_chip_id, p->at_xscom + NX_FIR_ACTION1, &npu_fir_action1)) { prerror("Couldn't read NPU registers with XSCOM\n"); return; } fatal_errors = npu_fir & ~npu_fir_mask & npu_fir_action0 & npu_fir_action1; /* If there's no errors, we don't need to do anything. */ if (!fatal_errors) return; prlog(PR_DEBUG, "NPU: FIR 0x%016llx mask 0x%016llx\n", npu_fir, npu_fir_mask); prlog(PR_DEBUG, "NPU: ACTION0 0x%016llx, ACTION1 0x%016llx\n", npu_fir_action0, npu_fir_action1); /* Set the NPU to fenced since it can't recover. */ npu_set_fence_state(p, true); /* Set up the HMI event */ hmi_evt->severity = OpalHMI_SEV_WARNING; hmi_evt->type = OpalHMI_ERROR_MALFUNC_ALERT; hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_NPU; hmi_evt->u.xstop_error.u.chip_id = flat_chip_id; /* The HMI is "recoverable" because it shouldn't crash the system */ queue_hmi_event(hmi_evt, 1, out_flags); }