]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
ram: k3-ddrss: Introduce ECC Functionality for full memory space
authorDave Gerlach <d-gerlach@ti.com>
Thu, 17 Mar 2022 17:03:43 +0000 (12:03 -0500)
committerTom Rini <trini@konsulko.com>
Mon, 4 Apr 2022 23:02:04 +0000 (19:02 -0400)
Introduce ECC Functionality for full memory space as implemented in the
DDRSS. The following is done to accomplish this:

 * Introduce a memory region "ss" to allow dt to provide DDRSS region,
   which is not the same as "ctl" which is the controller region.

 * Introduce a "ti,ecc-enable" flag which allows a memorycontroller
   instance to enable ecc.

 * Introduce functionality to properly program the DDRSS registers to
   enable ECC for the full DDR memory space if enabled with above flag.

 * Expose a k3_ddrss_ddr_fdt_fixup call to allow fixup of fdt blob to
   account from DDR memory that must be reserved for ECC operation.

Signed-off-by: Dave Gerlach <d-gerlach@ti.com>
drivers/ram/k3-ddrss/k3-ddrss.c
include/k3-ddrss.h [new file with mode: 0644]

index ffed426b9cca06fdecb53acfdec4fab76ccc5a43..2467f122a8292e5f13a20e1b2b807a1c3667de1e 100644 (file)
@@ -6,9 +6,12 @@
  */
 
 #include <common.h>
+#include <config.h>
 #include <clk.h>
+#include <div64.h>
 #include <dm.h>
 #include <dm/device_compat.h>
+#include <fdt_support.h>
 #include <ram.h>
 #include <hang.h>
 #include <log.h>
 #define DDRSS_V2A_R1_MAT_REG                   0x0020
 #define DDRSS_ECC_CTRL_REG                     0x0120
 
+#define DDRSS_ECC_CTRL_REG_ECC_EN              BIT(0)
+#define DDRSS_ECC_CTRL_REG_RMW_EN              BIT(1)
+#define DDRSS_ECC_CTRL_REG_ECC_CK              BIT(2)
+#define DDRSS_ECC_CTRL_REG_WR_ALLOC            BIT(4)
+
+#define DDRSS_ECC_R0_STR_ADDR_REG              0x0130
+#define DDRSS_ECC_R0_END_ADDR_REG              0x0134
+#define DDRSS_ECC_R1_STR_ADDR_REG              0x0138
+#define DDRSS_ECC_R1_END_ADDR_REG              0x013c
+#define DDRSS_ECC_R2_STR_ADDR_REG              0x0140
+#define DDRSS_ECC_R2_END_ADDR_REG              0x0144
+#define DDRSS_ECC_1B_ERR_CNT_REG               0x0150
+
 #define SINGLE_DDR_SUBSYSTEM   0x1
 #define MULTI_DDR_SUBSYSTEM    0x2
 
@@ -102,6 +118,13 @@ struct k3_msmc {
        enum emif_active active;
 };
 
+#define K3_DDRSS_MAX_ECC_REGIONS               3
+
+struct k3_ddrss_ecc_region {
+       u32 start;
+       u32 range;
+};
+
 struct k3_ddrss_desc {
        struct udevice *dev;
        void __iomem *ddrss_ss_cfg;
@@ -119,6 +142,9 @@ struct k3_ddrss_desc {
        lpddr4_obj *driverdt;
        lpddr4_config config;
        lpddr4_privatedata pd;
+       struct k3_ddrss_ecc_region ecc_regions[K3_DDRSS_MAX_ECC_REGIONS];
+       u64 ecc_reserved_space;
+       bool ti_ecc_enabled;
 };
 
 struct reginitdata {
@@ -329,6 +355,14 @@ static int k3_ddrss_ofdata_to_priv(struct udevice *dev)
        }
        ddrss->ddrss_ctrl_mmr = (void *)reg;
 
+       reg = dev_read_addr_name(dev, "ss_cfg");
+       if (reg == FDT_ADDR_T_NONE) {
+               dev_dbg(dev, "No reg property for SS Config region, but this is optional so continuing.\n");
+               ddrss->ddrss_ss_cfg = NULL;
+       } else {
+               ddrss->ddrss_ss_cfg = (void *)reg;
+       }
+
        ret = power_domain_get_by_index(dev, &ddrss->ddrcfg_pwrdmn, 0);
        if (ret) {
                dev_err(dev, "power_domain_get() failed: %d\n", ret);
@@ -372,6 +406,8 @@ static int k3_ddrss_ofdata_to_priv(struct udevice *dev)
        if (ret)
                dev_err(dev, "ddr fhs cnt not populated %d\n", ret);
 
+       ddrss->ti_ecc_enabled = dev_read_bool(dev, "ti,ecc-enable");
+
        return ret;
 }
 
@@ -513,6 +549,60 @@ void k3_lpddr4_start(struct k3_ddrss_desc *ddrss)
        }
 }
 
+static void k3_ddrss_set_ecc_range_r0(u32 base, u32 start_address, u32 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);
+}
+
+static void k3_ddrss_preload_ecc_mem_region(u32 *addr, u32 size, u32 word)
+{
+       int i;
+
+       printf("ECC is enabled, priming DDR which will take several seconds.\n");
+
+       for (i = 0; i < (size / 4); i++)
+               addr[i] = word;
+}
+
+static void k3_ddrss_lpddr4_ecc_calc_reserved_mem(struct k3_ddrss_desc *ddrss)
+{
+       fdtdec_setup_mem_size_base_lowest();
+
+       ddrss->ecc_reserved_space = gd->ram_size;
+       do_div(ddrss->ecc_reserved_space, 9);
+
+       /* Round to clean number */
+       ddrss->ecc_reserved_space = 1ull << (fls(ddrss->ecc_reserved_space));
+}
+
+static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss)
+{
+       u32 ecc_region_start = ddrss->ecc_regions[0].start;
+       u32 ecc_range = ddrss->ecc_regions[0].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 - gd->ram_base, ecc_range);
+
+       /* Enable ECC, RMW, WR_ALLOC */
+       writel(DDRSS_ECC_CTRL_REG_ECC_EN | DDRSS_ECC_CTRL_REG_RMW_EN |
+              DDRSS_ECC_CTRL_REG_WR_ALLOC, base + DDRSS_ECC_CTRL_REG);
+
+       /* Preload ECC Mem region with 0's */
+       k3_ddrss_preload_ecc_mem_region((u32 *)ecc_region_start, ecc_range,
+                                       0x00000000);
+
+       /* Clear Error Count Register */
+       writel(0x1, base + DDRSS_ECC_1B_ERR_CNT_REG);
+
+       /* Enable ECC Check */
+       val = readl(base + DDRSS_ECC_CTRL_REG);
+       val |= DDRSS_ECC_CTRL_REG_ECC_CK;
+       writel(val, base + DDRSS_ECC_CTRL_REG);
+}
+
 static int k3_ddrss_probe(struct udevice *dev)
 {
        int ret;
@@ -547,9 +637,52 @@ static int k3_ddrss_probe(struct udevice *dev)
 
        k3_lpddr4_start(ddrss);
 
+       if (ddrss->ti_ecc_enabled) {
+               if (!ddrss->ddrss_ss_cfg) {
+                       printf("%s: ss_cfg is required if ecc is enabled but not provided.",
+                              __func__);
+                       return -EINVAL;
+               }
+
+               k3_ddrss_lpddr4_ecc_calc_reserved_mem(ddrss);
+
+               /* Always configure one region that covers full DDR space */
+               ddrss->ecc_regions[0].start = gd->ram_base;
+               ddrss->ecc_regions[0].range = gd->ram_size - ddrss->ecc_reserved_space;
+               k3_ddrss_lpddr4_ecc_init(ddrss);
+       }
+
        return ret;
 }
 
+int k3_ddrss_ddr_fdt_fixup(struct udevice *dev, void *blob, struct bd_info *bd)
+{
+       struct k3_ddrss_desc *ddrss = dev_get_priv(dev);
+       u64 start[CONFIG_NR_DRAM_BANKS];
+       u64 size[CONFIG_NR_DRAM_BANKS];
+       int bank;
+
+       if (ddrss->ecc_reserved_space == 0)
+               return 0;
+
+       for (bank = CONFIG_NR_DRAM_BANKS - 1; bank >= 0; bank--) {
+               if (ddrss->ecc_reserved_space > bd->bi_dram[bank].size) {
+                       ddrss->ecc_reserved_space -= bd->bi_dram[bank].size;
+                       bd->bi_dram[bank].size = 0;
+               } else {
+                       bd->bi_dram[bank].size -= ddrss->ecc_reserved_space;
+                       break;
+               }
+       }
+
+       for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
+               start[bank] =  bd->bi_dram[bank].start;
+               size[bank] = bd->bi_dram[bank].size;
+       }
+
+       return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS);
+}
+
 static int k3_ddrss_get_info(struct udevice *dev, struct ram_info *info)
 {
        return 0;
diff --git a/include/k3-ddrss.h b/include/k3-ddrss.h
new file mode 100644 (file)
index 0000000..d7b3bf3
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Texas Instruments' K3 DDRSS Driver
+ *
+ * Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ */
+
+#ifndef _K3_DDRSS_
+#define _K3_DDRSS_
+
+struct udevice;
+
+int k3_ddrss_ddr_fdt_fixup(struct udevice *dev, void *blob, struct bd_info *bd);
+
+#endif