mirror of https://github.com/torvalds/linux.git
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:
parent
b01a731a4a
commit
ddb8a8a022
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
sdram_ctl = ddr_in32(pdata, FSL_MC_DDR_SDRAM_CFG);
|
||||||
if (!(sdram_ctl & DSC_ECC_EN)) {
|
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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue