]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
EDAC/fsl_ddr: Add support for i.MX9 DDR controller
authorYe Li <ye.li@nxp.com>
Wed, 16 Oct 2024 20:31:13 +0000 (16:31 -0400)
committerBorislav Petkov (AMD) <bp@alien8.de>
Wed, 23 Oct 2024 14:53:55 +0000 (16:53 +0200)
Add support for the i.MX9 DDR controller, which has different register
offsets and some function changes compared to the existing fsl_ddr
controller. The ECC and error injection functions are almost the same,
so update and reuse the driver for i.MX9. Add a special type 'TYPE_IMX9'
specifically for the i.MX9 controller to distinguish the differences.

Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20241016-imx95_edac-v3-5-86ae6fc2756a@nxp.com
drivers/edac/fsl_ddr_edac.c
drivers/edac/fsl_ddr_edac.h
drivers/edac/layerscape_edac.c

index fe822cb9b562d8cf92d7389f6607915a385be69a..e4eaec0aa81d5b9c9956d9c9d6f20da1d8a263fe 100644 (file)
 
 static int edac_mc_idx;
 
+static inline void __iomem *ddr_reg_addr(struct fsl_mc_pdata *pdata, unsigned int off)
+{
+       if (pdata->flag == TYPE_IMX9 && off >= FSL_MC_DATA_ERR_INJECT_HI && off <= FSL_MC_ERR_SBE)
+               return pdata->inject_vbase + off - FSL_MC_DATA_ERR_INJECT_HI
+                      + IMX9_MC_DATA_ERR_INJECT_OFF;
+
+       if (pdata->flag == TYPE_IMX9 && off >= IMX9_MC_ERR_EN)
+               return pdata->inject_vbase + off - IMX9_MC_ERR_EN;
+
+       return pdata->mc_vbase + off;
+}
+
 static inline u32 ddr_in32(struct fsl_mc_pdata *pdata, unsigned int off)
 {
-       void __iomem *addr = pdata->mc_vbase + off;
+       void __iomem *addr = ddr_reg_addr(pdata, off);
 
        return pdata->little_endian ? ioread32(addr) : ioread32be(addr);
 }
 
 static inline void ddr_out32(struct fsl_mc_pdata *pdata, unsigned int off, u32 value)
 {
-       void __iomem *addr = pdata->mc_vbase + off;
+       void __iomem *addr = ddr_reg_addr(pdata, off);
 
        if (pdata->little_endian)
                iowrite32(value, addr);
@@ -435,6 +447,9 @@ static void fsl_ddr_init_csrows(struct mem_ctl_info *mci)
                case 0x05000000:
                        mtype = MEM_DDR4;
                        break;
+               case 0x04000000:
+                       mtype = MEM_LPDDR4;
+                       break;
                default:
                        mtype = MEM_UNKNOWN;
                        break;
@@ -468,7 +483,9 @@ static void fsl_ddr_init_csrows(struct mem_ctl_info *mci)
                dimm->grain = 8;
                dimm->mtype = mtype;
                dimm->dtype = DEV_UNKNOWN;
-               if (sdram_ctl & DSC_X32_EN)
+               if (pdata->flag == TYPE_IMX9)
+                       dimm->dtype = DEV_X16;
+               else if (sdram_ctl & DSC_X32_EN)
                        dimm->dtype = DEV_X32;
                dimm->edac_mode = EDAC_SECDED;
        }
@@ -480,6 +497,7 @@ int fsl_mc_err_probe(struct platform_device *op)
        struct edac_mc_layer layers[2];
        struct fsl_mc_pdata *pdata;
        struct resource r;
+       u32 ecc_en_mask;
        u32 sdram_ctl;
        int res;
 
@@ -507,6 +525,8 @@ int fsl_mc_err_probe(struct platform_device *op)
        mci->ctl_name = pdata->name;
        mci->dev_name = pdata->name;
 
+       pdata->flag = (unsigned long)device_get_match_data(&op->dev);
+
        /*
         * Get the endianness of DDR controller registers.
         * Default is big endian.
@@ -535,8 +555,23 @@ int fsl_mc_err_probe(struct platform_device *op)
                goto err;
        }
 
-       sdram_ctl = ddr_in32(pdata, FSL_MC_DDR_SDRAM_CFG);
-       if (!(sdram_ctl & DSC_ECC_EN)) {
+       if (pdata->flag == TYPE_IMX9) {
+               pdata->inject_vbase = devm_platform_ioremap_resource_byname(op, "inject");
+               if (IS_ERR(pdata->inject_vbase)) {
+                       res = -ENOMEM;
+                       goto err;
+               }
+       }
+
+       if (pdata->flag == TYPE_IMX9) {
+               sdram_ctl = ddr_in32(pdata, IMX9_MC_ERR_EN);
+               ecc_en_mask = ERR_ECC_EN | ERR_INLINE_ECC;
+       } else {
+               sdram_ctl = ddr_in32(pdata, FSL_MC_DDR_SDRAM_CFG);
+               ecc_en_mask = DSC_ECC_EN;
+       }
+
+       if ((sdram_ctl & ecc_en_mask) != ecc_en_mask) {
                /* no ECC */
                pr_warn("%s: No ECC DIMMs discovered\n", __func__);
                res = -ENODEV;
@@ -547,7 +582,8 @@ int fsl_mc_err_probe(struct platform_device *op)
        mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR |
                         MEM_FLAG_DDR2 | MEM_FLAG_RDDR2 |
                         MEM_FLAG_DDR3 | MEM_FLAG_RDDR3 |
-                        MEM_FLAG_DDR4 | MEM_FLAG_RDDR4;
+                        MEM_FLAG_DDR4 | MEM_FLAG_RDDR4 |
+                        MEM_FLAG_LPDDR4;
        mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
        mci->edac_cap = EDAC_FLAG_SECDED;
        mci->mod_name = EDAC_MOD_STR;
index de66f9822fba1ff1912a87595cb012abda84732a..73618f79e587f9911c5bc4f80d1d0059a8ac8460 100644 (file)
@@ -39,6 +39,9 @@
 #define FSL_MC_CAPTURE_EXT_ADDRESS     0x0e54
 #define FSL_MC_ERR_SBE         0x0e58
 
+#define IMX9_MC_ERR_EN                 0x1000
+#define IMX9_MC_DATA_ERR_INJECT_OFF    0x100
+
 #define DSC_MEM_EN     0x80000000
 #define DSC_ECC_EN     0x20000000
 #define DSC_RD_EN      0x10000000
@@ -46,6 +49,9 @@
 #define DSC_DBW_32     0x00080000
 #define DSC_DBW_64     0x00000000
 
+#define ERR_ECC_EN      0x80000000
+#define ERR_INLINE_ECC  0x40000000
+
 #define DSC_SDTYPE_MASK                0x07000000
 #define DSC_X32_EN     0x00000020
 
 #define        DDR_EDI_SBED    0x4     /* single-bit ECC error disable */
 #define        DDR_EDI_MBED    0x8     /* multi-bit ECC error disable */
 
+#define TYPE_IMX9      0x1     /* MC used by iMX9 having registers changed */
+
 struct fsl_mc_pdata {
        char *name;
        int edac_idx;
        void __iomem *mc_vbase;
+       void __iomem *inject_vbase;
        int irq;
        u32 orig_ddr_err_disable;
        u32 orig_ddr_err_sbe;
        bool little_endian;
+       unsigned long flag;
 };
 int fsl_mc_err_probe(struct platform_device *op);
 void fsl_mc_err_remove(struct platform_device *op);
index 0d42c1238908bdad547f3b14685e4e5dd43459c3..9a0c92ebbc3c4f812d38bf3a4ebd3e40443a383e 100644 (file)
@@ -21,6 +21,7 @@
 
 static const struct of_device_id fsl_ddr_mc_err_of_match[] = {
        { .compatible = "fsl,qoriq-memory-controller", },
+       { .compatible = "nxp,imx9-memory-controller", .data = (void *)TYPE_IMX9, },
        {},
 };
 MODULE_DEVICE_TABLE(of, fsl_ddr_mc_err_of_match);