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 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) 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); return pdata->little_endian ? ioread32(addr) : ioread32be(addr);
} }
static inline void ddr_out32(struct fsl_mc_pdata *pdata, unsigned int off, u32 value) 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) if (pdata->little_endian)
iowrite32(value, addr); iowrite32(value, addr);
@ -435,6 +447,9 @@ static void fsl_ddr_init_csrows(struct mem_ctl_info *mci)
case 0x05000000: case 0x05000000:
mtype = MEM_DDR4; mtype = MEM_DDR4;
break; break;
case 0x04000000:
mtype = MEM_LPDDR4;
break;
default: default:
mtype = MEM_UNKNOWN; mtype = MEM_UNKNOWN;
break; break;
@ -468,7 +483,9 @@ static void fsl_ddr_init_csrows(struct mem_ctl_info *mci)
dimm->grain = 8; dimm->grain = 8;
dimm->mtype = mtype; dimm->mtype = mtype;
dimm->dtype = DEV_UNKNOWN; 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->dtype = DEV_X32;
dimm->edac_mode = EDAC_SECDED; 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 edac_mc_layer layers[2];
struct fsl_mc_pdata *pdata; struct fsl_mc_pdata *pdata;
struct resource r; struct resource r;
u32 ecc_en_mask;
u32 sdram_ctl; u32 sdram_ctl;
int res; int res;
@ -507,6 +525,8 @@ int fsl_mc_err_probe(struct platform_device *op)
mci->ctl_name = pdata->name; mci->ctl_name = pdata->name;
mci->dev_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. * Get the endianness of DDR controller registers.
* Default is big endian. * Default is big endian.
@ -535,8 +555,23 @@ int fsl_mc_err_probe(struct platform_device *op)
goto err; goto err;
} }
sdram_ctl = ddr_in32(pdata, FSL_MC_DDR_SDRAM_CFG); if (pdata->flag == TYPE_IMX9) {
if (!(sdram_ctl & DSC_ECC_EN)) { 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 */ /* no ECC */
pr_warn("%s: No ECC DIMMs discovered\n", __func__); pr_warn("%s: No ECC DIMMs discovered\n", __func__);
res = -ENODEV; res = -ENODEV;
@ -547,7 +582,8 @@ int fsl_mc_err_probe(struct platform_device *op)
mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR | mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR |
MEM_FLAG_DDR2 | MEM_FLAG_RDDR2 | MEM_FLAG_DDR2 | MEM_FLAG_RDDR2 |
MEM_FLAG_DDR3 | MEM_FLAG_RDDR3 | 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_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_SECDED;
mci->mod_name = EDAC_MOD_STR; mci->mod_name = EDAC_MOD_STR;

View File

@ -39,6 +39,9 @@
#define FSL_MC_CAPTURE_EXT_ADDRESS 0x0e54 #define FSL_MC_CAPTURE_EXT_ADDRESS 0x0e54
#define FSL_MC_ERR_SBE 0x0e58 #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_MEM_EN 0x80000000
#define DSC_ECC_EN 0x20000000 #define DSC_ECC_EN 0x20000000
#define DSC_RD_EN 0x10000000 #define DSC_RD_EN 0x10000000
@ -46,6 +49,9 @@
#define DSC_DBW_32 0x00080000 #define DSC_DBW_32 0x00080000
#define DSC_DBW_64 0x00000000 #define DSC_DBW_64 0x00000000
#define ERR_ECC_EN 0x80000000
#define ERR_INLINE_ECC 0x40000000
#define DSC_SDTYPE_MASK 0x07000000 #define DSC_SDTYPE_MASK 0x07000000
#define DSC_X32_EN 0x00000020 #define DSC_X32_EN 0x00000020
@ -65,14 +71,18 @@
#define DDR_EDI_SBED 0x4 /* single-bit ECC error disable */ #define DDR_EDI_SBED 0x4 /* single-bit ECC error disable */
#define DDR_EDI_MBED 0x8 /* multi-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 { struct fsl_mc_pdata {
char *name; char *name;
int edac_idx; int edac_idx;
void __iomem *mc_vbase; void __iomem *mc_vbase;
void __iomem *inject_vbase;
int irq; int irq;
u32 orig_ddr_err_disable; u32 orig_ddr_err_disable;
u32 orig_ddr_err_sbe; u32 orig_ddr_err_sbe;
bool little_endian; bool little_endian;
unsigned long flag;
}; };
int fsl_mc_err_probe(struct platform_device *op); int fsl_mc_err_probe(struct platform_device *op);
void fsl_mc_err_remove(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[] = { static const struct of_device_id fsl_ddr_mc_err_of_match[] = {
{ .compatible = "fsl,qoriq-memory-controller", }, { .compatible = "fsl,qoriq-memory-controller", },
{ .compatible = "nxp,imx9-memory-controller", .data = (void *)TYPE_IMX9, },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, fsl_ddr_mc_err_of_match); MODULE_DEVICE_TABLE(of, fsl_ddr_mc_err_of_match);