diff options
Diffstat (limited to 'drivers/ram')
-rw-r--r-- | drivers/ram/k3-ddrss/k3-ddrss.c | 122 |
1 files changed, 114 insertions, 8 deletions
diff --git a/drivers/ram/k3-ddrss/k3-ddrss.c b/drivers/ram/k3-ddrss/k3-ddrss.c index 28129d52a6..b6ff73e74e 100644 --- a/drivers/ram/k3-ddrss/k3-ddrss.c +++ b/drivers/ram/k3-ddrss/k3-ddrss.c @@ -7,6 +7,7 @@ #include <common.h> #include <config.h> +#include <time.h> #include <clk.h> #include <div64.h> #include <dm.h> @@ -557,14 +558,118 @@ static void k3_ddrss_set_ecc_range_r0(u32 base, u32 start_address, u32 size) writel((start_address + size - 1) >> 16, base + DDRSS_ECC_R0_END_ADDR_REG); } -static void k3_ddrss_preload_ecc_mem_region(u32 *addr, u32 size, u32 word) +#define BIST_MODE_MEM_INIT 4 +#define BIST_MEM_INIT_TIMEOUT 10000 /* 1msec loops per block = 10s */ +static void k3_lpddr4_bist_init_mem_region(struct k3_ddrss_desc *ddrss, + u64 addr, u64 size, + u32 pattern) { - int i; + lpddr4_obj *driverdt = ddrss->driverdt; + lpddr4_privatedata *pd = &ddrss->pd; + u32 status, offset, regval; + bool int_status; + int i = 0; + + /* Set BIST_START_ADDR_0 [31:0] */ + regval = (u32)(addr & TH_FLD_MASK(LPDDR4__BIST_START_ADDRESS_0__FLD)); + TH_OFFSET_FROM_REG(LPDDR4__BIST_START_ADDRESS_0__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Set BIST_START_ADDR_1 [32 or 34:32] */ + regval = (u32)(addr >> TH_FLD_WIDTH(LPDDR4__BIST_START_ADDRESS_0__FLD)); + regval &= TH_FLD_MASK(LPDDR4__BIST_START_ADDRESS_1__FLD); + TH_OFFSET_FROM_REG(LPDDR4__BIST_START_ADDRESS_1__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Set ADDR_SPACE = log2(size) */ + regval = (u32)(ilog2(size) << TH_FLD_SHIFT(LPDDR4__ADDR_SPACE__FLD)); + TH_OFFSET_FROM_REG(LPDDR4__ADDR_SPACE__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Enable the BIST data check. On 32bit lpddr4 (e.g J7) this shares a + * register with ADDR_SPACE and BIST_GO. + */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_DATA_CHECK__REG, CTL_SHIFT, offset); + driverdt->readreg(pd, LPDDR4_CTL_REGS, offset, ®val); + regval |= TH_FLD_MASK(LPDDR4__BIST_DATA_CHECK__FLD); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + /* Clear the address check bit */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_ADDR_CHECK__REG, CTL_SHIFT, offset); + driverdt->readreg(pd, LPDDR4_CTL_REGS, offset, ®val); + regval &= ~TH_FLD_MASK(LPDDR4__BIST_ADDR_CHECK__FLD); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Set BIST_TEST_MODE[2:0] to memory initialize (4) */ + regval = BIST_MODE_MEM_INIT; + TH_OFFSET_FROM_REG(LPDDR4__BIST_TEST_MODE__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Set BIST_DATA_PATTERN[31:0] */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_DATA_PATTERN_0__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, pattern); + + /* Set BIST_DATA_PATTERN[63:32] */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_DATA_PATTERN_1__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, pattern); + + udelay(1000); + + /* Enable the programmed BIST operation - BIST_GO = 1 */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_GO__REG, CTL_SHIFT, offset); + driverdt->readreg(pd, LPDDR4_CTL_REGS, offset, ®val); + regval |= TH_FLD_MASK(LPDDR4__BIST_GO__FLD); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, regval); + + /* Wait for the BIST_DONE interrupt */ + while (i < BIST_MEM_INIT_TIMEOUT) { + status = driverdt->checkctlinterrupt(pd, LPDDR4_INTR_BIST_DONE, + &int_status); + if (!status & int_status) { + /* Clear LPDDR4_INTR_BIST_DONE */ + driverdt->ackctlinterrupt(pd, LPDDR4_INTR_BIST_DONE); + break; + } + udelay(1000); + i++; + } - printf("ECC is enabled, priming DDR which will take several seconds.\n"); + /* Before continuing we have to stop BIST - BIST_GO = 0 */ + TH_OFFSET_FROM_REG(LPDDR4__BIST_GO__REG, CTL_SHIFT, offset); + driverdt->writereg(pd, LPDDR4_CTL_REGS, offset, 0); + + /* Timeout hit while priming the memory. We can't continue, + * since the memory is not fully initialized and we most + * likely get an uncorrectable error exception while booting. + */ + if (i == BIST_MEM_INIT_TIMEOUT) { + printf("ERROR: Timeout while priming the memory.\n"); + hang(); + } +} - for (i = 0; i < (size / 4); i++) - addr[i] = word; +static void k3_ddrss_lpddr4_preload_full_mem(struct k3_ddrss_desc *ddrss, + u64 total_size, u32 pattern) +{ + u32 done, max_size2; + + /* Get the max size (log2) supported in this config (16/32 lpddr4) + * from the start_addess width - 16bit: 8G, 32bit: 32G + */ + max_size2 = TH_FLD_WIDTH(LPDDR4__BIST_START_ADDRESS_0__FLD) + + TH_FLD_WIDTH(LPDDR4__BIST_START_ADDRESS_1__FLD) + 1; + + /* ECC is enabled in dt but we can't preload the memory if + * the memory configuration is recognized and supported. + */ + if (!total_size || total_size > (1ull << max_size2) || + total_size & (total_size - 1)) { + printf("ECC: the memory configuration is not supported\n"); + hang(); + } + printf("ECC is enabled, priming DDR which will take several seconds.\n"); + done = get_timer(0); + k3_lpddr4_bist_init_mem_region(ddrss, 0, total_size, pattern); + printf("ECC: priming DDR completed in %lu msec\n", get_timer(done)); } static void k3_ddrss_lpddr4_ecc_calc_reserved_mem(struct k3_ddrss_desc *ddrss) @@ -592,9 +697,10 @@ static void k3_ddrss_lpddr4_ecc_init(struct k3_ddrss_desc *ddrss) writel(DDRSS_ECC_CTRL_REG_ECC_EN | DDRSS_ECC_CTRL_REG_RMW_EN | DDRSS_ECC_CTRL_REG_WR_ALLOC, base + DDRSS_ECC_CTRL_REG); - /* Preload ECC Mem region with 0's */ - k3_ddrss_preload_ecc_mem_region((u32 *)ecc_region_start, ecc_range, - 0x00000000); + /* Preload the full memory with 0's using the BIST engine of + * the LPDDR4 controller. + */ + k3_ddrss_lpddr4_preload_full_mem(ddrss, gd->ram_size, 0); /* Clear Error Count Register */ writel(0x1, base + DDRSS_ECC_1B_ERR_CNT_REG); |