diff options
author | Jim Lin <jilin@nvidia.com> | 2011-07-11 18:39:23 +0800 |
---|---|---|
committer | Simon Glass <sjg@chromium.org> | 2011-08-29 10:59:11 -0700 |
commit | 060e61562de8a27401c72998c4f52eb52274f29d (patch) | |
tree | bb3402de4573766a0a2f1ffa04bd9ac7eacd14c7 /board | |
parent | 85f57fbab5f770e8bbdb2c4f196e834d6968b7b3 (diff) |
Chromium: tegra: nand: Add NAND ECC support on data area
BootROM code supports ECC on data area.
Original u-boot code doesn't support NAND ECC function on data area.
This will cause data not the same after reading back in u-boot NAND function.
BUG=chrome-os-partner:3221
TEST=Ran the following in u-boot command console to see whether system is
bootable.
nand read 500000 0 500000
nand erase 0 500000
nand write 500000 0 500000
nand read 10000000 0 500000
cmp.b 500000 10000000 500000
check whether data bytes are same.
If yes, press RESET button to see whether system is bootable.
Change-Id: Id06fc58ed35dc0c17694b52aaf26ce1abd590260
Signed-off-by: Jim Lin <jilin@nvidia.com>
Reviewed-on: http://gerrit.chromium.org/gerrit/3853
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Tom Warren <twarren@nvidia.com>
Reviewed-on: http://gerrit.chromium.org/gerrit/4674
Tested-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Anton Staaf <robotboy@chromium.org>
Diffstat (limited to 'board')
-rw-r--r-- | board/nvidia/common/Makefile | 1 | ||||
-rw-r--r-- | board/nvidia/common/tegra2_nand.c | 962 | ||||
-rw-r--r-- | board/nvidia/common/tegra2_nand.h | 280 |
3 files changed, 1243 insertions, 0 deletions
diff --git a/board/nvidia/common/Makefile b/board/nvidia/common/Makefile index d8e97009d93..44888a535fb 100644 --- a/board/nvidia/common/Makefile +++ b/board/nvidia/common/Makefile @@ -33,6 +33,7 @@ COBJS-$(CONFIG_TEGRA2_I2C) += pmu.o COBJS-$(CONFIG_TEGRA2_I2C) += emc.o COBJS-$(CONFIG_TEGRA2_LP0) += crypto/aes_ref.o COBJS-$(CONFIG_TEGRA2_LP0) += crypto/crypto.o +COBJS-$(CONFIG_TEGRA2_NAND) += tegra2_nand.o COBJS := $(COBJS-y) SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) diff --git a/board/nvidia/common/tegra2_nand.c b/board/nvidia/common/tegra2_nand.c new file mode 100644 index 00000000000..9e95c99b45f --- /dev/null +++ b/board/nvidia/common/tegra2_nand.c @@ -0,0 +1,962 @@ +/* + * (C) Copyright 2006 Detlev Zundel, dzu@denx.de + * (C) Copyright 2006 DENX Software Engineering + * (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <nand.h> +#include <asm/arch/clk_rst.h> +#include <asm/arch/clock.h> +#include <asm/clocks.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/gpio.h> +#include <asm/errno.h> +#include "tegra2_nand.h" + +#define NAND_CMD_TIMEOUT_MS 10 + +static struct nand_ecclayout eccoob = { + .eccbytes = CONFIG_NAND_RS_DATA_ECC_BYTES + + CONFIG_NAND_TAG_ECC_BYTES, + .eccpos = { + 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, + 60, 61, 62, 63, + }, + .oobavail = CONFIG_NAND_TAG_BYTES, + .oobfree = { + { .offset = (CONFIG_NAND_SKIPPED_SPARE_BYTES + + CONFIG_NAND_RS_DATA_ECC_BYTES), + .length = CONFIG_NAND_TAG_BYTES, + }, + }, +}; + +enum { + ECC_OK, + ECC_TAG_ERROR = 1 << 0, + ECC_DATA_ERROR = 1 << 1 +}; + +struct nand_info { + struct nand_ctlr *reg; +/* + * When running in PIO mode to get READ ID bytes from register + * RESP_0, we need this variable as an index to know which byte in + * register RESP_0 should be read. + * Because common code in nand_base.c invokes read_byte function two times + * for NAND_CMD_READID. + * And our controller returns 4 bytes at once in register RESP_0. + */ + int pio_byte_index; + int bus_width; /* n bits */ + int page_data_bytes; /* n bytes in the data area of each page */ + int page_spare_bytes;/* n bytes in the spare area of each page */ + int skipped_spare_bytes; /* At the beginning of spare area. */ + int rs_data_ecc_bytes; /* How many ECC bytes for data area */ + int tag_bytes; /* How many tag bytes in spare area */ + int tag_ecc_bytes; /* How many ECC bytes for tag bytes */ + int wp_gpio; /* GPIO to enable WRITE */ +}; + +struct nand_info nand_ctrl; + +/** + * nand_waitfor_cmd_completion - wait for command completion + * @param reg: nand_ctlr structure + * @return: + * 1 - Command completed + * 0 - Timeout + */ +static int nand_waitfor_cmd_completion(struct nand_ctlr *reg) +{ + int i; + u32 reg_val; + + for (i = 0; i < NAND_CMD_TIMEOUT_MS * 1000; i++) { + if ((readl(®->command) & CMD_GO) || + !(readl(®->status) & + STATUS_RBSY0) || + !(readl(®->isr) & + ISR_IS_CMD_DONE)) { + udelay(1); + continue; + } + reg_val = readl(®->dma_mst_ctrl); + /* + * If DMA_MST_CTRL_EN_A_ENABLE or + * DMA_MST_CTRL_EN_B_ENABLE is set, + * that means DMA engine is running, then we + * have to wait until + * DMA_MST_CTRL_IS_DMA_DONE + * is cleared for DMA transfer completion. + */ + if (reg_val & (DMA_MST_CTRL_EN_A_ENABLE | + DMA_MST_CTRL_EN_B_ENABLE)) { + if (reg_val & DMA_MST_CTRL_IS_DMA_DONE) + return 1; + } else + return 1; + udelay(1); + } + return 0; +} + +/** + * nand_read_byte - [DEFAULT] read one byte from the chip + * @param mtd: MTD device structure + * @return: data byte + * + * Default read function for 8bit bus-width + */ +static uint8_t nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + int dword_read; + struct nand_info *nand_config; + + nand_config = (struct nand_info *) chip->priv; + + dword_read = readl(&nand_config->reg->resp); + dword_read = dword_read >> (8 * nand_config->pio_byte_index); + nand_config->pio_byte_index++; + return (uint8_t) dword_read; +} + +/** + * nand_write_buf - [DEFAULT] write buffer to chip + * @param mtd: MTD device structure + * @param buf: data buffer + * @param len: number of bytes to write + * + * Default write function for 8bit bus-width + */ +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + int i, j, l; + struct nand_chip *chip = mtd->priv; + struct nand_info *nand_config; + + nand_config = (struct nand_info *) chip->priv; + + for (i = 0; i < len / 4; i++) { + l = ((int *)buf)[i]; + writel(l, &nand_config->reg->resp); + writel(CMD_GO | CMD_PIO | CMD_TX | + (CMD_TRANS_SIZE_BYTES4 << + CMD_TRANS_SIZE_SHIFT) + | CMD_A_VALID | CMD_CE0, + &nand_config->reg->command); + + if (!nand_waitfor_cmd_completion(nand_config->reg)) + printf("Command timeout during write_buf\n"); + } + if (len & 3) { + l = 0; + for (j = 0; j < (len & 3); j++) + l |= (((int) buf[i * 4 + j]) << (8 * j)); + + writel(l, &nand_config->reg->resp); + writel(CMD_GO | CMD_PIO | CMD_TX | + (((len & 3) - 1) << CMD_TRANS_SIZE_SHIFT) | + CMD_A_VALID | CMD_CE0, + &nand_config->reg->command); + if (!nand_waitfor_cmd_completion(nand_config->reg)) + printf("Command timeout during write_buf\n"); + } +} + +/** + * nand_read_buf - [DEFAULT] read chip data into buffer + * @param mtd: MTD device structure + * @param buf: buffer to store date + * @param len: number of bytes to read + * + * Default read function for 8bit bus-width + */ +static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i, j, l; + struct nand_chip *chip = mtd->priv; + int *buf_dword; + struct nand_info *nand_config; + + nand_config = (struct nand_info *) chip->priv; + + buf_dword = (int *) buf; + for (i = 0; i < len / 4; i++) { + writel(CMD_GO | CMD_PIO | CMD_RX | + (CMD_TRANS_SIZE_BYTES4 << + CMD_TRANS_SIZE_SHIFT) + | CMD_A_VALID | CMD_CE0, + &nand_config->reg->command); + if (!nand_waitfor_cmd_completion(nand_config->reg)) + printf("Command timeout during read_buf\n"); + l = readl(&nand_config->reg->resp); + buf_dword[i] = l; + } + if (len & 3) { + writel(CMD_GO | CMD_PIO | CMD_RX | + (((len & 3) - 1) << CMD_TRANS_SIZE_SHIFT) | + CMD_A_VALID | CMD_CE0, + &nand_config->reg->command); + if (!nand_waitfor_cmd_completion(nand_config->reg)) + printf("Command timeout during read_buf\n"); + l = readl(&nand_config->reg->resp); + for (j = 0; j < (len & 3); j++) + buf[i * 4 + j] = (char) (l >> (8 * j)); + } +} + +/** + * nand_dev_ready - check NAND status is ready or not + * @param mtd: MTD device structure + * @return: + * 1 - ready + * 0 - not ready + */ +static int nand_dev_ready(struct mtd_info *mtd) +{ + register struct nand_chip *chip = mtd->priv; + int reg_val; + struct nand_info *nand_config; + + nand_config = (struct nand_info *) chip->priv; + + reg_val = readl(&nand_config->reg->status); + if (reg_val & STATUS_RBSY0) + return 1; + else + return 0; +} + +/* Hardware specific access to control-lines */ +static void nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ +} + +/** + * nand_clear_interrupt_status - clear all interrupt status bits + * @param reg: nand_ctlr structure + */ +static void nand_clear_interrupt_status(struct nand_ctlr *reg) +{ + u32 reg_val; + + /* Clear interrupt status */ + reg_val = readl(®->isr); + writel(reg_val, ®->isr); +} + +/** + * nand_command - [DEFAULT] Send command to NAND device + * @param mtd: MTD device structure + * @param command: the command to be sent + * @param column: the column address for this command, -1 if none + * @param page_addr: the page address for this command, -1 if none + */ +static void nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd->priv; + struct nand_info *nand_config; + + nand_config = (struct nand_info *) chip->priv; + + /* + * Write out the command to the device. + */ + if (mtd->writesize < 2048) { + /* + * Only command NAND_CMD_RESET or NAND_CMD_READID will come + * here before mtd->writesize is initialized, we don't have + * any action here because page size of NAND HY27UF084G2B + * is 2048 bytes and mtd->writesize will be 2048 after + * initialized. + */ + } else { + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Adjust columns for 16 bit bus-width */ + if (column != -1 && (chip->options & NAND_BUSWIDTH_16)) + column >>= 1; + } + + nand_clear_interrupt_status(nand_config->reg); + + /* Stop DMA engine, clear DMA completion status */ + writel(DMA_MST_CTRL_EN_A_DISABLE + | DMA_MST_CTRL_EN_B_DISABLE + | DMA_MST_CTRL_IS_DMA_DONE, + &nand_config->reg->dma_mst_ctrl); + + /* + * Program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + case NAND_CMD_READID: + writel(NAND_CMD_READID, &nand_config->reg->cmd_reg1); + writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_PIO + | CMD_RX | + (CMD_TRANS_SIZE_BYTES4 << CMD_TRANS_SIZE_SHIFT) + | CMD_CE0, + &nand_config->reg->command); + nand_config->pio_byte_index = 0; + break; + case NAND_CMD_READ0: + writel(NAND_CMD_READ0, &nand_config->reg->cmd_reg1); + writel(NAND_CMD_READSTART, &nand_config->reg->cmd_reg2); + writel((page_addr << 16) | (column & 0xFFFF), + &nand_config->reg->addr_reg1); + writel(page_addr >> 16, &nand_config->reg->addr_reg2); + return; + case NAND_CMD_SEQIN: + writel(NAND_CMD_SEQIN, &nand_config->reg->cmd_reg1); + writel(NAND_CMD_PAGEPROG, &nand_config->reg->cmd_reg2); + writel((page_addr << 16) | (column & 0xFFFF), + &nand_config->reg->addr_reg1); + writel(page_addr >> 16, + &nand_config->reg->addr_reg2); + return; + case NAND_CMD_PAGEPROG: + return; + case NAND_CMD_ERASE1: + writel(NAND_CMD_ERASE1, &nand_config->reg->cmd_reg1); + writel(NAND_CMD_ERASE2, &nand_config->reg->cmd_reg2); + writel(page_addr, &nand_config->reg->addr_reg1); + writel(CMD_GO | CMD_CLE | CMD_ALE | + CMD_SEC_CMD | CMD_CE0 | CMD_ALE_BYTES3, + &nand_config->reg->command); + break; + case NAND_CMD_RNDOUT: + return; + case NAND_CMD_ERASE2: + return; + case NAND_CMD_STATUS: + writel(NAND_CMD_STATUS, &nand_config->reg->cmd_reg1); + writel(CMD_GO | CMD_CLE | CMD_PIO | CMD_RX + | (CMD_TRANS_SIZE_BYTES1 << + CMD_TRANS_SIZE_SHIFT) + | CMD_CE0, + &nand_config->reg->command); + nand_config->pio_byte_index = 0; + break; + case NAND_CMD_RESET: + writel(NAND_CMD_RESET, &nand_config->reg->cmd_reg1); + writel(CMD_GO | CMD_CLE | CMD_CE0, + &nand_config->reg->command); + break; + default: + return; + } + if (!nand_waitfor_cmd_completion(nand_config->reg)) + printf("Command 0x%02X timeout\n", command); +} + +/* + * blank_check - check whether the pointed buffer are all FF (blank). + * @param: buf - data buffer for blank check + * @param: len - length of the buffer in byte + * @return: + * 1 - blank + * 0 - non-blank + */ +static int blank_check(u8 *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + if (buf[i] != 0xFF) + return 0; + return 1; +} + +/* + * check_ecc_error - After a DMA transfer for read, we call this function to + * see whether there is any uncorrectable error on the pointed data buffer + * or oob buffer. + * + * @param reg: nand_ctlr structure + * @param databuf: data buffer + * @param a_len: data buffer length + * @param oobbuf: oob buffer + * @param b_len: oob buffer length + * @return: + * ECC_OK - no ECC error or correctable ECC error + * ECC_TAG_ERROR - uncorrectable tag ECC error + * ECC_DATA_ERROR - uncorrectable data ECC error + * ECC_DATA_ERROR + ECC_TAG_ERROR - uncorrectable data+tag ECC error + */ +static int check_ecc_error(struct nand_ctlr *reg, u8 *databuf, + int a_len, u8 *oobbuf, int b_len) +{ + int return_val = ECC_OK; + u32 reg_val; + + if (!(readl(®->isr) & ISR_IS_ECC_ERR)) + return ECC_OK; + + reg_val = readl(®->dec_status); + if ((reg_val & DEC_STATUS_A_ECC_FAIL) && databuf) { + reg_val = readl(®->bch_dec_status_buf); + /* + * If uncorrectable error occurs on data area, then see whether + * they are all FF. If all are FF, it's a blank page. + * Not error. + */ + if ((reg_val & BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK) && + !blank_check(databuf, a_len)) + return_val |= ECC_DATA_ERROR; + } + + if ((reg_val & DEC_STATUS_B_ECC_FAIL) && oobbuf) { + reg_val = readl(®->bch_dec_status_buf); + /* + * If uncorrectable error occurs on tag area, then see whether + * they are all FF. If all are FF, it's a blank page. + * Not error. + */ + if ((reg_val & BCH_DEC_STATUS_FAIL_TAG_MASK) && + !blank_check(oobbuf, b_len)) + return_val |= ECC_TAG_ERROR; + } + + return return_val; +} + +/** + * start_command - set GO bit to send command to device + * @param reg: nand_ctlr structure + */ +static void start_command(struct nand_ctlr *reg) +{ + u32 reg_val; + + reg_val = readl(®->command); + reg_val |= CMD_GO; + writel(reg_val, ®->command); +} + +/** + * stop_command - clear command GO bit, DMA GO bit, and DMA completion status + * @param reg: nand_ctlr structure + */ +static void stop_command(struct nand_ctlr *reg) +{ + /* Stop command */ + writel(0, ®->command); + + /* Stop DMA engine and clear DMA completion status */ + writel(DMA_MST_CTRL_GO_DISABLE + | DMA_MST_CTRL_IS_DMA_DONE, + ®->dma_mst_ctrl); +} + +/* + * set_bus_width_page_size - set up NAND bus width and page size + * @param nand_config: nand_info structure + * @param *reg_val: address of reg_val + * @return: value is set in reg_val + */ +static void set_bus_width_page_size(struct nand_info *nand_config, + u32 *reg_val) +{ + if (nand_config->bus_width == 8) + *reg_val = CFG_BUS_WIDTH_8BIT; + else + *reg_val = CFG_BUS_WIDTH_16BIT; + + if (nand_config->page_data_bytes == 256) + *reg_val |= CFG_PAGE_SIZE_256; + else if (nand_config->page_data_bytes == 512) + *reg_val |= CFG_PAGE_SIZE_512; + else if (nand_config->page_data_bytes == 1024) + *reg_val |= CFG_PAGE_SIZE_1024; + else if (nand_config->page_data_bytes == 2048) + *reg_val |= CFG_PAGE_SIZE_2048; +} + +/** + * nand_rw_page - page read/write function + * @param mtd: mtd info structure + * @param chip: nand chip info structure + * @param buf: data buffer + * @param page: page number + * @param with_ecc: 1 to enable ECC, 0 to disable ECC + * @param is_writing: 0 for read, 1 for write + * @return: 0 when successfully completed + * -EIO when command timeout + */ +static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page, int with_ecc, int is_writing) +{ + u32 reg_val; + int tag_size; + struct nand_oobfree *free = chip->ecc.layout->oobfree; + /* 128 is larger than the value that our HW can support. */ + u32 tag_buf[128]; + char *tag_ptr; + struct nand_info *nand_config; + + if (((int) buf) & 0x03) { + printf("buf 0x%X has to be 4-byte aligned\n", (u32) buf); + return -EINVAL; + } + + nand_config = (struct nand_info *) chip->priv; + + /* Need to be 4-byte aligned */ + tag_ptr = (char *) &tag_buf; + + stop_command(nand_config->reg); + + writel((1 << chip->page_shift) - 1, &nand_config->reg->dma_cfg_a); + writel((u32) buf, &nand_config->reg->data_block_ptr); + + if (with_ecc) { + writel((u32) tag_ptr, &nand_config->reg->tag_ptr); + if (is_writing) + memcpy(tag_ptr, chip->oob_poi + free->offset, + nand_config->tag_bytes + + nand_config->tag_ecc_bytes); + } else + writel((u32) chip->oob_poi, &nand_config->reg->tag_ptr); + + set_bus_width_page_size(nand_config, ®_val); + + /* Set ECC selection, configure ECC settings */ + if (with_ecc) { + tag_size = nand_config->tag_bytes + nand_config->tag_ecc_bytes; + reg_val |= (CFG_SKIP_SPARE_SEL_4 + | CFG_SKIP_SPARE_ENABLE + | CFG_HW_ECC_CORRECTION_ENABLE + | CFG_ECC_EN_TAG_DISABLE + | CFG_HW_ECC_SEL_RS + | CFG_HW_ECC_ENABLE + | CFG_TVAL4 + | (tag_size - 1)); + + if (!is_writing) { + tag_size += nand_config->skipped_spare_bytes; + invalidate_dcache_range((unsigned long) tag_ptr, + ((unsigned long) tag_ptr) + tag_size); + } else + flush_dcache_range((unsigned long) tag_ptr, + ((unsigned long) tag_ptr) + tag_size); + } else { + tag_size = mtd->oobsize; + reg_val |= (CFG_SKIP_SPARE_DISABLE + | CFG_HW_ECC_CORRECTION_DISABLE + | CFG_ECC_EN_TAG_DISABLE + | CFG_HW_ECC_DISABLE + | (tag_size - 1)); + if (!is_writing) { + invalidate_dcache_range((unsigned long) chip->oob_poi, + ((unsigned long) chip->oob_poi) + tag_size); + } else { + flush_dcache_range((unsigned long) chip->oob_poi, + ((unsigned long) chip->oob_poi) + tag_size); + } + } + writel(reg_val, &nand_config->reg->config); + + if (!is_writing) { + invalidate_dcache_range((unsigned long) buf, + ((unsigned long) buf) + + (1 << chip->page_shift)); + } else { + flush_dcache_range((unsigned long) buf, + ((unsigned long) buf) + + (1 << chip->page_shift)); + } + + writel(BCH_CONFIG_BCH_ECC_DISABLE, &nand_config->reg->bch_config); + + writel(tag_size - 1, &nand_config->reg->dma_cfg_b); + + nand_clear_interrupt_status(nand_config->reg); + + reg_val = CMD_CLE | CMD_ALE + | CMD_SEC_CMD + | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT) + | CMD_A_VALID + | CMD_B_VALID + | (CMD_TRANS_SIZE_BYTES_PAGE_SIZE_SEL << + CMD_TRANS_SIZE_SHIFT) + | CMD_CE0; + if (!is_writing) + reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX); + else + reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX); + writel(reg_val, &nand_config->reg->command); + + /* Setup DMA engine */ + reg_val = DMA_MST_CTRL_GO_ENABLE + | DMA_MST_CTRL_BURST_8WORDS + | DMA_MST_CTRL_EN_A_ENABLE + | DMA_MST_CTRL_EN_B_ENABLE; + + if (!is_writing) + reg_val |= DMA_MST_CTRL_DIR_READ; + else + reg_val |= DMA_MST_CTRL_DIR_WRITE; + + writel(reg_val, &nand_config->reg->dma_mst_ctrl); + + start_command(nand_config->reg); + + if (!nand_waitfor_cmd_completion(nand_config->reg)) { + if (!is_writing) + printf("Read Page 0x%X timeout ", page); + else + printf("Write Page 0x%X timeout ", page); + if (with_ecc) + printf("with ECC"); + else + printf("without ECC"); + printf("\n"); + return -EIO; + } + + if (with_ecc && !is_writing) { + memcpy(chip->oob_poi, tag_ptr, + nand_config->skipped_spare_bytes); + memcpy(chip->oob_poi + free->offset, + tag_ptr + nand_config->skipped_spare_bytes, + nand_config->tag_bytes); + reg_val = (u32) check_ecc_error(nand_config->reg, (u8 *) buf, + 1 << chip->page_shift, + (u8 *) (tag_ptr + nand_config->skipped_spare_bytes), + nand_config->tag_bytes); + if (reg_val & ECC_TAG_ERROR) + printf("Read Page 0x%X tag ECC error\n", page); + if (reg_val & ECC_DATA_ERROR) + printf("Read Page 0x%X data ECC error\n", + page); + if (reg_val & (ECC_DATA_ERROR | ECC_TAG_ERROR)) + return -EIO; + } + return 0; +} + +/** + * nand_read_page_hwecc - hardware ecc based page read function + * @param mtd: mtd info structure + * @param chip: nand chip info structure + * @param buf: buffer to store read data + * @param page: page number to read + * @return: 0 when successfully completed + * -EIO when command timeout + */ +static int nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int page) +{ + return nand_rw_page(mtd, chip, buf, page, 1, 0); +} + +/** + * nand_write_page_hwecc - hardware ecc based page write function + * @param mtd: mtd info structure + * @param chip: nand chip info structure + * @param buf: data buffer + */ +static void nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + int page; + struct nand_info *nand_config; + + nand_config = (struct nand_info *) chip->priv; + + page = (readl(&nand_config->reg->addr_reg1) >> 16) | + (readl(&nand_config->reg->addr_reg2) << 16); + + nand_rw_page(mtd, chip, (uint8_t *) buf, page, 1, 1); +} + + +/** + * nand_read_page_raw - read raw page data without ecc + * @param mtd: mtd info structure + * @param chip: nand chip info structure + * @param buf: buffer to store read data + * @param page: page number to read + * @return: 0 when successfully completed + * -EINVAL when chip->oob_poi is not double-word aligned + * -EIO when command timeout + */ +static int nand_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int page) +{ + return nand_rw_page(mtd, chip, buf, page, 0, 0); +} + +/** + * nand_write_page_raw - raw page write function + * @param mtd: mtd info structure + * @param chip: nand chip info structure + * @param buf: data buffer + */ +static void nand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + int page; + struct nand_info *nand_config; + + nand_config = (struct nand_info *) chip->priv; + page = (readl(&nand_config->reg->addr_reg1) >> 16) | + (readl(&nand_config->reg->addr_reg2) << 16); + + nand_rw_page(mtd, chip, (uint8_t *) buf, page, 0, 1); +} + +/** + * nand_rw_oob - OOB data read/write function + * @param mtd: mtd info structure + * @param chip: nand chip info structure + * @param page: page number to read + * @param with_ecc: 1 to enable ECC, 0 to disable ECC + * @param is_writing: 0 for read, 1 for write + * @return: 0 when successfully completed + * -EINVAL when chip->oob_poi is not double-word aligned + * -EIO when command timeout + */ +static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int with_ecc, int is_writing) +{ + u32 reg_val; + int tag_size; + struct nand_oobfree *free = chip->ecc.layout->oobfree; + struct nand_info *nand_config; + + if (((int) chip->oob_poi) & 0x03) + return -EINVAL; + + nand_config = (struct nand_info *) chip->priv; + stop_command(nand_config->reg); + + writel((u32) chip->oob_poi, &nand_config->reg->tag_ptr); + + set_bus_width_page_size(nand_config, ®_val); + + /* Set ECC selection */ + tag_size = mtd->oobsize; + if (with_ecc) + reg_val |= CFG_ECC_EN_TAG_ENABLE; + else + reg_val |= (CFG_ECC_EN_TAG_DISABLE); + + reg_val |= ((tag_size - 1) | + CFG_SKIP_SPARE_DISABLE | + CFG_HW_ECC_CORRECTION_DISABLE | + CFG_HW_ECC_DISABLE); + writel(reg_val, &nand_config->reg->config); + + if (!is_writing) + invalidate_dcache_range((unsigned long) chip->oob_poi, + ((unsigned long) chip->oob_poi) + tag_size); + else + flush_dcache_range((unsigned long) chip->oob_poi, + ((unsigned long) chip->oob_poi) + tag_size); + + writel(BCH_CONFIG_BCH_ECC_DISABLE, &nand_config->reg->bch_config); + + if (is_writing && with_ecc) + tag_size -= nand_config->tag_ecc_bytes; + + writel(tag_size - 1, &nand_config->reg->dma_cfg_b); + + nand_clear_interrupt_status(nand_config->reg); + + reg_val = CMD_CLE | CMD_ALE + | CMD_SEC_CMD + | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT) + | CMD_B_VALID + | CMD_CE0; + if (!is_writing) + reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX); + else + reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX); + writel(reg_val, &nand_config->reg->command); + + /* Setup DMA engine */ + reg_val = DMA_MST_CTRL_GO_ENABLE + | DMA_MST_CTRL_BURST_8WORDS + | DMA_MST_CTRL_EN_B_ENABLE; + if (!is_writing) + reg_val |= DMA_MST_CTRL_DIR_READ; + else + reg_val |= DMA_MST_CTRL_DIR_WRITE; + + writel(reg_val, &nand_config->reg->dma_mst_ctrl); + + start_command(nand_config->reg); + + if (!nand_waitfor_cmd_completion(nand_config->reg)) { + if (!is_writing) + printf("Read OOB of Page 0x%X timeout\n", page); + else + printf("Write OOB of Page 0x%X timeout\n", page); + return -EIO; + } + + if (with_ecc && !is_writing) { + reg_val = (u32) check_ecc_error(nand_config->reg, 0, 0, + (u8 *) (chip->oob_poi + free->offset), + nand_config->tag_bytes); + if (reg_val & ECC_TAG_ERROR) + printf("Read OOB of Page 0x%X tag ECC error\n", page); + } + return 0; +} + +/** + * nand_read_oob - OOB data read function + * @param mtd: mtd info structure + * @param chip: nand chip info structure + * @param page: page number to read + * @param sndcmd: flag whether to issue read command or not + * @return: 1 - issue read command next time + * 0 - not to issue + */ +static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + nand_rw_oob(mtd, chip, page, 0, 0); + return sndcmd; +} + +/** + * nand_write_oob - OOB data write function + * @param mtd: mtd info structure + * @param chip: nand chip info structure + * @param page: page number to write + * @return: 0 when successfully completed + * -EINVAL when chip->oob_poi is not double-word aligned + * -EIO when command timeout + */ +static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + + return nand_rw_oob(mtd, chip, page, 0, 1); +} + +/* + * Board-specific NAND initialization. + * @param nand: nand chip info structure + * @return: 0, after initialized. + */ +int board_nand_init(struct nand_chip *nand) +{ + struct nand_info *nand_config = &nand_ctrl; + u32 reg_val, clk_rate, clk_period; + + /* get configuration settings from header file */ + nand_config->reg = (struct nand_ctlr *) CONFIG_SYS_NAND_BASE; + nand_config->bus_width = CONFIG_NAND_BUS_WIDTH; + nand_config->page_data_bytes = CONFIG_NAND_PAGE_DATA_BYTES; + nand_config->page_spare_bytes = CONFIG_NAND_PAGE_SPARE_BYTES; + nand_config->skipped_spare_bytes = CONFIG_NAND_SKIPPED_SPARE_BYTES; + nand_config->rs_data_ecc_bytes = CONFIG_NAND_RS_DATA_ECC_BYTES; + nand_config->tag_bytes = CONFIG_NAND_TAG_BYTES; + nand_config->tag_ecc_bytes = CONFIG_NAND_TAG_ECC_BYTES; + + /* Adjust controller clock rate */ + clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, CLK_52M); + + /* Adjust timing for NAND device */ + clk_rate = (u32) clock_get_periph_rate(PERIPH_ID_NDFLASH, + CLOCK_ID_PERIPH) / 1000000; + clk_period = 1000 / clk_rate; + reg_val = ((CONFIG_NAND_MAX_TRP_TREA / clk_period) << + TIMING_TRP_RESP_CNT_SHIFT) & TIMING_TRP_RESP_CNT_MASK; + reg_val |= ((CONFIG_NAND_TWB / clk_period) << + TIMING_TWB_CNT_SHIFT) & TIMING_TWB_CNT_MASK; + if ((CONFIG_NAND_MAX_TCR_TAR_TRR / clk_period) > 2) + reg_val |= (((CONFIG_NAND_MAX_TCR_TAR_TRR / clk_period) - 2) + << TIMING_TCR_TAR_TRR_CNT_SHIFT) & + TIMING_TCR_TAR_TRR_CNT_MASK; + reg_val |= ((CONFIG_NAND_TWHR / clk_period) << + TIMING_TWHR_CNT_SHIFT) & TIMING_TWHR_CNT_MASK; + if ((CONFIG_NAND_MAX_TCS_TCH_TALS_TALH / clk_period) > 1) + reg_val |= (((CONFIG_NAND_MAX_TCS_TCH_TALS_TALH / clk_period) + - 1) << TIMING_TCS_CNT_SHIFT) & TIMING_TCS_CNT_MASK; + reg_val |= ((CONFIG_NAND_TWH / clk_period) << + TIMING_TWH_CNT_SHIFT) & TIMING_TWH_CNT_MASK; + reg_val |= ((CONFIG_NAND_TWP / clk_period) << + TIMING_TWP_CNT_SHIFT) & TIMING_TWP_CNT_MASK; + reg_val |= ((CONFIG_NAND_TRH / clk_period) << + TIMING_TRH_CNT_SHIFT) & TIMING_TRH_CNT_MASK; + reg_val |= ((CONFIG_NAND_MAX_TRP_TREA / clk_period) << + TIMING_TRP_CNT_SHIFT) & TIMING_TRP_CNT_MASK; + writel(reg_val, &nand_config->reg->timing); + + reg_val = 0; + if ((CONFIG_NAND_TADL / clk_period) > 2) + reg_val = ((CONFIG_NAND_TADL / clk_period) - 2) & + TIMING2_TADL_CNT_MASK; + writel(reg_val, &nand_config->reg->timing2); + + /* Pinmux ATC_SEL uses NAND */ + pinmux_set_func(PINGRP_ATC, PMUX_FUNC_NAND); + + nand_config->wp_gpio = CONFIG_NAND_WP_GPIO; + gpio_direction_output(nand_config->wp_gpio, 1); + + nand->cmd_ctrl = nand_hwcontrol; + nand->dev_ready = nand_dev_ready; + + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &eccoob; + nand->ecc.size = CONFIG_NAND_PAGE_DATA_BYTES; + nand->ecc.bytes = CONFIG_NAND_PAGE_SPARE_BYTES; + + nand->options = LP_OPTIONS; + nand->cmdfunc = nand_command; + nand->read_byte = nand_read_byte; + nand->read_buf = nand_read_buf; + nand->write_buf = nand_write_buf; + nand->ecc.read_page = nand_read_page_hwecc; + nand->ecc.write_page = nand_write_page_hwecc; + nand->ecc.read_page_raw = nand_read_page_raw; + nand->ecc.write_page_raw = nand_write_page_raw; + nand->ecc.read_oob = nand_read_oob; + nand->ecc.write_oob = nand_write_oob; + nand->priv = &nand_ctrl; + return 0; +} diff --git a/board/nvidia/common/tegra2_nand.h b/board/nvidia/common/tegra2_nand.h new file mode 100644 index 00000000000..9ecb80c50f9 --- /dev/null +++ b/board/nvidia/common/tegra2_nand.h @@ -0,0 +1,280 @@ +enum { + Bit0 = 1 << 0, + Bit1 = 1 << 1, + Bit2 = 1 << 2, + Bit3 = 1 << 3, + Bit4 = 1 << 4, + Bit5 = 1 << 5, + Bit6 = 1 << 6, + Bit7 = 1 << 7, + Bit8 = 1 << 8, + Bit9 = 1 << 9, + Bit10 = 1 << 10, + Bit11 = 1 << 11, + Bit12 = 1 << 12, + Bit13 = 1 << 13, + Bit14 = 1 << 14, + Bit15 = 1 << 15, + Bit16 = 1 << 16, + Bit17 = 1 << 17, + Bit18 = 1 << 18, + Bit19 = 1 << 19, + Bit20 = 1 << 20, + Bit21 = 1 << 21, + Bit22 = 1 << 22, + Bit23 = 1 << 23, + Bit24 = 1 << 24, + Bit25 = 1 << 25, + Bit26 = 1 << 26, + Bit27 = 1 << 27, + Bit28 = 1 << 28, + Bit29 = 1 << 29, + Bit30 = 1 << 30, + Bit31 = 1 << 31 +}; + +/* register offset */ +#define COMMAND_0 0x00 +#define CMD_GO Bit31 +#define CMD_CLE Bit30 +#define CMD_ALE Bit29 +#define CMD_PIO Bit28 +#define CMD_TX Bit27 +#define CMD_RX Bit26 +#define CMD_SEC_CMD Bit25 +#define CMD_AFT_DAT_MASK Bit24 +#define CMD_AFT_DAT_DISABLE 0 +#define CMD_AFT_DAT_ENABLE Bit24 +#define CMD_TRANS_SIZE_SHIFT 20 +enum { + CMD_TRANS_SIZE_BYTES1 = 0, + CMD_TRANS_SIZE_BYTES2, + CMD_TRANS_SIZE_BYTES3, + CMD_TRANS_SIZE_BYTES4, + CMD_TRANS_SIZE_BYTES5, + CMD_TRANS_SIZE_BYTES6, + CMD_TRANS_SIZE_BYTES7, + CMD_TRANS_SIZE_BYTES8, + CMD_TRANS_SIZE_BYTES_PAGE_SIZE_SEL +}; + +#define CMD_TRANS_SIZE_BYTES_PAGE_SIZE_SEL 8 +#define CMD_A_VALID Bit19 +#define CMD_B_VALID Bit18 +#define CMD_RD_STATUS_CHK Bit17 +#define CMD_R_BSY_CHK Bit16 +#define CMD_CE7 Bit15 +#define CMD_CE6 Bit14 +#define CMD_CE5 Bit13 +#define CMD_CE4 Bit12 +#define CMD_CE3 Bit11 +#define CMD_CE2 Bit10 +#define CMD_CE1 Bit9 +#define CMD_CE0 Bit8 +#define CMD_CLE_BYTE_SIZE_SHIFT 4 +enum { + CMD_CLE_BYTES1 = 0, + CMD_CLE_BYTES2, + CMD_CLE_BYTES3, + CMD_CLE_BYTES4, +}; +#define CMD_ALE_BYTE_SIZE_SHIFT 0 +enum { + CMD_ALE_BYTES1 = 0, + CMD_ALE_BYTES2, + CMD_ALE_BYTES3, + CMD_ALE_BYTES4, + CMD_ALE_BYTES5, + CMD_ALE_BYTES6, + CMD_ALE_BYTES7, + CMD_ALE_BYTES8 +}; + +#define STATUS_0 0x04 +#define STATUS_RBSY0 Bit8 + +#define ISR_0 0x08 +#define ISR_IS_CMD_DONE Bit5 +#define ISR_IS_ECC_ERR Bit4 + +#define IER_0 0x0C + +#define CFG_0 0x10 +#define CFG_HW_ECC_MASK Bit31 +#define CFG_HW_ECC_DISABLE 0 +#define CFG_HW_ECC_ENABLE Bit31 +#define CFG_HW_ECC_SEL_MASK Bit30 +#define CFG_HW_ECC_SEL_HAMMING 0 +#define CFG_HW_ECC_SEL_RS Bit30 +#define CFG_HW_ECC_CORRECTION_MASK Bit29 +#define CFG_HW_ECC_CORRECTION_DISABLE 0 +#define CFG_HW_ECC_CORRECTION_ENABLE Bit29 +#define CFG_PIPELINE_EN_MASK Bit28 +#define CFG_PIPELINE_EN_DISABLE 0 +#define CFG_PIPELINE_EN_ENABLE Bit28 +#define CFG_ECC_EN_TAG_MASK Bit27 +#define CFG_ECC_EN_TAG_DISABLE 0 +#define CFG_ECC_EN_TAG_ENABLE Bit27 +#define CFG_TVALUE_MASK (Bit25 | Bit24) +enum { + CFG_TVAL4 = 0 << 24, + CFG_TVAL6 = 1 << 24, + CFG_TVAL8 = 2 << 24 +}; +#define CFG_SKIP_SPARE_MASK Bit23 +#define CFG_SKIP_SPARE_DISABLE 0 +#define CFG_SKIP_SPARE_ENABLE Bit23 +#define CFG_COM_BSY_MASK Bit22 +#define CFG_COM_BSY_DISABLE 0 +#define CFG_COM_BSY_ENABLE Bit22 +#define CFG_BUS_WIDTH_MASK Bit21 +#define CFG_BUS_WIDTH_8BIT 0 +#define CFG_BUS_WIDTH_16BIT Bit21 +#define CFG_LPDDR1_MODE_MASK Bit20 +#define CFG_LPDDR1_MODE_DISABLE 0 +#define CFG_LPDDR1_MODE_ENABLE Bit20 +#define CFG_EDO_MODE_MASK Bit19 +#define CFG_EDO_MODE_DISABLE 0 +#define CFG_EDO_MODE_ENABLE Bit19 +#define CFG_PAGE_SIZE_SEL_MASK (Bit18 | Bit17 | Bit16) +enum { + CFG_PAGE_SIZE_256 = 0 << 16, + CFG_PAGE_SIZE_512 = 1 << 16, + CFG_PAGE_SIZE_1024 = 2 << 16, + CFG_PAGE_SIZE_2048 = 3 << 16, + CFG_PAGE_SIZE_4096 = 4 << 16 +}; +#define CFG_SKIP_SPARE_SEL_MASK (Bit15 | Bit14) +enum { + CFG_SKIP_SPARE_SEL_4 = 0 << 14, + CFG_SKIP_SPARE_SEL_8 = 1 << 14, + CFG_SKIP_SPARE_SEL_12 = 2 << 14, + CFG_SKIP_SPARE_SEL_16 = 3 << 14 +}; +#define CFG_TAG_BYTE_SIZE_MASK 0x1FF + +#define TIMING_0 0x14 +#define TIMING_TRP_RESP_CNT_SHIFT 28 +#define TIMING_TRP_RESP_CNT_MASK (Bit31 | Bit30 | Bit29 | Bit28) +#define TIMING_TWB_CNT_SHIFT 24 +#define TIMING_TWB_CNT_MASK (Bit27 | Bit26 | Bit25 | Bit24) +#define TIMING_TCR_TAR_TRR_CNT_SHIFT 20 +#define TIMING_TCR_TAR_TRR_CNT_MASK (Bit23 | Bit22 | Bit21 | Bit20) +#define TIMING_TWHR_CNT_SHIFT 16 +#define TIMING_TWHR_CNT_MASK (Bit19 | Bit18 | Bit17 | Bit16) +#define TIMING_TCS_CNT_SHIFT 14 +#define TIMING_TCS_CNT_MASK (Bit15 | Bit14) +#define TIMING_TWH_CNT_SHIFT 12 +#define TIMING_TWH_CNT_MASK (Bit13 | Bit12) +#define TIMING_TWP_CNT_SHIFT 8 +#define TIMING_TWP_CNT_MASK (Bit11 | Bit10 | Bit9 | Bit8) +#define TIMING_TRH_CNT_SHIFT 4 +#define TIMING_TRH_CNT_MASK (Bit5 | Bit4) +#define TIMING_TRP_CNT_SHIFT 0 +#define TIMING_TRP_CNT_MASK (Bit3 | Bit2 | Bit1 | Bit0) + +#define RESP_0 0x18 + +#define TIMING2_0 0x1C +#define TIMING2_TADL_CNT_SHIFT 0 +#define TIMING2_TADL_CNT_MASK (Bit3 | Bit2 | Bit1 | Bit0) + +#define CMD_REG1_0 0x20 +#define CMD_REG2_0 0x24 +#define ADDR_REG1_0 0x28 +#define ADDR_REG2_0 0x2C + +#define DMA_MST_CTRL_0 0x30 +#define DMA_MST_CTRL_GO_MASK Bit31 +#define DMA_MST_CTRL_GO_DISABLE 0 +#define DMA_MST_CTRL_GO_ENABLE Bit31 +#define DMA_MST_CTRL_DIR_MASK Bit30 +#define DMA_MST_CTRL_DIR_READ 0 +#define DMA_MST_CTRL_DIR_WRITE Bit30 +#define DMA_MST_CTRL_PERF_EN_MASK Bit29 +#define DMA_MST_CTRL_PERF_EN_DISABLE 0 +#define DMA_MST_CTRL_PERF_EN_ENABLE Bit29 +#define DMA_MST_CTRL_REUSE_BUFFER_MASK Bit27 +#define DMA_MST_CTRL_REUSE_BUFFER_DISABLE 0 +#define DMA_MST_CTRL_REUSE_BUFFER_ENABLE Bit27 +#define DMA_MST_CTRL_BURST_SIZE_MASK (Bit26 | Bit25 | Bit24) +enum { + DMA_MST_CTRL_BURST_1WORDS = 2 << 24, + DMA_MST_CTRL_BURST_4WORDS = 3 << 24, + DMA_MST_CTRL_BURST_8WORDS = 4 << 24, + DMA_MST_CTRL_BURST_16WORDS = 5 << 24 +}; +#define DMA_MST_CTRL_IS_DMA_DONE Bit20 +#define DMA_MST_CTRL_EN_A_MASK Bit2 +#define DMA_MST_CTRL_EN_A_DISABLE 0 +#define DMA_MST_CTRL_EN_A_ENABLE Bit2 +#define DMA_MST_CTRL_EN_B_MASK Bit1 +#define DMA_MST_CTRL_EN_B_DISABLE 0 +#define DMA_MST_CTRL_EN_B_ENABLE Bit1 + +#define DMA_CFG_A_0 0x34 +#define DMA_CFG_B_0 0x38 +#define FIFO_CTRL_0 0x3C +#define DATA_BLOCK_PTR_0 0x40 +#define TAG_PTR_0 0x44 +#define ECC_PTR_0 0x48 + +#define DEC_STATUS_0 0x4C +#define DEC_STATUS_A_ECC_FAIL Bit1 +#define DEC_STATUS_B_ECC_FAIL Bit0 + +#define BCH_CONFIG_0 0xCC +#define BCH_CONFIG_BCH_TVALUE_MASK (Bit5 | Bit4) +enum { + BCH_CONFIG_BCH_TVAL4 = 0 << 4, + BCH_CONFIG_BCH_TVAL8 = 1 << 4, + BCH_CONFIG_BCH_TVAL14 = 2 << 4, + BCH_CONFIG_BCH_TVAL16 = 3 << 4 +}; +#define BCH_CONFIG_BCH_ECC_MASK Bit0 +#define BCH_CONFIG_BCH_ECC_DISABLE 0 +#define BCH_CONFIG_BCH_ECC_ENABLE Bit0 + +#define BCH_DEC_RESULT_0 0xD0 +#define BCH_DEC_RESULT_CORRFAIL_ERR_MASK Bit8 +#define BCH_DEC_RESULT_PAGE_COUNT_MASK 0xFF + +#define BCH_DEC_STATUS_BUF_0 0xD4 +#define BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK 0xFF000000 +#define BCH_DEC_STATUS_CORR_SEC_FLAG_MASK 0x00FF0000 +#define BCH_DEC_STATUS_FAIL_TAG_MASK Bit14 +#define BCH_DEC_STATUS_CORR_TAG_MASK Bit13 +#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK (Bit12 | Bit11 | Bit10 | Bit9 | Bit8) +#define BCH_DEC_STATUS_PAGE_NUMBER_MASK 0xFF + +#define LP_OPTIONS (NAND_NO_READRDY | NAND_NO_AUTOINCR) + +struct nand_ctlr { + u32 command; /* offset 00h */ + u32 status; /* offset 04h */ + u32 isr; /* offset 08h */ + u32 ier; /* offset 0Ch */ + u32 config; /* offset 10h */ + u32 timing; /* offset 14h */ + u32 resp; /* offset 18h */ + u32 timing2; /* offset 1Ch */ + u32 cmd_reg1; /* offset 20h */ + u32 cmd_reg2; /* offset 24h */ + u32 addr_reg1; /* offset 28h */ + u32 addr_reg2; /* offset 2Ch */ + u32 dma_mst_ctrl; /* offset 30h */ + u32 dma_cfg_a; /* offset 34h */ + u32 dma_cfg_b; /* offset 38h */ + u32 fifo_ctrl; /* offset 3Ch */ + u32 data_block_ptr; /* offset 40h */ + u32 tag_ptr; /* offset 44h */ + u32 resv1; /* offset 48h */ + u32 dec_status; /* offset 4Ch */ + u32 hwstatus_cmd; /* offset 50h */ + u32 hwstatus_mask; /* offset 54h */ + u32 resv2[29]; + u32 bch_config; /* offset CCh */ + u32 bch_dec_result; /* offset D0h */ + u32 bch_dec_status_buf; + /* offset D4h */ +}; |