mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 16:52:18 +00:00
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 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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user