diff options
author | Tingting Meng <tingting.meng@altera.com> | 2025-02-21 21:49:41 +0800 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2025-02-25 10:54:01 -0600 |
commit | 04ea9147d5bdab1370ced118acf35db7ac9e281c (patch) | |
tree | b513cab2449c154da3ee1470256507cfd5ea78ea /drivers/ddr/altera/iossm_mailbox.c | |
parent | 034ebe3302200c033078455c5774ed739cd4f2ac (diff) |
ddr: altera: Add DDR driver for Agilex5 series
Adding DDR driver support for Agilex5 series.
Signed-off-by: Tingting Meng <tingting.meng@altera.com>
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; +} |