From: Piotr Zierhoffer <piotr.zierhof...@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 <pgie...@antmicro.com> Signed-off-by: Tomasz Gorochowik <tgorocho...@antmicro.com> Signed-off-by: Mateusz Holenko <mhole...@antmicro.com> Signed-off-by: Piotr Zierhoffer <pzierhof...@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 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot