Hi Masahiro, On Fri, 2014-09-05 at 14:50 +0900, Masahiro Yamada wrote: > The SPL-mode driver for Denali(Cadence) NAND Flash Memory Controller IP. > > This driver requires two CONFIG macros: > - CONFIG_SPL_NAND_DENALI > Define to enable this driver. > - CONFIG_SYS_NAND_BAD_BLOCK_POS > Specify bad block mark position in the oob space. Typically 0. > > Signed-off-by: Masahiro Yamada <yamad...@jp.panasonic.com> > Cc: Chin Liang See <cl...@altera.com> > Cc: Scott Wood <scottw...@freescale.com> > --- > > Changes in v4: > - Add a workaround to not depend on the Denali driver > posted by Chin Liang See. > This driver has been taking too long: > http://patchwork.ozlabs.org/patch/381305/ >
Yup, hopefully v10 would be the final patch. > Changes in v3: None > Changes in v2: > - Avoid unaligned access > - Replace a magic number 0x2000 with PIPELINE_ACCESS > > drivers/mtd/nand/Makefile | 1 + > drivers/mtd/nand/denali_spl.c | 245 > ++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 246 insertions(+) > create mode 100644 drivers/mtd/nand/denali_spl.c > > diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile > index bf1312a..f90f9a0 100644 > --- a/drivers/mtd/nand/Makefile > +++ b/drivers/mtd/nand/Makefile > @@ -12,6 +12,7 @@ NORMAL_DRIVERS=y > endif > > obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o > +obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o > obj-$(CONFIG_SPL_NAND_DOCG4) += docg4_spl.o > obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o > obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o > diff --git a/drivers/mtd/nand/denali_spl.c b/drivers/mtd/nand/denali_spl.c > new file mode 100644 > index 0000000..ab23743 > --- /dev/null > +++ b/drivers/mtd/nand/denali_spl.c > @@ -0,0 +1,245 @@ > +/* > + * Copyright (C) 2014 Panasonic Corporation > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <asm/io.h> > +#include <asm/unaligned.h> > +#include <linux/mtd/nand.h> > +#if 0 > +#include "denali.h" > +#else > +/* workaround until denali.h is merged */ > +#define TRANSFER_SPARE_REG 0x10 > +#define ECC_ENABLE 0xe0 > +#define PAGES_PER_BLOCK 0x150 > +#define DEVICE_MAIN_AREA_SIZE 0x170 > +#define DEVICE_SPARE_AREA_SIZE 0x180 > + > +#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50)) > +#define INTR_STATUS__ECC_UNCOR_ERR 0x0001 > +#define INTR_STATUS__LOAD_COMP 0x0040 > + > +#define INDEX_CTRL_REG 0x0 > +#define INDEX_DATA_REG 0x10 > +#define MODE_01 0x04000000 > +#define MODE_10 0x08000000 > +#endif > + > +#define SPARE_ACCESS 0x41 > +#define MAIN_ACCESS 0x42 > +#define PIPELINE_ACCESS 0x2000 > + > +#define BANK(x) ((x) << 24) > + > +static void __iomem *denali_flash_mem = > + (void __iomem *)CONFIG_SYS_NAND_DATA_BASE; > +static void __iomem *denali_flash_reg = > + (void __iomem *)CONFIG_SYS_NAND_REGS_BASE; > + > +static const int flash_bank; > +static uint8_t page_buffer[NAND_MAX_PAGESIZE]; > +static int page_size, oob_size, pages_per_block; > + > +static void index_addr(uint32_t address, uint32_t data) > +{ > + writel(address, denali_flash_mem + INDEX_CTRL_REG); > + writel(data, denali_flash_mem + INDEX_DATA_REG); > +} > + > +static int wait_for_irq(uint32_t irq_mask) > +{ > + unsigned long timeout = 1000000; > + uint32_t intr_status; > + > + do { > + intr_status = readl(denali_flash_reg + INTR_STATUS(flash_bank)); > + > + if (intr_status & INTR_STATUS__ECC_UNCOR_ERR) { > + debug("Uncorrected ECC detected\n"); > + return -EIO; > + } > + > + if (intr_status & irq_mask) > + break; > + > + udelay(1); > + timeout--; > + } while (timeout); > + > + if (!timeout) { > + debug("Timeout with interrupt status %08x\n", intr_status); > + return -EIO; > + } > + > + return 0; > +} > + > +static void read_data_from_flash_mem(uint8_t *buf, int len) > +{ > + int i; > + uint32_t *buf32; > + > + /* transfer the data from the flash */ > + buf32 = (uint32_t *)buf; > + > + /* > + * Let's take care of unaligned access although it rarely happens. > + * Avoid put_unaligned() for the normal use cases since it leads to > + * a bit performance regression. > + */ > + if ((unsigned long)buf32 % 4) { > + for (i = 0; i < len / 4; i++) > + put_unaligned(readl(denali_flash_mem + INDEX_DATA_REG), > + buf32++); > + } else { > + for (i = 0; i < len / 4; i++) > + *buf32++ = readl(denali_flash_mem + INDEX_DATA_REG); > + } > + > + if (len % 4) { > + u32 tmp; > + > + tmp = cpu_to_le32(readl(denali_flash_mem + INDEX_DATA_REG)); > + buf = (uint8_t *)buf32; > + for (i = 0; i < len % 4; i++) { > + *buf++ = tmp; > + tmp >>= 8; > + } > + } > +} > + > +int denali_send_pipeline_cmd(int page, int ecc_en, int access_type) > +{ > + uint32_t addr, cmd; > + static uint32_t page_count = 1; > + > + writel(ecc_en, denali_flash_reg + ECC_ENABLE); > + > + /* clear all bits of intr_status. */ > + writel(0xffff, denali_flash_reg + INTR_STATUS(flash_bank)); > + > + addr = BANK(flash_bank) | page; > + > + /* setup the acccess type */ > + cmd = MODE_10 | addr; > + index_addr(cmd, access_type); > + > + /* setup the pipeline command */ > + index_addr(cmd, PIPELINE_ACCESS | page_count); > + > + cmd = MODE_01 | addr; > + writel(cmd, denali_flash_mem + INDEX_CTRL_REG); > + > + return wait_for_irq(INTR_STATUS__LOAD_COMP); > +} > + > +static int nand_read_oob(void *buf, int page) > +{ > + int ret; > + > + ret = denali_send_pipeline_cmd(page, 0, SPARE_ACCESS); > + if (ret < 0) > + return ret; > + > + read_data_from_flash_mem(buf, oob_size); > + > + return 0; > +} > + > +static int nand_read_page(void *buf, int page) > +{ > + int ret; > + > + ret = denali_send_pipeline_cmd(page, 1, MAIN_ACCESS); > + if (ret < 0) > + return ret; > + > + read_data_from_flash_mem(buf, page_size); > + > + return 0; > +} > + > +static int nand_block_isbad(int block) > +{ > + int ret; > + > + ret = nand_read_oob(page_buffer, block * pages_per_block); > + if (ret < 0) > + return ret; > + > + return page_buffer[CONFIG_SYS_NAND_BAD_BLOCK_POS] != 0xff; > +} > + > +/* nand_init() - initialize data to make nand usable by SPL */ > +void nand_init(void) > +{ > + /* access to main area */ > + writel(0, denali_flash_reg + TRANSFER_SPARE_REG); > + > + page_size = readl(denali_flash_reg + DEVICE_MAIN_AREA_SIZE); > + oob_size = readl(denali_flash_reg + DEVICE_SPARE_AREA_SIZE); > + pages_per_block = readl(denali_flash_reg + PAGES_PER_BLOCK); I believe this will work for ONFI NAND devices only. For non-ONFI, the value might not correct. > +} > + > +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) > +{ > + int block, page, column, readlen; > + int ret; > + int force_bad_block_check = 1; > + > + page = offs / page_size; > + column = offs % page_size; > + > + block = page / pages_per_block; > + page = page % pages_per_block; > + > + while (size) { I believe we need to error out when reading beyond last block. > + if (force_bad_block_check || page == 0) { > + ret = nand_block_isbad(block); > + if (ret < 0) > + return ret; > + > + if (ret) { > + block++; > + continue; > + } > + } > + > + force_bad_block_check = 0; I believe we still need to check the subsequent block whether is bad or not too. This can be enable when cross the block boundary. > + > + if (unlikely(column || size < page_size)) { > + /* Partial page read */ > + ret = nand_read_page(page_buffer, > + block * pages_per_block + page); > + if (ret < 0) > + return ret; > + > + readlen = min(page_size - column, size); > + memcpy(dst, page_buffer, readlen); > + > + column = 0; > + } else { > + ret = nand_read_page(dst, > + block * pages_per_block + page); > + if (ret < 0) > + return ret; > + > + readlen = page_size; > + } > + > + size -= readlen; > + dst += readlen; > + page++; > + if (page == pages_per_block) { > + block++; > + page = 0; > + } > + } > + > + return 0; > +} > + > +void nand_deselect(void) {} Currently U-Boot has drivers/mtd/nand/nand_spl_simple.c which handling the SPL NAND image load. Wonder this driver will be integrated into nand_spl_simple.c once drivers/mtd/nand/denali.c is applied? Thanks Chin Liang _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot