[U-Boot] [PATCH 1/4] sunxi: nand: Add basic sunxi NAND driver with DMA support
Scott Wood
scottwood at freescale.com
Thu Jul 16 23:15:58 CEST 2015
On Thu, 2015-07-16 at 13:25 +0200, Piotr Zierhoffer wrote:
> 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.
It looks a lot like an SPL-only driver to me...
>
> 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
Please keep NAND drivers in drivers/mtd/nand/.
>
> 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
If 0x0 is the address of an SRAM, its address should be symbolically defined.
Also consider mapping it to a different virtual address, to avoid potential
compiler mischief.
> +#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)
> +{
Here and elsewhere, please either use static or less generic name.
+int nand_initialized;
static bool nand_initialized;
Or better yet, get rid of this and stop using init-on-first-use.
> +
> +void nand_init(void)
> +{
> + board_nand_init(NULL);
> +}
nand_init() is defined in drivers/mtd/nand/nand.c, which is only optional for
SPL -- and I don't see an SPL ifdef here.
> +uint32_t ecc_errors;
static
> +
> +/* read 0x400 bytes from real_addr to temp_buf */
> +void nand_read_block(unsigned int real_addr, int syndrome)
I don't know of any NAND chips whose erase blocks are as small as 0x400
bytes. The read/program unit is called a page, not a block.
Is there a reason to hardcode the page size here? Especially since the
comment doesn't appear to match the code, which uses SPL_WRITE_SIZE.
> +{
> + uint32_t val;
> + int ECC_OFF = 0;
Why is this capitalized?
> + 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);
board_nand_init() is called from nand_init(). Why are you calling it here?
> + switch (CONFIG_SUNXI_ECC_STRENGTH) {
No documentation for this symbol.
If it's an attribute of the hardware rather than something the user selects,
it should be CONFIG_SYS_SUNXI_ECC_STRENGTH, or just SUNXI_ECC_STRENGTH if it
doesn't need to integrate with kconfig or makefiles.
> + /* clear temp_buf */
> + memset((void *)temp_buf, 0, SPL_WRITE_SIZE);
Unnecessary cast.
> +
> + /* 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);
Unnecessary parens (and inconsistent with the following line).
> + 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 */
What is 0x00e00530? Define it symbolically.
> + 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) |
Why is ADDR_CYCLE commented out, and a magic number used in its place?
> +int helper_load(uint32_t offs, unsigned int size, void *dest)
This name is not clear (in addition to being way too vague for a global
symbol).
> +{
> + uint32_t dst;
Don't have both "dest" and "dst". It's confusing.
> + uint32_t count;
> + uint32_t count_c;
What does the "_c" mean?
> + uint32_t adr = offs;
So address is actually a NAND offset, not a memory address? Confusing.
Don't put addresses in uint32_t (even if it doesn't make a difference on this
platform, it's a bad habit and a bad example to others). Use a pointer for
virtual addresses, and phys_addr_t for physical addresses.
> +
> + memset((void *)dest, 0x0, size); /* clean destination memory */
Unnecessary cast.
> + 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);
What does 0x400000 represent?
> +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
> +{
> + helper_load(offs, size, dest);
> + return 0;
> +}
Why didn't you just call helper_load() nand_spl_load_image()?
> +
> +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
Don't use such a generic ifdef protector in a non-generic file. It's just
chance that include/nand.h is using _NAND_H_ instead of NAND_H...
-Scott
More information about the U-Boot
mailing list