int opb_target_init(struct target *target, const char *name, uint64_t base, struct target *next) { target_init(target, name, base, opb_read, opb_write, NULL, next); /* Clear any outstanding errors */ write_next_target(target, PIB2OPB_REG_RESET, PPC_BIT(0)); write_next_target(target, PIB2OPB_REG_STAT, PPC_BIT(0)); return 0; }
static uint64_t opb_poll(struct target *target, uint64_t *read_data) { unsigned long retries = MFSI_OPB_MAX_TRIES; uint64_t sval; uint32_t stat; int64_t rc; /* We try again every 10us for a bit more than 1ms */ for (;;) { /* Read OPB status register */ rc = read_next_target(target, PIB2OPB_REG_STAT, &sval); if (rc) { /* Do something here ? */ PR_ERROR("XSCOM error %lld read OPB STAT\n", rc); return -1; } PR_DEBUG(" STAT=0x%16llx...\n", sval); stat = sval >> 32; /* Complete */ if (!(stat & OPB_STAT_BUSY)) break; if (retries-- == 0) { /* This isn't supposed to happen (HW timeout) */ PR_ERROR("OPB POLL timeout !\n"); return -1; } usleep(1); } /* * TODO: Add the full error analysis that skiboot has. For now * we just reset things so we can continue. Also need to * improve error handling as we expect these occasionally when * probing the system. */ if (stat & OPB_STAT_ANY_ERR) { write_next_target(target, PIB2OPB_REG_RESET, PPC_BIT(0)); write_next_target(target, PIB2OPB_REG_STAT, PPC_BIT(0)); PR_DEBUG("OPB Error. Status 0x%08x\n", stat); rc = -1; } else if (read_data) { if (!(stat & OPB_STAT_READ_VALID)) { PR_DEBUG("Read successful but no data !\n"); rc = -1; } *read_data = sval & 0xffffffff; } return rc; }
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 inline void __tlbie_lpid(unsigned long lpid, unsigned long ric) { unsigned long rb,rs,prs,r; rb = PPC_BIT(52); /* IS = 2 */ rs = lpid; prs = 0; /* partition scoped */ r = 1; /* radix format */ asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); trace_tlbie(lpid, 0, rb, rs, ric, prs, r); }
static inline void __tlbie_pid(unsigned long pid, unsigned long ric) { unsigned long rb,rs,prs,r; rb = PPC_BIT(53); /* IS = 1 */ rs = pid << PPC_BITLSHIFT(31); prs = 1; /* process scoped */ r = 1; /* radix format */ asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); trace_tlbie(0, 0, rb, rs, ric, prs, r); }
static inline void _tlbie_pid(unsigned long pid, unsigned long ric) { unsigned long rb,rs,prs,r; rb = PPC_BIT(53); /* IS = 1 */ rs = pid << PPC_BITLSHIFT(31); prs = 1; /* process scoped */ r = 1; /* raidx format */ asm volatile("ptesync": : :"memory"); asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1) : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); asm volatile("eieio; tlbsync; ptesync": : :"memory"); }
static inline void __tlbiel_pid(unsigned long pid, int set, unsigned long ric) { unsigned long rb,rs,prs,r; rb = PPC_BIT(53); /* IS = 1 */ rb |= set << PPC_BITLSHIFT(51); rs = ((unsigned long)pid) << PPC_BITLSHIFT(31); prs = 1; /* process scoped */ r = 1; /* raidx format */ asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1) : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); trace_tlbie(0, 1, rb, rs, ric, prs, r); }
static inline void __tlbiel_lpid_guest(unsigned long lpid, int set, unsigned long ric) { unsigned long rb,rs,prs,r; rb = PPC_BIT(52); /* IS = 2 */ rb |= set << PPC_BITLSHIFT(51); rs = 0; /* LPID comes from LPIDR */ prs = 1; /* process scoped */ r = 1; /* radix format */ asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1) : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory"); trace_tlbie(lpid, 1, rb, rs, ric, prs, r); }
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; }
long hmi_exception_realmode(struct pt_regs *regs) { __this_cpu_inc(irq_stat.hmi_exceptions); #ifdef CONFIG_PPC_BOOK3S_64 /* Workaround for P9 vector CI loads (see p9_hmi_special_emu) */ if (pvr_version_is(PVR_POWER9)) { unsigned long hmer = mfspr(SPRN_HMER); /* Do we have the debug bit set */ if (hmer & PPC_BIT(17)) { hmer &= ~PPC_BIT(17); mtspr(SPRN_HMER, hmer); /* * Now to avoid problems with soft-disable we * only do the emulation if we are coming from * user space */ if (user_mode(regs)) local_paca->hmi_p9_special_emu = 1; /* * Don't bother going to OPAL if that's the * only relevant bit. */ if (!(hmer & mfspr(SPRN_HMEER))) return local_paca->hmi_p9_special_emu; } } #endif /* CONFIG_PPC_BOOK3S_64 */ wait_for_subcore_guest_exit(); if (ppc_md.hmi_exception_early) ppc_md.hmi_exception_early(regs); wait_for_tb_resync(); return 1; }
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 bool decode_core_fir(struct cpu_thread *cpu, struct OpalHMIEvent *hmi_evt) { uint64_t core_fir; uint32_t core_id; int i, swkup_rc; bool found = false; int64_t ret; const char *loc; /* Sanity check */ if (!cpu || !hmi_evt) return false; core_id = pir_to_core_id(cpu->pir); /* Force the core to wakeup, otherwise reading core_fir is unrealiable * if stop-state 5 is enabled. */ swkup_rc = dctl_set_special_wakeup(cpu); /* Get CORE FIR register value. */ ret = read_core_fir(cpu->chip_id, core_id, &core_fir); if (!swkup_rc) dctl_clear_special_wakeup(cpu); if (ret == OPAL_WRONG_STATE) { /* * CPU is asleep, so it probably didn't cause the checkstop. * If no other HMI cause is found a "catchall" checkstop * will be raised, so if this CPU should've been awake the * error will be handled appropriately. */ prlog(PR_DEBUG, "FIR read failed, chip %d core %d asleep\n", cpu->chip_id, core_id); return false; } else if (ret != OPAL_SUCCESS) { prerror("XSCOM error reading CORE FIR\n"); /* If the FIR can't be read, we should checkstop. */ return true; } if (!core_fir) return false; loc = chip_loc_code(cpu->chip_id); prlog(PR_INFO, "[Loc: %s]: CHIP ID: %x, CORE ID: %x, FIR: %016llx\n", loc ? loc : "Not Available", cpu->chip_id, core_id, core_fir); /* Check CORE FIR bits and populate HMI event with error info. */ for (i = 0; i < ARRAY_SIZE(xstop_bits); i++) { if (core_fir & PPC_BIT(xstop_bits[i].bit)) { found = true; hmi_evt->u.xstop_error.xstop_reason |= xstop_bits[i].reason; } } return found; }
static void hfscr_pmu_enable(void) { u64 hfscr = mfspr(SPRN_HFSCR); hfscr |= PPC_BIT(60); mtspr(SPRN_HFSCR, hfscr); }