K3 Inline ECC mechanism can support up to 3 regions of inline ECC, add this support for single controller.
Signed-off-by: Neha Malcom Francis <n-fran...@ti.com> --- drivers/ram/k3-ddrss/k3-ddrss.c | 125 +++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 42 deletions(-) diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index e00711a2235..ee123b3b9eb 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -176,7 +176,7 @@ struct k3_ddrss_desc { lpddr4_obj *driverdt; lpddr4_config config; lpddr4_privatedata pd; - struct k3_ddrss_ecc_region ecc_range; + struct k3_ddrss_ecc_region ecc_ranges[K3_DDRSS_MAX_ECC_REG]; struct k3_ddrss_ecc_region ecc_regions[K3_DDRSS_MAX_ECC_REG]; u64 ecc_reserved_space; u64 ddr_bank_base[CONFIG_NR_DRAM_BANKS]; @@ -576,10 +576,26 @@ void k3_lpddr4_start(struct k3_ddrss_desc *ddrss) } } -static void k3_ddrss_set_ecc_range_r0(u32 base, u64 start_address, u64 size) +static void k3_ddrss_set_ecc_range_rx(u32 x, u32 base, u64 start_address, u64 size) { - writel((start_address) >> 16, base + DDRSS_ECC_R0_STR_ADDR_REG); - writel((start_address + size - 1) >> 16, base + DDRSS_ECC_R0_END_ADDR_REG); + u32 start_reg, end_reg; + + switch (x) { + case 1: + start_reg = DDRSS_ECC_R1_STR_ADDR_REG; + end_reg = DDRSS_ECC_R1_END_ADDR_REG; + break; + case 2: + start_reg = DDRSS_ECC_R2_STR_ADDR_REG; + end_reg = DDRSS_ECC_R2_END_ADDR_REG; + break; + default: + start_reg = DDRSS_ECC_R0_STR_ADDR_REG; + end_reg = DDRSS_ECC_R0_END_ADDR_REG; + break; + } + writel((start_address) >> 16, base + start_reg); + writel((start_address + size - 1) >> 16, base + end_reg); } #define BIST_MODE_MEM_INIT 4 @@ -755,27 +771,40 @@ static void k3_ddrss_ddr_reg_init(struct k3_ddrss_desc *ddrss) writel(DDRSS_ECC_CTRL_REG_DEFAULT, ddrss->ddrss_ss_cfg + DDRSS_ECC_CTRL_REG); } +ofnode get_next_ecc_node(ofnode ecc) +{ + do { + ecc = ofnode_by_prop_value(ecc, "device_type", "ecc", 4); + } while (!ofnode_is_enabled(ecc)); + + return ecc; +} + static void k3_ddrss_ddr_inline_ecc_base_size_calc(struct k3_ddrss_ecc_region *range) { fdt_addr_t base; fdt_size_t size; - ofnode node1; - - node1 = ofnode_null(); - - do { - node1 = ofnode_by_prop_value(node1, "device_type", "ecc", 4); - } while (!ofnode_is_enabled(node1)); - - base = ofnode_get_addr_size(node1, "reg", &size); + ofnode ecc_node = ofnode_null(); - if (base == FDT_ADDR_T_NONE) { - debug("%s: Failed to get ECC node reg and size\n", __func__); + ecc_node = get_next_ecc_node(ecc_node); + if (!ofnode_valid(ecc_node)) { + debug("%s: No ECC node, enabling for entire region\n", __func__); range->start = 0; range->range = 0; - } else { + return; + } + + for (int i = 0; i < K3_DDRSS_MAX_ECC_REG; i++) { + base = ofnode_get_addr_size(ecc_node, "reg", &size); + if (base == FDT_ADDR_T_NONE) { + range->start = 0; + range->range = 0; + break; + } range->start = base; range->range = size; + range++; + ecc_node = get_next_ecc_node(ecc_node); } } @@ -797,13 +826,21 @@ static void k3_ddrss_lpddr4_ecc_calc_reserved_mem(struct k3_ddrss_desc *ddrss) static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) { - u64 ecc_region_start = ddrss->ecc_regions[0].start; - u64 ecc_range = ddrss->ecc_regions[0].range; + u64 ecc_region0_start = ddrss->ecc_regions[0].start; + u64 ecc_range0 = ddrss->ecc_regions[0].range; + u64 ecc_region1_start = ddrss->ecc_regions[1].start; + u64 ecc_range1 = ddrss->ecc_regions[1].range; + u64 ecc_region2_start = ddrss->ecc_regions[2].start; + u64 ecc_range2 = ddrss->ecc_regions[2].range; u32 base = (u32)ddrss->ddrss_ss_cfg; u32 val; /* Only Program region 0 which covers full ddr space */ - k3_ddrss_set_ecc_range_r0(base, ecc_region_start, ecc_range); + k3_ddrss_set_ecc_range_rx(0, base, ecc_region0_start, ecc_range0); + if (ecc_range1) + k3_ddrss_set_ecc_range_rx(1, base, ecc_region1_start, ecc_range1); + if (ecc_range2) + k3_ddrss_set_ecc_range_rx(2, base, ecc_region2_start, ecc_range2); /* Enable ECC, RMW, WR_ALLOC */ writel(DDRSS_ECC_CTRL_REG_ECC_EN | DDRSS_ECC_CTRL_REG_RMW_EN | @@ -828,11 +865,11 @@ static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) static int k3_ddrss_probe(struct udevice *dev) { - u64 end, bank0, bank1; + u64 end, bank0, bank1, bank0_size; int ret; struct k3_ddrss_desc *ddrss = dev_get_priv(dev); - __maybe_unused u32 inst, ddr_ram_size, ecc_res, st; - __maybe_unused struct k3_ddrss_ecc_region *range = &ddrss->ecc_range; + __maybe_unused u32 inst, ddr_ram_size, ecc_res; + __maybe_unused struct k3_ddrss_ecc_region *range = ddrss->ecc_ranges; __maybe_unused struct k3_msmc *msmc_parent = NULL; debug("%s(dev=%p)\n", __func__, dev); @@ -873,39 +910,43 @@ static int k3_ddrss_probe(struct udevice *dev) k3_ddrss_ddr_inline_ecc_base_size_calc(range); - end = ddrss->ecc_range.start + ddrss->ecc_range.range; - inst = ddrss->instance; - ddr_ram_size = ddrss->ddr_ram_size; - ecc_res = ddrss->ecc_reserved_space; bank0 = ddrss->ddr_bank_base[0]; bank1 = ddrss->ddr_bank_base[1]; + bank0_size = ddrss->ddr_bank_size[0]; if (!range->range) { /* Configure entire DDR space by default */ debug("%s: Defaulting to protecting entire DDR space using inline ECC\n", __func__); - ddrss->ecc_range.start = bank0; - ddrss->ecc_range.range = ddr_ram_size - ecc_res; + ddrss->ecc_ranges[0].start = bank0; + ddrss->ecc_ranges[0].range = ddr_ram_size - ecc_res; } else { - ddrss->ecc_range.start = range->start; - ddrss->ecc_range.range = range->range; + ddrss->ecc_ranges[0].start = range->start; + ddrss->ecc_ranges[0].range = range->range; } - st = ddrss->ecc_range.start; - if (!CONFIG_IS_ENABLED(K3_MULTI_DDR)) { - if (end > (ddr_ram_size - ecc_res)) - ddrss->ecc_regions[0].range = ddr_ram_size - ecc_res; - else - ddrss->ecc_regions[0].range = ddrss->ecc_range.range; + struct k3_ddrss_ecc_region *r = range; - /* Check in which bank we are */ - if (st > bank1) - ddrss->ecc_regions[0].start = st - bank1 + ddrss->ddr_bank_size[0]; - else - ddrss->ecc_regions[0].start = st - bank0; + for (int i = 0; (i < K3_DDRSS_MAX_ECC_REG) && (r->range != 0); i++, r++) { + end = r->start + r->range; + ddr_ram_size = ddrss->ddr_ram_size; + ecc_res = ddrss->ecc_reserved_space; + + if (end > (ddr_ram_size - ecc_res)) + ddrss->ecc_regions[i].range = ddr_ram_size - ecc_res; + else + ddrss->ecc_regions[i].range = r->range; + + /* Check in which bank we are */ + if (r->start > bank1) + ddrss->ecc_regions[i].start = r->start - bank1 + bank0_size; + else + ddrss->ecc_regions[i].start = r->start - bank0; + } } else { /* For multi-DDR, we rely on MSMC's calculation of regions for each DDR */ + inst = ddrss->instance; msmc_parent = kzalloc(sizeof(msmc_parent), GFP_KERNEL); if (!msmc_parent) return -ENOMEM; @@ -920,7 +961,7 @@ static int k3_ddrss_probe(struct udevice *dev) if (msmc_parent->R0[0].start < 0) { /* Configure entire DDR space by default */ - ddrss->ecc_regions[0].start = ddrss->ddr_bank_base[0]; + ddrss->ecc_regions[0].start = bank0; ddrss->ecc_regions[0].range = ddr_ram_size - ecc_res; } else { end = msmc_parent->R0[inst].start + msmc_parent->R0[inst].range; -- 2.34.1