zynqmp: spl: support DRAM ECC initialization

Use the ZDMA channel 0 to initialize the DRAM banks. This avoid
spurious ECC errors that can occur when accessing unitialized memory.

The feature is enabled by setting the option
CONFIG_SPL_ZYNQMP_DRAM_ECC_INIT and providing the following data:

 SPL_ZYNQMP_DRAM_BANK1_BASE: start of memory to initialize
 SPL_ZYNQMP_DRAM_BANK1_LEN : len of memory to initialize (hex)
 SPL_ZYNQMP_DRAM_BANK2_BASE: start of memory to initialize
 SPL_ZYNQMP_DRAM_BANK2_LEN : len of memory to initialize (hex)

Setting SPL_ZYNQMP_DRAM_BANK_LEN to 0 takes no action.

Signed-off-by: Jorge Ramirez-Ortiz <jorge@foundries.io>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
This commit is contained in:
Jorge Ramirez-Ortiz
2021-06-13 20:55:53 +02:00
committed by Michal Simek
parent ca8bb0ccb7
commit 01c7714a7b
6 changed files with 218 additions and 0 deletions

View File

@@ -92,6 +92,41 @@ config ZYNQMP_NO_DDR
This option configures MMU with no DDR to avoid speculative This option configures MMU with no DDR to avoid speculative
access to DDR memory where DDR is not present. access to DDR memory where DDR is not present.
config SPL_ZYNQMP_DRAM_ECC_INIT
bool "Initialize DRAM ECC"
depends on SPL
help
This option initializes all memory to 0xdeadbeef. Must be set if your
memory is of ECC type.
config SPL_ZYNQMP_DRAM_BANK1_BASE
depends on SPL_ZYNQMP_DRAM_ECC_INIT
hex "DRAM Bank1 address"
default 0x00000000
help
Start address of DRAM ECC bank1
config SPL_ZYNQMP_DRAM_BANK1_LEN
depends on SPL_ZYNQMP_DRAM_ECC_INIT
hex "DRAM Bank1 size"
default 0x80000000
help
Size in bytes of the DRAM ECC bank1
config SPL_ZYNQMP_DRAM_BANK2_BASE
depends on SPL_ZYNQMP_DRAM_ECC_INIT
hex "DRAM Bank2 address"
default 0x800000000
help
Start address of DRAM ECC bank2
config SPL_ZYNQMP_DRAM_BANK2_LEN
depends on SPL_ZYNQMP_DRAM_ECC_INIT
hex "DRAM Bank2 size"
default 0x0
help
Size in bytes of the DRAM ECC bank2. A null size takes no action.
config SYS_MALLOC_F_LEN config SYS_MALLOC_F_LEN
default 0x600 default 0x600

View File

@@ -7,4 +7,5 @@ obj-y += clk.o
obj-y += cpu.o obj-y += cpu.o
obj-$(CONFIG_MP) += mp.o obj-$(CONFIG_MP) += mp.o
obj-$(CONFIG_SPL_BUILD) += spl.o handoff.o obj-$(CONFIG_SPL_BUILD) += spl.o handoff.o
obj-$(CONFIG_SPL_ZYNQMP_DRAM_ECC_INIT) += ecc_spl_init.o
obj-$(CONFIG_ZYNQMP_PSU_INIT_ENABLED) += psu_spl_init.o obj-$(CONFIG_ZYNQMP_PSU_INIT_ENABLED) += psu_spl_init.o

View File

@@ -0,0 +1,163 @@
// SPDX-License-Identifier: MIT
/*
* Copyright(c) 2015 - 2020 Xilinx, Inc.
*
* Jorge Ramirez-Ortiz <jorge@foundries.io>
*/
#include <common.h>
#include <cpu_func.h>
#include <asm/arch/hardware.h>
#include <asm/arch/ecc_spl_init.h>
#include <asm/io.h>
#include <linux/delay.h>
#define ZDMA_TRANSFER_MAX_LEN (0x3FFFFFFFU - 7U)
#define ZDMA_CH_STATUS ((ADMA_CH0_BASEADDR) + 0x0000011CU)
#define ZDMA_CH_STATUS_STATE_MASK 0x00000003U
#define ZDMA_CH_STATUS_STATE_DONE 0x00000000U
#define ZDMA_CH_STATUS_STATE_ERR 0x00000003U
#define ZDMA_CH_CTRL0 ((ADMA_CH0_BASEADDR) + 0x00000110U)
#define ZDMA_CH_CTRL0_POINT_TYPE_MASK (u32)0x00000040U
#define ZDMA_CH_CTRL0_POINT_TYPE_NORMAL (u32)0x00000000U
#define ZDMA_CH_CTRL0_MODE_MASK (u32)0x00000030U
#define ZDMA_CH_CTRL0_MODE_WR_ONLY (u32)0x00000010U
#define ZDMA_CH_CTRL0_TOTAL_BYTE_COUNT ((ADMA_CH0_BASEADDR) + 0x00000188U)
#define ZDMA_CH_WR_ONLY_WORD0 ((ADMA_CH0_BASEADDR) + 0x00000148U)
#define ZDMA_CH_WR_ONLY_WORD1 ((ADMA_CH0_BASEADDR) + 0x0000014CU)
#define ZDMA_CH_WR_ONLY_WORD2 ((ADMA_CH0_BASEADDR) + 0x00000150U)
#define ZDMA_CH_WR_ONLY_WORD3 ((ADMA_CH0_BASEADDR) + 0x00000154U)
#define ZDMA_CH_DST_DSCR_WORD0 ((ADMA_CH0_BASEADDR) + 0x00000138U)
#define ZDMA_CH_DST_DSCR_WORD0_LSB_MASK 0xFFFFFFFFU
#define ZDMA_CH_DST_DSCR_WORD1 ((ADMA_CH0_BASEADDR) + 0x0000013CU)
#define ZDMA_CH_DST_DSCR_WORD1_MSB_MASK 0x0001FFFFU
#define ZDMA_CH_SRC_DSCR_WORD2 ((ADMA_CH0_BASEADDR) + 0x00000130U)
#define ZDMA_CH_DST_DSCR_WORD2 ((ADMA_CH0_BASEADDR) + 0x00000140U)
#define ZDMA_CH_CTRL2 ((ADMA_CH0_BASEADDR) + 0x00000200U)
#define ZDMA_CH_CTRL2_EN_MASK 0x00000001U
#define ZDMA_CH_ISR ((ADMA_CH0_BASEADDR) + 0x00000100U)
#define ZDMA_CH_ISR_DMA_DONE_MASK 0x00000400U
#define ECC_INIT_VAL_WORD 0xDEADBEEFU
#define ZDMA_IDLE_TIMEOUT_USEC 1000000
#define ZDMA_DONE_TIMEOUT_USEC 5000000
static void ecc_zdma_restore(void)
{
/* Restore reset values for the DMA registers used */
writel(ZDMA_CH_CTRL0, 0x00000080U);
writel(ZDMA_CH_WR_ONLY_WORD0, 0x00000000U);
writel(ZDMA_CH_WR_ONLY_WORD1, 0x00000000U);
writel(ZDMA_CH_WR_ONLY_WORD2, 0x00000000U);
writel(ZDMA_CH_WR_ONLY_WORD3, 0x00000000U);
writel(ZDMA_CH_DST_DSCR_WORD0, 0x00000000U);
writel(ZDMA_CH_DST_DSCR_WORD1, 0x00000000U);
writel(ZDMA_CH_SRC_DSCR_WORD2, 0x00000000U);
writel(ZDMA_CH_DST_DSCR_WORD2, 0x00000000U);
writel(ZDMA_CH_CTRL0_TOTAL_BYTE_COUNT, 0x00000000U);
}
static void ecc_dram_bank_init(u64 addr, u64 len)
{
bool retry = true;
u32 timeout;
u64 bytes;
u32 size;
u64 src;
u32 reg;
if (!len)
return;
retry:
bytes = len;
src = addr;
ecc_zdma_restore();
while (bytes > 0) {
size = bytes > ZDMA_TRANSFER_MAX_LEN ?
ZDMA_TRANSFER_MAX_LEN : (u32)bytes;
/* Wait until the DMA is in idle state */
timeout = ZDMA_IDLE_TIMEOUT_USEC;
do {
udelay(1);
reg = readl(ZDMA_CH_STATUS);
reg &= ZDMA_CH_STATUS_STATE_MASK;
if (!timeout--) {
puts("error, ECC DMA failed to idle\n");
goto done;
}
} while ((reg != ZDMA_CH_STATUS_STATE_DONE) &&
(reg != ZDMA_CH_STATUS_STATE_ERR));
/* Enable Simple (Write Only) Mode */
reg = readl(ZDMA_CH_CTRL0);
reg &= (ZDMA_CH_CTRL0_POINT_TYPE_MASK |
ZDMA_CH_CTRL0_MODE_MASK);
reg |= (ZDMA_CH_CTRL0_POINT_TYPE_NORMAL |
ZDMA_CH_CTRL0_MODE_WR_ONLY);
writel(reg, ZDMA_CH_CTRL0);
/* Fill in the data to be written */
writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD0);
writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD1);
writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD2);
writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD3);
/* Write Destination Address */
writel((u32)(src & ZDMA_CH_DST_DSCR_WORD0_LSB_MASK),
ZDMA_CH_DST_DSCR_WORD0);
writel((u32)((src >> 32) & ZDMA_CH_DST_DSCR_WORD1_MSB_MASK),
ZDMA_CH_DST_DSCR_WORD1);
/* Size to be Transferred. Recommended to set both src and dest sizes */
writel(size, ZDMA_CH_SRC_DSCR_WORD2);
writel(size, ZDMA_CH_DST_DSCR_WORD2);
/* DMA Enable */
reg = readl(ZDMA_CH_CTRL2);
reg |= ZDMA_CH_CTRL2_EN_MASK;
writel(reg, ZDMA_CH_CTRL2);
/* Check the status of the transfer by polling on DMA Done */
timeout = ZDMA_DONE_TIMEOUT_USEC;
do {
udelay(1);
reg = readl(ZDMA_CH_ISR);
reg &= ZDMA_CH_ISR_DMA_DONE_MASK;
if (!timeout--) {
puts("error, ECC DMA timeout\n");
goto done;
}
} while (reg != ZDMA_CH_ISR_DMA_DONE_MASK);
/* Clear DMA status */
reg = readl(ZDMA_CH_ISR);
reg |= ZDMA_CH_ISR_DMA_DONE_MASK;
writel(ZDMA_CH_ISR_DMA_DONE_MASK, ZDMA_CH_ISR);
/* Read the channel status for errors */
reg = readl(ZDMA_CH_STATUS);
if (reg == ZDMA_CH_STATUS_STATE_ERR) {
if (retry) {
retry = false;
goto retry;
}
puts("error, ECC DMA error\n");
break;
}
bytes -= size;
src += size;
}
done:
ecc_zdma_restore();
}
void zynqmp_ecc_init(void)
{
ecc_dram_bank_init(CONFIG_SPL_ZYNQMP_DRAM_BANK1_BASE,
CONFIG_SPL_ZYNQMP_DRAM_BANK1_LEN);
ecc_dram_bank_init(CONFIG_SPL_ZYNQMP_DRAM_BANK2_BASE,
CONFIG_SPL_ZYNQMP_DRAM_BANK2_LEN);
}

View File

@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright(c) 2015 - 2020 Xilinx, Inc.
*
* Jorge Ramirez-Ortiz <jorge@foundries.io>
*/
#ifndef __ARCH_ZYNQMP_ECC_INIT_H
#define __ARCH_ZYNQMP_ECC_INIT_H
void zynqmp_ecc_init(void);
#endif

View File

@@ -24,6 +24,8 @@
+ 0x00000114) + 0x00000114)
#define ZYNQMP_PS_SYSMON_ANALOG_BUS_VAL 0x00003210 #define ZYNQMP_PS_SYSMON_ANALOG_BUS_VAL 0x00003210
#define ADMA_CH0_BASEADDR 0xFFA80000
#define PS_MODE0 BIT(0) #define PS_MODE0 BIT(0)
#define PS_MODE1 BIT(1) #define PS_MODE1 BIT(1)
#define PS_MODE2 BIT(2) #define PS_MODE2 BIT(2)

View File

@@ -15,6 +15,7 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/spl.h> #include <asm/spl.h>
#include <asm/arch/hardware.h> #include <asm/arch/hardware.h>
#include <asm/arch/ecc_spl_init.h>
#include <asm/arch/psu_init_gpl.h> #include <asm/arch/psu_init_gpl.h>
#include <asm/arch/sys_proto.h> #include <asm/arch/sys_proto.h>
@@ -22,6 +23,9 @@ void board_init_f(ulong dummy)
{ {
board_early_init_f(); board_early_init_f();
board_early_init_r(); board_early_init_r();
#ifdef CONFIG_SPL_ZYNQMP_DRAM_ECC_INIT
zynqmp_ecc_init();
#endif
} }
static void ps_mode_reset(ulong mode) static void ps_mode_reset(ulong mode)