mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-28 15:41:40 +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_SBRWDATA0(X) (DDRC_IPS_BASE_ADDR(X) + 0xf2c)
|
||||||
#define DDRC_SBRWDATA1(X) (DDRC_IPS_BASE_ADDR(X) + 0xf30)
|
#define DDRC_SBRWDATA1(X) (DDRC_IPS_BASE_ADDR(X) + 0xf30)
|
||||||
#define DDRC_PDCH(X) (DDRC_IPS_BASE_ADDR(X) + 0xf34)
|
#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_DERATEEN(X) (DDRC_IPS_BASE_ADDR(X) + 0x2020)
|
||||||
#define DDRC_FREQ1_DERATEINT(X) (DDRC_IPS_BASE_ADDR(X) + 0x2024)
|
#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 load_lpddr4_phy_pie(void);
|
||||||
void ddrphy_trained_csr_save(struct dram_cfg_param *param, unsigned int num);
|
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 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 */
|
/* utils function for ddr phy training */
|
||||||
int wait_ddrphy_training_complete(void);
|
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
|
info into memory for low power use. OCRAM_S is used for this
|
||||||
purpose on i.MX8MM.
|
purpose on i.MX8MM.
|
||||||
default 0x180000
|
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
|
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)
|
int ddr_init(struct dram_timing_info *dram_timing)
|
||||||
{
|
{
|
||||||
unsigned int tmp, initial_drate, target_freq;
|
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);
|
reg32_write(DDRC_PCTRL_0(0), 0x00000001);
|
||||||
debug("DDRINFO: ddrmix config done\n");
|
debug("DDRINFO: ddrmix config done\n");
|
||||||
|
|
||||||
|
board_dram_ecc_scrub();
|
||||||
|
|
||||||
/* save the dram timing config into memory */
|
/* save the dram timing config into memory */
|
||||||
dram_config_save(dram_timing, CONFIG_SAVED_DRAM_TIMING_BASE);
|
dram_config_save(dram_timing, CONFIG_SAVED_DRAM_TIMING_BASE);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue