EDAC/fsl_ddr: Add support for i.MX9 DDR controller

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
This commit is contained in:
Ye Li 2024-10-16 16:31:13 -04:00 committed by Borislav Petkov (AMD)
parent b01a731a4a
commit ddb8a8a022
3 changed files with 53 additions and 6 deletions

View File

@ -31,16 +31,28 @@
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;

View 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
@ -65,14 +71,18 @@
#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);

View 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);