Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Ye Li and committed by
Borislav Petkov (AMD)
ddb8a8a0 b01a731a

+53 -6
+42 -6
drivers/edac/fsl_ddr_edac.c
··· 31 31 32 32 static int edac_mc_idx; 33 33 34 + static inline void __iomem *ddr_reg_addr(struct fsl_mc_pdata *pdata, unsigned int off) 35 + { 36 + if (pdata->flag == TYPE_IMX9 && off >= FSL_MC_DATA_ERR_INJECT_HI && off <= FSL_MC_ERR_SBE) 37 + return pdata->inject_vbase + off - FSL_MC_DATA_ERR_INJECT_HI 38 + + IMX9_MC_DATA_ERR_INJECT_OFF; 39 + 40 + if (pdata->flag == TYPE_IMX9 && off >= IMX9_MC_ERR_EN) 41 + return pdata->inject_vbase + off - IMX9_MC_ERR_EN; 42 + 43 + return pdata->mc_vbase + off; 44 + } 45 + 34 46 static inline u32 ddr_in32(struct fsl_mc_pdata *pdata, unsigned int off) 35 47 { 36 - void __iomem *addr = pdata->mc_vbase + off; 48 + void __iomem *addr = ddr_reg_addr(pdata, off); 37 49 38 50 return pdata->little_endian ? ioread32(addr) : ioread32be(addr); 39 51 } 40 52 41 53 static inline void ddr_out32(struct fsl_mc_pdata *pdata, unsigned int off, u32 value) 42 54 { 43 - void __iomem *addr = pdata->mc_vbase + off; 55 + void __iomem *addr = ddr_reg_addr(pdata, off); 44 56 45 57 if (pdata->little_endian) 46 58 iowrite32(value, addr); ··· 447 435 case 0x05000000: 448 436 mtype = MEM_DDR4; 449 437 break; 438 + case 0x04000000: 439 + mtype = MEM_LPDDR4; 440 + break; 450 441 default: 451 442 mtype = MEM_UNKNOWN; 452 443 break; ··· 483 468 dimm->grain = 8; 484 469 dimm->mtype = mtype; 485 470 dimm->dtype = DEV_UNKNOWN; 486 - if (sdram_ctl & DSC_X32_EN) 471 + if (pdata->flag == TYPE_IMX9) 472 + dimm->dtype = DEV_X16; 473 + else if (sdram_ctl & DSC_X32_EN) 487 474 dimm->dtype = DEV_X32; 488 475 dimm->edac_mode = EDAC_SECDED; 489 476 } ··· 497 480 struct edac_mc_layer layers[2]; 498 481 struct fsl_mc_pdata *pdata; 499 482 struct resource r; 483 + u32 ecc_en_mask; 500 484 u32 sdram_ctl; 501 485 int res; 502 486 ··· 524 506 dev_set_drvdata(mci->pdev, mci); 525 507 mci->ctl_name = pdata->name; 526 508 mci->dev_name = pdata->name; 509 + 510 + pdata->flag = (unsigned long)device_get_match_data(&op->dev); 527 511 528 512 /* 529 513 * Get the endianness of DDR controller registers. ··· 555 535 goto err; 556 536 } 557 537 558 - sdram_ctl = ddr_in32(pdata, FSL_MC_DDR_SDRAM_CFG); 559 - if (!(sdram_ctl & DSC_ECC_EN)) { 538 + if (pdata->flag == TYPE_IMX9) { 539 + pdata->inject_vbase = devm_platform_ioremap_resource_byname(op, "inject"); 540 + if (IS_ERR(pdata->inject_vbase)) { 541 + res = -ENOMEM; 542 + goto err; 543 + } 544 + } 545 + 546 + if (pdata->flag == TYPE_IMX9) { 547 + sdram_ctl = ddr_in32(pdata, IMX9_MC_ERR_EN); 548 + ecc_en_mask = ERR_ECC_EN | ERR_INLINE_ECC; 549 + } else { 550 + sdram_ctl = ddr_in32(pdata, FSL_MC_DDR_SDRAM_CFG); 551 + ecc_en_mask = DSC_ECC_EN; 552 + } 553 + 554 + if ((sdram_ctl & ecc_en_mask) != ecc_en_mask) { 560 555 /* no ECC */ 561 556 pr_warn("%s: No ECC DIMMs discovered\n", __func__); 562 557 res = -ENODEV; ··· 582 547 mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR | 583 548 MEM_FLAG_DDR2 | MEM_FLAG_RDDR2 | 584 549 MEM_FLAG_DDR3 | MEM_FLAG_RDDR3 | 585 - MEM_FLAG_DDR4 | MEM_FLAG_RDDR4; 550 + MEM_FLAG_DDR4 | MEM_FLAG_RDDR4 | 551 + MEM_FLAG_LPDDR4; 586 552 mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; 587 553 mci->edac_cap = EDAC_FLAG_SECDED; 588 554 mci->mod_name = EDAC_MOD_STR;
+10
drivers/edac/fsl_ddr_edac.h
··· 39 39 #define FSL_MC_CAPTURE_EXT_ADDRESS 0x0e54 40 40 #define FSL_MC_ERR_SBE 0x0e58 41 41 42 + #define IMX9_MC_ERR_EN 0x1000 43 + #define IMX9_MC_DATA_ERR_INJECT_OFF 0x100 44 + 42 45 #define DSC_MEM_EN 0x80000000 43 46 #define DSC_ECC_EN 0x20000000 44 47 #define DSC_RD_EN 0x10000000 45 48 #define DSC_DBW_MASK 0x00180000 46 49 #define DSC_DBW_32 0x00080000 47 50 #define DSC_DBW_64 0x00000000 51 + 52 + #define ERR_ECC_EN 0x80000000 53 + #define ERR_INLINE_ECC 0x40000000 48 54 49 55 #define DSC_SDTYPE_MASK 0x07000000 50 56 #define DSC_X32_EN 0x00000020 ··· 71 65 #define DDR_EDI_SBED 0x4 /* single-bit ECC error disable */ 72 66 #define DDR_EDI_MBED 0x8 /* multi-bit ECC error disable */ 73 67 68 + #define TYPE_IMX9 0x1 /* MC used by iMX9 having registers changed */ 69 + 74 70 struct fsl_mc_pdata { 75 71 char *name; 76 72 int edac_idx; 77 73 void __iomem *mc_vbase; 74 + void __iomem *inject_vbase; 78 75 int irq; 79 76 u32 orig_ddr_err_disable; 80 77 u32 orig_ddr_err_sbe; 81 78 bool little_endian; 79 + unsigned long flag; 82 80 }; 83 81 int fsl_mc_err_probe(struct platform_device *op); 84 82 void fsl_mc_err_remove(struct platform_device *op);
+1
drivers/edac/layerscape_edac.c
··· 21 21 22 22 static const struct of_device_id fsl_ddr_mc_err_of_match[] = { 23 23 { .compatible = "fsl,qoriq-memory-controller", }, 24 + { .compatible = "nxp,imx9-memory-controller", .data = (void *)TYPE_IMX9, }, 24 25 {}, 25 26 }; 26 27 MODULE_DEVICE_TABLE(of, fsl_ddr_mc_err_of_match);