/* The DL registers can be accessed indirectly via the NTL */ static void dl_write(struct npu_dev *npu_dev, uint32_t addr, uint32_t val) { xscom_write(npu_dev->npu->chip_id, npu_dev->xscom + NX_DL_REG_ADDR, addr); xscom_write(npu_dev->npu->chip_id, npu_dev->xscom + NX_DL_REG_DATA, val); }
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 void phy_write(struct npu_dev *npu_dev, uint64_t addr, uint32_t val) { if (pl_use_scom) xscom_write(npu_dev->npu->chip_id, npu_dev->pl_xscom_base | addr, val); else out_be16((void *) npu_dev->pl_base + PL_MMIO_ADDR(addr), val); }
void npu2_scom_write(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_write(gcid, scom_base + NPU2_MISC_SCOM_IND_SCOM_DATA, val); }
/** * @brief Write a FSI register */ int32_t putfsi( SCOM_Trgt_t i_trgt, uint32_t i_addr, uint32_t i_val ) { int32_t rc = SUCCESS; uint32_t fsi_addr = i_trgt.fsiBaseAddr | i_addr; /* setup the OPB command register */ /* only supporting 4-byte access */ uint64_t fsi_cmd = fsi_addr | 0xE0000000; /* 111=Write Full Word */ fsi_cmd <<= 32; /* Command is in the upper word of the scom */ fsi_cmd |= i_val; /* Data is in the bottom 32-bits */ /* Write the OPB command register to trigger the read */ rc = xscom_write( OPB_REG_CMD, fsi_cmd ); if ( SUCCESS != rc ) { fsi_recovery(); /* Try to recover the engine. */ return rc; } /* Poll for complete */ uint32_t junk = 0; // Not used. rc = poll_for_complete( &junk ); return rc; }
static int nx_cfg_umac_tx_wc(u32 gcid, u64 xcfg) { int rc = 0; u64 cfg; cfg = vas_get_wcbs_bar(gcid); if (!cfg) { prerror("NX%d: ERROR finding WC Backing store BAR\n", gcid); return -ENOMEM; } /* * NOTE: Write the entire bar address to SCOM. VAS/NX will extract * the relevant (NX_P9_UMAC_TX_WINDOW_CONTEXT_ADDR) bits. * IOW, _don't_ just write the bit field like: * * cfg = SETFIELD(NX_P9_UMAC_TX_WINDOW_CONTEXT_ADDR, 0ULL, cfg); */ rc = xscom_write(gcid, xcfg, cfg); if (rc) prerror("NX%d: ERROR: UMAC SEND WC BAR, %d\n", gcid, rc); else prlog(PR_DEBUG, "NX%d: UMAC SEND WC BAR, 0x%016lx, " "xcfg 0x%llx\n", gcid, (unsigned long)cfg, xcfg); return rc; }
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 ); }
/* * We use the indirect method because it uses the same addresses as * the MMIO offsets (NPU RING) */ static void npu2_scom_set_addr(uint64_t gcid, uint64_t scom_base, uint64_t addr, uint64_t size) { addr = SETFIELD(NPU2_MISC_DA_ADDR, 0ull, addr); addr = SETFIELD(NPU2_MISC_DA_LEN, addr, size); xscom_write(gcid, scom_base + NPU2_MISC_SCOM_IND_SCOM_ADDR, addr); }
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; }
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 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 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 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 int nx_cfg_dma_vas_mmio(u32 gcid, u64 xcfg) { int rc = 0; u64 cfg; cfg = vas_get_hvwc_mmio_bar(gcid); /* * NOTE: Write the entire bar address to SCOM. VAS/NX will extract * the relevant (NX_P9_UMAC_VAS_MMIO_ADDR) bits. IOW, _don't_ * just write the bit field like: * * cfg = SETFIELD(NX_P9_DMA_VAS_MMIO_ADDR, 0ULL, cfg); */ rc = xscom_write(gcid, xcfg, cfg); if (rc) prerror("NX%d: ERROR: DMA VAS MMIO BAR, %d\n", gcid, rc); else prerror("NX%d: DMA VAS MMIO BAR, 0x%016lx, xcfg 0x%llx\n", gcid, (unsigned long)cfg, xcfg); return rc; }
/** * @brief Read a FSI register */ int32_t getfsi( SCOM_Trgt_t i_trgt, uint32_t i_addr, uint32_t * o_val ) { int32_t rc = SUCCESS; uint32_t fsi_addr = i_trgt.fsiBaseAddr | i_addr; /* setup the OPB command register */ /* only supporting 4-byte access */ uint64_t fsi_cmd = fsi_addr | 0x60000000; /* 011=Read Full Word */ fsi_cmd <<= 32; /* Command is in the upper word of the scom */ /* Write the OPB command register to trigger the read */ rc = xscom_write( OPB_REG_CMD, fsi_cmd ); if ( SUCCESS != rc ) { fsi_recovery(); /* Try to recover the engine. */ return rc; } /* Poll for complete and get the data back. */ rc = poll_for_complete( o_val ); return rc; }
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_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); }