diff options
Diffstat (limited to 'package/boot/uboot-xburst/files/cpu/mips/jz4740_nand.c')
-rw-r--r-- | package/boot/uboot-xburst/files/cpu/mips/jz4740_nand.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/package/boot/uboot-xburst/files/cpu/mips/jz4740_nand.c b/package/boot/uboot-xburst/files/cpu/mips/jz4740_nand.c new file mode 100644 index 0000000000..7877194f14 --- /dev/null +++ b/package/boot/uboot-xburst/files/cpu/mips/jz4740_nand.c @@ -0,0 +1,199 @@ +/* + * Platform independend driver for JZ4740. + * + * Copyright (c) 2007 Ingenic Semiconductor Inc. + * Author: <jlwei@ingenic.cn> + * + * 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. + */ +#include <common.h> + +#if defined(CONFIG_CMD_NAND) && defined(CONFIG_JZ4740) + +#include <nand.h> +#include <asm/jz4740.h> +#include <asm/io.h> + +#define PAR_SIZE 9 +#define __nand_ecc_enable() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST ) +#define __nand_ecc_disable() (REG_EMC_NFECR &= ~EMC_NFECR_ECCE) + +#define __nand_select_rs_ecc() (REG_EMC_NFECR |= EMC_NFECR_RS) + +#define __nand_rs_ecc_encoding() (REG_EMC_NFECR |= EMC_NFECR_RS_ENCODING) +#define __nand_rs_ecc_decoding() (REG_EMC_NFECR |= EMC_NFECR_RS_DECODING) +#define __nand_ecc_encode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_ENCF)) +#define __nand_ecc_decode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_DECF)) + +static void jz_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + unsigned long nandaddr = (unsigned long)this->IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + /* Change this to use I/O accessors. */ + if (ctrl & NAND_NCE) + REG_EMC_NFCSR |= EMC_NFCSR_NFCE1; + else + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + } + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + nandaddr |= 0x00008000; + else /* must be ALE */ + nandaddr |= 0x00010000; + + writeb(cmd, (uint8_t *)nandaddr); +} + +static int jz_device_ready(struct mtd_info *mtd) +{ + int ready; + udelay(20); /* FIXME: add 20us delay */ + ready = (REG_GPIO_PXPIN(2) & 0x40000000) ? 1 : 0; + return ready; +} + +/* + * EMC setup + */ +static void jz_device_setup(void) +{ + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE1; + REG_EMC_SMCR1 = 0x094c4400; + /* REG_EMC_SMCR3 = 0x04444400; */ +} + +void board_nand_select_device(struct nand_chip *nand, int chip) +{ + /* + * Don't use "chip" to address the NAND device, + * generate the cs from the address where it is encoded. + */ +} + +static int jzsoc_nand_calculate_rs_ecc(struct mtd_info* mtd, const u_char* dat, + u_char* ecc_code) +{ + volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; + short i; + + __nand_ecc_encode_sync() + __nand_ecc_disable(); + + for(i = 0; i < PAR_SIZE; i++) + ecc_code[i] = *paraddr++; + + return 0; +} + +static void jzsoc_nand_enable_rs_hwecc(struct mtd_info* mtd, int mode) +{ + __nand_ecc_enable(); + __nand_select_rs_ecc(); + + REG_EMC_NFINTS = 0x0; + if (NAND_ECC_READ == mode){ + __nand_rs_ecc_decoding(); + } + if (NAND_ECC_WRITE == mode){ + __nand_rs_ecc_encoding(); + } +} + +/* Correct 1~9-bit errors in 512-bytes data */ +static void jzsoc_rs_correct(unsigned char *dat, int idx, int mask) +{ + int i; + + idx--; + + i = idx + (idx >> 3); + if (i >= 512) + return; + + mask <<= (idx & 0x7); + + dat[i] ^= mask & 0xff; + if (i < 511) + dat[i+1] ^= (mask >> 8) & 0xff; +} + +static int jzsoc_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; + short k; + u32 stat; + /* Set PAR values */ + + for (k = 0; k < PAR_SIZE; k++) { + *paraddr++ = read_ecc[k]; + } + + /* Set PRDY */ + REG_EMC_NFECR |= EMC_NFECR_PRDY; + + /* Wait for completion */ + __nand_ecc_decode_sync(); + __nand_ecc_disable(); + + /* Check decoding */ + stat = REG_EMC_NFINTS; + if (stat & EMC_NFINTS_ERR) { + if (stat & EMC_NFINTS_UNCOR) { + printk("Uncorrectable error occurred\n"); + return -1; + } + else { + u32 errcnt = (stat & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT; + switch (errcnt) { + case 4: + jzsoc_rs_correct(dat, (REG_EMC_NFERR3 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR3 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + case 3: + jzsoc_rs_correct(dat, (REG_EMC_NFERR2 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR2 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + case 2: + jzsoc_rs_correct(dat, (REG_EMC_NFERR1 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR1 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + case 1: + jzsoc_rs_correct(dat, (REG_EMC_NFERR0 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR0 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + return 0; + default: + break; + } + } + } + /* no error need to be correct */ + return 0; +} + +/* + * Main initialization routine + */ +int board_nand_init(struct nand_chip *nand) +{ + jz_device_setup(); + + nand->cmd_ctrl = jz_hwcontrol; + nand->dev_ready = jz_device_ready; + + /* FIXME: should use NAND_ECC_SOFT */ + nand->ecc.hwctl = jzsoc_nand_enable_rs_hwecc; + nand->ecc.correct = jzsoc_nand_rs_correct_data; + nand->ecc.calculate = jzsoc_nand_calculate_rs_ecc; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 512; + nand->ecc.bytes = 9; + + /* 20 us command delay time */ + nand->chip_delay = 20; + + return 0; +} +#endif /* (CONFIG_SYS_CMD_NAND) */ |