mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-28 07:31:15 +00:00
drivers: ddr: imx8mp: Add inline ECC feature support
the DRAM Controller in i.MX8MP will support a feature called "Inline ECC". This is supported for all 3 supported DRAM technologies (LPDDR4, DDR4 and DDR3L). When this feature is enabled by software, the DRAM Controller reserves 12.5% of DRAM capacity for ECC information, and presents only the non-ECC portion (lower 87.5% of the installed capacity of DRAM) to the rest of the SoC. The DRAM memory can be divided into 8 regions so that if a use case only requires ECC protection on a subset of memory, then only that subset of memory need support inline ECC. If this occurs, then there is no performance penalty accessing the non-ECC-protected memory (no need to access ECC for this portion of the memory map). This is all configured with the DRAM Controller. Signed-off-by: Sherry Sun <sherry.sun@nxp.com> Signed-off-by: Peng Fan <peng.fan@nxp.com>
This commit is contained in:
parent
1eb325af16
commit
f3acb02386
3 changed files with 86 additions and 0 deletions
|
@ -529,6 +529,8 @@ enum msg_response {
|
|||
#define DDRC_SBRWDATA0(X) (DDRC_IPS_BASE_ADDR(X) + 0xf2c)
|
||||
#define DDRC_SBRWDATA1(X) (DDRC_IPS_BASE_ADDR(X) + 0xf30)
|
||||
#define DDRC_PDCH(X) (DDRC_IPS_BASE_ADDR(X) + 0xf34)
|
||||
#define DDRC_SBRSTART0(X) (DDRC_IPS_BASE_ADDR(X) + 0xf38)
|
||||
#define DDRC_SBRRANGE0(X) (DDRC_IPS_BASE_ADDR(X) + 0xf40)
|
||||
|
||||
#define DDRC_FREQ1_DERATEEN(X) (DDRC_IPS_BASE_ADDR(X) + 0x2020)
|
||||
#define DDRC_FREQ1_DERATEINT(X) (DDRC_IPS_BASE_ADDR(X) + 0x2024)
|
||||
|
@ -708,6 +710,11 @@ int ddr_cfg_phy(struct dram_timing_info *timing_info);
|
|||
void load_lpddr4_phy_pie(void);
|
||||
void ddrphy_trained_csr_save(struct dram_cfg_param *param, unsigned int num);
|
||||
void dram_config_save(struct dram_timing_info *info, unsigned long base);
|
||||
void board_dram_ecc_scrub(void);
|
||||
void ddrc_inline_ecc_scrub(unsigned int start_address,
|
||||
unsigned int range_address);
|
||||
void ddrc_inline_ecc_scrub_end(unsigned int start_address,
|
||||
unsigned int range_address);
|
||||
|
||||
/* utils function for ddr phy training */
|
||||
int wait_ddrphy_training_complete(void);
|
||||
|
|
|
@ -29,4 +29,11 @@ config SAVED_DRAM_TIMING_BASE
|
|||
info into memory for low power use. OCRAM_S is used for this
|
||||
purpose on i.MX8MM.
|
||||
default 0x180000
|
||||
|
||||
config IMX8M_DRAM_INLINE_ECC
|
||||
bool "imx8mp inline ECC"
|
||||
depends on IMX8MP && IMX8M_LPDDR4
|
||||
help
|
||||
Select this config if you want to use inline ecc feature for
|
||||
imx8mp-evk board.
|
||||
endmenu
|
||||
|
|
|
@ -21,6 +21,76 @@ void ddr_cfg_umctl2(struct dram_cfg_param *ddrc_cfg, int num)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IMX8M_DRAM_INLINE_ECC
|
||||
void ddrc_inline_ecc_scrub(unsigned int start_address,
|
||||
unsigned int range_address)
|
||||
{
|
||||
unsigned int tmp;
|
||||
|
||||
/* Step1: Enable quasi-dynamic programming */
|
||||
reg32_write(DDRC_SWCTL(0), 0x00000000);
|
||||
/* Step2: Set ECCCFG1.ecc_parity_region_lock to 1 */
|
||||
reg32setbit(DDRC_ECCCFG1(0), 0x4);
|
||||
/* Step3: Block the AXI ports from taking the transaction */
|
||||
reg32_write(DDRC_PCTRL_0(0), 0x0);
|
||||
/* Step4: Set scrub start address */
|
||||
reg32_write(DDRC_SBRSTART0(0), start_address);
|
||||
/* Step5: Set scrub range address */
|
||||
reg32_write(DDRC_SBRRANGE0(0), range_address);
|
||||
/* Step6: Set scrub_mode to write */
|
||||
reg32_write(DDRC_SBRCTL(0), 0x00000014);
|
||||
/* Step7: Set the desired pattern through SBRWDATA0 registers */
|
||||
reg32_write(DDRC_SBRWDATA0(0), 0x55aa55aa);
|
||||
/* Step8: Enable the SBR by programming SBRCTL.scrub_en=1 */
|
||||
reg32setbit(DDRC_SBRCTL(0), 0x0);
|
||||
/* Step9: Poll SBRSTAT.scrub_done=1 */
|
||||
tmp = reg32_read(DDRC_SBRSTAT(0));
|
||||
while (tmp != 0x00000002)
|
||||
tmp = reg32_read(DDRC_SBRSTAT(0)) & 0x2;
|
||||
/* Step10: Poll SBRSTAT.scrub_busy=0 */
|
||||
tmp = reg32_read(DDRC_SBRSTAT(0));
|
||||
while (tmp != 0x0)
|
||||
tmp = reg32_read(DDRC_SBRSTAT(0)) & 0x1;
|
||||
/* Step11: Disable SBR by programming SBRCTL.scrub_en=0 */
|
||||
clrbits_le32(DDRC_SBRCTL(0), 0x1);
|
||||
/* Step12: Prepare for normal scrub operation(Read) and set scrub_interval*/
|
||||
reg32_write(DDRC_SBRCTL(0), 0x100);
|
||||
/* Step13: Enable the SBR by programming SBRCTL.scrub_en=1 */
|
||||
reg32_write(DDRC_SBRCTL(0), 0x101);
|
||||
/* Step14: Enable AXI ports by programming */
|
||||
reg32_write(DDRC_PCTRL_0(0), 0x1);
|
||||
/* Step15: Disable quasi-dynamic programming */
|
||||
reg32_write(DDRC_SWCTL(0), 0x00000001);
|
||||
}
|
||||
|
||||
void ddrc_inline_ecc_scrub_end(unsigned int start_address,
|
||||
unsigned int range_address)
|
||||
{
|
||||
/* Step1: Enable quasi-dynamic programming */
|
||||
reg32_write(DDRC_SWCTL(0), 0x00000000);
|
||||
/* Step2: Block the AXI ports from taking the transaction */
|
||||
reg32_write(DDRC_PCTRL_0(0), 0x0);
|
||||
/* Step3: Set scrub start address */
|
||||
reg32_write(DDRC_SBRSTART0(0), start_address);
|
||||
/* Step4: Set scrub range address */
|
||||
reg32_write(DDRC_SBRRANGE0(0), range_address);
|
||||
/* Step5: Disable SBR by programming SBRCTL.scrub_en=0 */
|
||||
clrbits_le32(DDRC_SBRCTL(0), 0x1);
|
||||
/* Step6: Prepare for normal scrub operation(Read) and set scrub_interval */
|
||||
reg32_write(DDRC_SBRCTL(0), 0x100);
|
||||
/* Step7: Enable the SBR by programming SBRCTL.scrub_en=1 */
|
||||
reg32_write(DDRC_SBRCTL(0), 0x101);
|
||||
/* Step8: Enable AXI ports by programming */
|
||||
reg32_write(DDRC_PCTRL_0(0), 0x1);
|
||||
/* Step9: Disable quasi-dynamic programming */
|
||||
reg32_write(DDRC_SWCTL(0), 0x00000001);
|
||||
}
|
||||
#endif
|
||||
|
||||
void __weak board_dram_ecc_scrub(void)
|
||||
{
|
||||
}
|
||||
|
||||
int ddr_init(struct dram_timing_info *dram_timing)
|
||||
{
|
||||
unsigned int tmp, initial_drate, target_freq;
|
||||
|
@ -169,6 +239,8 @@ int ddr_init(struct dram_timing_info *dram_timing)
|
|||
reg32_write(DDRC_PCTRL_0(0), 0x00000001);
|
||||
debug("DDRINFO: ddrmix config done\n");
|
||||
|
||||
board_dram_ecc_scrub();
|
||||
|
||||
/* save the dram timing config into memory */
|
||||
dram_config_save(dram_timing, CONFIG_SAVED_DRAM_TIMING_BASE);
|
||||
|
||||
|
|
Loading…
Reference in a new issue