> -----Original Message----- > From: Meng, Tingting <tingting.m...@altera.com> > Sent: Monday, April 21, 2025 1:15 PM > To: u-boot@lists.denx.de > Cc: Marek Vasut <ma...@denx.de>; Chee, Tien Fong > <tien.fong.c...@altera.com>; Meng, Tingting <tingting.m...@altera.com>; > Hea, Kok Kiang <kok.kiang....@altera.com>; Maniyam, Dinesh > <dinesh.mani...@altera.com>; Ng, Boon Khai <boon.khai...@altera.com>; > Yuslaimi, Alif Zakuan <alif.zakuan.yusla...@altera.com>; Rosdi, Danish > Ahmad <danish.ahmad.ro...@altera.com>; Zamri, Muhammad Hazim Izzat > <muhammad.hazim.izzat.za...@altera.com>; Lim, Jit Loon > <jit.loon....@altera.com> > Subject: [PATCH v1 4/5] ddr: altera: agilex5: LPDDRs in-line ECC support > > From: Tingting Meng <tingting.m...@altera.com> > > In-line ECC support was added for LPDDR by reserving the last one-eighth of > the memory space for ECC data. Full memory initialization using the BIST > MEM INIT mailbox command, based on address and size, is required to > correctly generate ECC data and enable proper ECC logic verification. > > Signed-off-by: Tingting Meng <tingting.m...@altera.com> > --- > .../arm/dts/socfpga_agilex5_socdk-u-boot.dtsi | 26 ++- > drivers/ddr/altera/iossm_mailbox.c | 161 ++++++++++++++++-- > drivers/ddr/altera/iossm_mailbox.h | 11 +- > drivers/ddr/altera/sdram_agilex5.c | 19 ++- > drivers/ddr/altera/sdram_soc64.c | 52 ++++-- > 5 files changed, 220 insertions(+), 49 deletions(-) > > diff --git a/arch/arm/dts/socfpga_agilex5_socdk-u-boot.dtsi > b/arch/arm/dts/socfpga_agilex5_socdk-u-boot.dtsi > index d7ab58267eb..8d7dc0945ab 100644 > --- a/arch/arm/dts/socfpga_agilex5_socdk-u-boot.dtsi > +++ b/arch/arm/dts/socfpga_agilex5_socdk-u-boot.dtsi > @@ -25,34 +25,44 @@ > /* > * Both Memory base address and size default info is retrieved from > HW setting. > * Reconfiguration / Overwrite these info can be done with examples > below. > - */ > - /* > + * > + * When LPDDR ECC is enabled, the last 1/8 of the memory region > must > + * be reserved for the Inline ECC buffer. > + * > * Example for memory size with 2GB: > * memory { > * reg = <0x0 0x80000000 0x0 0x80000000>; > * }; > - */ > - /* > + * > * Example for memory size with 8GB: > * memory { > * reg = <0x0 0x80000000 0x0 0x80000000>, > * <0x8 0x80000000 0x1 0x80000000>; > * }; > - */ > - /* > + * > * Example for memory size with 32GB: > * memory { > * reg = <0x0 0x80000000 0x0 0x80000000>, > * <0x8 0x80000000 0x7 0x80000000>; > * }; > - */ > - /* > + * > * Example for memory size with 512GB: > * memory { > * reg = <0x0 0x80000000 0x0 0x80000000>, > * <0x8 0x80000000 0x7 0x80000000>, > * <0x88 0x00000000 0x78 0x00000000>; > * }; > + * > + * Example for memory size with 2GB with LPDDR Inline ECC ON: > + * memory { > + * reg = <0x0 0x80000000 0x0 0x70000000>; > + * }; > + * > + * Example for memory size with 8GB with LPDDR Inline ECC ON: > + * memory { > + * reg = <0x0 0x80000000 0x0 0x80000000>, > + * <0x8 0x80000000 0x1 0x40000000>; > + * }; > */ > > chosen { > diff --git a/drivers/ddr/altera/iossm_mailbox.c > b/drivers/ddr/altera/iossm_mailbox.c > index db9435db657..26602858777 100644 > --- a/drivers/ddr/altera/iossm_mailbox.c > +++ b/drivers/ddr/altera/iossm_mailbox.c > @@ -10,6 +10,7 @@ > #include <asm/arch/base_addr_soc64.h> > #include <asm/io.h> > #include <linux/bitfield.h> > +#include <linux/sizes.h> > #include "iossm_mailbox.h" > > #define TIMEOUT_120000MS 120000 > @@ -87,6 +88,7 @@ > > /* offset info of ECC_ENABLE_INTF */ > #define INTF_ECC_ENABLE_TYPE_MASK GENMASK(1, 0) > +#define INTF_ECC_TYPE_MASK BIT(8) > > /* cmd opcode BIST_MEM_INIT_START, BIST performed on full memory > address range */ > #define BIST_FULL_MEM BIT(6) > @@ -107,6 +109,10 @@ > > #define MAX_ECC_ERR_INFO_COUNT 16 > > +#define BIST_START_ADDR_SPACE_MASK GENMASK(5, 0) > +#define BIST_START_ADDR_LOW_MASK GENMASK(31, 0) > +#define BIST_START_ADDR_HIGH_MASK GENMASK(37, 32) > + > #define IO96B_MB_REQ_SETUP(v, w, x, y, z) \ > usr_req.ip_type = v; \ > usr_req.ip_id = w; \ > @@ -512,7 +518,7 @@ 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; > + phys_size_t memory_size, total_memory_size = 0; > > u32 > mem_total_capacity_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = { > IOSSM_MEM_TOTAL_CAPACITY_INTF0_OFFSET, > @@ -526,8 +532,11 @@ int get_mem_width_info(struct io96b_info > *io96b_ctrl) > 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); > + io96b_ctrl->io96b[i].mb_ctrl.memory_size[j] = > + FIELD_GET(INTF_CAPACITY_GBITS_MASK, > mem_width_info) * SZ_1G / SZ_8; > + > + if (io96b_ctrl->io96b[i].mb_ctrl.memory_size[j] != 0) > + memory_size += io96b_ctrl- > >io96b[i].mb_ctrl.memory_size[j]; > } > > if (!memory_size) { > @@ -536,8 +545,6 @@ int get_mem_width_info(struct io96b_info > *io96b_ctrl) > goto err; > } > > - io96b_ctrl->io96b[i].size = memory_size; > - > total_memory_size = total_memory_size + memory_size; > } > > @@ -556,7 +563,7 @@ 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; > + bool ecc_status, ecc_status_set = false, inline_ecc = false; > > u32 ecc_enable_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = > { > IOSSM_ECC_ENABLE_INTF0_OFFSET, > @@ -565,6 +572,7 @@ int ecc_enable_status(struct io96b_info *io96b_ctrl) > > /* Initialize ECC status */ > io96b_ctrl->ecc_status = false; > + io96b_ctrl->inline_ecc = false; > > /* Get and ensure all memory interface(s) same ECC status */ > for (i = 0; i < io96b_ctrl->num_instance; i++) { @@ -572,15 +580,21 > @@ int ecc_enable_status(struct io96b_info *io96b_ctrl) > 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) > + ecc_status = > (FIELD_GET(INTF_ECC_ENABLE_TYPE_MASK, ecc_enable_intf) > == 0) ? false : true; > + inline_ecc = FIELD_GET(INTF_ECC_TYPE_MASK, > ecc_enable_intf); > + > + if (!ecc_status_set) { > + io96b_ctrl->ecc_status = ecc_status; > > - if (!ecc_stat_set) { > - io96b_ctrl->ecc_status = ecc_stat; > - ecc_stat_set = true; > + if (io96b_ctrl->ecc_status) > + io96b_ctrl->inline_ecc = inline_ecc; > + > + ecc_status_set = true; > } > > - if (ecc_stat != io96b_ctrl->ecc_status) { > + if (ecc_status != io96b_ctrl->ecc_status || > + (io96b_ctrl->ecc_status && inline_ecc != > +io96b_ctrl->inline_ecc)) { > printf("%s: Mismatch DDR ECC status on > IO96B_%d\n", __func__, i); > > ret = -EINVAL; > @@ -673,7 +687,7 @@ bool ecc_interrupt_status(struct io96b_info > *io96b_ctrl) > return ecc_error_flag; > } > > -int bist_mem_init_start(struct io96b_info *io96b_ctrl) > +int out_of_band_bist_mem_init_start(struct io96b_info *io96b_ctrl) > { > struct io96b_mb_req usr_req; > struct io96b_mb_resp usr_resp; > @@ -746,3 +760,126 @@ int bist_mem_init_start(struct io96b_info > *io96b_ctrl) > err: > return ret; > } > + > +int bist_mem_init_by_addr(struct io96b_info *io96b_ctrl, int inst_id, int > intf_id, > + phys_addr_t base_addr, phys_size_t size) { > + struct io96b_mb_req usr_req; > + struct io96b_mb_resp usr_resp; > + int n, ret = 0; > + bool bist_start, bist_success; > + u32 mem_exp, mem_init_status_intf, start; > + phys_size_t chunk_size; > + > + u32 mem_init_status_offset[MAX_MEM_INTERFACE_SUPPORTED] > = { > + IOSSM_MEM_INIT_STATUS_INTF0_OFFSET, > + IOSSM_MEM_INIT_STATUS_INTF1_OFFSET > + }; > + > + /* Check if size is a power of 2 */ > + if (size == 0 || (size & (size - 1)) != 0) { > + ret = -EINVAL; > + goto err; > + } > + > + mem_exp = 0; > + chunk_size = size; > + > + while (chunk_size >>= 1) > + mem_exp++; > + > + /* Start memory initialization BIST on the specified address range */ > + IO96B_MB_REQ_SETUP(io96b_ctrl- > >io96b[inst_id].mb_ctrl.ip_type[intf_id], > + io96b_ctrl->io96b[inst_id].mb_ctrl.ip_id[intf_id], > + CMD_TRIG_CONTROLLER_OP, > BIST_MEM_INIT_START, 0); > + > + /* CMD_PARAM_0 bit[5:0] = mem_exp */ > + /* CMD_PARAM_0 bit[6]: 0 - on the specified address range */ > + usr_req.cmd_param[0] = > FIELD_PREP(BIST_START_ADDR_SPACE_MASK, mem_exp); > + /* Extract address fields START_ADDR[31:0] */ > + usr_req.cmd_param[1] = > FIELD_GET(BIST_START_ADDR_LOW_MASK, base_addr); > + /* Extract address fields START_ADDR[37:32] */ > + usr_req.cmd_param[2] = > FIELD_GET(BIST_START_ADDR_HIGH_MASK, base_addr); > + /* Initialize memory to all zeros */ > + usr_req.cmd_param[3] = 0; > + > + bist_start = false; > + bist_success = false; > + > + /* Send request to DDR controller */ > + debug("%s:Initializing memory: Addr=0x%llx, Size=2^%u\n", > __func__, > + base_addr, mem_exp); > + ret = io96b_mb_req(io96b_ctrl->io96b[inst_id].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__, > + inst_id); > + 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[inst_id].io96b_csr_addr + > + mem_init_status_offset[intf_id]); > + > + 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__, inst_id); > + printf("%s: BIST_MEM_INIT_STATUS Error code > 0x%lx\n", > + __func__, > + > IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_statu > s)); > + > + ret = -ETIMEDOUT; > + goto err; > + } > + } > + > + debug("%s:DDR memory initializationat 0x%llx completed.\n", > __func__, > +base_addr); > + > +err: > + return ret; > +} > + > +int inline_ecc_bist_mem_init(struct io96b_info *io96b_ctrl) { > + int i, j, ret = 0; > + > + /* Memory initialization BIST performed on all memory interfaces */ > + for (i = 0; i < io96b_ctrl->num_instance; i++) { > + for (j = 0; j < io96b_ctrl- > >io96b[i].mb_ctrl.num_mem_interface; j++) { > + ret = bist_mem_init_by_addr(io96b_ctrl, i, j, 0, > + io96b_ctrl- > >io96b[i].mb_ctrl.memory_size[j]); > + if (ret) { > + printf("Error: Memory init failed at > Instance %d, Interface %d\n", > + i, j); > + goto err; > + } > + } > + } > + > +err: > + return ret; > +} > + > +int bist_mem_init_start(struct io96b_info *io96b_ctrl) { > + if (io96b_ctrl->inline_ecc) > + return inline_ecc_bist_mem_init(io96b_ctrl); > + else > + return out_of_band_bist_mem_init_start(io96b_ctrl); > +} > diff --git a/drivers/ddr/altera/iossm_mailbox.h > b/drivers/ddr/altera/iossm_mailbox.h > index 6f794781d30..02d1db28e20 100644 > --- a/drivers/ddr/altera/iossm_mailbox.h > +++ b/drivers/ddr/altera/iossm_mailbox.h > @@ -40,11 +40,13 @@ enum iossm_mailbox_cmd_opcode { > * @num_mem_interface: Number of memory interfaces instantiated > * @ip_type: IP type implemented on the IO96B > * @ip_instance_id: IP identifier for every IP instance implemented on > the IO96B > + * @memory_size[2]: Memory size for every IP instance implemented on > the IO96B > */ > struct io96b_mb_ctrl { > u32 num_mem_interface; > u32 ip_type[2]; > u32 ip_id[2]; > + phys_size_t memory_size[2]; > }; > > /* CMD_REQ Register Definition */ > @@ -53,6 +55,9 @@ struct io96b_mb_ctrl { > #define CMD_TYPE_MASK GENMASK(23, 16) > #define CMD_OPCODE_MASK GENMASK(15, 0) > > +/* Computes the Inline ECC data region size */ #define > +CALC_INLINE_ECC_HW_SIZE(size) (((size) * 7) / 8) > + > /* > * IOSSM mailbox request > * @ip_type: IP type for the specified memory interface > @@ -83,13 +88,11 @@ struct io96b_mb_resp { > /* > * IO96B instance specific information > * > - * @size: Memory size > * @io96b_csr_addr: IO96B instance CSR address > * @cal_status: IO96B instance calibration status > * @mb_ctrl: IOSSM mailbox required information > */ > struct io96b_instance { > - u16 size; > phys_addr_t io96b_csr_addr; > bool cal_status; > struct io96b_mb_ctrl mb_ctrl; > @@ -102,6 +105,7 @@ struct io96b_instance { > * @overall_cal_status: Overall calibration status for all IO96B instance(s) > * @ddr_type: DDR memory type > * @ecc_status: ECC enable status (false = disabled, true = > enabled) > + * @inline_ecc: Inline ECC or Out of Band ECC (false = Out of > Band ECC, true = Inline ECC) > * @overall_size: Total DDR memory size > * @io96b[]: IO96B instance specific information > * @ckgen_lock: IO96B GEN PLL lock (false = not locked, true = > locked) > @@ -115,7 +119,8 @@ struct io96b_info { > bool overall_cal_status; > const char *ddr_type; > bool ecc_status; > - u16 overall_size; > + bool inline_ecc; > + phys_size_t overall_size; > struct io96b_instance io96b[MAX_IO96B_SUPPORTED]; > bool ckgen_lock; > u8 num_port; > diff --git a/drivers/ddr/altera/sdram_agilex5.c > b/drivers/ddr/altera/sdram_agilex5.c > index 801a6bbab46..ee66c72157a 100644 > --- a/drivers/ddr/altera/sdram_agilex5.c > +++ b/drivers/ddr/altera/sdram_agilex5.c > @@ -291,7 +291,14 @@ int sdram_mmr_init_full(struct udevice *dev) > goto err; > } > > - hw_size = (phys_size_t)io96b_ctrl->overall_size * SZ_1G / SZ_8; > + ret = ecc_enable_status(io96b_ctrl); > + if (ret) { > + printf("DDR: Failed to get ECC enabled status\n"); > + > + goto err; > + } > + > + hw_size = io96b_ctrl->overall_size; > > /* Get bank configuration from devicetree */ > ret = fdtdec_decode_ram_size(gd->fdt_blob, NULL, 0, NULL, @@ - > 303,6 +310,9 @@ int sdram_mmr_init_full(struct udevice *dev) > goto err; > } > > + if (io96b_ctrl->inline_ecc) > + hw_size = CALC_INLINE_ECC_HW_SIZE(hw_size); > + > if (gd->ram_size > hw_size) { > printf("DDR: Warning: DRAM size from device tree (%lld MiB) > exceeds\n", > gd->ram_size >> 20); > @@ -355,13 +365,6 @@ int sdram_mmr_init_full(struct udevice *dev) > > printf("%s: %lld MiB\n", io96b_ctrl->ddr_type, gd->ram_size >> 20); > > - ret = ecc_enable_status(io96b_ctrl); > - if (ret) { > - printf("DDR: Failed to get ECC enabled status\n"); > - > - goto err; > - } > - > /* Is HPS cold or warm reset? If yes, Skip full memory initialization if > ECC > * enabled to preserve memory content > */ > diff --git a/drivers/ddr/altera/sdram_soc64.c > b/drivers/ddr/altera/sdram_soc64.c > index 75c6de1e4ec..27fbe80ed41 100644 > --- a/drivers/ddr/altera/sdram_soc64.c > +++ b/drivers/ddr/altera/sdram_soc64.c > @@ -185,35 +185,51 @@ void sdram_init_ecc_bits(struct bd_info *bd) void > sdram_size_check(struct bd_info *bd) { > phys_size_t total_ram_check = 0; > - phys_size_t ram_check = 0; > - phys_addr_t start = 0; > - phys_size_t size, remaining_size; > int bank; > > /* Sanity check ensure correct SDRAM size specified */ > debug("DDR: Running SDRAM size sanity check\n"); > > for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { > + phys_size_t ram_check = 0; > + phys_addr_t start = 0; > + phys_size_t remaining_size; > + > start = bd->bi_dram[bank].start; > remaining_size = bd->bi_dram[bank].size; > + debug("Checking bank %d: start=0x%llx, size=0x%llx\n", > + bank, start, remaining_size); > + > while (ram_check < bd->bi_dram[bank].size) { > - size = min((phys_addr_t)SZ_1G, > - (phys_addr_t)remaining_size); > - > - /* > - * Ensure the size is power of two, this is requirement > - * to run get_ram_size() / memory test > - */ > - if (size != 0 && ((size & (size - 1)) == 0)) { > - ram_check += get_ram_size((void *) > - (start + ram_check), size); > - remaining_size = bd->bi_dram[bank].size - > - ram_check; > - } else { > - puts("DDR: Memory test requires SDRAM > size "); > - puts("in power of two!\n"); > + phys_size_t size, test_size, detected_size; > + > + size = min((phys_addr_t)SZ_1G, > (phys_addr_t)remaining_size); > + > + if (size < SZ_8) { > + puts("Invalid size: Memory size required to > be multiple\n"); > + puts("of 64-Bit word!\n"); > hang(); > } > + > + /* Adjust size to the nearest power of two to support > get_ram_size() */ > + test_size = SZ_8; > + > + while (test_size * 2 <= size) > + test_size *= 2; > + > + debug("Testing memory at 0x%llx with size 0x%llx\n", > + start + ram_check, test_size); > + detected_size = get_ram_size((void *)(start + > ram_check), > +test_size); > + > + if (detected_size != test_size) { > + debug("Detected size 0x%llx doesn’t match > the test size 0x%llx!\n", > + detected_size, test_size); > + puts("Memory testing failed!\n"); > + hang(); > + } > + > + ram_check += detected_size; > + remaining_size = bd->bi_dram[bank].size - > ram_check; > } > > total_ram_check += ram_check; > -- > 2.25.1
Reviewed-by: Tien Fong Chee <tien.fong.c...@altera.com> Best regards, Tien Fong