[U-Boot] [PATCH 1/4] sunxi: nand: Add basic sunxi NAND driver with DMA support

Piotr Zierhoffer pzierhoffer at antmicro.com
Thu Jul 16 13:25:12 CEST 2015


From: Piotr Zierhoffer <piotr.zierhoffer at cs.put.poznan.pl>

This driver adds NAND support to both SPL and full U-Boot.
It was tested on AllWinner A20.

Signed-off-by: Peter Gielda <pgielda at antmicro.com>
Signed-off-by: Tomasz Gorochowik <tgorochowik at antmicro.com>
Signed-off-by: Mateusz Holenko <mholenko at antmicro.com>
Signed-off-by: Piotr Zierhoffer <pzierhoffer at antmicro.com>
---

 board/sunxi/Makefile |   1 +
 board/sunxi/nand.c   | 315 +++++++++++++++++++++++++++++++++++++++++++++++++++
 board/sunxi/nand.h   | 146 ++++++++++++++++++++++++
 3 files changed, 462 insertions(+)
 create mode 100644 board/sunxi/nand.c
 create mode 100644 board/sunxi/nand.h

diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile
index 43766e0..6f1d571 100644
--- a/board/sunxi/Makefile
+++ b/board/sunxi/Makefile
@@ -9,6 +9,7 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 obj-y	+= board.o
+obj-$(CONFIG_SUNXI_NAND)	+= nand.o
 obj-$(CONFIG_SUNXI_GMAC)	+= gmac.o
 obj-$(CONFIG_SUNXI_AHCI)	+= ahci.o
 obj-$(CONFIG_MACH_SUN4I)	+= dram_sun4i_auto.o
diff --git a/board/sunxi/nand.c b/board/sunxi/nand.c
new file mode 100644
index 0000000..16e8e4d
--- /dev/null
+++ b/board/sunxi/nand.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2014-2015, Antmicro Ltd <www.antmicro.com>
+ * Copyright (c) 2015, AW-SOM Technologies <www.aw-som.com>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <config.h>
+#include <asm/io.h>
+#include <nand.h>
+#include "nand.h" /* sunxi specific header */
+
+/* minimal "boot0" style NAND support for Allwinner A20 */
+
+/* temporary buffer in internal ram */
+#ifdef CONFIG_SPL_BUILD
+/* in SPL temporary buffer cannot be @ 0x0 */
+unsigned char temp_buf[SPL_WRITE_SIZE] __aligned(0x10) __section(".text#");
+#else
+/* put temporary buffer @ 0x0 */
+unsigned char *temp_buf = (unsigned char *)0x0;
+#endif
+
+#define MAX_RETRIES 10
+
+static int check_value_inner(int offset, int expected_bits,
+				int max_number_of_retries, int negation)
+{
+	int retries = 0;
+	do {
+		int val = readl(offset) & expected_bits;
+		if (negation ? !val : val)
+			return 1;
+		mdelay(1);
+		retries++;
+	} while (retries < max_number_of_retries);
+
+	return 0;
+}
+
+static inline int check_value(int offset, int expected_bits,
+				int max_number_of_retries)
+{
+	return check_value_inner(offset, expected_bits,
+					max_number_of_retries, 0);
+}
+
+static inline int check_value_negated(int offset, int unexpected_bits,
+					int max_number_of_retries)
+{
+	return check_value_inner(offset, unexpected_bits,
+					max_number_of_retries, 1);
+}
+
+void nand_set_clocks(void)
+{
+	uint32_t val;
+
+	writel(PORTC_PC_CFG0_NRB1 |
+		PORTC_PC_CFG0_NRB0 |
+		PORTC_PC_CFG0_NRE  |
+		PORTC_PC_CFG0_NCE0 |
+		PORTC_PC_CFG0_NCE1 |
+		PORTC_PC_CFG0_NCLE |
+		PORTC_PC_CFG0_NALE |
+		PORTC_PC_CFG0_NWE, PORTC_BASE + PORTC_PC_CFG0);
+
+	writel(PORTC_PC_CFG1_NDQ7 |
+		PORTC_PC_CFG1_NDQ6 |
+		PORTC_PC_CFG1_NDQ5 |
+		PORTC_PC_CFG1_NDQ4 |
+		PORTC_PC_CFG1_NDQ3 |
+		PORTC_PC_CFG1_NDQ2 |
+		PORTC_PC_CFG1_NDQ1 |
+		PORTC_PC_CFG1_NDQ0, PORTC_BASE + PORTC_PC_CFG1);
+
+	writel(PORTC_PC_CFG2_NCE7 |
+		PORTC_PC_CFG2_NCE6 |
+		PORTC_PC_CFG2_NCE5 |
+		PORTC_PC_CFG2_NCE4 |
+		PORTC_PC_CFG2_NCE3 |
+		PORTC_PC_CFG2_NCE2 |
+		PORTC_PC_CFG2_NWP, PORTC_BASE + PORTC_PC_CFG2);
+
+	writel(PORTC_PC_CFG3_NDQS, PORTC_BASE + PORTC_PC_CFG3);
+
+	val = readl(CCU_BASE + CCU_AHB_GATING_REG0);
+	writel(CCU_AHB_GATING_REG0_NAND | val, CCU_BASE + CCU_AHB_GATING_REG0);
+
+	val = readl(CCU_BASE + CCU_NAND_SCLK_CFG_REG);
+	writel(val | CCU_NAND_SCLK_CFG_REG_SCLK_GATING
+		| CCU_NAND_SCLK_CFG_REG_CLK_DIV_RATIO,
+		CCU_BASE + CCU_NAND_SCLK_CFG_REG);
+}
+
+int nand_initialized;
+
+void nand_init(void)
+{
+	board_nand_init(NULL);
+}
+
+uint32_t ecc_errors;
+
+/* read 0x400 bytes from real_addr to temp_buf */
+void nand_read_block(unsigned int real_addr, int syndrome)
+{
+	uint32_t val;
+	int ECC_OFF = 0;
+	uint16_t ecc_mode = 0;
+	uint16_t rand_seed;
+	uint32_t page;
+	uint16_t column;
+	uint32_t oob_offset;
+
+	if (!nand_initialized)
+		board_nand_init(NULL);
+
+	switch (CONFIG_SUNXI_ECC_STRENGTH) {
+	case 16:
+		ecc_mode = 0;
+		ECC_OFF = 0x20;
+		break;
+	case 24:
+		ecc_mode = 1;
+		ECC_OFF = 0x2e;
+		break;
+	case 28:
+		ecc_mode = 2;
+		ECC_OFF = 0x32;
+		break;
+	case 32:
+		ecc_mode = 3;
+		ECC_OFF = 0x3c;
+		break;
+	case 40:
+		ecc_mode = 4;
+		ECC_OFF = 0x4a;
+		break;
+	case 48:
+		ecc_mode = 4;
+		ECC_OFF = 0x52;
+		break;
+	case 56:
+		ecc_mode = 4;
+		ECC_OFF = 0x60;
+		break;
+	case 60:
+		ecc_mode = 4;
+		ECC_OFF = 0x0;
+		break;
+	case 64:
+		ecc_mode = 4;
+		ECC_OFF = 0x0;
+		break;
+	default:
+		ecc_mode = 0;
+		ECC_OFF = 0;
+	}
+
+	if (ECC_OFF == 0) {
+		printf("Unsupported ECC strength (%d)!\n",
+		       CONFIG_SUNXI_ECC_STRENGTH);
+		return;
+	}
+
+	/* clear temp_buf */
+	memset((void *)temp_buf, 0, SPL_WRITE_SIZE);
+
+	/* set CMD  */
+	writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | 0xff,
+	       NANDFLASHC_BASE + NANDFLASHC_CMD);
+
+	if (!check_value(NANDFLASHC_BASE + NANDFLASHC_ST, (1 << 1),
+			 MAX_RETRIES)) {
+		printf("Error while initilizing command interrupt\n");
+		return;
+	}
+
+	page = (real_addr / BLOCK_SIZE);
+	column = real_addr % BLOCK_SIZE;
+
+	if (syndrome) {
+		/* shift every 1kB in syndrome */
+		column += (column / SPL_WRITE_SIZE) * ECC_OFF;
+	}
+
+	/* clear ecc status */
+	writel(0, NANDFLASHC_BASE + NANDFLASHC_ECC_ST);
+
+	/* Choose correct seed */
+	if (syndrome)
+		rand_seed = random_seed_syndrome;
+	else
+		rand_seed = random_seed[page % 128];
+
+	writel((rand_seed << 16) | NFC_ECC_RANDOM_EN | NFC_ECC_EN
+		| NFC_ECC_PIPELINE | (ecc_mode << 12),
+		NANDFLASHC_BASE + NANDFLASHC_ECC_CTL);
+
+	val = readl(NANDFLASHC_BASE + NANDFLASHC_CTL);
+	writel(val | NFC_CTL_RAM_METHOD, NANDFLASHC_BASE + NANDFLASHC_CTL);
+
+	if (syndrome) {
+		writel(SPL_WRITE_SIZE, NANDFLASHC_BASE + NANDFLASHC_SPARE_AREA);
+	} else {
+		oob_offset = BLOCK_SIZE + (column / SPL_WRITE_SIZE) * ECC_OFF;
+		writel(oob_offset, NANDFLASHC_BASE + NANDFLASHC_SPARE_AREA);
+	}
+
+	/* DMAC */
+	writel(0x0, DMAC_BASE + DMAC_CFG_REG0); /* clr dma cmd */
+	/* read from REG_IO_DATA */
+	writel(NANDFLASHC_BASE + NANDFLASHC_IO_DATA,
+	       DMAC_BASE + DMAC_SRC_START_ADDR_REG0);
+	writel((uint32_t)temp_buf,
+	       DMAC_BASE + DMAC_DEST_START_ADDRR_REG0); /* read to RAM */
+	writel(DMAC_DDMA_PARA_REG_SRC_WAIT_CYC
+			| DMAC_DDMA_PARA_REG_SRC_BLK_SIZE,
+			DMAC_BASE + DMAC_DDMA_PARA_REG0);
+	writel(SPL_WRITE_SIZE, DMAC_BASE + DMAC_DDMA_BC_REG0); /* 1kB */
+	writel(DMAC_DDMA_CFG_REG_LOADING
+		| DMAC_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32
+		| DMAC_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32
+		| DMAC_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO
+		| DMAC_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC,
+		DMAC_BASE + DMAC_CFG_REG0);
+
+	writel(0x00E00530, NANDFLASHC_BASE + NANDFLASHC_RCMD_SET); /* READ */
+	writel(1, NANDFLASHC_BASE + NANDFLASHC_SECTOR_NUM);
+	writel(((page & 0xFFFF) << 16) | column,
+	       NANDFLASHC_BASE + NANDFLASHC_ADDR_LOW);
+	writel((page >> 16) & 0xFF, NANDFLASHC_BASE + NANDFLASHC_ADDR_HIGH);
+	writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_DATA_TRANS |
+		NFC_PAGE_CMD | NFC_WAIT_FLAG | /* ADDR_CYCLE */ (4 << 16) |
+		NFC_SEND_ADR | NFC_DATA_SWAP_METHOD | (syndrome ? NFC_SEQ : 0),
+		NANDFLASHC_BASE + NANDFLASHC_CMD);
+
+	if (!check_value(NANDFLASHC_BASE + NANDFLASHC_ST, (1 << 2),
+			 MAX_RETRIES)) {
+		printf("Error while initializing dma interrupt\n");
+		return;
+	}
+
+	if (!check_value_negated(DMAC_BASE + DMAC_CFG_REG0,
+				 DMAC_DDMA_CFG_REG_LOADING, MAX_RETRIES)) {
+		printf("Error while waiting for dma transfer to finish\n");
+		return;
+	}
+
+	if (readl(NANDFLASHC_BASE + NANDFLASHC_ECC_ST))
+		ecc_errors++;
+}
+
+int helper_load(uint32_t offs, unsigned int size, void *dest)
+{
+	uint32_t dst;
+	uint32_t count;
+	uint32_t count_c;
+	uint32_t adr = offs;
+
+	memset((void *)dest, 0x0, size); /* clean destination memory */
+	ecc_errors = 0;
+	for (dst = (uint32_t)dest;
+			dst < ((uint32_t)dest + size);
+			dst += SPL_WRITE_SIZE) {
+		/* if < 0x400000 then syndrome read */
+		nand_read_block(adr, adr < 0x400000);
+		count = dst - (uint32_t)dest;
+
+		if (size - count > SPL_WRITE_SIZE)
+			count_c = SPL_WRITE_SIZE;
+		else
+			count_c = size - count;
+
+		memcpy((void *)dst,
+		       (void *)temp_buf,
+		       count_c);
+		adr += SPL_WRITE_SIZE;
+	}
+	return ecc_errors;
+}
+
+int board_nand_init(struct nand_chip *nand)
+{
+	uint32_t val;
+
+	if (nand_initialized) {
+		printf("NAND already initialized, should not be here...\n");
+		return 0;
+	}
+	nand_initialized = 1;
+	nand_set_clocks();
+	val = readl(NANDFLASHC_BASE + NANDFLASHC_CTL);
+	/* enable and reset CTL */
+	writel(val | NFC_CTL_EN | NFC_CTL_RESET,
+	       NANDFLASHC_BASE + NANDFLASHC_CTL);
+
+	if (!check_value_negated(NANDFLASHC_BASE + NANDFLASHC_CTL,
+				 NFC_CTL_RESET, MAX_RETRIES)) {
+		printf("Couldn't initialize nand\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
+{
+	helper_load(offs, size, dest);
+	return 0;
+}
+
+void nand_deselect(void) {}
diff --git a/board/sunxi/nand.h b/board/sunxi/nand.h
new file mode 100644
index 0000000..b3c1e93
--- /dev/null
+++ b/board/sunxi/nand.h
@@ -0,0 +1,146 @@
+#ifndef NAND_H
+
+#define NAND_H
+
+#define PORTC_BASE                 0x01c20800
+#define CCU_BASE                   0x01c20000
+#define NANDFLASHC_BASE            0x01c03000
+#define DMAC_BASE                  0x01c02000
+
+#define CCU_AHB_GATING_REG0        0x60
+#define CCU_NAND_SCLK_CFG_REG      0x80
+#define CCU_AHB_GATING_REG0_NAND   (1 << 13)
+
+#define CCU_NAND_SCLK_CFG_REG_SCLK_GATING (1 << 31)
+#define CCU_NAND_SCLK_CFG_REG_CLK_DIV_RATIO (1 << 0)
+
+#define PORTC_PC_CFG0              0x48
+#define PORTC_PC_CFG1              0x4C
+#define PORTC_PC_CFG2              0x50
+#define PORTC_PC_CFG3              0x54
+
+#define PORTC_PC_CFG0_NRB1         (2 << 28)
+#define PORTC_PC_CFG0_NRB0         (2 << 24)
+#define PORTC_PC_CFG0_NRE          (2 << 20)
+#define PORTC_PC_CFG0_NCE0         (2 << 16)
+#define PORTC_PC_CFG0_NCE1         (2 << 12)
+#define PORTC_PC_CFG0_NCLE         (2 << 8)
+#define PORTC_PC_CFG0_NALE         (2 << 4)
+#define PORTC_PC_CFG0_NWE          (2 << 0)
+
+#define PORTC_PC_CFG1_NDQ7         (2 << 28)
+#define PORTC_PC_CFG1_NDQ6         (2 << 24)
+#define PORTC_PC_CFG1_NDQ5         (2 << 20)
+#define PORTC_PC_CFG1_NDQ4         (2 << 16)
+#define PORTC_PC_CFG1_NDQ3         (2 << 12)
+#define PORTC_PC_CFG1_NDQ2         (2 << 8)
+#define PORTC_PC_CFG1_NDQ1         (2 << 4)
+#define PORTC_PC_CFG1_NDQ0         (2 << 0)
+
+#define PORTC_PC_CFG2_NCE7         (2 << 24)
+#define PORTC_PC_CFG2_NCE6         (2 << 20)
+#define PORTC_PC_CFG2_NCE5         (2 << 16)
+#define PORTC_PC_CFG2_NCE4         (2 << 12)
+#define PORTC_PC_CFG2_NCE3         (2 << 8)
+#define PORTC_PC_CFG2_NCE2         (2 << 4)
+#define PORTC_PC_CFG2_NWP          (2 << 0)
+
+#define PORTC_PC_CFG3_NDQS         (2 << 0)
+
+#define BLOCK_SIZE                 0x2000
+
+#define DMAC_CFG_REG0              0x300
+#define DMAC_SRC_START_ADDR_REG0   0x304
+#define DMAC_DEST_START_ADDRR_REG0 0x308
+#define DMAC_DDMA_BC_REG0          0x30C
+#define DMAC_DDMA_PARA_REG0        0x318
+
+#define DMAC_DDMA_CFG_REG_LOADING  (1 << 31)
+#define DMAC_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 (2 << 25)
+#define DMAC_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 (2 << 9)
+#define DMAC_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO (1 << 5)
+#define DMAC_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC (3 << 0)
+
+#define DMAC_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0)
+#define DMAC_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8)
+
+#define NANDFLASHC_CTL             0x00000000
+
+#define NFC_CTL_EN                 (1 << 0)
+#define NFC_CTL_RESET              (1 << 1)
+#define NFC_CTL_RAM_METHOD         (1 << 14)
+
+#define NANDFLASHC_ST              0x00000004
+#define NANDFLASHC_INT             0x00000008
+#define NANDFLASHC_TIMING_CTL      0x0000000C
+#define NANDFLASHC_TIMING_CFG      0x00000010
+#define NANDFLASHC_ADDR_LOW        0x00000014
+#define NANDFLASHC_ADDR_HIGH       0x00000018
+#define NANDFLASHC_SECTOR_NUM      0x0000001C
+#define NANDFLASHC_CNT             0x00000020
+#define NANDFLASHC_CMD             0x00000024
+#define NANDFLASHC_RCMD_SET        0x00000028
+#define NANDFLASHC_WCMD_SET        0x0000002C
+#define NANDFLASHC_IO_DATA         0x00000030
+#define NANDFLASHC_ECC_CTL         0x00000034
+
+#define NFC_ECC_EN                 (1 << 0)
+#define NFC_ECC_PIPELINE           (1 << 3)
+#define NFC_ECC_EXCEPTION          (1 << 4)
+#define NFC_ECC_BLOCK_SIZE         (1 << 5)
+#define NFC_ECC_RANDOM_EN          (1 << 9)
+#define NFC_ECC_RANDOM_DIRECTION   (1 << 10)
+
+#define NANDFLASHC_ECC_ST          0x00000038
+#define NANDFLASHC_DEBUG           0x0000003C
+#define NANDFLASHC_ECC_CNT0        0x00000040
+#define NANDFLASHC_ECC_CNT1        0x00000044
+#define NANDFLASHC_ECC_CNT2        0x00000048
+#define NANDFLASHC_ECC_CNT3        0x0000004C
+#define NANDFLASHC_USER_DATA_BASE  0x00000050
+#define NANDFLASHC_EFNAND_STATUS   0x00000090
+#define NANDFLASHC_SPARE_AREA      0x000000A0
+#define NANDFLASHC_PATTERN_ID      0x000000A4
+#define NANDFLASHC_RAM0_BASE       0x00000400
+#define NANDFLASHC_RAM1_BASE       0x00000800
+
+#define NFC_SEND_ADR               (1 << 19)
+#define NFC_ACCESS_DIR             (1 << 20)
+#define NFC_DATA_TRANS             (1 << 21)
+#define NFC_SEND_CMD1              (1 << 22)
+#define NFC_WAIT_FLAG              (1 << 23)
+#define NFC_SEND_CMD2              (1 << 24)
+#define NFC_SEQ                    (1 << 25)
+#define NFC_DATA_SWAP_METHOD       (1 << 26)
+#define NFC_ROW_AUTO_INC           (1 << 27)
+#define NFC_SEND_CMD3              (1 << 28)
+#define NFC_SEND_CMD4              (1 << 29)
+
+#define NFC_PAGE_CMD               (2 << 30)
+
+#define SPL_WRITE_SIZE             CONFIG_CMD_SPL_WRITE_SIZE
+
+/* random seed used by linux */
+const uint16_t random_seed[128] = {
+	0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
+	0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
+	0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
+	0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
+	0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
+	0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
+	0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
+	0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
+	0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
+	0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
+	0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
+	0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
+	0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
+	0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
+	0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
+	0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
+};
+
+/* random seed used for syndrome calls */
+const uint16_t random_seed_syndrome = 0x4a80;
+
+#endif /* end of include guard: NAND_H */
-- 
2.3.6



More information about the U-Boot mailing list