diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/u-boot/drivers/mtd/nand/nand_spl_simple.c | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/u-boot/drivers/mtd/nand/nand_spl_simple.c')
-rw-r--r-- | qemu/roms/u-boot/drivers/mtd/nand/nand_spl_simple.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/qemu/roms/u-boot/drivers/mtd/nand/nand_spl_simple.c b/qemu/roms/u-boot/drivers/mtd/nand/nand_spl_simple.c new file mode 100644 index 000000000..cead4b506 --- /dev/null +++ b/qemu/roms/u-boot/drivers/mtd/nand/nand_spl_simple.c @@ -0,0 +1,270 @@ +/* + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <nand.h> +#include <asm/io.h> +#include <linux/mtd/nand_ecc.h> + +static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +static nand_info_t mtd; +static struct nand_chip nand_chip; + +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + + +#if (CONFIG_SYS_NAND_PAGE_SIZE <= 512) +/* + * NAND command for small page NAND devices (512) + */ +static int nand_command(int block, int page, uint32_t offs, + u8 cmd) +{ + struct nand_chip *this = mtd.priv; + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + + while (!this->dev_ready(&mtd)) + ; + + /* Begin command latch cycle */ + this->cmd_ctrl(&mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + /* Set ALE and clear CLE to start address cycle */ + /* Column address */ + this->cmd_ctrl(&mtd, offs, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + this->cmd_ctrl(&mtd, page_addr & 0xff, NAND_CTRL_ALE); /* A[16:9] */ + this->cmd_ctrl(&mtd, (page_addr >> 8) & 0xff, + NAND_CTRL_ALE); /* A[24:17] */ +#ifdef CONFIG_SYS_NAND_4_ADDR_CYCLE + /* One more address cycle for devices > 32MiB */ + this->cmd_ctrl(&mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE); /* A[28:25] */ +#endif + /* Latch in address */ + this->cmd_ctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + while (!this->dev_ready(&mtd)) + ; + + return 0; +} +#else +/* + * NAND command for large page NAND devices (2k) + */ +static int nand_command(int block, int page, uint32_t offs, + u8 cmd) +{ + struct nand_chip *this = mtd.priv; + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + + while (!this->dev_ready(&mtd)) + ; + + /* Emulate NAND_CMD_READOOB */ + if (cmd == NAND_CMD_READOOB) { + offs += CONFIG_SYS_NAND_PAGE_SIZE; + cmd = NAND_CMD_READ0; + } + + /* Shift the offset from byte addressing to word addressing. */ + if (this->options & NAND_BUSWIDTH_16) + offs >>= 1; + + /* Begin command latch cycle */ + hwctrl(&mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + /* Set ALE and clear CLE to start address cycle */ + /* Column address */ + hwctrl(&mtd, offs & 0xff, + NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */ + hwctrl(&mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */ + /* Row address */ + hwctrl(&mtd, (page_addr & 0xff), NAND_CTRL_ALE); /* A[19:12] */ + hwctrl(&mtd, ((page_addr >> 8) & 0xff), + NAND_CTRL_ALE); /* A[27:20] */ +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + /* One more address cycle for devices > 128MiB */ + hwctrl(&mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE); /* A[31:28] */ +#endif + /* Latch in address */ + hwctrl(&mtd, NAND_CMD_READSTART, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + while (!this->dev_ready(&mtd)) + ; + + return 0; +} +#endif + +static int nand_is_bad_block(int block) +{ + struct nand_chip *this = mtd.priv; + + nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, + NAND_CMD_READOOB); + + /* + * Read one byte (or two if it's a 16 bit chip). + */ + if (this->options & NAND_BUSWIDTH_16) { + if (readw(this->IO_ADDR_R) != 0xffff) + return 1; + } else { + if (readb(this->IO_ADDR_R) != 0xff) + return 1; + } + + return 0; +} + +#if defined(CONFIG_SYS_NAND_HW_ECC_OOBFIRST) +static int nand_read_page(int block, int page, uchar *dst) +{ + struct nand_chip *this = mtd.priv; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + uint8_t *p = dst; + + nand_command(block, page, 0, NAND_CMD_READOOB); + this->read_buf(&mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); + nand_command(block, page, 0, NAND_CMD_READ0); + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + this->ecc.hwctl(&mtd, NAND_ECC_READ); + this->read_buf(&mtd, p, eccsize); + this->ecc.calculate(&mtd, p, &ecc_calc[i]); + this->ecc.correct(&mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} +#else +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = mtd.priv; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + uint8_t *p = dst; + + nand_command(block, page, 0, NAND_CMD_READ0); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + if (this->ecc.mode != NAND_ECC_SOFT) + this->ecc.hwctl(&mtd, NAND_ECC_READ); + this->read_buf(&mtd, p, eccsize); + this->ecc.calculate(&mtd, p, &ecc_calc[i]); + } + this->read_buf(&mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + eccsteps = ECCSTEPS; + p = dst; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + /* No chance to do something with the possible error message + * from correct_data(). We just hope that all possible errors + * are corrected by this routine. + */ + this->ecc.correct(&mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} +#endif + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ + unsigned int block, lastblock; + unsigned int page; + + /* + * offs has to be aligned to a page address! + */ + block = offs / CONFIG_SYS_NAND_BLOCK_SIZE; + lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE; + page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE; + + while (block <= lastblock) { + if (!nand_is_bad_block(block)) { + /* + * Skip bad blocks + */ + while (page < CONFIG_SYS_NAND_PAGE_COUNT) { + nand_read_page(block, page, dst); + dst += CONFIG_SYS_NAND_PAGE_SIZE; + page++; + } + + page = 0; + } else { + lastblock++; + } + + block++; + } + + return 0; +} + +/* nand_init() - initialize data to make nand usable by SPL */ +void nand_init(void) +{ + /* + * Init board specific nand support + */ + mtd.priv = &nand_chip; + nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + board_nand_init(&nand_chip); + +#ifdef CONFIG_SPL_NAND_SOFTECC + if (nand_chip.ecc.mode == NAND_ECC_SOFT) { + nand_chip.ecc.calculate = nand_calculate_ecc; + nand_chip.ecc.correct = nand_correct_data; + } +#endif + + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, 0); +} + +/* Unselect after operation */ +void nand_deselect(void) +{ + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, -1); +} |