diff options
Diffstat (limited to 'drivers/mtd/nand/rda_nand_v2.c')
-rw-r--r-- | drivers/mtd/nand/rda_nand_v2.c | 941 |
1 files changed, 941 insertions, 0 deletions
diff --git a/drivers/mtd/nand/rda_nand_v2.c b/drivers/mtd/nand/rda_nand_v2.c new file mode 100644 index 0000000000..925a1c81c8 --- /dev/null +++ b/drivers/mtd/nand/rda_nand_v2.c @@ -0,0 +1,941 @@ +#include <common.h> +#include <malloc.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/types.h> +#include <nand.h> + +#include <asm/arch/hardware.h> +#include <asm/arch/reg_nand.h> +#include <asm/arch/reg_sysctrl.h> +#include <asm/arch/hwcfg.h> +#include <mtd/nand/rda_nand.h> + +#include "tgt_ap_board_config.h" +#include "tgt_ap_clock_config.h" + +#ifdef CONFIG_SPL_BUILD +#undef CONFIG_NAND_RDA_DMA +#endif + +#ifdef CONFIG_NAND_RDA_DMA +#include <asm/dma-mapping.h> +#include <asm/arch/dma.h> +#endif + +//#define NAND_DEBUG +//#define NAND_DEBUG_VERBOSE + +#define NAND_NEED_OOB_OFF_FIX +#ifdef NAND_NEED_OOB_OFF_FIX +static uint8_t oob_ff_pattern[] = { 0xff, 0xff }; +#define NAND_4K_86_OOB_OFF 0x10B8 +#define NAND_8K_86_OOB_OFF 0x2170 +#define NAND_8K_83_OOB_OFF 0x23C0 +#endif + +#define hal_gettime get_ticks +#define SECOND * CONFIG_SYS_HZ_CLOCK +#define NAND_TIMEOUT ( 2 SECOND ) +#define PLL_BUS_FREQ (_TGT_AP_PLL_BUS_FREQ * 1000000) + +/* clock division value map */ +const static u8 clk_div_map[] = { + 4*60, /* 0 */ + 4*60, /* 1 */ + 4*60, /* 2 */ + 4*60, /* 3 */ + 4*60, /* 4 */ + 4*60, /* 5 */ + 4*60, /* 6 */ + 4*60, /* 7 */ + 4*40, /* 8 */ + 4*30, /* 9 */ + 4*24, /* 10 */ + 4*20, /* 11 */ + 4*17, /* 12 */ + 4*15, /* 13 */ + 4*13, /* 14 */ + 4*12, /* 15 */ + 4*11, /* 16 */ + 4*10, /* 17 */ + 4*9, /* 18 */ + 4*8, /* 19 */ + 4*7, /* 20 */ + 4*13/2, /* 21 */ + 4*6, /* 22 */ + 4*11/2, /* 23 */ + 4*5, /* 24 */ + 4*9/2, /* 25 */ + 4*4, /* 26 */ + 4*7/2, /* 27 */ + 4*3, /* 28 */ + 4*5/2, /* 29 */ + 4*2, /* 30 */ + 4*1, /* 31 */ +}; + +static struct nand_ecclayout rda_nand_oob_40 = { + .eccbytes = 0, + .eccpos = {}, + .oobfree = { + {.offset = 2, + .length = 38} } +}; + +static struct nand_ecclayout rda_nand_oob_64 = { + .eccbytes = 0, + .eccpos = {}, + .oobfree = { + {.offset = 2, + .length = 62} } +}; + +static struct nand_ecclayout rda_nand_oob_128 = { + .eccbytes = 0, + .eccpos = {}, + .oobfree = { + {.offset = 2, + .length = 126} } +}; + +static struct nand_ecclayout rda_nand_oob_256 = { + .eccbytes = 0, + .eccpos = {}, + .oobfree = { + {.offset = 2, + .length = 254} } +}; + +static struct nand_ecclayout rda_nand_oob_512 = { + .eccbytes = 0, + .eccpos = {}, + .oobfree = { + {.offset = 2, + .length = 510} } +}; + +extern void rda_dump_buf(char *data, size_t len); + +static void hal_send_cmd(unsigned char cmd, unsigned int page_addr) +{ + unsigned long cmd_reg; + + cmd_reg = NANDFC_DCMD(cmd) | NANDFC_PAGE_ADDR(page_addr); +#ifdef NAND_DEBUG + printf(" hal_send_cmd 0x%08lx\n", cmd_reg); +#endif + __raw_writel(cmd_reg, NANDFC_REG_DCMD_ADDR); +} + +static void hal_set_col_addr(unsigned int col_addr) +{ + __raw_writel(col_addr, NANDFC_REG_COL_ADDR); +} + +static void hal_flush_buf(struct mtd_info *mtd) +{ + /* + there is no reg NANDFC_REG_BUF_CTRL + */ + //__raw_writel(0x7, NANDFC_REG_BUF_CTRL); +} + +static unsigned long hal_wait_cmd_complete(void) +{ + unsigned long int_stat; + unsigned long long wait_time = NAND_TIMEOUT; + unsigned long long start_time = hal_gettime(); + int timeout = 0; + + /* wait done */ + /* + * Use NANDFC_INT_STAT_IDLE instead of NANDFC_INT_DONE + * this is HW bug, NANDFC_INT_DONE comes too early + * however, NANDFC_INT_STAT_IDLE is not an interrupt source + * need HW fix anyway + */ + do { + int_stat = __raw_readl(NANDFC_REG_INT_STAT); + if (hal_gettime() - start_time >= wait_time) { + timeout = 1; + } +#ifdef CONFIG_MACH_RDA8810E + } while (!(int_stat & NANDFC_INT_STAT_IDLE) && !timeout); +#else + } while (!(int_stat & NANDFC_INT_DONE) && !timeout); +#endif + + /* to clear */ + __raw_writel(int_stat & NANDFC_INT_CLR_MASK, NANDFC_REG_INT_STAT); + + if (timeout) { + printf("nand error, cmd timeout\n"); + return -ETIME; + } + + if (int_stat & NANDFC_INT_ERR_ALL) { + printf("nand error, int_stat = %lx\n", int_stat); + return (int_stat & NANDFC_INT_ERR_ALL); + } + return 0; +} + +static int init_nand_info(struct rda_nand_info *info, + int nand_type, int nand_ecc, int bus_width_16) +{ + info->type = nand_type; + info->ecc_mode = nand_ecc; + info->bus_width_16 = bus_width_16; + switch (nand_type) { + case NAND_TYPE_2K: + info->page_size = 2048; + info->page_shift = 11; + info->oob_size = 64; + break; + case NAND_TYPE_4K: + info->page_size = 4096; + info->page_shift = 12; + info->oob_size = 224; + info->vir_oob_size = 40; + break; + case NAND_TYPE_8K: + info->page_size = 8192; + info->page_shift = 13; + info->oob_size = 744; + break; + default: + printf("invalid nand type %d\n", nand_type); + return -EINVAL; + } + return 0; +} + +static u32 cal_freq_by_divreg(u32 basefreq, u32 reg, u32 div2) +{ + u32 newfreq; + + if (reg >= ARRAY_SIZE(clk_div_map)) { + printf("nand:Invalid div reg: %u\n", reg); + reg = ARRAY_SIZE(clk_div_map) - 1; + } + /* Assuming basefreq is smaller than 2^31 (2.147G Hz) */ + newfreq = (basefreq << (div2 ? 0 : 1)) / (clk_div_map[reg] >> 1); + return newfreq; +} + +unsigned long get_master_clk_rate(u32 reg) +{ + u32 div2; + unsigned long rate; + + div2 = reg & SYS_CTRL_AP_AP_APB2_SRC_SEL; + reg = GET_BITFIELD(reg, SYS_CTRL_AP_AP_APB2_FREQ); + rate = cal_freq_by_divreg(PLL_BUS_FREQ, reg, div2); + + return rate; +} + +static unsigned long rda_nand_cld_div_tbl[16] = { + 3, 4, 5, 6, 7, 8, 9, 10, + 12, 14, 16, 18, 20, 22, 24, 28 +}; + +static unsigned long hal_calc_divider(struct rda_nand_info *info) +{ + unsigned long mclk = info->master_clk; + unsigned long div, clk_div; + int i; + + div = mclk / info->clk; + if (mclk % info->clk) + div += 1; + + if (div < 5) { + /* 5 is minimal divider by V2 hardware */ + div = 5; + } + + for (i=0;i<16;i++) { + if(div <= rda_nand_cld_div_tbl[i]) { + clk_div = i; + break; + } + } + + if (i>=16) { + clk_div = 15; + } + + printf("NAND: max clk: %ld, bus clk: %ld\n", info->clk, mclk); + printf("NAND: div: %ld, clk_div: %ld\n", div, clk_div); + return clk_div; +} + +static int hal_init(struct rda_nand_info *info) +{ + unsigned long config_a, config_b; +#ifdef CONFIG_RDA_FPGA + unsigned long clk_div = 4; +#else + unsigned long clk_div = hal_calc_divider(info); +#endif + + /* setup config_a and config_b */ + config_a = NANDFC_CYCLE(clk_div); + config_a |= NANDFC_CHIP_SEL(0x0e); + config_a |= NANDFC_POLARITY_IO(0); // set 0 invert IO + config_a |= NANDFC_POLARITY_MEM(1); // set 1 invert MEM + + switch (info->type) { + case NAND_TYPE_2K: + config_a |= NANDFC_TIMING(0x8B); + break; + case NAND_TYPE_4K: + config_a |= NANDFC_TIMING(0x8C); + break; + case NAND_TYPE_8K: + config_a |= NANDFC_TIMING(0x8D); + break; + default: + printf("invalid nand type %d\n", info->type); + return -EINVAL; + } + + /* enable the 16bit mode; */ + if (info->bus_width_16) + config_a |= NANDFC_WDITH_16BIT(1); + + config_b = NANDFC_HWECC(1) | NANDFC_ECC_MODE(info->ecc_mode); + + __raw_writel(config_a, NANDFC_REG_CONFIG_A); + __raw_writel(config_b, NANDFC_REG_CONFIG_B); + +#if 1 + /* Set an interval of filter for erasing operation. */ + unsigned long delay; + delay = __raw_readl(NANDFC_REG_DELAY); + delay |= 0x10; + __raw_writel(delay, NANDFC_REG_DELAY); +#endif /* #if 0 */ + + printf("NAND: nand init done, %08lx %08lx\n", config_a, config_b); + + return 0; +} + +static u8 nand_rda_read_byte(struct mtd_info *mtd) +{ + u8 ret; + struct nand_chip *chip = mtd->priv; + struct rda_nand_info *info = chip->priv; + ret = *((u8 *) info->byte_buf + info->index); + info->index++; +#ifdef NAND_DEBUG + printf("nand_read_byte, ret = %02x\n", ret); +#endif + return ret; +} + +static u16 nand_rda_read_word(struct mtd_info *mtd) +{ + u16 ret; + struct nand_chip *chip = mtd->priv; + struct rda_nand_info *info = chip->priv; + ret = *(u16 *) ((uint8_t *) info->byte_buf + info->index); + info->index += 2; +#ifdef NAND_DEBUG + printf("nand_read_word, ret = %04x\n", ret); +#endif + return ret; +} + +static void nand_rda_read_buf(struct mtd_info *mtd, uint8_t * buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct rda_nand_info *info = chip->priv; + u8 *nand_ptr = (u8 *) (chip->IO_ADDR_R + info->read_ptr); + +#ifdef NAND_DEBUG + printf("read : buf addr = 0x%p, len = 0x%x, read_ptr = 0x%x \n", buf, len, + info->read_ptr); +#endif /* NAND_DEBUG */ + +#ifndef CONFIG_NAND_RDA_DMA + memcpy((void *)buf, (void *)nand_ptr, len); + if(info->ecc_mode == NAND_ECC_1K24BIT) + info->read_ptr += 184; + info->read_ptr += len; + +#ifdef NAND_DEBUG_VERBOSE + rda_dump_buf((char *)buf, 2); +#endif + +#else + struct rda_dma_chan_params dma_param; + dma_addr_t phys_addr; + void *addr = (void *)buf; + int ret = 0; + + /* + * If size is less than the size of oob, + * we copy directly them to mapping buffer. + */ + if (len <= mtd->oobsize) { + memcpy(buf, (void *)nand_ptr, len); + info->read_ptr = 0; + return; + } + + if (((u32)addr & 0x7) != 0) { + printf("ERROR, nand dma read buffer %p is not 8bytes aligned\n", + addr); + return; + } + + phys_addr = dma_map_single(addr, len, DMA_FROM_DEVICE); + + dma_param.src_addr = (u32) info->nand_data_phys; + dma_param.dst_addr = phys_addr; + dma_param.xfer_size = len; + //dma_param.dma_mode = RDA_DMA_FR_MODE; + dma_param.dma_mode = RDA_DMA_NOR_MODE; + + ret = rda_set_dma_params(info->dma_ch, &dma_param); + if (ret < 0) { + printf("rda nand : Failed to set parameter\n"); + dma_unmap_single(addr, len, DMA_FROM_DEVICE); + return; + } + + /* use flush to avoid annoying unaligned warning */ + /* however, invalidate after the dma it the right thing to do */ + flush_dcache_range((u32)addr, (u32)(addr + len)); + + rda_start_dma(info->dma_ch); + rda_poll_dma(info->dma_ch); + rda_stop_dma(info->dma_ch); + + /* use flush to avoid annoying unaligned warning */ + //invalidate_dcache_range((u32)addr, (u32)(addr + len)); + + /* Free the specified physical address */ + dma_unmap_single(addr, len, DMA_FROM_DEVICE); + info->read_ptr += len; + + return; +#endif /* CONFIG_NAND_RDA_DMA */ +} + +static void nand_rda_write_buf(struct mtd_info *mtd, const uint8_t * buf, + int len) +{ + struct nand_chip *chip = mtd->priv; + struct rda_nand_info *info = chip->priv; + u8 *nand_ptr = (u8 *) (chip->IO_ADDR_W + info->write_ptr); + +#ifdef NAND_DEBUG + printf("write : buf addr = 0x%p, len = %d, write_ptr = %d\n", buf, len, + info->write_ptr); +#endif /* NAND_DEBUG */ + +#ifdef NAND_DEBUG_VERBOSE + rda_dump_buf((char *)buf, len); +#endif + +#ifdef NAND_NEED_OOB_OFF_FIX + u8 *nand_oob_ptr; + switch (info->type) { + case NAND_TYPE_2K: + break; + case NAND_TYPE_4K: + if(info->write_ptr > 0 && info->ecc_mode == NAND_ECC_1K24BIT){ + nand_ptr = (u8 *)(chip->IO_ADDR_W + NAND_4K_86_OOB_OFF); + len = 40; + } + break; + case NAND_TYPE_8K: + if (info->ecc_mode == NAND_ECC_1K24BIT) + nand_oob_ptr = (u8 *)(chip->IO_ADDR_W + + NAND_8K_86_OOB_OFF); + else + nand_oob_ptr = (u8 *)(chip->IO_ADDR_W + + NAND_8K_83_OOB_OFF); + memcpy((void *)nand_oob_ptr, (void *)oob_ff_pattern, 2); + break; + default: + break; + } +#endif + +#ifndef CONFIG_NAND_RDA_DMA + memcpy((void *)nand_ptr, (void *)buf, len); + info->write_ptr += len; + +#else + struct rda_dma_chan_params dma_param; + dma_addr_t phys_addr; + void *addr = (void *)buf; + int ret = 0; + + /* + * If size is less than the size of oob, + * we copy directly them to mapping buffer. + */ + if (len <= mtd->oobsize) { + memcpy((void *)nand_ptr, (void *)buf, len); + info->write_ptr += len; + return; + } + + if (((u32)addr & 0x7) != 0) { + printf("ERROR, nand dma write %p buffer is not 8bytes aligned\n", + addr); + return; + } + phys_addr = dma_map_single(addr, len, DMA_TO_DEVICE); + + dma_param.src_addr = phys_addr; + dma_param.dst_addr = (u32) info->nand_data_phys; + dma_param.xfer_size = len; + //dma_param.dma_mode = RDA_DMA_FW_MODE; + dma_param.dma_mode = RDA_DMA_NOR_MODE; + + ret = rda_set_dma_params(info->dma_ch, &dma_param); + if (ret < 0) { + printf("rda nand : Failed to set parameter\n"); + dma_unmap_single(addr, len, DMA_TO_DEVICE); + return; + } + + flush_dcache_range((u32)addr, (u32)(addr + len)); + + rda_start_dma(info->dma_ch); + rda_poll_dma(info->dma_ch); + rda_stop_dma(info->dma_ch); + + /* Free the specified physical address */ + dma_unmap_single(addr, len, DMA_TO_DEVICE); + info->write_ptr += len; + + return; +#endif /* CONFIG_NAND_RDA_DMA */ +} + +static void nand_rda_do_cmd_pre(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct rda_nand_info *info = this->priv; + + __raw_writel(0xfff, NANDFC_REG_INT_STAT); + switch (info->cmd) { + case NAND_CMD_SEQIN: + hal_flush_buf(mtd); + info->write_ptr = 0; + break; + case NAND_CMD_READ0: + hal_flush_buf(mtd); + //info->read_ptr = 0; + break; + default: + break; + } +} + +static void nand_rda_do_cmd_post(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct rda_nand_info *info = this->priv; + u32 temp; + + switch (info->cmd) { + case NAND_CMD_RESET: + return; + case NAND_CMD_READID: + temp = __raw_readl(NANDFC_REG_IDCODE_A); + info->byte_buf[0] = temp; + temp = __raw_readl(NANDFC_REG_IDCODE_B); + info->byte_buf[1] = temp; + info->index = 0; + break; + case NAND_CMD_STATUS: + temp = __raw_readl(NANDFC_REG_OP_STATUS); + info->byte_buf[0] = (temp & 0xFF); + info->index = 0; + if (info->byte_buf[0] != 0xe0) { + printf("nand error in op status %x\n", + info->byte_buf[0]); + } +#ifdef NAND_DEBUG + printf("post_cmd read status %x\n", info->byte_buf[0]); +#endif + break; + case NAND_CMD_READ0: // NAND_CMD_READOOB goes here too + break; +#if 0 +/* read back after write, for err checking */ + case NAND_CMD_SEQIN: + { + int cmd_ret; + /* read back instantly and check fcs/crc result */ + hal_send_cmd((unsigned char)NAND_CMD_READ0, + info->page_addr); + cmd_ret = hal_wait_cmd_complete(); + if (cmd_ret) { + printf + ("readback cmd fail, page_addr = %x, ret = %x\n", + info->page_addr, cmd_ret); + } + } + break; +#endif +#if 0 +/* delay 10ms, for erase to complete */ + case NAND_CMD_ERASE1: + udelay(10000); + break; +#endif + default: + break; + } +} + +static void nand_rda_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ +#ifdef NAND_DEBUG + printf("nand_rda_hwcontrol, cmd = %x, ctrl = %x, not use anymore\n", + cmd, ctrl); +#endif +} + +static void nand_rda_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd->priv; + struct rda_nand_info *info = chip->priv; + unsigned long cmd_ret; + +#ifdef NAND_DEBUG + printf("nand_rda_cmdfunc, cmd = %x, page_addr = %x, col_addr = %x\n", + command, page_addr, column); +#endif + + /* remap command */ + switch (command) { + case NAND_CMD_SEQIN: /* 0x80 do nothing, wait for 0x10 */ + info->page_addr = page_addr; + info->col_addr = column; + info->cmd = NAND_CMD_NONE; + info->write_ptr= column; + break; + case NAND_CMD_READSTART: /* hw auto gen 0x30 for read */ + case NAND_CMD_ERASE2: /* hw auto gen 0xd0 for erase */ +#ifdef NAND_DEBUG + printf("erase block, erase_size = %x\n", mtd->erasesize); +#endif + info->cmd = NAND_CMD_NONE; + break; + case NAND_CMD_PAGEPROG: /* 0x10 do the real program */ + info->cmd = NAND_CMD_SEQIN; + info->col_addr = 0; + break; + case NAND_CMD_READOOB: /* Emulate NAND_CMD_READOOB */ + info->page_addr = page_addr; + info->cmd = NAND_CMD_READ0; + /* Offset of oob */ + info->col_addr = column; + info->read_ptr = mtd->writesize; +#ifdef NAND_NEED_OOB_OFF_FIX + switch (info->type) { + case NAND_TYPE_2K: + break; + case NAND_TYPE_4K: + //info->col_addr = NAND_4K_86_OOB_OFF; + info->read_ptr = NAND_4K_86_OOB_OFF; + break; + case NAND_TYPE_8K: + if (info->ecc_mode == NAND_ECC_1K24BIT) { + info->col_addr = NAND_8K_86_OOB_OFF; + info->read_ptr = NAND_8K_86_OOB_OFF; + } + else { + info->col_addr = NAND_8K_83_OOB_OFF; + info->read_ptr = NAND_8K_83_OOB_OFF; + } + break; + default: + break; + } +#endif + break; + case NAND_CMD_READ0: /* Emulate NAND_CMD_READOOB */ + info->read_ptr = 0; + /*fall though, don't need break;*/ + default: + info->page_addr = page_addr; + info->col_addr = column; + info->cmd = command; + break; + } + if (info->cmd == NAND_CMD_NONE) { + return; + } + //info->col_addr >>= 1; +#ifdef NAND_DEBUG + printf("after cmd remap, cmd = %x, page_addr = %x, col_addr = %x\n", + info->cmd, info->page_addr, info->col_addr); +#endif + + if (info->col_addr != -1) { + hal_set_col_addr(info->col_addr); + } + if (info->page_addr == -1) + info->page_addr = 0; + + nand_rda_do_cmd_pre(mtd); + + hal_send_cmd((unsigned char)info->cmd, info->page_addr); + cmd_ret = hal_wait_cmd_complete(); + if (cmd_ret) { + printf("cmd fail, cmd = %x, page_addr = %x, col_addr = %x\n", + info->cmd, info->page_addr, info->col_addr); + } + + nand_rda_do_cmd_post(mtd); + + nand_wait_ready(mtd); +} + +static int nand_rda_dev_ready(struct mtd_info *mtd) +{ + return 1; +} + +static int nand_reset_flash(void) +{ + int ret = 0; + + hal_send_cmd(NAND_CMD_RESET, 0); + ret = hal_wait_cmd_complete(); + if (ret) { + printf("nand_reset_flash failed\n"); + return ret; + } + + return ret; +} + +static int nand_read_id(unsigned int id[2]) +{ + int ret = 0; + + hal_send_cmd(NAND_CMD_READID, 0); + ret = hal_wait_cmd_complete(); + if (ret) { + printf("nand_read_id failed\n"); + return ret; + } + + id[0] = __raw_readl(NANDFC_REG_IDCODE_A); + id[1] = __raw_readl(NANDFC_REG_IDCODE_B); + + printf("NAND: Nand ID: %08x %08x\n", id[0], id[1]); + + return ret; +} + +static void read_ID(struct mtd_info *mtd) +{ + unsigned int id[2]; + + nand_read_id(id); +} + +static int nand_get_type(struct rda_nand_info *info, unsigned int id[2], + int *rda_nand_type, int *rda_nand_ecc, int *bus_width_16) +{ + u16 hwcfg_nand_type; + int metal_id; + + info->spl_adjust_ratio = 2; + metal_id = rda_metal_id_get(); + +#if defined(CONFIG_MACH_RDA8810) || defined(CONFIG_MACH_RDA8850) +#error "RDA8810/RDA8850 does not match RDA NAND Controller V2" +#elif defined(CONFIG_MACH_RDA8810E) || defined(CONFIG_MACH_RDA8820) + printf("NAND: RDA8810E/RDA8820 get type, metal %d\n", metal_id); + if (1) { +#define RDA_NAND_TYPE_BIT_7 (1 << 7) /* 1: 8K NAND, 0 - 2K/4K NAND */ +#define RDA_NAND_TYPE_BIT_BUS (1 << 1) /* 1: 8-bit; 0: 16-bit */ +#define RDA_NAND_TYPE_BIT_0 (1 << 0) /* if BIT_7 == 1 (8K NAND) : 1 - 1K64bit ECC, 0 - 1K24bit ECC */ + /* if BIT_7 == 0 (4K/2K) : 1 - 4K(1K24bit), 0 - 2K(2K24bit) */ + hwcfg_nand_type = rda_hwcfg_get() & 0x83; + if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_BUS) + *bus_width_16 = 0; + else + *bus_width_16 = 1; + if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_7) { + /* 8K */ + *rda_nand_type = NAND_TYPE_8K; + if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_0) { + printf("NAND: 8K page, 1K64BIT ECC, %s bus\n", + (*bus_width_16)?"16bit":"8bit"); + *rda_nand_ecc = NAND_ECC_1K64BIT; + } else { + printf("NAND: 8K page, 1K24BIT ECC, %s bus\n", + (*bus_width_16)?"16bit":"8bit"); + *rda_nand_ecc = NAND_ECC_1K24BIT; + } + } else { + /* 4K/2K */ + if (hwcfg_nand_type & RDA_NAND_TYPE_BIT_0) { + printf("NAND: 4K page, 1K24BIT ECC, %s bus\n", + (*bus_width_16)?"16bit":"8bit"); + *rda_nand_type = NAND_TYPE_4K; + *rda_nand_ecc = NAND_ECC_1K24BIT; + } else { + printf("NAND: 2K page, 2K24BIT ECC, %s bus\n", + (*bus_width_16)?"16bit":"8bit"); + *rda_nand_type = NAND_TYPE_2K; + *rda_nand_ecc = NAND_ECC_2K24BIT; + } + } + } +#else +#error "unknown MACH" +#endif + + printf("NAND: actually ratio * 2, SPL adjust ratio is %d. \n", + info->spl_adjust_ratio); + return 0; +} + +static int nand_rda_init(struct nand_chip *this, struct rda_nand_info *info) +{ + unsigned int id[2]; + int ret = 0; + int rda_nand_type = NAND_TYPE_INVALID; + int rda_nand_ecc = NAND_ECC_INVALID; + int bus_width_16 = 0; + + ret = nand_reset_flash(); + if (ret) + return ret; + ret = nand_read_id(id); + if (ret) + return ret; + ret = nand_get_type(info, id, &rda_nand_type, &rda_nand_ecc, &bus_width_16); + if (ret) + return ret; + ret = init_nand_info(info, rda_nand_type, rda_nand_ecc, bus_width_16); + if (ret) + return ret; + if (info->bus_width_16) + this->options |= NAND_BUSWIDTH_16; + + return hal_init(info); +} + +#if defined(CONFIG_SPL_BUILD) +/* this is for u-boot-spl only */ +static int nand_rda_init_size(struct mtd_info *mtd, struct nand_chip *this, + u8 *id_data) +{ + struct rda_nand_info *info = this->priv; + + /* TODO: this is not always right + * assuming nand is 2k/4k SLC for now + * for other types of nand, 64 pages per block is not always true + */ + mtd->erasesize = info->page_size * 64; + mtd->writesize = info->page_size; + mtd->oobsize = (info->oob_size > 256)?256:info->oob_size; + + return (info->bus_width_16) ? NAND_BUSWIDTH_16 : 0; +} +#endif + +int rda_nand_init(struct nand_chip *nand) +{ + struct rda_nand_info *info; + static struct rda_nand_info rda_nand_info; + + info = &rda_nand_info; + + nand->chip_delay = 0; +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + nand->options |= NAND_USE_FLASH_BBT; +#endif + /* + * in fact, nand controler we do hardware ECC silently, and + * we don't need tell mtd layer, and will simple nand operations + */ + nand->ecc.mode = NAND_ECC_NONE; + /* Set address of hardware control function */ + nand->cmd_ctrl = nand_rda_hwcontrol; +#if defined(CONFIG_SPL_BUILD) + nand->init_size = nand_rda_init_size; +#else + nand->init_size = NULL; +#endif + nand->cmdfunc = nand_rda_cmdfunc; + + nand->read_nand_ID = read_ID; + nand->read_byte = nand_rda_read_byte; + nand->read_word = nand_rda_read_word; + nand->read_buf = nand_rda_read_buf; + nand->write_buf = nand_rda_write_buf; + + nand->dev_ready = nand_rda_dev_ready; + + nand->priv = (void *)info; + nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)NANDFC_DATA_BUF; +#ifdef CONFIG_NAND_RDA_DMA + info->nand_data_phys = (void __iomem *)NANDFC_DATA_BUF; +#endif /* CONFIG_NAND_RDA_DMA */ + info->index = 0; + info->write_ptr = 0; + info->read_ptr = 0; + info->master_clk = get_master_clk_rate(_TGT_AP_CLK_APB2); + info->clk=_TGT_AP_NAND_CLOCK; + +#ifdef CONFIG_NAND_RDA_DMA + rda_request_dma(&info->dma_ch); +#endif /* CONFIG_NAND_RDA_DMA */ + + nand_rda_init(nand, info); + + if (!nand->ecc.layout && (nand->ecc.mode != NAND_ECC_SOFT_BCH)) { + switch (info->vir_oob_size) { + case 40: + nand->ecc.layout = &rda_nand_oob_40; + break; + case 64: + nand->ecc.layout = &rda_nand_oob_64; + break; + case 128: + nand->ecc.layout = &rda_nand_oob_128; + break; + case 256: + nand->ecc.layout = &rda_nand_oob_256; + break; + case 512: + nand->ecc.layout = &rda_nand_oob_512; + break; + default: + printf("error oob size: %d \n", info->vir_oob_size); + } + } + + return 0; +} + +#if 0 +/* move to rda_nand_base.c */ +int board_nand_init(struct nand_chip *chip) __attribute__ ((weak)); + +int board_nand_init(struct nand_chip *chip) +{ + return rda_nand_init(chip); +} +#endif |