diff options
Diffstat (limited to 'drivers/ddr/altera/iossm_mailbox.c')
| -rw-r--r-- | drivers/ddr/altera/iossm_mailbox.c | 748 | 
1 files changed, 748 insertions, 0 deletions
| diff --git a/drivers/ddr/altera/iossm_mailbox.c b/drivers/ddr/altera/iossm_mailbox.c new file mode 100644 index 00000000000..db9435db657 --- /dev/null +++ b/drivers/ddr/altera/iossm_mailbox.c @@ -0,0 +1,748 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Altera Corporation <www.altera.com> + * + */ + +#include <hang.h> +#include <string.h> +#include <wait_bit.h> +#include <asm/arch/base_addr_soc64.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include "iossm_mailbox.h" + +#define TIMEOUT_120000MS			120000 +#define TIMEOUT_60000MS				60000 +#define TIMEOUT					TIMEOUT_120000MS +#define IOSSM_STATUS_CAL_SUCCESS		BIT(0) +#define IOSSM_STATUS_CAL_FAIL			BIT(1) +#define IOSSM_STATUS_CAL_BUSY			BIT(2) +#define IOSSM_STATUS_COMMAND_RESPONSE_READY	BIT(0) +#define IOSSM_CMD_RESPONSE_STATUS_OFFSET	0x45C +#define IOSSM_CMD_RESPONSE_DATA_0_OFFSET	0x458 +#define IOSSM_CMD_RESPONSE_DATA_1_OFFSET	0x454 +#define IOSSM_CMD_RESPONSE_DATA_2_OFFSET	0x450 +#define IOSSM_CMD_REQ_OFFSET			0x43C +#define IOSSM_CMD_PARAM_0_OFFSET		0x438 +#define IOSSM_CMD_PARAM_1_OFFSET		0x434 +#define IOSSM_CMD_PARAM_2_OFFSET		0x430 +#define IOSSM_CMD_PARAM_3_OFFSET		0x42C +#define IOSSM_CMD_PARAM_4_OFFSET		0x428 +#define IOSSM_CMD_PARAM_5_OFFSET		0x424 +#define IOSSM_CMD_PARAM_6_OFFSET		0x420 +#define IOSSM_CMD_RESPONSE_DATA_SHORT_MASK	GENMASK(31, 16) +#define IOSSM_CMD_RESPONSE_DATA_SHORT(n)	FIELD_GET(IOSSM_CMD_RESPONSE_DATA_SHORT_MASK, n) +#define IOSSM_STATUS_CMD_RESPONSE_ERROR_MASK	GENMASK(7, 5) +#define IOSSM_STATUS_CMD_RESPONSE_ERROR(n)	FIELD_GET(IOSSM_STATUS_CMD_RESPONSE_ERROR_MASK, n) +#define IOSSM_STATUS_GENERAL_ERROR_MASK		GENMASK(4, 1) +#define IOSSM_STATUS_GENERAL_ERROR(n)		FIELD_GET(IOSSM_STATUS_GENERAL_ERROR_MASK, n) + +/* Offset of Mailbox Read-only Registers  */ +#define IOSSM_MAILBOX_HEADER_OFFSET			0x0 +#define IOSSM_MEM_INTF_INFO_0_OFFSET			0X200 +#define IOSSM_MEM_INTF_INFO_1_OFFSET			0x280 +#define IOSSM_MEM_TECHNOLOGY_INTF0_OFFSET		0x210 +#define IOSSM_MEM_TECHNOLOGY_INTF1_OFFSET		0x290 +#define IOSSM_MEM_WIDTH_INFO_INTF0_OFFSET		0x230 +#define IOSSM_MEM_WIDTH_INFO_INTF1_OFFSET		0x2B0 +#define IOSSM_MEM_TOTAL_CAPACITY_INTF0_OFFSET		0x234 +#define IOSSM_MEM_TOTAL_CAPACITY_INTF1_OFFSET		0x2B4 +#define IOSSM_ECC_ENABLE_INTF0_OFFSET			0x240 +#define IOSSM_ECC_ENABLE_INTF1_OFFSET			0x2C0 +#define IOSSM_ECC_SCRUB_STATUS_INTF0_OFFSET		0x244 +#define IOSSM_ECC_SCRUB_STATUS_INTF1_OFFSET		0x2C4 +#define IOSSM_LP_MODE_INTF0_OFFSET			0x250 +#define IOSSM_LP_MODE_INTF1_OFFSET			0x2D0 +#define IOSSM_MEM_INIT_STATUS_INTF0_OFFSET		0x260 +#define IOSSM_MEM_INIT_STATUS_INTF1_OFFSET		0x2E0 +#define IOSSM_BIST_STATUS_INTF0_OFFSET			0x264 +#define IOSSM_BIST_STATUS_INTF1_OFFSET			0x2E4 +#define IOSSM_ECC_ERR_STATUS_OFFSET			0x300 +#define IOSSM_ECC_ERR_DATA_START_OFFSET			0x310 +#define IOSSM_STATUS_OFFSET				0x400 +#define IOSSM_STATUS_CAL_INTF0_OFFSET			0x404 +#define IOSSM_STATUS_CAL_INTF1_OFFSET			0x408 + +#define ECC_INTSTATUS_SERR SOCFPGA_SYSMGR_ADDRESS + 0x9C +#define ECC_INISTATUS_DERR SOCFPGA_SYSMGR_ADDRESS + 0xA0 +#define DDR_CSR_CLKGEN_LOCKED_IO96B0_MASK BIT(16) +#define DDR_CSR_CLKGEN_LOCKED_IO96B1_MASK BIT(17) + +/* offset info of GET_MEM_INTF_INFO */ +#define INTF_IP_TYPE_MASK		GENMASK(31, 29) +#define INTF_INSTANCE_ID_MASK		GENMASK(28, 24) + +/* offset info of GET_MEM_CAL_STATUS */ +#define INTF_UNUSED			0x0 +#define INTF_MEM_CAL_STATUS_SUCCESS	0x1 +#define INTF_MEM_CAL_STATUS_FAIL	0x2 +#define INTF_MEM_CAL_STATUS_ONGOING	0x4 + +/* offset info of  MEM_TECHNOLOGY_INTF */ +#define INTF_DDR_TYPE_MASK		GENMASK(2, 0) + +/* offset info of MEM_TOTAL_CAPACITY_INTF */ +#define INTF_CAPACITY_GBITS_MASK	GENMASK(7, 0) + +/* offset info of ECC_ENABLE_INTF */ +#define INTF_ECC_ENABLE_TYPE_MASK	GENMASK(1, 0) + +/* cmd opcode BIST_MEM_INIT_START, BIST performed on full memory address range */ +#define BIST_FULL_MEM			BIT(6) + +/* offset info of ECC_ENABLE_INTF */ +#define INTF_BIST_STATUS_MASK		BIT(0) + +/* offset info of ECC_ERR_STATUS */ +#define ECC_ERR_COUNTER_MASK		GENMASK(15, 0) + +/* offset info of ECC_ERR_DATA */ +#define ECC_ERR_IP_TYPE_MASK		GENMASK(24, 22) +#define ECC_ERR_INSTANCE_ID_MASK	GENMASK(21, 17) +#define ECC_ERR_SOURCE_ID_MASK		GENMASK(16, 10) +#define ECC_ERR_TYPE_MASK		GENMASK(9, 6) +#define ECC_ERR_ADDR_UPPER_MASK		GENMASK(5, 0) +#define ECC_ERR_ADDR_LOWER_MASK		GENMASK(31, 0) + +#define MAX_ECC_ERR_INFO_COUNT 16 + +#define IO96B_MB_REQ_SETUP(v, w, x, y, z) \ +	usr_req.ip_type = v; \ +	usr_req.ip_id = w; \ +	usr_req.usr_cmd_type = x; \ +	usr_req.usr_cmd_opcode = y; \ +	usr_req.cmd_param[0] = z; \ +	for (n = 1; n < NUM_CMD_PARAM; n++) \ +		usr_req.cmd_param[n] = 0 +#define MAX_RETRY_COUNT 3 +#define NUM_CMD_RESPONSE_DATA 3 + +#define IO96B0_PLL_A_MASK	BIT(0) +#define IO96B0_PLL_B_MASK	BIT(1) +#define IO96B1_PLL_A_MASK	BIT(2) +#define IO96B1_PLL_B_MASK	BIT(3) + +/* supported DDR type list */ +static const char *ddr_type_list[7] = { +		"DDR4", "DDR5", "DDR5_RDIMM", "LPDDR4", "LPDDR5", "QDRIV", "UNKNOWN" +}; + +/* Define an enumeration for ECC error types */ +enum ecc_error_type { +	SINGLE_BIT_ERROR = 0,			/* 0b0000 */ +	MULTIPLE_SINGLE_BIT_ERRORS = 1,		/* 0b0001 */ +	DOUBLE_BIT_ERROR = 2,			/* 0b0010 */ +	MULTIPLE_DOUBLE_BIT_ERRORS = 3,		/* 0b0011 */ +	SINGLE_BIT_ERROR_SCRUBBING = 8,		/* 0b1000 */ +	WRITE_LINK_SINGLE_BIT_ERROR = 9,	/* 0b1001 */ +	WRITE_LINK_DOUBLE_BIT_ERROR = 10,	/* 0b1010 */ +	READ_LINK_SINGLE_BIT_ERROR = 11,	/* 0b1011 */ +	READ_LINK_DOUBLE_BIT_ERROR = 12,	/* 0b1100 */ +	READ_MODIFY_WRITE_DOUBLE_BIT_ERROR = 13	/* 0b1101 */ +}; + +/* + * ecc error info + * + * @ip_type:		The IP type of the interface that produced the ECC interrupt. + * @instance_id:	The instance ID of the interface that produced the ECC interrupt. + * @ecc_err_source_id:	The source ID associated with the ECC event. + * @ecc_err_type:	The ECC error type of the ECC event. + * @ecc_err_addr_upper:	Upper 6 bits of the address of the read data that caused the ECC event. + * @ecc_err_addr_lower:	Lower 32 bits of the address of the read data that caused the ECC event. + */ +struct ecc_err_info { +	u32 ip_type; +	u32 instance_id; +	u32 source_id; +	enum ecc_error_type err_type; +	u32 addr_upper; +	u32 addr_lower; +}; + +static int is_ddr_csr_clkgen_locked(u8 io96b_pll) +{ +	int ret = 0; +	const char *pll_names[MAX_IO96B_SUPPORTED][2] = { +		{"io96b_0 clkgenA", "io96b_0 clkgenB"}, +		{"io96b_1 clkgenA", "io96b_1 clkgenB"} +	}; +	u32 masks[MAX_IO96B_SUPPORTED][2] = { +		{IO96B0_PLL_A_MASK, IO96B0_PLL_B_MASK}, +		{IO96B1_PLL_A_MASK, IO96B1_PLL_B_MASK} +	}; +	u32 lock_masks[MAX_IO96B_SUPPORTED] = { +		DDR_CSR_CLKGEN_LOCKED_IO96B0_MASK, +		DDR_CSR_CLKGEN_LOCKED_IO96B1_MASK +	}; + +	for (int i = 0; i < MAX_IO96B_SUPPORTED ; i++) { +		/* Check for PLL_A */ +		if (io96b_pll & masks[i][0]) { +			ret = wait_for_bit_le32((const void *)(ECC_INTSTATUS_SERR), lock_masks[i], +						true, TIMEOUT, false); + +			if (ret) { +				debug("%s: ddr csr %s locked is timeout\n", +				      __func__, pll_names[i][0]); +				goto err; +			} else { +				debug("%s: ddr csr %s is successfully locked\n", +				      __func__, pll_names[i][0]); +			} +		} + +		/* Check for PLL_B */ +		if (io96b_pll & masks[i][1]) { +			ret = wait_for_bit_le32((const void *)(ECC_INISTATUS_DERR), lock_masks[i], +						true, TIMEOUT, false); + +			if (ret) { +				debug("%s: ddr csr %s locked is timeout\n", +				      __func__, pll_names[i][1]); +				goto err; +			} else { +				debug("%s: ddr csr %s is successfully locked\n", +				      __func__, pll_names[i][1]); +			} +		} +	} + +err: +	return ret; +} + +/* + * Mailbox request function + * This function will send the request to IOSSM mailbox and wait for response return + * + * @io96b_csr_addr: CSR address for the target IO96B + * @req:            Structure contain command request for IOSSM mailbox command + * @resp_data_len:  User desire extra response data fields other than + *		    CMD_RESPONSE_DATA_SHORT field on CMD_RESPONSE_STATUS + * @resp:           Structure contain responses returned from the requested IOSSM + *		    mailbox command + */ +int io96b_mb_req(phys_addr_t io96b_csr_addr, struct io96b_mb_req req, +		 u32 resp_data_len, struct io96b_mb_resp *resp) +{ +	int i, ret; +	u32 cmd_req; + +	if (!resp) { +		ret = -EINVAL; +		goto err; +	} + +	/* Zero initialization for responses */ +	resp->cmd_resp_status = 0; + +	/* Ensure CMD_REQ is cleared before write any command request */ +	ret = wait_for_bit_le32((const void *)(io96b_csr_addr + IOSSM_CMD_REQ_OFFSET), +				GENMASK(31, 0), false, TIMEOUT, false); +	if (ret) { +		printf("%s: Timeout of waiting DDR mailbox ready to be functioned!\n", +		       __func__); +		goto err; +	} + +	/* Write CMD_PARAM_* */ +	for (i = 0; i < NUM_CMD_PARAM ; i++) { +		switch (i) { +		case 0: +			if (req.cmd_param[0]) +				writel(req.cmd_param[0], io96b_csr_addr + IOSSM_CMD_PARAM_0_OFFSET); +			break; +		case 1: +			if (req.cmd_param[1]) +				writel(req.cmd_param[1], io96b_csr_addr + IOSSM_CMD_PARAM_1_OFFSET); +			break; +		case 2: +			if (req.cmd_param[2]) +				writel(req.cmd_param[2], io96b_csr_addr + IOSSM_CMD_PARAM_2_OFFSET); +			break; +		case 3: +			if (req.cmd_param[3]) +				writel(req.cmd_param[3], io96b_csr_addr + IOSSM_CMD_PARAM_3_OFFSET); +			break; +		case 4: +			if (req.cmd_param[4]) +				writel(req.cmd_param[4], io96b_csr_addr + IOSSM_CMD_PARAM_4_OFFSET); +			break; +		case 5: +			if (req.cmd_param[5]) +				writel(req.cmd_param[5], io96b_csr_addr + IOSSM_CMD_PARAM_5_OFFSET); +			break; +		case 6: +			if (req.cmd_param[6]) +				writel(req.cmd_param[6], io96b_csr_addr + IOSSM_CMD_PARAM_6_OFFSET); +			break; +		} +	} + +	/* Write CMD_REQ (IP_TYPE, IP_INSTANCE_ID, CMD_TYPE and CMD_OPCODE) */ +	cmd_req = FIELD_PREP(CMD_TARGET_IP_TYPE_MASK, req.ip_type) | +		FIELD_PREP(CMD_TARGET_IP_INSTANCE_ID_MASK, req.ip_id) | +		FIELD_PREP(CMD_TYPE_MASK, req.usr_cmd_type) | +		FIELD_PREP(CMD_OPCODE_MASK, req.usr_cmd_opcode); +	writel(cmd_req, io96b_csr_addr + IOSSM_CMD_REQ_OFFSET); + +	debug("%s: Write 0x%x to IOSSM_CMD_REQ_OFFSET 0x%llx\n", __func__, cmd_req, +	      io96b_csr_addr + IOSSM_CMD_REQ_OFFSET); + +	/* Read CMD_RESPONSE_READY in CMD_RESPONSE_STATUS */ +	ret = wait_for_bit_le32((const void *)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET), +				IOSSM_STATUS_COMMAND_RESPONSE_READY, true, TIMEOUT, false); + +	/* read CMD_RESPONSE_STATUS */ +	resp->cmd_resp_status = readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + +	debug("%s: CMD_RESPONSE_STATUS 0x%llx: 0x%x\n", __func__, io96b_csr_addr + +	      IOSSM_CMD_RESPONSE_STATUS_OFFSET, resp->cmd_resp_status); + +	if (ret) { +		printf("%s: CMD_RESPONSE ERROR:\n", __func__); + +		printf("%s: STATUS_GENERAL_ERROR: 0x%lx\n", __func__, +		       IOSSM_STATUS_GENERAL_ERROR(resp->cmd_resp_status)); +		printf("%s: STATUS_CMD_RESPONSE_ERROR: 0x%lx\n", __func__, +		       IOSSM_STATUS_CMD_RESPONSE_ERROR(resp->cmd_resp_status)); +		goto err; +	} + +	/* read CMD_RESPONSE_DATA_* */ +	for (i = 0; i < resp_data_len; i++) { +		switch (i) { +		case 0: +			resp->cmd_resp_data[i] = +					readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET); + +			debug("%s: IOSSM_CMD_RESPONSE_DATA_0_OFFSET 0x%llx: 0x%x\n", __func__, +			      io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET, +			      resp->cmd_resp_data[i]); +			break; +		case 1: +			resp->cmd_resp_data[i] = +					readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET); + +			debug("%s: IOSSM_CMD_RESPONSE_DATA_1_OFFSET 0x%llx: 0x%x\n", __func__, +			      io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET, +			      resp->cmd_resp_data[i]); +			break; +		case 2: +			resp->cmd_resp_data[i] = +					readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET); + +			debug("%s: IOSSM_CMD_RESPONSE_DATA_2_OFFSET 0x%llx: 0x%x\n", __func__, +			      io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET, +			      resp->cmd_resp_data[i]); +			break; +		default: +			resp->cmd_resp_data[i] = 0; +			printf("%s: Invalid response data\n", __func__); +		} +	} + +	/* write CMD_RESPONSE_READY = 0 */ +	clrbits_le32((u32 *)(uintptr_t)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET), +		     IOSSM_STATUS_COMMAND_RESPONSE_READY); + +	debug("%s: After clear CMD_RESPONSE_READY bit: 0x%llx: 0x%x\n", __func__, +	      io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, +	      readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET)); + +err: +	return ret; +} + +/* + * Initial function to be called to set memory interface IP type and instance ID + * IP type and instance ID need to be determined before sending mailbox command + */ +void io96b_mb_init(struct io96b_info *io96b_ctrl) +{ +	int i, j; +	u32 mem_intf_info_0, mem_intf_info_1; + +	debug("%s: num_instance %d\n", __func__, io96b_ctrl->num_instance); + +	for (i = 0; i < io96b_ctrl->num_instance; i++) { +		debug("%s: get memory interface IO96B %d\n", __func__, i); +		io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface = 0; + +		mem_intf_info_0 = readl(io96b_ctrl->io96b[i].io96b_csr_addr + +					IOSSM_MEM_INTF_INFO_0_OFFSET); +		mem_intf_info_1 = readl(io96b_ctrl->io96b[i].io96b_csr_addr + +					IOSSM_MEM_INTF_INFO_1_OFFSET); + +		io96b_ctrl->io96b[i].mb_ctrl.ip_type[0] = FIELD_GET(INTF_IP_TYPE_MASK, +								    mem_intf_info_0); +		io96b_ctrl->io96b[i].mb_ctrl.ip_id[0] = FIELD_GET(INTF_INSTANCE_ID_MASK, +								  mem_intf_info_0); +		io96b_ctrl->io96b[i].mb_ctrl.ip_type[1] = FIELD_GET(INTF_IP_TYPE_MASK, +								    mem_intf_info_1); +		io96b_ctrl->io96b[i].mb_ctrl.ip_id[1] = FIELD_GET(INTF_INSTANCE_ID_MASK, +								  mem_intf_info_1); + +		for (j = 0; j < MAX_MEM_INTERFACE_SUPPORTED; j++) { +			if (io96b_ctrl->io96b[i].mb_ctrl.ip_type[j]) { +				io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface++; + +				debug("%s: IO96B %d mem_interface %d: ip_type_ret: 0x%x\n", +				      __func__, i, j, io96b_ctrl->io96b[i].mb_ctrl.ip_type[j]); +				debug("%s: IO96B %d mem_interface %d: instance_id_ret: 0x%x\n", +				      __func__, i, j, io96b_ctrl->io96b[i].mb_ctrl.ip_id[j]); +			} +		} + +		debug("%s: IO96B %d: num_mem_interface: 0x%x\n", __func__, i, +		      io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface); +	} +} + +int io96b_cal_status(phys_addr_t addr) +{ +	u32 cal_success, cal_fail; +	phys_addr_t status_addr = addr + IOSSM_STATUS_OFFSET; +	u32 start = get_timer(0); + +	do { +		if (get_timer(start) > TIMEOUT_60000MS) { +			printf("%s: SDRAM calibration for IO96B instance 0x%llx timeout!\n", +			       __func__, status_addr); +			hang(); +		} + +		udelay(1); +		schedule(); + +		/* Polling until getting any calibration result */ +		cal_success = readl(status_addr) & IOSSM_STATUS_CAL_SUCCESS; +		cal_fail = readl(status_addr) & IOSSM_STATUS_CAL_FAIL; +	} while (!cal_success && !cal_fail); + +	debug("%s: Calibration for IO96B instance 0x%llx done at %ld msec!\n", +	      __func__,  status_addr, get_timer(start)); + +	if (cal_success && !cal_fail) +		return 0; +	else +		return -EPERM; +} + +void init_mem_cal(struct io96b_info *io96b_ctrl) +{ +	int count, i, ret; + +	/* Initialize overall calibration status */ +	io96b_ctrl->overall_cal_status = false; + +	if (io96b_ctrl->ckgen_lock) { +		ret = is_ddr_csr_clkgen_locked(io96b_ctrl->io96b_pll); +		if (ret) { +			printf("%s: iossm IO96B ckgena_lock is not locked\n", __func__); +			hang(); +		} +	} + +	/* Check initial calibration status for the assigned IO96B */ +	count = 0; +	for (i = 0; i < io96b_ctrl->num_instance; i++) { +		ret = io96b_cal_status(io96b_ctrl->io96b[i].io96b_csr_addr); +		if (ret) { +			io96b_ctrl->io96b[i].cal_status = false; + +			printf("%s: Initial DDR calibration IO96B_%d failed %d\n", __func__, +			       i, ret); + +			hang(); +		} + +		io96b_ctrl->io96b[i].cal_status = true; + +		printf("%s: Initial DDR calibration IO96B_%d succeed\n", __func__, i); + +		count++; +	} + +	if (count == io96b_ctrl->num_instance) +		io96b_ctrl->overall_cal_status = true; +} + +int get_mem_technology(struct io96b_info *io96b_ctrl) +{ +	int i, j, ret = 0; +	u32 mem_technology_intf; +	u8 ddr_type_ret; + +	u32 mem_technology_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = { +		IOSSM_MEM_TECHNOLOGY_INTF0_OFFSET, +		IOSSM_MEM_TECHNOLOGY_INTF1_OFFSET +	}; + +	/* Initialize ddr type */ +	io96b_ctrl->ddr_type = ddr_type_list[6]; + +	/* Get and ensure all memory interface(s) same DDR type */ +	for (i = 0; i < io96b_ctrl->num_instance; i++) { +		for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { +			mem_technology_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr + +						    mem_technology_intf_offset[j]); + +			ddr_type_ret = FIELD_GET(INTF_DDR_TYPE_MASK, mem_technology_intf); + +			if (!strcmp(io96b_ctrl->ddr_type, "UNKNOWN")) +				io96b_ctrl->ddr_type = ddr_type_list[ddr_type_ret]; + +			if (ddr_type_list[ddr_type_ret] != io96b_ctrl->ddr_type) { +				printf("%s: Mismatch DDR type on IO96B_%d\n", __func__, i); + +				ret = -EINVAL; +				goto err; +			} +		} +	} + +err: +	return ret; +} + +int get_mem_width_info(struct io96b_info *io96b_ctrl) +{ +	int i, j, ret = 0; +	u32 mem_width_info; +	u16 memory_size, total_memory_size = 0; + +	u32 mem_total_capacity_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = { +		IOSSM_MEM_TOTAL_CAPACITY_INTF0_OFFSET, +		IOSSM_MEM_TOTAL_CAPACITY_INTF1_OFFSET +	}; + +	/* Get all memory interface(s) total memory size on all instance(s) */ +	for (i = 0; i < io96b_ctrl->num_instance; i++) { +		memory_size = 0; +		for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { +			mem_width_info = readl(io96b_ctrl->io96b[i].io96b_csr_addr + +					       mem_total_capacity_intf_offset[j]); + +			memory_size = memory_size + +					FIELD_GET(INTF_CAPACITY_GBITS_MASK, mem_width_info); +		} + +		if (!memory_size) { +			printf("%s: Failed to get valid memory size\n", __func__); +			ret = -EINVAL; +			goto err; +		} + +		io96b_ctrl->io96b[i].size = memory_size; + +		total_memory_size = total_memory_size + memory_size; +	} + +	if (!total_memory_size) { +		printf("%s: Failed to get valid memory size\n", __func__); +		ret = -EINVAL; +	} + +	io96b_ctrl->overall_size = total_memory_size; + +err: +	return ret; +} + +int ecc_enable_status(struct io96b_info *io96b_ctrl) +{ +	int i, j, ret = 0; +	u32 ecc_enable_intf; +	bool ecc_stat, ecc_stat_set = false; + +	u32 ecc_enable_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = { +		IOSSM_ECC_ENABLE_INTF0_OFFSET, +		IOSSM_ECC_ENABLE_INTF1_OFFSET +	}; + +	/* Initialize ECC status */ +	io96b_ctrl->ecc_status = false; + +	/* Get and ensure all memory interface(s) same ECC status */ +	for (i = 0; i < io96b_ctrl->num_instance; i++) { +		for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { +			ecc_enable_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr + +						ecc_enable_intf_offset[j]); + +			ecc_stat = (FIELD_GET(INTF_ECC_ENABLE_TYPE_MASK, ecc_enable_intf) +				    == 0) ? false : true; + +			if (!ecc_stat_set) { +				io96b_ctrl->ecc_status = ecc_stat; +				ecc_stat_set = true; +			} + +			if (ecc_stat != io96b_ctrl->ecc_status) { +				printf("%s: Mismatch DDR ECC status on IO96B_%d\n", __func__, i); + +				ret = -EINVAL; +				goto err; +			} +		} +	} + +	debug("%s: ECC enable status: %d\n", __func__, io96b_ctrl->ecc_status); + +err: +	return ret; +} + +bool is_double_bit_error(enum ecc_error_type err_type) +{ +	switch (err_type) { +	case DOUBLE_BIT_ERROR: +	case MULTIPLE_DOUBLE_BIT_ERRORS: +	case WRITE_LINK_DOUBLE_BIT_ERROR: +	case READ_LINK_DOUBLE_BIT_ERROR: +	case READ_MODIFY_WRITE_DOUBLE_BIT_ERROR: +		return true; + +	default: +		return false; +	} +} + +bool ecc_interrupt_status(struct io96b_info *io96b_ctrl) +{ +	int i, j; +	u32 ecc_err_status; +	u16 ecc_err_counter; +	bool ecc_error_flag = false; + +	/* Get ECC double-bit error status */ +	for (i = 0; i < io96b_ctrl->num_instance; i++) { +		ecc_err_status = readl(io96b_ctrl->io96b[i].io96b_csr_addr + +					IOSSM_ECC_ERR_STATUS_OFFSET); +		ecc_err_counter = FIELD_GET(ECC_ERR_COUNTER_MASK, ecc_err_status); +		debug("%s: ECC error number detected on IO96B_%d: %d\n", +		      __func__, i, ecc_err_counter); + +		if (ecc_err_counter != 0) { +			phys_addr_t address; +			u32 ecc_err_data; +			struct ecc_err_info err_info; + +			address = io96b_ctrl->io96b[i].io96b_csr_addr + +				    IOSSM_ECC_ERR_DATA_START_OFFSET; + +			for (j = 0; j < ecc_err_counter && j < MAX_ECC_ERR_INFO_COUNT; j++) { +				ecc_err_data = readl(address); +				err_info.err_type = FIELD_GET(ECC_ERR_TYPE_MASK, +							      ecc_err_data); +				err_info.ip_type = FIELD_GET(ECC_ERR_IP_TYPE_MASK, +							     ecc_err_data); +				err_info.instance_id = FIELD_GET(ECC_ERR_INSTANCE_ID_MASK, +								 ecc_err_data); +				err_info.source_id = FIELD_GET(ECC_ERR_SOURCE_ID_MASK, +							       ecc_err_data); +				err_info.addr_upper = FIELD_GET(ECC_ERR_ADDR_UPPER_MASK, +								ecc_err_data); +				err_info.addr_lower = readl(address + sizeof(u32)); + +				debug("%s: ECC double-bit error detected on IO96B_%d:\n", +				      __func__, i); +				debug("- error info address :0x%llx\n", address); +				debug("- error ip type: %d\n", err_info.ip_type); +				debug("- error instance id: %d\n", err_info.instance_id); +				debug("- error source id: %d\n", err_info.source_id); +				debug("- error type: %d\n", err_info.err_type); +				debug("- error address upper: 0x%x\n", err_info.addr_upper); +				debug("- error address lower: 0x%x\n", err_info.addr_lower); + +				if (is_double_bit_error(err_info.err_type)) { +					if (!ecc_error_flag) +						ecc_error_flag = true; +				} + +				address += sizeof(u32) * 2; +			} +		} +	} + +	if (ecc_error_flag) +		printf("\n%s: ECC double-bit error detected!\n", __func__); + +	return ecc_error_flag; +} + +int bist_mem_init_start(struct io96b_info *io96b_ctrl) +{ +	struct io96b_mb_req usr_req; +	struct io96b_mb_resp usr_resp; +	int i, j, n, ret = 0; +	bool bist_start, bist_success; +	u32 mem_init_status_intf, start; + +	u32 mem_init_status_offset[MAX_MEM_INTERFACE_SUPPORTED] = { +		IOSSM_MEM_INIT_STATUS_INTF0_OFFSET, +		IOSSM_MEM_INIT_STATUS_INTF1_OFFSET +	}; + +	/* Full memory initialization BIST performed on all memory interface(s) */ +	for (i = 0; i < io96b_ctrl->num_instance; i++) { +		for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { +			bist_start = false; +			bist_success = false; + +			/* Start memory initialization BIST on full memory address */ +			IO96B_MB_REQ_SETUP(io96b_ctrl->io96b[i].mb_ctrl.ip_type[j], +					   io96b_ctrl->io96b[i].mb_ctrl.ip_id[j], +					   CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_START, +					   BIST_FULL_MEM); + +			ret = io96b_mb_req(io96b_ctrl->io96b[i].io96b_csr_addr, +					   usr_req, 0, &usr_resp); +			if (ret) +				goto err; + +			bist_start = IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) +				& BIT(0); + +			if (!bist_start) { +				printf("%s: Failed to initialize memory on IO96B_%d\n", __func__, +				       i); +				printf("%s: BIST_MEM_INIT_START Error code 0x%lx\n", __func__, +				       IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status)); + +				ret = -EINVAL; +				goto err; +			} + +			/* Polling for the initiated memory initialization BIST status */ +			start = get_timer(0); +			while (!bist_success) { +				udelay(1); + +				mem_init_status_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr + +							     mem_init_status_offset[j]); + +				bist_success = FIELD_GET(INTF_BIST_STATUS_MASK, +							 mem_init_status_intf); + +				if (!bist_success && (get_timer(start) > TIMEOUT)) { +					printf("%s: Timeout initialize memory on IO96B_%d\n", +					       __func__, i); +					printf("%s: BIST_MEM_INIT_STATUS Error code 0x%lx\n", +					       __func__, +					IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status)); + +					ret = -ETIMEDOUT; +					goto err; +				} +			} +		} + +		debug("%s: Memory initialized successfully on IO96B_%d\n", __func__, i); +	} + +err: +	return ret; +} | 
