Esempio n. 1
0
/* 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);
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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);
}
Esempio n. 4
0
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);
}
Esempio n. 5
0
File: fsi.c Progetto: code-hippo/occ
/**
 * @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;
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
File: fsi.c Progetto: code-hippo/occ
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 );
}
Esempio n. 8
0
/*
 * 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);
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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);
	}
Esempio n. 11
0
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);
}
Esempio n. 12
0
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;
}
Esempio n. 13
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;
}
Esempio n. 14
0
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;
}
Esempio n. 15
0
File: fsi.c Progetto: code-hippo/occ
/**
 * @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;
}
Esempio n. 16
0
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;
}
Esempio n. 17
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);
}