mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-03 21:48:15 +00:00 
			
		
		
		
	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>
		
			
				
	
	
		
			164 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// 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);
 | 
						|
}
 |