]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
ram: k3-ddrss: Add support for MSMC calculation of DDR inline ECC regions
authorNeha Malcom Francis <n-francis@ti.com>
Tue, 12 Aug 2025 12:43:22 +0000 (18:13 +0530)
committerTom Rini <trini@konsulko.com>
Tue, 19 Aug 2025 17:26:20 +0000 (11:26 -0600)
Add support for calculation of the protected regions for each DDR in
multi-DDR systems. Since MSMC is the parent node of the individual DDRs
as well as responsible for their interleaving, it only makes sense for
MSMC to contain the logic for dividing the regions.

Signed-off-by: Neha Malcom Francis <n-francis@ti.com>
drivers/ram/k3-ddrss/k3-ddrss.c

index 075e664ff1a0f813e5bf647fb3a2ef8ec23a3002..c79380003144510800dfb77ccf809e387499f55b 100644 (file)
@@ -59,6 +59,7 @@
 
 #define SINGLE_DDR_SUBSYSTEM   0x1
 #define MULTI_DDR_SUBSYSTEM    0x2
+#define MAX_MULTI_DDR 4
 
 #define MULTI_DDR_CFG0  0x00114100
 #define MULTI_DDR_CFG1  0x00114104
@@ -82,6 +83,24 @@ enum intrlv_gran {
        GRAN_16GB
 };
 
+u64 gran_bytes[] = {
+       0x80,
+       0x200,
+       0x800,
+       0x1000,
+       0x4000,
+       0x8000,
+       0x80000,
+       0x40000000,
+       0x60000000,
+       0x80000000,
+       0xC0000000,
+       0x100000000,
+       0x180000000,
+       0x200000000,
+       0x400000000
+};
+
 enum intrlv_size {
        SIZE_0,
        SIZE_128MB,
@@ -135,6 +154,7 @@ struct k3_msmc {
        enum emif_config config;
        enum emif_active active;
        u32 num_ddr_controllers;
+       struct k3_ddrss_ecc_region R0[MAX_MULTI_DDR];
 };
 
 struct k3_ddrss_desc {
@@ -942,6 +962,80 @@ U_BOOT_DRIVER(k3_ddrss) = {
        .priv_auto              = sizeof(struct k3_ddrss_desc),
 };
 
+__maybe_unused static int k3_msmc_calculate_r0_regions(struct k3_msmc *msmc)
+{
+       u32 n1;
+       u32 size, ret = 0;
+       u32 gran = gran_bytes[msmc->gran];
+       u32 num_ddr = msmc->num_ddr_controllers;
+       struct k3_ddrss_ecc_region *range = NULL;
+       struct k3_ddrss_ecc_region R[num_ddr];
+
+       range = kzalloc(sizeof(range), GFP_KERNEL);
+       if (!range) {
+               ret = -ENOMEM;
+               return ret;
+       }
+
+       k3_ddrss_ddr_inline_ecc_base_size_calc(range);
+
+       if (!range->range) {
+               ret = -EINVAL;
+               goto range_err;
+       }
+
+       memset(R, 0, num_ddr);
+
+       /* Find the first controller in the range */
+       n1 = ((range->start / gran) % num_ddr);
+       size = range->range;
+
+       if (size < gran) {
+               R[n1].start = range->start - 0x80000000;
+               R[n1].range = range->start + range->range - 0x80000000;
+       } else {
+               u32 chunk_start_addr = range->start;
+               u32 chunk_size = range->range;
+
+               while (chunk_size > 0) {
+                       u32 edge;
+                       u32 end = range->start + range->range;
+
+                       if ((chunk_start_addr % gran) == 0)
+                               edge = chunk_start_addr + gran;
+                       else
+                               edge = ((chunk_start_addr + (gran - 1)) & (-gran));
+
+                       if (edge > end)
+                               break;
+
+                       if (R[n1].start == 0)
+                               R[n1].start = chunk_start_addr;
+
+                       R[n1].range = edge - R[n1].start;
+                       chunk_size = end - edge;
+                       chunk_start_addr = edge;
+
+                       if (n1 == (num_ddr - 1))
+                               n1 = 0;
+                       else
+                               n1++;
+               }
+
+               for (int i = 0; i < num_ddr; i++)
+                       R[i].start = (R[i].start - 0x80000000 - (gran * i)) / num_ddr;
+       }
+
+       for (int i = 0; i < num_ddr; i++) {
+               msmc->R0[i].start = R[i].start;
+               msmc->R0[i].range = R[i].range;
+       }
+
+range_err:
+       free(range);
+       return ret;
+}
+
 static int k3_msmc_set_config(struct k3_msmc *msmc)
 {
        u32 ddr_cfg0 = 0;
@@ -1021,6 +1115,18 @@ static int k3_msmc_probe(struct udevice *dev)
        }
        msmc->num_ddr_controllers = ret;
 
+       if (IS_ENABLED(CONFIG_K3_MULTI_DDR) && IS_ENABLED(CONFIG_K3_INLINE_ECC)) {
+               ret = k3_msmc_calculate_r0_regions(msmc);
+               if (ret) {
+                       /* Default to enabling inline ECC for entire DDR region */
+                       debug("%s: calculation of inline ECC regions failed, defaulting to entire region\n",
+                             __func__);
+
+                       /* Use first R0 entry as a flag to denote MSMC calculation failure */
+                       msmc->R0[0].start = -1;
+               }
+       }
+
        return 0;
 }