| /* |
| * drivers/mtd/nand/mv_nand.c |
| * |
| * Copyright (C) 2008 hzchen |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| */ |
| |
| #include <common.h> |
| #if defined(__UBOOT__) |
| #include <malloc.h> |
| #include "galois_io.h" |
| #else |
| #include "io.h" |
| #include "lgpl_printf.h" |
| #endif /* #if defined(__UBOOT__) */ |
| |
| #include "global.h" |
| #include "memmap.h" |
| #include "nand_priv.h" |
| #include "mv_nand.h" |
| #include "galois_speed.h" |
| |
| #define SUPPORT_RANDOMIZATION |
| |
| #define debug_printf(a) |
| #define NAND_TIME_OUT 10000000 |
| char mvnand_page_buffer[MV_NAND_MAX_PAGE_SIZE + MV_NAND_MAX_OOB_SIZE]; |
| |
| #define RAND_DATA_POS 0xB00000 |
| /* |
| * Macro to remove unused variable warning for function args not used for |
| * specific platform. |
| */ |
| #define UNUSED(var) do { (void)(var); } while(0) |
| |
| #if defined(__UBOOT__) |
| static struct mv_nand_flash_dev nand_flash_ids[] = { |
| /* |
| {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, |
| {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, |
| {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, |
| {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, |
| {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, |
| {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, |
| {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, |
| {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, |
| {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, |
| {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, |
| |
| {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, |
| {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, |
| {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, |
| {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, |
| |
| {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, |
| {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, |
| {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, |
| {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, |
| */ |
| {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, |
| {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, |
| {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, |
| {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, |
| |
| {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, |
| {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, |
| {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, |
| {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, |
| |
| {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, |
| {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, |
| {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, |
| {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, |
| {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, |
| {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, |
| {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, |
| |
| {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, |
| |
| #define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) |
| #define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) |
| |
| /*512 Megabit */ |
| {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, |
| {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, |
| {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, |
| {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, |
| |
| /* 1 Gigabit */ |
| {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, |
| {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, |
| {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, |
| {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, |
| |
| /* 2 Gigabit */ |
| {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, |
| {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS}, |
| {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16}, |
| {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16}, |
| |
| /* 4 Gigabit */ |
| {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS}, |
| {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS}, |
| {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16}, |
| {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16}, |
| |
| /* 8 Gigabit */ |
| {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS}, |
| {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS}, |
| {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16}, |
| {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16}, |
| |
| /* 16 Gigabit */ |
| {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS}, |
| {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS}, |
| {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, |
| {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, |
| {"NAND 2GiB 3,3V 16-bit", 0x48, 0, 2048, 0, LP_OPTIONS16}, |
| |
| /* 32 Gigabit */ |
| {"NAND 4GiB 1,8V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS}, |
| {"NAND 4GiB 1,8V 8-bit", 0x68, 0, 4096, 0, LP_OPTIONS}, |
| |
| {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, |
| NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | |
| BBT_AUTO_REFRESH}, |
| {0,} |
| }; |
| |
| static struct nand_manufacturers nand_manuf_ids[] = { |
| {NAND_MFR_TOSHIBA, "Toshiba"}, |
| {NAND_MFR_SAMSUNG, "Samsung"}, |
| {NAND_MFR_FUJITSU, "Fujitsu"}, |
| {NAND_MFR_NATIONAL, "National"}, |
| {NAND_MFR_RENESAS, "Renesas"}, |
| {NAND_MFR_STMICRO, "ST Micro"}, |
| {NAND_MFR_HYNIX, "Hynix"}, |
| {NAND_MFR_MICRON, "Micron"}, |
| {0x0, "Unknown"} |
| }; |
| |
| static char rand_nand_id_lst[][8] = { |
| {0xEC, 0xD7, 0x94, 0x7A, 0x54, 0x43, 0x0, 0x0}, |
| {0}, |
| }; |
| |
| #endif /* #if defined(__UBOOT__) */ |
| |
| static int mv_nand_cmd_request(void); |
| |
| static unsigned int real_device_oobsize; |
| |
| struct mv_nand_data nand_data; |
| loff_t chip_size=0; |
| extern unsigned char partition_type_s[16]; |
| |
| #define NAKED_CMD(cs, cmd) \ |
| do{ \ |
| int ctrl; \ |
| mv_nand_cmd_request(); \ |
| ctrl = NDCB0_CS(cs) | NDCB0_CMD_NAKED_CMD | NDCB0_NC \ |
| | NDCB0_ADDRC(5) | NDCB0_CMD2(0x0) | NDCB0_CMD1(cmd); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); \ |
| if( wait_status_bit_set(cs ? MV_NAND_NDSR_CS1_CMDD : MV_NAND_NDSR_CS0_CMDD) ) \ |
| return -1; \ |
| }while(0); |
| |
| #define NAKED_CMD_SENSE(cs, cmd) \ |
| do{ \ |
| int ctrl; \ |
| mv_nand_cmd_request(); \ |
| ctrl = NDCB0_CS(cs) | NDCB0_CMD_RESET | NDCB0_NC | NDCB0_CMD1(cmd); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); \ |
| if(wait_status_bit_set(cs ? MV_NAND_NDSR_RDY1 : MV_NAND_NDSR_RDY0)) \ |
| return -1; \ |
| }while(0); |
| |
| #define READ_ID(cs, ovlen, addr, cmd) \ |
| do { \ |
| int ctrl; \ |
| mv_nand_cmd_request(); \ |
| ctrl = NDCB0_CS(cs) | NDCB0_LENOVRD | NDCB0_CMD_READID \ |
| | NDCB0_ADDRC(1) | NDCB0_NC | NDCB0_CMD1(cmd); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, addr); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, \ |
| NDCB3_NDLENCNT(ovlen)); \ |
| } while(0); |
| |
| #define NAKED_ADDR(cs, ac, ndcr1, ndcr2, ndcr3) \ |
| do{\ |
| int ctrl; \ |
| mv_nand_cmd_request(); \ |
| ctrl = NDCB0_CS(cs) | NDCB0_CMD_NAKED_ADDR | NDCB0_NC \ |
| | NDCB0_ADDRC(ac) | NDCB0_CMD2(0x0) | NDCB0_CMD1(0x0); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ndcr1); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ndcr2); \ |
| if(ac >= 6) \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ndcr3); \ |
| if( wait_status_bit_set(cs ? MV_NAND_NDSR_CS1_CMDD : MV_NAND_NDSR_CS0_CMDD) ) \ |
| return -1; \ |
| }while(0); |
| |
| #define NAKED_WR_DATA(cs, ovlen) \ |
| do { \ |
| int ctrl; \ |
| mv_nand_cmd_request(); \ |
| ctrl = NDCB0_CMDX_NWRITE | NDCB0_LENOVRD | NDCB0_CS(cs) \ |
| | NDCB0_CMD_PROGRAM | NDCB0_NC \ |
| | NDCB0_ADDRC(5) | NDCB0_CMD2(0x0) | NDCB0_CMD1(0x0); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, \ |
| NDCB3_NDLENCNT(ovlen)); \ |
| if( wait_status_bit_set(MV_NAND_NDSR_WRDREQ) ) \ |
| return -1; \ |
| }while(0); |
| |
| #define NAKED_RD_DATA(cs, ovlen) \ |
| do { \ |
| int ctrl; \ |
| mv_nand_cmd_request(); \ |
| ctrl = NDCB0_CMDX_NREAD |NDCB0_LENOVRD | NDCB0_CS(cs) \ |
| | NDCB0_CMD_READ | NDCB0_NC \ |
| | NDCB0_ADDRC(0) | NDCB0_CMD2(0x0) | NDCB0_CMD1(0x0); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); \ |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, \ |
| NDCB3_NDLENCNT(ovlen)); \ |
| }while(0); |
| |
| unsigned int mv_nand_page_size(void){ |
| return (nand_data.szofpg); |
| } |
| unsigned int mv_nand_block_size(void){ |
| return (nand_data.szofblk); |
| } |
| unsigned int mv_nand_oob_size(void){ |
| return real_device_oobsize; |
| } |
| loff_t mv_nand_chip_size(void){ |
| return chip_size; |
| } |
| |
| static inline int wait_status_bit_set(unsigned int bit) |
| { |
| unsigned int read, i=NAND_TIME_OUT; |
| |
| //by pass ready check |
| do { |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDSR_OFFSET , &read); |
| read &= bit; |
| } while (!read && --i); |
| |
| if(!i) { |
| printf("wait bit %08X time out!\n", bit); |
| return -1; |
| } |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDSR_OFFSET, read); |
| return 0; |
| } |
| |
| /* |
| * clean all status bit, set NDCR.ND_RUN bit |
| */ |
| static void mv_nand_start(void) |
| { |
| unsigned int read; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE ,MV_NAND_NDSR_OFFSET , MV_NAND_NDSR_VAL); |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET , &read); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, |
| (read | MV_NAND_NDCR_ND_RUN)); |
| } |
| |
| /* |
| * clean all status bit, clean NDCR.ND_RUN bit |
| */ |
| static void mv_nand_disable(void) |
| { |
| unsigned int read; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDSR_OFFSET , MV_NAND_NDSR_VAL); |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET , &read); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, |
| (read &(~ MV_NAND_NDCR_ND_RUN))); |
| } |
| |
| static inline int mv_nand_cmd_request(void) |
| { |
| return wait_status_bit_set(MV_NAND_NDSR_WRCMDREQ); |
| } |
| |
| #if defined(__UBOOT__) |
| static void dump_registers(void) |
| { |
| unsigned int read; |
| |
| // Read back for sanity check |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDTR0CS0_OFFSET, &read); |
| printf("NDTR0CS0 reads 0x%X\n", read); |
| |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDTR1CS0_OFFSET, &read); |
| printf("NDTR1CS0 reads 0x%X\n", read); |
| |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDREDEL_OFFSET, &read); |
| printf("NDREDEL reads 0x%X\n", read); |
| |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDSR_OFFSET, &read); |
| printf("NDSR reads 0x%X\n", read); |
| |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDCR_OFFSET, &read); |
| printf("NDCR reads 0x%X\n", read); |
| |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDECCCTRL_OFFSET, &read); |
| printf("NDECCCTRL reads 0x%X\n", read); |
| |
| |
| } |
| |
| int process_inital_bad_block(void) |
| { |
| return 0; |
| } |
| #endif /* #if defined(__UBOOT__) */ |
| #ifdef SUPPORT_RANDOMIZATION |
| /**************************************************************************** |
| * |
| * For Randomizer Start |
| * |
| ****************************************************************************/ |
| #include "nand_randomizer.h" |
| |
| #define TRACE_LOG(...) //printf |
| |
| #define DUMP_8_BYTES(name, b) \ |
| if (b) \ |
| { \ |
| TRACE_LOG("%s(0x%08X): %02X %02X %02X %02X %02X %02X %02X %02X\n", \ |
| name, (int)(b), \ |
| (b)[0], (b)[1], (b)[2], (b)[3], (b)[4], (b)[5], (b)[6], (b)[7]);\ |
| } |
| |
| #define NAND_ID_SIZE (4) // FIXME: 4 is a hack. we need to fix it. |
| // most of the chip should be 6 |
| |
| #define RANDOMIZER_BUFFER_SIZE_MAX (MV_NAND_RANDOMIZER_BUFFER_SIZE_MAX) |
| #define RANDOMIZED_BUFFER_SIZE_MAX (MV_NAND_MAX_PAGE_SIZE + MV_NAND_MAX_OOB_SIZE) |
| |
| //unsigned char g_randomizer_buffer[RANDOMIZER_BUFFER_SIZE_MAX]; |
| static unsigned char g_randomized_buffer[RANDOMIZED_BUFFER_SIZE_MAX]; |
| |
| |
| static void nand_randomizer_init_by_flash_type (void) |
| { |
| struct mv_nand_data *board = &nand_data; |
| unsigned int block_size = board->szofblk; |
| unsigned int page_size = board->szofpg; |
| unsigned int oob_size = 32; // should be mtd->oobsize, we fix 32 to make sure it can work on marvell NFC |
| unsigned char id_data[8]; |
| int randomized = 0; |
| static unsigned char g_randomizer_buffer[RANDOMIZER_BUFFER_SIZE_MAX]; |
| |
| // Get randomizer type by chip_id |
| if (0 != mv_nand_read_id((char *)id_data)) |
| { |
| return; |
| } |
| |
| randomized = mv_nand_randomizer_init( |
| id_data, |
| NAND_ID_SIZE, |
| block_size, |
| page_size, |
| oob_size, |
| g_randomizer_buffer, |
| RANDOMIZER_BUFFER_SIZE_MAX); |
| printf("nand_randomizer_init_by_flash_type(chip_id = 0x%02X%02X%02X%02X%02X%02X): !!! %s !!!\n", |
| id_data[0], id_data[1], id_data[2], id_data[3], id_data[4], id_data[5], |
| randomized ? "RANDOMIZED" : "UNRANDOMIZED"); |
| } |
| #if 0 |
| static void nand_randomizer_release (void) |
| { |
| return; |
| } |
| #endif |
| static void dump_block_randomization_info(unsigned int page_addr, int randomized) |
| { |
| static int s_pre_block_index = -1; |
| int block_index = page_addr >> 7; |
| int page_index = page_addr & 7; |
| UNUSED(randomized); |
| if (s_pre_block_index != block_index) |
| { |
| printf("block %d: !!! %s !!! (page=%d)\n", block_index, |
| randomized ? "RANDOMIZED" : "UNRANDOMIZED", page_index); |
| s_pre_block_index = block_index; |
| } |
| } |
| |
| #if 0 |
| static int is_chunk_blank(const unsigned char *buf, int is_last_chunk) |
| { |
| const int error_pos = is_last_chunk ? 0x7cd : 0x7c8; |
| int i; |
| for (i = 0; i < CHUNK_SIZE; i++) { |
| if (*buf++ != 0xff && i != error_pos) |
| return 0; |
| } |
| return 1; |
| } |
| #endif |
| static const unsigned char g_byte_zero_bits[256] = { |
| 8, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 4, |
| 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, |
| 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, |
| 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, |
| 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, |
| 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, |
| 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, |
| 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, |
| 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, |
| 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, |
| 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, |
| 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, |
| 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2, |
| 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, |
| 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1, |
| 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0 |
| }; |
| |
| static int is_chunk_blank(int ecc, unsigned char *buf, unsigned int len) |
| { |
| unsigned int zero_bits = 0; |
| unsigned int i = 0; |
| |
| for (i = 0; i < len; i++) { |
| zero_bits += g_byte_zero_bits[buf[i]]; |
| /* for blank chunk, the error bit number should not larger |
| * than ecc strength. |
| */ |
| if (zero_bits > (unsigned int)ecc) { |
| printf("[%s:%d] not blank! data_len=%d, ecc=%d, zero_bits=%d\n", |
| __func__, __LINE__, len, ecc, zero_bits); |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| static int is_read_page_blank(int ecc, unsigned int chunk_size, |
| unsigned char *page_buf, unsigned int page_data_len) |
| { |
| int chunk_num = page_data_len / chunk_size; |
| unsigned char *buf = page_buf; |
| unsigned int len = page_data_len; |
| int i; |
| |
| /* not last chunk */ |
| for (i = 0; i < chunk_num - 1; i++) { |
| if (!is_chunk_blank(ecc, buf, chunk_size)) |
| return 0; |
| buf += chunk_size; |
| len -= chunk_size; |
| } |
| /* last chunk */ |
| if (!is_chunk_blank(ecc, buf, len)) |
| return 0; |
| |
| return 1; |
| } |
| |
| #if defined(CONFIG_SLC) |
| static void mv_nand_enable_ecc(int enable) |
| { |
| unsigned int ctrl; |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, &ctrl); |
| if (enable) |
| ctrl |= MV_NAND_NDCR_ECC_EN; |
| else |
| ctrl &= ~MV_NAND_NDCR_ECC_EN; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, ctrl); |
| |
| } |
| #endif |
| |
| static int nand_read_page_post_process (unsigned int page_addr, |
| const unsigned char *data_src, const unsigned char *oob_src, |
| unsigned char *data_dst, unsigned char *oob_dst) |
| { |
| unsigned int randomized_length = 0; |
| randomized_length = mv_nand_randomizer_randomize_page(page_addr, |
| data_src, oob_src, data_dst, oob_dst); |
| |
| dump_block_randomization_info(page_addr, randomized_length); |
| return randomized_length > 0 ? 1 : 0; |
| } |
| |
| static int nand_write_page_pre_process (unsigned int page_addr, |
| const unsigned char *data_src, const unsigned char *oob_src, |
| unsigned char *data_dst, unsigned char *oob_dst) |
| { |
| unsigned int randomized_length = 0; |
| |
| randomized_length = mv_nand_randomizer_randomize_page(page_addr, |
| data_src, oob_src, data_dst, oob_dst) ? 1 : 0; |
| |
| dump_block_randomization_info(page_addr, randomized_length); |
| return randomized_length > 0 ? 1 : 0; |
| } |
| |
| static void mv_nand_decode_address (loff_t addr, unsigned int *p_block_index, |
| unsigned int *p_page_index, unsigned int *p_pos_in_page) |
| { |
| struct mv_nand_data *board = &nand_data; |
| unsigned int block_size = board->szofblk; |
| unsigned int page_size = board->szofpg; |
| unsigned int page_shift = board->page_shift; |
| unsigned int block_shift = page_shift; // start from page_shift to calculate it |
| |
| while (((unsigned int)1 << block_shift) != block_size) |
| { |
| block_shift++; |
| } |
| |
| if (p_block_index) |
| { |
| *p_block_index = (unsigned int)(addr >> block_shift); |
| } |
| |
| if (p_page_index) |
| { |
| *p_page_index = ((unsigned int)addr & (block_size - 1)) >> page_shift; |
| } |
| |
| if (p_pos_in_page) |
| { |
| *p_pos_in_page = (unsigned int)addr & (page_size - 1); |
| } |
| } |
| /**************************************************************************** |
| * |
| * For Randomizer End |
| * |
| ****************************************************************************/ |
| #endif // SUPPORT_RANDOMIZATION |
| |
| static int mv_nand_page_bad(unsigned int block_index, unsigned int page_index) |
| { |
| struct mv_nand_data *board = (struct mv_nand_data *)&nand_data; |
| unsigned int block_size = board->szofblk; |
| unsigned int page_size = board->szofpg; |
| loff_t ofs = (loff_t)block_size * block_index + page_size * page_index; |
| unsigned char bad_marker; |
| |
| mv_nand_read_page(ofs, mvnand_page_buffer); |
| bad_marker = mvnand_page_buffer[page_size]; |
| if((bad_marker & 0xFF)!= 0xff){ |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * mv_nand_block_bad - [DEFAULT] Read bad block marker from the chip |
| * @ofs: offset from device start |
| * Check, if the block is bad. |
| */ |
| int mv_nand_block_bad(loff_t ofs, int getchip) |
| { |
| //struct mv_nand_data *board = (struct mv_nand_data *)&nand_data; |
| #ifdef SUPPORT_RANDOMIZATION |
| //unsigned int block_size = board->szofblk; |
| //unsigned int page_size = board->szofpg; |
| unsigned int block_index; |
| |
| mv_nand_decode_address (ofs, &block_index, NULL, NULL); |
| #endif // SUPPORT_RANDOMIZATION |
| UNUSED(getchip); |
| if (mv_nand_page_bad(block_index, 0)) |
| { |
| return 1; |
| } |
| if (mv_nand_page_bad(block_index, 1)) |
| { |
| return 1; |
| } |
| |
| /* |
| // last page |
| if (mv_nand_page_bad(block_index, block_size/page_size - 1)) |
| { |
| return 1; |
| } |
| */ |
| return 0; |
| |
| } |
| |
| int mv_nand_mark_badblock(loff_t ofs, int getchip) |
| { |
| int res = 0; |
| int res1 = 0; |
| struct mv_nand_data *board = &nand_data; |
| unsigned int block_size = board->szofblk; |
| unsigned int page_size = board->szofpg; |
| UNUSED(getchip); |
| ofs = ofs & (~((loff_t)block_size - 1)); |
| mvnand_page_buffer[page_size] = 0x55; |
| res = mv_nand_write_page(ofs, mvnand_page_buffer); |
| ofs = ofs + page_size; |
| res1 = mv_nand_write_page(ofs, mvnand_page_buffer); |
| return res && res1; |
| } |
| |
| #if defined(__UBOOT__) |
| /* |
| * configure controller using default para |
| * |
| */ |
| static void mv_nand_chip_init(void ) |
| { |
| int i,j; |
| |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDTR0CS0_OFFSET, MV_NAND_NDTR0CS0_VAL); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDTR1CS0_OFFSET, MV_NAND_NDTR1CS0_VAL); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDREDEL_OFFSET, MV_NAND_NDREDEL_VAL); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDSR_OFFSET, MV_NAND_NDSR_VAL); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDECCCTRL_OFFSET, MV_NAND_NDECCCTRL_VAL); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, MV_NAND_NDCR_VAL); |
| |
| for (i = 0; i < 2176; i++) |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDDB_OFFSET, &j); |
| } |
| #endif /* #if defined(__UBOOT__) */ |
| |
| static int mv_nand_chip_reinit(void) |
| { |
| struct mv_nand_data *board = (struct mv_nand_data *)&nand_data; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDTR0CS0_OFFSET, board->ndtr0cs0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDTR1CS0_OFFSET, board->ndtr1cs0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDREDEL_OFFSET, board->ndredel); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDECCCTRL_OFFSET, board->ndeccctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, board->ndcr); |
| return 0; |
| } |
| |
| /* |
| * disable ECC and BCH |
| */ |
| static void mv_nand_preinit_exrw(void) |
| { |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDECCCTRL_OFFSET, MV_NAND_NDECCCTRL_VAL); |
| /* int ctrl; |
| struct mv_nand_data *board = (struct mv_nand_data *)&nand_data; |
| if(board->conbch & 0x7F) { |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDECCCTRL_OFFSET ,&ctrl); |
| ctrl &= ~1; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDECCCTRL_OFFSET, ctrl); |
| } |
| if(board->conecc){ |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, &ctrl); |
| ctrl &= ~MV_NAND_NDCR_ECC_EN; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, ctrl); |
| } |
| if(board->conspare) { |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET ,&ctrl); |
| ctrl &= ~MV_NAND_NDCR_SPARE_EN; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, ctrl); |
| } |
| */ |
| } |
| |
| /* |
| * enable ECC and BCH as nessasary |
| */ |
| static void mv_nand_preinit_rw(void) |
| { |
| struct mv_nand_data *board = (struct mv_nand_data *)&nand_data; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDECCCTRL_OFFSET, board->ndeccctrl); |
| /* int ctrl; |
| struct mv_nand_data *board = (struct mv_nand_data *)&nand_data; |
| if(board->conecc){ |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, &ctrl); |
| ctrl |= MV_NAND_NDCR_ECC_EN; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, ctrl); |
| } |
| if(board->conspare) { |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET ,&ctrl); |
| ctrl |= MV_NAND_NDCR_SPARE_EN; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCR_OFFSET, ctrl); |
| } |
| if(board->conbch & 0x7F) { |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDECCCTRL_OFFSET ,&ctrl); |
| ctrl |= (board->conbch & 0x7F); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDECCCTRL_OFFSET, ctrl); |
| } |
| */ |
| } |
| |
| int mv_nand_reset_chip(int getchip) |
| { |
| int ctrl; |
| |
| mv_nand_preinit_exrw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| if (mv_nand_cmd_request()) |
| return -1; |
| ctrl = 0x00A000FF | (getchip ? MV_NAND_NDCB_CSEL : 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| |
| if(wait_status_bit_set(getchip ? MV_NAND_NDSR_RDY1 : MV_NAND_NDSR_RDY0)) |
| return -1; |
| |
| if(wait_status_bit_set(getchip ? MV_NAND_NDSR_CS1_CMDD : MV_NAND_NDSR_CS0_CMDD)) |
| return -1; |
| |
| mv_nand_disable(); |
| return 0; |
| } |
| |
| |
| static inline void mv_nand_read_buf(char *buf ,int len) |
| { |
| unsigned int *buf_tsf = (unsigned int *)buf; |
| unsigned int i; |
| for (i = 0; i < len / sizeof(int); i++){ |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDDB_OFFSET, &buf_tsf[i]); |
| } |
| } |
| |
| static inline void mv_nand_write_buf(char *buf ,int len) |
| { |
| unsigned int *buf_tsf = (unsigned int *)buf; |
| unsigned int i; |
| for (i = 0; i < len/sizeof(int); i++) |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDDB_OFFSET, buf_tsf[i]); |
| } |
| |
| static int mv_nand_read_status(void) |
| { |
| int ctrl; |
| int cmd[2]; |
| struct mv_nand_data *board = &nand_data; |
| mv_nand_preinit_exrw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| mv_nand_cmd_request(); |
| ctrl = 0x00800070 | (board->ncs ? MV_NAND_NDCB_CSEL : 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDCB0_OFFSET, 0); |
| if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| mv_nand_read_buf((char *)&cmd[0] , 8); |
| if( wait_status_bit_set(board->ncs ? (MV_NAND_NDSR_CS1_CMDD) : (MV_NAND_NDSR_CS0_CMDD )) ) |
| return -1; |
| |
| return cmd[0] & 0xff; |
| } |
| |
| #if defined(CONFIG_SLC) |
| static void nand_udelay(int u) |
| { |
| volatile int i, j; |
| for (j = 0; j < u; j++) { |
| for (i=0; i<0x100;i++) |
| i=i; |
| } |
| } |
| #endif |
| |
| #if defined(CONFIG_ENHANCED_SLC) |
| unsigned int config_enhanced_SLC = 0; |
| unsigned int enhanced_SLC_enabled = 0; |
| |
| /* pair page table for HINIX_H27UAG8T2CTR */ |
| const unsigned char paired_table[128][2] = |
| { |
| {0x00,0x04}, {0x01,0x05}, {0x02,0x08}, {0x03,0x09}, |
| {0x06,0x0C}, {0x07,0x0D}, {0x0A,0x10}, {0x0B,0x11}, |
| {0x0E,0x14}, {0x0F,0x15}, {0x12,0x18}, {0x13,0x19}, |
| {0x16,0x1C}, {0x17,0x1D}, {0x1A,0x20}, {0x1B,0x21}, |
| {0x1E,0x24}, {0x1F,0x25}, {0x22,0x28}, {0x23,0x29}, |
| {0x26,0x2C}, {0x27,0x2D}, {0x2A,0x30}, {0x2B,0x31}, |
| {0x2E,0x34}, {0x2F,0x35}, {0x32,0x38}, {0x33,0x39}, |
| {0x36,0x3C}, {0x37,0x3D}, {0x3A,0x40}, {0x3B,0x41}, |
| {0x3E,0x44}, {0x3F,0x45}, {0x42,0x48}, {0x43,0x49}, |
| {0x46,0x4C}, {0x47,0x4D}, {0x4A,0x50}, {0x4B,0x51}, |
| {0x4E,0x54}, {0x4F,0x55}, {0x52,0x58}, {0x53,0x59}, |
| {0x56,0x5C}, {0x57,0x5D}, {0x5A,0x60}, {0x5B,0x61}, |
| {0x5E,0x64}, {0x5F,0x65}, {0x62,0x68}, {0x63,0x69}, |
| {0x66,0x6C}, {0x67,0x6D}, {0x6A,0x70}, {0x6B,0x71}, |
| {0x6E,0x74}, {0x6F,0x75}, {0x72,0x78}, {0x73,0x79}, |
| {0x76,0x7C}, {0x77,0x7D}, {0x7A,0x80}, {0x7B,0x81}, |
| {0x7E,0x84}, {0x7F,0x85}, {0x82,0x88}, {0x83,0x89}, |
| {0x86,0x8C}, {0x87,0x8D}, {0x8A,0x90}, {0x8B,0x91}, |
| {0x8E,0x94}, {0x8F,0x95}, {0x92,0x98}, {0x93,0x99}, |
| {0x96,0x9C}, {0x97,0x9D}, {0x9A,0xA0}, {0x9B,0xA1}, |
| {0x9E,0xA4}, {0x9F,0xA5}, {0xA2,0xA8}, {0xA3,0xA9}, |
| {0xA6,0xAC}, {0xA7,0xAD}, {0xAA,0xB0}, {0xAB,0xB1}, |
| {0xAE,0xB4}, {0xAF,0xB5}, {0xB2,0xB8}, {0xB3,0xB9}, |
| {0xB6,0xBC}, {0xB7,0xBD}, {0xBA,0xC0}, {0xBB,0xC1}, |
| {0xBE,0xC4}, {0xBF,0xC5}, {0xC2,0xC8}, {0xC3,0xC9}, |
| {0xC6,0xCC}, {0xC7,0xCD}, {0xCA,0xD0}, {0xCB,0xD1}, |
| {0xCE,0xD4}, {0xCF,0xD5}, {0xD2,0xD8}, {0xD3,0xD9}, |
| {0xD6,0xDC}, {0xD7,0xDD}, {0xDA,0xE0}, {0xDB,0xE1}, |
| {0xDE,0xE4}, {0xDF,0xE5}, {0xE2,0xE8}, {0xE3,0xE9}, |
| {0xE6,0xEC}, {0xE7,0xED}, {0xEA,0xF0}, {0xEB,0xF1}, |
| {0xEE,0xF4}, {0xEF,0xF5}, {0xF2,0xF8}, {0xF3,0xF9}, |
| {0xF6,0xFC}, {0xF7,0xFD}, {0xFA,0xFE}, {0xFB,0xFF} |
| }; |
| |
| /* |
| * Search paired page in paired page table. |
| */ |
| static unsigned int mv_nand_enhanced_SLC_paired_page(unsigned int src_page) |
| { |
| unsigned int src_addr = src_page; |
| unsigned int paired_addr; |
| struct mv_nand_data *board = &nand_data; |
| unsigned int paired_page_index; |
| unsigned int block_size = board->szofblk; |
| unsigned int page_shift = board->page_shift; |
| |
| unsigned int block_count = (unsigned int) (src_addr / block_size); |
| unsigned int page_offset = (unsigned int) (src_addr % block_size); |
| unsigned int page_index = (unsigned int)(page_offset >> page_shift); |
| |
| page_index = (page_index >= 127)? 127: page_index; |
| |
| paired_page_index = paired_table[page_index][0]; |
| |
| //printf("page_index = %d, paired_page_index = %d \n", page_index, paired_page_index); |
| |
| paired_addr = block_count * block_size; |
| paired_addr += (paired_page_index << page_shift); |
| //printf("src_page = 0x%llu, paired_addr = 0x%llu \n", src_page, paired_addr); |
| return paired_addr; |
| } |
| |
| #endif |
| |
| #if defined(CONFIG_SLC) |
| unsigned int config_SLC = 0; |
| unsigned int SLC_enabled = 0; |
| unsigned int slc_partition_start; |
| |
| static int mv_nand_SLC_enable() |
| { |
| int ctrl; |
| int ret = 0; |
| unsigned int dst_4321 = 0; |
| unsigned int dst_xxx5 = 0; |
| struct mv_nand_data *board = &nand_data; |
| |
| mv_nand_enable_ecc(0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDECCCTRL_OFFSET, 0); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| //printf("[%s:%d] enable SLC.\n", __func__, __LINE__); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD |
| | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0xBF); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| |
| mv_nand_enable_ecc(1); |
| mv_nand_preinit_rw(); |
| |
| nand_udelay(1); |
| return ret; |
| |
| } |
| |
| static int mv_nand_SLC_disable() |
| { |
| int ctrl; |
| int ret = 0; |
| unsigned int dst_4321 = 0; |
| unsigned int dst_xxx5 = 0; |
| struct mv_nand_data *board = &nand_data; |
| |
| mv_nand_enable_ecc(0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDECCCTRL_OFFSET, 0); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| //printf("[%s:%d] enable SLC.\n", __func__, __LINE__); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD |
| | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0xBE); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| |
| mv_nand_enable_ecc(1); |
| mv_nand_preinit_rw(); |
| |
| nand_udelay(1); |
| return ret; |
| |
| } |
| |
| static unsigned int mv_nand_SLC_paired_page(unsigned int src_page) |
| { |
| unsigned int src_addr = src_page; |
| unsigned int paired_addr; |
| struct mv_nand_data *board = &nand_data; |
| unsigned int paired_page_index; |
| unsigned int block_size = board->szofblk; |
| unsigned int page_shift = board->page_shift; |
| |
| unsigned int block_count = (unsigned int) (src_addr / block_size); |
| unsigned int page_offset = (unsigned int) (src_addr % block_size); |
| unsigned int page_index = (unsigned int)(page_offset >> page_shift); |
| if (block_count < slc_partition_start) |
| return -1; |
| |
| paired_page_index = (block_count - slc_partition_start) * 256; |
| paired_page_index += page_index; |
| |
| paired_addr = (loff_t)slc_partition_start * block_size / 2; |
| paired_addr += (paired_page_index << page_shift); |
| |
| //printf("src_page = 0x%llu, slc_paired_addr = 0x%llu \n", src_page, paired_addr); |
| return paired_addr; |
| } |
| #endif |
| |
| /* |
| * oob: 1, buffer contain oob. 0, buffer does not contain oob |
| */ |
| int mv_nand_write_large_page(loff_t srcx, char *buf, int oob) |
| { |
| int ctrl, i; |
| unsigned int row_addr; |
| unsigned int dst_4321, dst_xxx5; |
| struct mv_nand_data *board = &nand_data; |
| int page_size = board->szofpg; |
| int oob_per_chunk; |
| int page_layout = board->conspare; |
| const unsigned char default_oob_data[32] = { |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| char* oob_buf = oob ? (buf + page_size) : (char *)default_oob_data; |
| |
| unsigned int page_shift = board->page_shift; |
| |
| #ifdef SUPPORT_RANDOMIZATION |
| unsigned int page_addr = (unsigned int)(srcx >> page_shift); |
| int randomized = 0; |
| |
| DUMP_8_BYTES("WriteBefore:", buf); |
| randomized = nand_write_page_pre_process(page_addr, |
| (unsigned char *)buf, |
| (const unsigned char *) (oob ? (buf + page_size) : (char *)default_oob_data), |
| g_randomized_buffer, |
| g_randomized_buffer + page_size); |
| |
| if (randomized) |
| { |
| buf = (char *)g_randomized_buffer; |
| oob_buf = (char *)g_randomized_buffer + page_size; |
| } |
| TRACE_LOG("Write Page(0x%08X): %s\n", (int)srcx, randomized ? "RANDOMIZED" : "UNRANDOMIZED"); |
| DUMP_8_BYTES("WriteReal:", buf); |
| #endif // SUPPORT_RANDOMIZATION |
| |
| mv_nand_preinit_rw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0x80); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| if( wait_status_bit_set(board->ncs ? MV_NAND_NDSR_CS1_CMDD : MV_NAND_NDSR_CS0_CMDD) ) |
| return -1; |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_ADDR | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0xFF); |
| |
| row_addr = (srcx >> page_shift); |
| dst_4321 = (row_addr << 16) & 0xFFFF0000; |
| dst_xxx5 = (row_addr >> 16) & 0xFF; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| if( wait_status_bit_set(board->ncs ? MV_NAND_NDSR_CS1_CMDD : MV_NAND_NDSR_CS0_CMDD) ) |
| return -1; |
| |
| i = (page_size - 1 + board->chunk_size)/board->chunk_size; |
| if(page_layout==MODE_MULTI_SPARE) |
| oob_per_chunk = board->oobsize/i; |
| else |
| oob_per_chunk = board->oobsize; |
| while(i-->0){ |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CMDX_NWRITE | NDCB0_LENOVRD | NDCB0_CS(board->ncs) |
| | NDCB0_CMD_PROGRAM | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0x0) | NDCB0_CMD1(0x00); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| if((i==0) || (page_layout==MODE_MULTI_SPARE)) |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, |
| NDCB3_NDLENCNT(board->chunk_size + oob_per_chunk)); |
| else |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, |
| NDCB3_NDLENCNT(board->chunk_size)); |
| |
| if( wait_status_bit_set(MV_NAND_NDSR_WRDREQ) ) |
| return -1; |
| |
| /* write data */ |
| mv_nand_write_buf(buf, board->chunk_size); |
| buf += board->chunk_size; |
| /* write oob */ |
| if((i==0) || (page_layout==MODE_MULTI_SPARE)) { |
| mv_nand_write_buf(oob_buf, oob_per_chunk); |
| if (oob && (page_layout==MODE_MULTI_SPARE)) { |
| oob_buf += oob_per_chunk; |
| } |
| } |
| } |
| |
| /* commit write */ |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CMDX_NWRITE | NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0x10); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| |
| if( wait_status_bit_set(board->ncs ? MV_NAND_NDSR_RDY1 : MV_NAND_NDSR_RDY0) ) |
| return -1; |
| |
| if( wait_status_bit_set(board->ncs ? (MV_NAND_NDSR_CS1_CMDD| MV_NAND_NDSR_CS1_PAGED): |
| (MV_NAND_NDSR_CS0_CMDD| MV_NAND_NDSR_CS0_PAGED)) ) |
| return -1; |
| |
| mv_nand_disable(); |
| /* we just add a patch here, would be remove in the future. |
| * The interface of nand should be: (all the other function should call this interfaces) |
| * int mv_nand_read_page(uint64_t addr, uint8_t *data, uint8_t *oob); |
| * int mv_nand_write_page(uint64_t addr, const uint8_t *data, const uint8_t *oob); |
| */ |
| if (1) { |
| int ret = 0; |
| do { |
| ret = mv_nand_read_status(); |
| }while(!(ret & 0x40)); |
| return ret & 0x01; |
| } |
| return 0; |
| } |
| |
| #if defined(__UBOOT__) |
| //buffer should not contain oob |
| //page_size must be greater than 2048 |
| int mv_nand_write_multi_plane(loff_t pos, char *buf, long size) |
| { |
| int i,j, k; |
| int cs=0; |
| struct mv_nand_data *board = &nand_data; |
| int blk_size, page_size, chk_size; |
| int page_shift; |
| int row_addr; |
| int repeat; |
| int oob_per_chunk = 32; |
| char oob_buf[32]; |
| int page_layout; |
| int planes; |
| unsigned check_sum_w, check_sum_r; |
| int start, end; |
| char* curr_buf; |
| loff_t curr_pos; |
| int pages_per_blk; |
| int dst_4321, dst_xxx5; |
| char *tmp_buf; |
| |
| check_sum_w = crc32(0, (unsigned char*)buf, size); |
| |
| start = get_timer(0); |
| |
| mv_nand_preinit_rw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| blk_size = board->szofblk; |
| page_size = board->szofpg; |
| chk_size = board->chunk_size; |
| page_shift = board->page_shift; |
| page_layout = board->conspare; |
| planes=2; |
| |
| if(page_size < 2048) { |
| printf("multi_plane write for page_size %d is not supported.\n", page_size); |
| return -1; |
| } |
| |
| // printf("pos=%Lx, size=%x.\n", pos, size); |
| if(( pos % blk_size ) || (size % (2*blk_size))) { |
| printf("pos=%Lx, size=%x, alignment error.\n", pos, (int)size); |
| return -1; |
| } |
| |
| repeat= size/(planes*blk_size); |
| pages_per_blk =blk_size/page_size; |
| |
| memset(oob_buf, 0xff, sizeof(oob_buf)); |
| curr_buf = buf; |
| curr_pos = pos; //firsr plane |
| for(k=0;k<repeat;k++) { |
| curr_pos += blk_size; //make plane bit high |
| for(i=0;i<pages_per_blk;i++) |
| { |
| |
| // printf("%u\n", i); |
| tmp_buf = curr_buf; |
| |
| //first plane |
| // printf("1\n"); |
| NAKED_CMD(cs, 0x80); |
| // printf("2\n"); |
| NAKED_ADDR(cs, 5, 0, 0, 0); |
| // printf("3\n"); |
| j = page_size/chk_size; |
| while(j-->0) { |
| if((j==0) || (page_layout==MODE_MULTI_SPARE)) { |
| NAKED_WR_DATA(cs, chk_size + oob_per_chunk); |
| } else { |
| NAKED_WR_DATA(cs, chk_size); |
| } |
| /* write data */ |
| mv_nand_write_buf(tmp_buf, chk_size); |
| tmp_buf += chk_size; |
| /* write oob */ |
| if((j==0) || (page_layout==MODE_MULTI_SPARE)) |
| mv_nand_write_buf(oob_buf, oob_per_chunk); |
| } |
| // printf("4\n"); |
| NAKED_CMD(cs, 0x11); |
| |
| tmp_buf = curr_buf + blk_size; |
| // printf("curr_pos=0x%Lx, A20=%Lx\n", curr_pos, curr_pos & (1<<19)); |
| row_addr = (curr_pos >> page_shift); |
| dst_4321 = (row_addr << 16) & 0xFFFF0000; |
| dst_xxx5 = (row_addr >> 16) & 0xFF; |
| |
| if( wait_status_bit_set(cs ? MV_NAND_NDSR_RDY1 : MV_NAND_NDSR_RDY0) ) |
| return -1; |
| |
| //second plane |
| // printf("5\n"); |
| NAKED_CMD(cs, 0x81); |
| // printf("6\n"); |
| NAKED_ADDR(cs, 5, dst_4321, dst_xxx5, 0); |
| j = page_size/chk_size; |
| while(j-->0) { |
| if((j==0) || (page_layout==MODE_MULTI_SPARE)) { |
| NAKED_WR_DATA(cs, chk_size + oob_per_chunk); |
| } else { |
| NAKED_WR_DATA(cs, chk_size); |
| } |
| /* write data */ |
| mv_nand_write_buf(tmp_buf, chk_size); |
| tmp_buf += chk_size; |
| /* write oob */ |
| if((j==0) || (page_layout==MODE_MULTI_SPARE)) |
| mv_nand_write_buf(oob_buf, oob_per_chunk); |
| } |
| // printf("7\n"); |
| NAKED_CMD(cs, 0x10); |
| curr_pos += page_size; |
| curr_buf += page_size; |
| |
| // printf("8\n"); |
| if( wait_status_bit_set(cs ? MV_NAND_NDSR_RDY1 : MV_NAND_NDSR_RDY0) ) |
| return -1; |
| } |
| curr_buf += blk_size; |
| } |
| mv_nand_disable(); |
| |
| end = get_timer(start); |
| printf("time used: %u ms.\n", end); |
| |
| for(i=0;i<size/page_size;i++) { |
| curr_buf = buf + i*page_size; |
| curr_pos = pos + i*page_size; //firsr plane |
| mv_nand_read_page(curr_pos, curr_buf); |
| } |
| |
| check_sum_r = crc32(0, (unsigned char*)buf, size); |
| printf("check_sum_w=%08x, check_sum_r=%08x\n", check_sum_w, check_sum_r); |
| if(check_sum_w != check_sum_r) |
| return -1; |
| return 0; |
| } |
| #endif /* #if defined(__UBOOT__) */ |
| |
| static int mv_nand_write512_ecc(int srcx, char *buf) |
| { |
| int ctrl ; |
| unsigned int dst, dst_4321, dst_xxx5; |
| struct mv_nand_data *board = &nand_data; |
| mv_nand_preinit_rw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| mv_nand_cmd_request(); |
| ctrl = 0x022c1080 | (board->ncs ? MV_NAND_NDCB_CSEL : 0); |
| dst = srcx; |
| dst_4321 = ((dst & 0xFF) | (dst >> (board->page_shift - 8))); |
| dst_xxx5 = 0; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| if( wait_status_bit_set(MV_NAND_NDSR_WRDREQ) ) |
| return -1; |
| |
| mv_nand_write_buf(buf, board->szofpg); |
| mv_nand_write_buf(buf + board->szofpg, board->oobsize/2); |
| if( wait_status_bit_set(board->ncs? MV_NAND_NDSR_RDY1 : MV_NAND_NDSR_RDY0) ) |
| return -1; |
| |
| if( wait_status_bit_set(board->ncs ? ( MV_NAND_NDSR_CS1_PAGED) : (MV_NAND_NDSR_CS0_PAGED)) ) |
| return -1; |
| mv_nand_disable(); |
| |
| return 0; |
| } |
| |
| /* |
| * return - 0: success, others: fail |
| */ |
| int mv_nand_write_page(loff_t ofs, char *buf) |
| { |
| struct mv_nand_data *board = &nand_data; |
| int ret = 0; |
| |
| if(board->szofpg == 512) |
| ret = mv_nand_write512_ecc(ofs, buf); |
| else |
| ret = mv_nand_write_large_page(ofs, buf, 1); |
| |
| if(ret) |
| return ret; |
| |
| do { |
| ret = mv_nand_read_status(); |
| }while(!(ret & 0x40)); |
| |
| return ret & 0x01; |
| } |
| |
| /* |
| * return - 0, success; 1, fail |
| */ |
| int mv_nand_erase(loff_t srcx) |
| { |
| int ctrl; |
| unsigned int dst_4321, dst; |
| struct mv_nand_data *board = &nand_data; |
| |
| mv_nand_preinit_exrw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| mv_nand_cmd_request(); |
| |
| dst = (int)(srcx >> board->page_shift); |
| ctrl= 0x005BD060 | (board->ncs ? MV_NAND_NDCB_CSEL : 0); |
| |
| dst_4321 = (dst & ~((board->szofblk / board->szofpg) - 1)); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDCB0_OFFSET, 0); |
| if( wait_status_bit_set(board->ncs ? MV_NAND_NDSR_CS1_CMDD : MV_NAND_NDSR_CS0_CMDD) ) |
| return -1; |
| ctrl = 0; |
| |
| do { |
| ctrl = mv_nand_read_status(); |
| }while(!(ctrl & 0x40)); |
| return ctrl & 0x01; |
| |
| } |
| |
| void mv_nand_select_chip(int i) |
| { |
| UNUSED(i); |
| return; |
| } |
| |
| #if defined(CONFIG_NAND_READ_RETRY) |
| /*The real size of rrt is 1025 bytes.*/ |
| #define RRT_SIZE (2048) |
| #define RR_PARAMETERS_COUNT (8) |
| #define NAND_UNCERR (-2) |
| #define NAND_RR_NOERROR (0) |
| #define NAND_RR_ERROR (-1) |
| #define NAND_GET_RRT_ERROR (-2) |
| #define NAND_SET_RRT_ERROR (-3) |
| #define RRT_COPY_COUNT (64) |
| |
| unsigned char RRT_BUFF[2<<10]; |
| unsigned int nand_read_retry = 0; |
| unsigned int nand_block_count = 0; |
| /* Here is the command sequence to get Read Retry table |
| * from OTP for H27UAG8T2CTR |
| */ |
| const int RRT_command_sequences[]= |
| { |
| NDCB0_CMD_NAKED_CMD, 0x36, |
| NDCB0_CMD_NAKED_ADDR,0xff, |
| NDCB0_CMD_PROGRAM, 0x40, |
| NDCB0_CMD_NAKED_ADDR,0xcc, |
| NDCB0_CMD_PROGRAM, 0x4d, |
| NDCB0_CMD_NAKED_CMD, 0x16, |
| NDCB0_CMD_NAKED_CMD, 0x17, |
| NDCB0_CMD_NAKED_CMD, 0x04, |
| NDCB0_CMD_NAKED_CMD, 0x19, |
| NDCB0_CMD_NAKED_CMD, 0x00, |
| NDCB0_CMD_NAKED_ADDR,0x00, |
| NDCB0_CMD_NAKED_ADDR,0x00, |
| NDCB0_CMD_NAKED_ADDR,0x00, |
| NDCB0_CMD_NAKED_ADDR,0x02, |
| NDCB0_CMD_NAKED_ADDR,0x00, |
| NDCB0_CMD_NAKED_CMD, 0x30, |
| -1,-1 |
| }; |
| /* |
| * Here is the register addr to store read retry parameters |
| */ |
| const int RRT_register_addr[]= |
| { |
| 0xCC, 0xBF, 0xAA, 0xAB, |
| 0xCD, 0xAD, 0xAE, 0xAF |
| }; |
| |
| /* |
| * Get Read retry Table from OTP, rrt_len should be greater than 1025 bytes. |
| */ |
| static int mv_nand_get_RRT_from_OTP(char * rrt_buf, unsigned int rrt_len) |
| { |
| int ctrl, i; |
| int ret = 0; |
| unsigned int dst_4321, dst_xxx5; |
| unsigned char data_buf[32]; |
| struct mv_nand_data *board = &nand_data; |
| //disable ECC |
| mv_nand_enable_ecc(0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDECCCTRL_OFFSET, 0); |
| |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| //printf("[%s:%d] Get RRT from OTP\n", __func__, __LINE__); |
| for (i = 0; RRT_command_sequences[i] >= 0; i = i+2) |
| { |
| dst_4321 = 0; |
| dst_xxx5 = 0; |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | RRT_command_sequences[i] |
| | NDCB0_CMD2(0xFF) | NDCB0_NC | NDCB0_CMD1(RRT_command_sequences[i+1]); |
| |
| if (RRT_command_sequences[i] == NDCB0_CMD_NAKED_ADDR) |
| { |
| ctrl = ctrl | NDCB0_CMD1(0xFF); |
| ctrl = ctrl | NDCB0_ADDRC(1); |
| dst_4321 = RRT_command_sequences[i+1]; |
| } |
| if (RRT_command_sequences[i] == NDCB0_CMD_PROGRAM) |
| { |
| ctrl = ctrl | NDCB0_LENOVRD; |
| ctrl = ctrl | NDCB0_CMDX_NWRITE; |
| } |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| |
| if (RRT_command_sequences[i] == NDCB0_CMD_PROGRAM) |
| { |
| memset(data_buf,(unsigned char)RRT_command_sequences[i+1],32); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, |
| NDCB3_NDLENCNT(8)); |
| if(wait_status_bit_set(MV_NAND_NDSR_WRDREQ)) |
| return -1; |
| mv_nand_write_buf(data_buf, 8); |
| |
| //mv_nand_preinit_rw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| } |
| |
| } |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMDX_NREAD |
| | NDCB0_LENOVRD | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0xFF); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, |
| NDCB3_NDLENCNT(2048)); |
| if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ)) |
| return -1; |
| mv_nand_read_buf(rrt_buf, 2048); |
| printf("[%s:%d] Got RRT from OTP\n", __func__, __LINE__); |
| |
| mv_nand_start(); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD | NDCB0_NC | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0xFF); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0x38); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| |
| mv_nand_enable_ecc(1); |
| mv_nand_preinit_rw(); |
| |
| return ret; |
| } |
| |
| /* |
| * This function used to write Read Retry Table to system block. |
| * There are 64 RRT copies in system block. |
| * The start of system block is the start of redundant. It will take 8 blocks. |
| * RRT is stored in the first 8 pages of system block, one copy for each page. |
| * The size of RRT is 1025 Bytes. |
| */ |
| static int mv_nand_set_RRT_to_systemblock(char * rrt_buf, unsigned int rrt_len) |
| { |
| long long pos; |
| int ret = 0; |
| int size = rrt_len; |
| int page_size, block_size; |
| unsigned int rrt_copy_count = RRT_COPY_COUNT; |
| int i,j; |
| block_size = mv_nand_block_size(); |
| page_size = mv_nand_page_size(); |
| pos = (long long)nand_block_count * block_size; |
| |
| memcpy(mvnand_page_buffer, rrt_buf, size); |
| memset(&mvnand_page_buffer[size], 0xff, page_size-size); |
| printf("[%s:%d] Set RRT to System Block\n", __func__, __LINE__); |
| |
| for (i = 0; i < 8; i++) |
| { |
| pos = (nand_block_count + i) * block_size; |
| if (mv_nand_erase(pos)) |
| { |
| printf("[%s:%d] Erase system block ERROR!!!!", __func__, __LINE__); |
| return -1; |
| } |
| for (j = 0; j < 8; j++) |
| { |
| pos = (nand_block_count + i) * block_size + j * page_size; |
| if(pos & (page_size - 1)) |
| return -1; |
| if(mv_nand_write_large_page(pos, mvnand_page_buffer, 0)) |
| { |
| printf("[%s:%d] write RRT to system block ERROR!!!!", __func__, __LINE__); |
| return -1; |
| } |
| } |
| } |
| return ret; |
| } |
| |
| /* |
| * This function used to get Read Retry Table from system block. |
| * There are 64 RRT copies in system block. |
| * The start of system block is the start of redundant. It will take 8 blocks. |
| * RRT is stored in the first 8 pages of system block, one copy for each page. |
| * The size of RRT is 1025 Bytes. |
| * copy_index is the index of RRT copies. |
| */ |
| static int mv_nand_get_RRT_from_systemblock(char * rrt_buf, unsigned int rrt_len, unsigned int copy_index) |
| { |
| long long pos; |
| int ret = 0; |
| int size = rrt_len; |
| int page_size, block_size; |
| |
| page_size = mv_nand_page_size(); |
| block_size = mv_nand_block_size(); |
| pos = (long long)nand_block_count * block_size; |
| pos += (copy_index / 8) * block_size; |
| pos += (copy_index % 8) * page_size; |
| |
| ret = mv_nand_read_large_page_RR(pos, mvnand_page_buffer, 0); |
| if(ret) |
| { |
| printf("[%s:%d] read RRT from system block ERROR!!!!.\n", __func__, __LINE__); |
| return -1; |
| } |
| |
| memcpy(rrt_buf, mvnand_page_buffer, size); |
| return ret; |
| } |
| |
| /* |
| * Get current Read retry parameters from registers. |
| */ |
| static int mv_nand_get_read_retry_parameters(char * rr_buf, unsigned int rr_len) |
| { |
| int ctrl, i,j ; |
| int ret = 0; |
| unsigned int dst_4321 = 0; |
| unsigned int dst_xxx5 = 0; |
| char rr_tmp[8]; |
| struct mv_nand_data *board = &nand_data; |
| |
| if (rr_len != RR_PARAMETERS_COUNT) |
| { |
| printf("[%s:%d] parameters count error.\n", __func__, __LINE__); |
| return 1; |
| } |
| mv_nand_enable_ecc(0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDECCCTRL_OFFSET, 0); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| printf("[%s:%d] Get read retry register.\n", __func__, __LINE__); |
| |
| for (i = 0; i < rr_len; i++) |
| { |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD | NDCB0_NC |
| | NDCB0_ADDRC(1) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0x37); |
| |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_ADDR | NDCB0_ADDRC(1) |
| | NDCB0_NC | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0xFF); |
| dst_4321 = RRT_register_addr[i]; |
| |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMDX_NREAD |
| | NDCB0_LENOVRD | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0XFF); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, |
| NDCB3_NDLENCNT(8)); |
| if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ)) |
| return -1; |
| mv_nand_read_buf(rr_tmp, 8); |
| rr_buf[i] = rr_tmp[0]; |
| mv_nand_disable(); |
| mv_nand_start(); |
| } |
| mv_nand_enable_ecc(1); |
| mv_nand_preinit_rw(); |
| return ret; |
| |
| } |
| |
| /* |
| * Set Read retry parameters to registers. |
| */ |
| static int mv_nand_set_read_retry_parameters(char * rr_buf, unsigned int rr_len) |
| { |
| int ctrl, i; |
| int ret = 0; |
| unsigned int dst_4321 = 0; |
| unsigned int dst_xxx5 = 0; |
| unsigned char data_buf[32]; |
| struct mv_nand_data *board = &nand_data; |
| |
| if (rr_len != RR_PARAMETERS_COUNT) |
| { |
| printf("[%s:%d] parameters count error.\n", __func__, __LINE__); |
| return 1; |
| } |
| mv_nand_enable_ecc(0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDECCCTRL_OFFSET, 0); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| //printf("[%s:%d] Set read retry register.\n", __func__, __LINE__); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD | NDCB0_NC |
| | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0x36); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| |
| for (i = 0; i < rr_len; i++) |
| { |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_ADDR | NDCB0_ADDRC(1) |
| | NDCB0_NC | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0xFF); |
| dst_4321 = RRT_register_addr[i]; |
| |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_PROGRAM | NDCB0_LENOVRD |
| | NDCB0_CMDX_NWRITE | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0xFF); |
| dst_4321 = 0; |
| |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| memset(data_buf,(unsigned char)rr_buf[i],32); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, |
| NDCB3_NDLENCNT(8)); |
| if(wait_status_bit_set(MV_NAND_NDSR_WRDREQ)) |
| return -1; |
| mv_nand_write_buf(data_buf, 8); |
| |
| mv_nand_disable(); |
| mv_nand_start(); |
| } |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD |
| | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0x16); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| |
| mv_nand_enable_ecc(1); |
| mv_nand_preinit_rw(); |
| return ret; |
| |
| } |
| |
| /* |
| * 1. Get RRT from System block. |
| * 2. check the XOR of orginal and inverse parameters. If not 0xFF, get next copy. |
| * If all 64 copies are invalid, get RRT from OTP, and do the same check. |
| * 3. If the parameters in OTP is valid, then update RRT in system block. Or, disable |
| * nand read retry. |
| */ |
| int mv_nand_read_retry(int RRT_index) |
| { |
| int ret = NAND_RR_NOERROR; |
| int i, j, k; |
| int reg_count, copy_count, RRT_copy_count; |
| unsigned char * rrt_buff = RRT_BUFF; |
| unsigned char * origianl_pos; |
| unsigned char * inverse_pos; |
| unsigned char rr_buff[8]; |
| |
| copy_count = 8; |
| reg_count = 8; |
| RRT_copy_count = RRT_COPY_COUNT; |
| |
| printf("RRT_index = %d.\n", RRT_index); |
| origianl_pos = &rrt_buff[RRT_index * reg_count + 2]; |
| inverse_pos = origianl_pos + copy_count * reg_count; |
| |
| printf("Read RRT info from system block.\n"); |
| for (k = 0; k < RRT_copy_count; k++) |
| { |
| if (mv_nand_get_RRT_from_systemblock(rrt_buff, RRT_SIZE, k)) |
| continue; |
| #if 0 |
| printf("#############RRT in system block############\n"); |
| for (j = 0; j < 256; j++) |
| { |
| printf("0x%08x", j*8); |
| for (i = 0; i < 8; i++) |
| { |
| printf(" 0x%02x ", rrt_buff[j*8 + i]); |
| } |
| printf("\n"); |
| } |
| #endif |
| for (j = 0; j < copy_count; j++) |
| {//There are 8 copies of parameters in a RRT. |
| for (i = 0; i < reg_count; i++) |
| {//The length of parameters is 8 bytes |
| if((unsigned char)(origianl_pos[j*reg_count*copy_count*2+i]^inverse_pos[j*reg_count*copy_count*2+i]) != 0xFF) |
| { |
| //printf("j*Reg_count*copy_count+i = 0x%08x \n",j*reg_count*copy_count*2+i); |
| //printf("origianl = 0x%02x \n",origianl_pos[j*reg_count*copy_count*2+i]); |
| //printf("inverse = 0x%02x \n",inverse_pos[j*reg_count*copy_count*2+i]); |
| printf("nand read retry parameters error.\n"); |
| break; |
| } |
| } |
| if (i == reg_count) |
| break; |
| } |
| if (j < copy_count) |
| break; |
| } |
| if (k == RRT_copy_count) |
| { |
| printf("Get nand read retry parameters from OTP.\n"); |
| memset(rrt_buff,0x0,2048); |
| mv_nand_get_RRT_from_OTP(rrt_buff, RRT_SIZE); |
| #if 0 |
| printf("#############RRT in OTP############\n"); |
| for (j = 0; j < 256; j++) |
| { |
| printf("0x%08x", j*8); |
| for (i = 0; i < 8; i++) |
| { |
| printf(" 0x%02x ", rrt_buff[j*8 + i]); |
| } |
| printf("\n"); |
| } |
| #endif |
| for (j = 0; j < copy_count; j++) |
| { |
| for (i = 0; i < reg_count; i++) |
| { |
| if((unsigned char)(origianl_pos[j*reg_count*copy_count*2+i]^inverse_pos[j*reg_count*copy_count*2+i]) != 0xFF) |
| { |
| printf("j*Reg_count*copy_count+i = 0x%08x \n",j*reg_count*copy_count*2+i); |
| printf("origianl = 0x%02x \n",origianl_pos[j*reg_count*copy_count*2+i]); |
| printf("inverse = 0x%02x \n",inverse_pos[j*reg_count*copy_count*2+i]); |
| break; |
| } |
| } |
| if (i == reg_count) |
| break; |
| } |
| /*Command padding read operation requested by Hynix*/ |
| mv_nand_read_large_page_RR((loff_t)0x1400000, mvnand_page_buffer, 1); |
| |
| if (j == copy_count) |
| { |
| printf("RRT info error.\n"); |
| printf("Disable NAND read retry.\n"); |
| nand_read_retry = 0; |
| return NAND_RR_ERROR; |
| } |
| mv_nand_set_RRT_to_systemblock(rrt_buff, RRT_SIZE); |
| } |
| #if 0 |
| printf("The parameters from OTP:\n"); |
| printf("j = %d\n",j); |
| for (k = 0; k < 8; k++){ |
| printf(" 0x%02x ", origianl_pos[j*Reg_count*copy_count*2+k]); |
| } |
| printf("\n"); |
| |
| printf("The origianl parameters:\n"); |
| ret = mv_nand_get_read_retry_parameters(rr_buff, 8); |
| if (ret){ |
| printf("Get parameters error.\n"); |
| return NAND_GET_RRT_ERROR; |
| } |
| for (k = 0; k < 8; k++) |
| { |
| printf("0x%02x \t", rr_buff[k]); |
| } |
| printf("\n"); |
| mv_nand_read_large_page_RR((loff_t)0x1400000, mvnand_page_buffer, 1); |
| #endif |
| ret = mv_nand_set_read_retry_parameters(&origianl_pos[j*reg_count*copy_count*2], 8); |
| if (ret){ |
| printf("Set parameters error.\n"); |
| return NAND_SET_RRT_ERROR; |
| } |
| /* padding read operation requested by Hynix*/ |
| mv_nand_read_large_page_RR((loff_t)0x1400000, mvnand_page_buffer, 1); |
| #if 0 |
| printf("Set parameters and read back:\n"); |
| mv_nand_get_read_retry_parameters(rr_buff, 8); |
| if (ret){ |
| printf("Set parameters error.\n"); |
| return NAND_GET_RRT_ERROR; |
| } |
| for (k = 0; k < 8; k++) |
| { |
| printf("0x%02x \t", rr_buff[k]); |
| } |
| printf("\n"); |
| mv_nand_read_large_page_RR((loff_t)0x1400000, mvnand_page_buffer, 1); |
| #endif |
| return ret; |
| } |
| |
| /* |
| * oob, 1 buffer contain oob, 0 buffer doesn't contain oob |
| * return: |
| * 0: sucess |
| * 1: uncorrectable error |
| * -1: fail |
| * -2: fail, need to do read retry |
| */ |
| int mv_nand_read_large_page_RR(loff_t srcx , char *buf, int oob) |
| { |
| int ctrl , i, j, uncerr=0; |
| unsigned int row_addr, page_shift; |
| unsigned int dst_4321, dst_xxx5; |
| struct mv_nand_data *board = &nand_data; |
| int page_size = board->szofpg; |
| int chunk_size = board->chunk_size; |
| int bch_enabled = board->conbch; |
| int oob_per_chunk; |
| int page_layout = board->conspare; |
| char* oob_buf; |
| char tmp_oob_buf[128]; |
| |
| char* out_buf = buf; |
| unsigned int page_addr = (unsigned int)(srcx >> board->page_shift); |
| #ifdef SUPPORT_RANDOMIZATION |
| int randomized = 0; |
| #endif // SUPPORT_RANDOMIZATION |
| |
| // printf("read @0x%08X\n", srcx); |
| // printf("%s: 0\n", __FUNCTION__); |
| page_shift = board->page_shift; |
| |
| mv_nand_preinit_rw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_ADDR | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0xFF); |
| |
| row_addr = (srcx >> page_shift); |
| dst_4321 = (row_addr << 16) & 0xFFFF0000; |
| dst_xxx5 = (row_addr >> 16) & 0xFF; |
| |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0x30); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| |
| i = (page_size - 1 + board->chunk_size)/board->chunk_size; |
| if(oob) |
| oob_buf = buf + page_size; |
| else |
| oob_buf = tmp_oob_buf; |
| if(page_layout==MODE_MULTI_SPARE) |
| oob_per_chunk = board->oobsize/i; |
| else |
| oob_per_chunk = board->oobsize; |
| |
| // printf("%s: 3\n", __FUNCTION__); |
| while(i-->0){ |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CMDX_NREAD |NDCB0_LENOVRD | NDCB0_CS(board->ncs) | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0x30); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| if((i==0) || (page_layout==MODE_MULTI_SPARE)) |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, |
| NDCB3_NDLENCNT(board->chunk_size + oob_per_chunk)); |
| else |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, |
| NDCB3_NDLENCNT(board->chunk_size)); |
| |
| /*read data*/ |
| if(bch_enabled) { |
| for(j = 0 ;j < board->chunk_size/32;j++) { |
| if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| mv_nand_read_buf(buf, 32); |
| buf += 32; |
| } |
| } else { |
| if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| mv_nand_read_buf(buf, board->chunk_size); |
| buf += board->chunk_size; |
| } |
| /* read oob */ |
| if((i==0) || (page_layout==MODE_MULTI_SPARE)) { |
| if( bch_enabled && wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| mv_nand_read_buf(oob_buf, oob_per_chunk); |
| oob_buf += oob_per_chunk; |
| } |
| /* read state */ |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDSR_OFFSET ,&ctrl); |
| ctrl &= MV_NAND_NDSR_UNCERR; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDSR_OFFSET, ctrl); |
| uncerr |= ctrl; |
| } |
| // printf("%s: 5\n", __FUNCTION__); |
| if( wait_status_bit_set(board->ncs ? (MV_NAND_NDSR_CS1_PAGED):(MV_NAND_NDSR_CS0_PAGED )) ) |
| return -1; |
| if( wait_status_bit_set(board->ncs ? (MV_NAND_NDSR_CS1_CMDD) : (MV_NAND_NDSR_CS0_CMDD )) ) |
| return -1; |
| |
| mv_nand_disable(); |
| |
| if(uncerr) { |
| /* |
| * below cases will cause Uncorrectable error. |
| * 1. blank/erased page |
| * 2. bad block (page is full zero bits) |
| * 3. bad block (only some colums are zero bits) |
| * |
| * For blank/erased page, re-read the page without ECC and check |
| * whether it's blank page. |
| * If it's blank page, then recover it to all 0xff. |
| * Note: here each chunk size is including ECC length. |
| */ |
| #if 0 |
| char *temp_buf = mvnand_page_buffer; |
| int page_with_ecc_size = mv_nand_read_large_page_without_ecc(srcx, temp_buf); |
| |
| if (page_with_ecc_size > 0 && |
| is_read_page_blank(board->t, chunk_size + get_ecc_size(board->t), |
| temp_buf, page_with_ecc_size)) { |
| #else |
| if (is_read_page_blank(board->t, board->chunk_size, out_buf, page_size + 32)) { |
| #endif |
| /* recover all the bytes to 0xff */ |
| int i; |
| int recover_len = page_size + (oob ? 32 : 0); |
| for (i = 0; i < recover_len; i++) |
| out_buf[i] = 0xff; |
| |
| TRACE_LOG("Read blank page\n"); |
| return 0; |
| } |
| printf("[%s:%d] ECC uncorrectable error (page=0x%09llx)\n", __func__, __LINE__, srcx); |
| return NAND_UNCERR; |
| } else { |
| |
| #ifdef SUPPORT_RANDOMIZATION |
| DUMP_8_BYTES("ReadBefore:", out_buf); |
| randomized = nand_read_page_post_process(page_addr, |
| out_buf, |
| oob ? (out_buf + page_size) : NULL, |
| out_buf, |
| oob ? (out_buf + page_size) : NULL); |
| TRACE_LOG("Read Page(0x%08X): %s\n", (int)srcx, randomized ? "RANDOMIZED" : "UNRANDOMIZED"); |
| DUMP_8_BYTES("ReadReal:", out_buf); |
| #endif // SUPPORT_RANDOMIZATION |
| return 0; |
| } |
| } |
| /* |
| * if defined CONFIG_NAND_READ_RETRY, this function will be used to read nand page with or without oob. |
| */ |
| int mv_nand_read_large_page(loff_t srcx , char *buf, int oob) |
| { |
| int ret = 0; |
| int read_retry_ret = 0; |
| int i; |
| if(!strncmp(partition_type_s, "mlc", 3)){ |
| } |
| #if defined(CONFIG_ENHANCED_SLC) |
| else if (config_enhanced_SLC == 1 && !strncmp(partition_type_s, "eslc", 4)){ |
| srcx = mv_nand_enhanced_SLC_paired_page(srcx); |
| } |
| #endif |
| |
| #if defined(CONFIG_SLC) |
| else if (config_SLC == 1 && !strncmp(partition_type_s, "slc", 3)){ |
| srcx = mv_nand_SLC_paired_page(srcx); |
| if(srcx < 0){ |
| printf("Cannot get SLC paired page addr.\n"); |
| return srcx; |
| } |
| ret = mv_nand_SLC_enable(); |
| if(ret){ |
| printf("Enable SLC error.\n"); |
| return ret; |
| } |
| } |
| #endif |
| |
| ret = mv_nand_read_large_page_RR(srcx, buf, oob); |
| if ((nand_read_retry == 1) && (ret == NAND_UNCERR)) |
| { |
| printf("[%s:%d] DO NAND READ RETRY......\n", __func__, __LINE__); |
| for (i = 0; i < 8; i++) |
| { |
| read_retry_ret = mv_nand_read_retry(i); |
| if (read_retry_ret == NAND_RR_NOERROR) |
| ret = mv_nand_read_large_page_RR(srcx, buf, oob); |
| if (ret != NAND_UNCERR) |
| break; |
| } |
| } |
| //printf("[%s:%d] *************Finish read large page*********\n", __func__, __LINE__); |
| #if defined(CONFIG_SLC) |
| if (config_SLC == 1 && !strncmp(partition_type_s, "slc", 3)){ |
| /* disable SLC*/ |
| mv_nand_SLC_disable(); |
| } |
| #endif |
| return ret; |
| } |
| |
| #else |
| |
| /* |
| * oob, 1 buffer contain oob, 0 buffer doesn't contain oob |
| * return: |
| * 0: sucess |
| * 1: uncorrectable error |
| * -1: fail |
| */ |
| int mv_nand_read_large_page(loff_t srcx , char *buf, int oob) |
| { |
| int ctrl , i, uncerr=0; |
| unsigned int j; |
| unsigned int row_addr, page_shift; |
| unsigned int dst_4321, dst_xxx5; |
| struct mv_nand_data *board = &nand_data; |
| int page_size = board->szofpg; |
| int bch_enabled = board->conbch; |
| int oob_per_chunk; |
| int page_layout = board->conspare; |
| char* oob_buf; |
| char tmp_oob_buf[128]; |
| |
| char* out_buf = buf; |
| int ret; |
| (void)ret; |
| unsigned int page_addr = (unsigned int)(srcx >> board->page_shift); |
| |
| if(!strncmp((const char *)partition_type_s, "mlc", 3)){ |
| |
| } |
| #if defined(CONFIG_ENHANCED_SLC) |
| else if (config_enhanced_SLC == 1 && !strncmp(partition_type_s, "eslc", 4)){ |
| srcx = mv_nand_enhanced_SLC_paired_page(srcx); |
| } |
| #endif |
| |
| #if defined(CONFIG_SLC) |
| else if (config_SLC == 1 && !strncmp(partition_type_s, "slc", 3)){ |
| srcx = mv_nand_SLC_paired_page(srcx); |
| if(srcx < 0){ |
| printf("Cannot get SLC paired page addr.\n"); |
| return srcx; |
| } |
| ret = mv_nand_SLC_enable(); |
| if(ret){ |
| printf("Enable SLC error.\n"); |
| return ret; |
| } |
| } |
| #endif |
| |
| #ifdef SUPPORT_RANDOMIZATION |
| int randomized = 0; |
| UNUSED(randomized); |
| #endif // SUPPORT_RANDOMIZATION |
| |
| // printf("read @0x%08X\n", srcx); |
| // printf("%s: 0\n", __FUNCTION__); |
| page_shift = board->page_shift; |
| |
| mv_nand_preinit_rw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_ADDR | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0xFF); |
| |
| row_addr = (srcx >> page_shift); |
| dst_4321 = (row_addr << 16) & 0xFFFF0000; |
| dst_xxx5 = (row_addr >> 16) & 0xFF; |
| |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CS(board->ncs) | NDCB0_CMD_NAKED_CMD | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0x30); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| |
| i = (page_size - 1 + board->chunk_size)/board->chunk_size; |
| if(oob) |
| oob_buf = buf + page_size; |
| else |
| oob_buf = tmp_oob_buf; |
| if(page_layout==MODE_MULTI_SPARE) |
| oob_per_chunk = board->oobsize/i; |
| else |
| oob_per_chunk = board->oobsize; |
| |
| // printf("%s: 3\n", __FUNCTION__); |
| while(i-->0){ |
| mv_nand_cmd_request(); |
| ctrl = NDCB0_CMDX_NREAD |NDCB0_LENOVRD | NDCB0_CS(board->ncs) | NDCB0_NC |
| | NDCB0_ADDRC(board->addr_cycle) | NDCB0_CMD2(0xFF) | NDCB0_CMD1(0x30); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, 0); |
| if((i==0) || (page_layout==MODE_MULTI_SPARE)) |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, |
| NDCB3_NDLENCNT(board->chunk_size + oob_per_chunk)); |
| else |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, |
| NDCB3_NDLENCNT(board->chunk_size)); |
| |
| /*read data*/ |
| if(bch_enabled) { |
| for(j = 0 ;j < board->chunk_size/32;j++) { |
| if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| mv_nand_read_buf(buf, 32); |
| buf += 32; |
| } |
| } else { |
| if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| mv_nand_read_buf(buf, board->chunk_size); |
| buf += board->chunk_size; |
| } |
| /* read oob */ |
| if((i==0) || (page_layout==MODE_MULTI_SPARE)) { |
| if( bch_enabled && wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| mv_nand_read_buf(oob_buf, oob_per_chunk); |
| oob_buf += oob_per_chunk; |
| } |
| /* read state */ |
| MV_NAND_REG_READ_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDSR_OFFSET ,&ctrl); |
| ctrl &= MV_NAND_NDSR_UNCERR; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE,MV_NAND_NDSR_OFFSET, ctrl); |
| uncerr |= ctrl; |
| } |
| // printf("%s: 5\n", __FUNCTION__); |
| if( wait_status_bit_set(board->ncs ? (MV_NAND_NDSR_CS1_PAGED):(MV_NAND_NDSR_CS0_PAGED )) ) |
| return -1; |
| if( wait_status_bit_set(board->ncs ? (MV_NAND_NDSR_CS1_CMDD) : (MV_NAND_NDSR_CS0_CMDD )) ) |
| return -1; |
| |
| mv_nand_disable(); |
| |
| if(uncerr) { |
| /* |
| * below cases will cause Uncorrectable error. |
| * 1. blank/erased page |
| * 2. bad block (page is full zero bits) |
| * 3. bad block (only some colums are zero bits) |
| * |
| * For blank/erased page, re-read the page without ECC and check |
| * whether it's blank page. |
| * If it's blank page, then recover it to all 0xff. |
| * Note: here each chunk size is including ECC length. |
| */ |
| #if 0 |
| char *temp_buf = mvnand_page_buffer; |
| int page_with_ecc_size = mv_nand_read_large_page_without_ecc(srcx, temp_buf); |
| |
| if (page_with_ecc_size > 0 && |
| is_read_page_blank(board->t, chunk_size + get_ecc_size(board->t), |
| temp_buf, page_with_ecc_size)) { |
| #else |
| if (is_read_page_blank(board->t, board->chunk_size, (unsigned char *)out_buf, page_size + 32)) { |
| #endif |
| /* recover all the bytes to 0xff */ |
| int i; |
| int recover_len = page_size + (oob ? 32 : 0); |
| for (i = 0; i < recover_len; i++) |
| out_buf[i] = 0xff; |
| |
| TRACE_LOG("Read blank page\n"); |
| return 0; |
| } |
| printf("[%s:%d] ECC uncorrectable error (page=0x%09llx)\n", __func__, __LINE__, srcx); |
| return -1; |
| } else { |
| |
| #ifdef SUPPORT_RANDOMIZATION |
| DUMP_8_BYTES("ReadBefore:", out_buf); |
| randomized = nand_read_page_post_process(page_addr, |
| (const unsigned char *)out_buf, |
| (const unsigned char *)(oob ? (out_buf + page_size) : NULL), |
| (unsigned char *)out_buf, |
| (unsigned char *)(oob ? (out_buf + page_size) : NULL)); |
| //printf("Read Page(0x%08X): %s\n", (int)srcx, randomized ? "RANDOMIZED" : "UNRANDOMIZED"); |
| DUMP_8_BYTES("ReadReal:", out_buf); |
| #endif // SUPPORT_RANDOMIZATION |
| |
| #if defined(CONFIG_SLC) |
| if (config_SLC == 1 && !strncmp(partition_type_s, "slc", 3)){ |
| /* disable SLC*/ |
| mv_nand_SLC_disable(); |
| } |
| #endif |
| return 0; |
| } |
| } |
| |
| #endif |
| |
| static int mv_nand_read512_ecc(int srcx, char *buf) |
| { |
| int ctrl; |
| unsigned int addr, dst_4321, dst_xxx5; |
| struct mv_nand_data *board = &nand_data; |
| mv_nand_preinit_rw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| mv_nand_cmd_request(); |
| ctrl = 0x001c3000 | (board->ncs ? MV_NAND_NDCB_CSEL : 0); |
| addr = srcx; |
| dst_4321 = ((addr & 0xFF) | (addr >> (board->page_shift - 8))); |
| dst_xxx5 = 0; |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_4321); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDCB0_OFFSET, dst_xxx5); |
| if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| |
| mv_nand_read_buf(buf, board->szofpg); |
| mv_nand_read_buf(buf + board->szofpg, board->oobsize/2); |
| |
| if( wait_status_bit_set(board->ncs ? (MV_NAND_NDSR_CS1_PAGED) : (MV_NAND_NDSR_CS0_PAGED)) ) |
| return -1; |
| |
| if( wait_status_bit_set(board->ncs ? (MV_NAND_NDSR_CS1_CMDD) : (MV_NAND_NDSR_CS0_CMDD )) ) |
| return -1; |
| |
| mv_nand_disable(); |
| return 0; |
| } |
| |
| int mv_nand_read_page(loff_t ofs,char *buf) |
| { |
| struct mv_nand_data *board = &nand_data; |
| int ret; |
| |
| if(board->szofpg == 512) |
| ret = mv_nand_read512_ecc(ofs, buf); |
| else |
| ret = mv_nand_read_large_page(ofs, buf, 1); |
| |
| return ret; |
| } |
| |
| #if defined(__UBOOT__) |
| static int mv_nand_read_param_page(char *buf) |
| { |
| mv_nand_preinit_exrw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| NAKED_CMD(0, 0xEC); |
| NAKED_ADDR(0, 1, 0x00, 0, 0); |
| udelay(100); |
| NAKED_RD_DATA(0, 256); |
| if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| |
| mv_nand_read_buf(buf,8); |
| mv_nand_disable(); |
| return 0; |
| } |
| |
| static int mv_nand_read_onfi_id(char *buf) |
| { |
| mv_nand_preinit_exrw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| READ_ID(0, 6, 0x00, 0x90); |
| |
| /* NAKED_CMD(0, 0x90); |
| NAKED_ADDR(0, 1, 0x00, 0, 0); |
| udelay(10); |
| NAKED_RD_DATA(0, 6); |
| */ if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| |
| mv_nand_read_buf(buf,8); |
| mv_nand_disable(); |
| return 0; |
| } |
| #endif /* #if defined(__UBOOT__) */ |
| |
| int mv_nand_read_id(char *buf) |
| { |
| int ctrl ; |
| struct mv_nand_data *board = &nand_data; |
| mv_nand_preinit_exrw(); |
| mv_nand_disable(); |
| mv_nand_start(); |
| |
| mv_nand_cmd_request(); |
| ctrl = 0x00610090 | (board->ncs? MV_NAND_NDCB_CSEL : 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE ,MV_NAND_NDCB0_OFFSET , ctrl); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE ,MV_NAND_NDCB0_OFFSET , 0); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE ,MV_NAND_NDCB0_OFFSET , 0); |
| if( wait_status_bit_set(MV_NAND_NDSR_RDDREQ) ) |
| return -1; |
| |
| /* read data */ |
| mv_nand_read_buf(buf,8); |
| mv_nand_disable(); |
| return 0; |
| } |
| |
| #if defined(__UBOOT__) |
| static void init_timing(struct mv_nand_data * pndata) |
| { |
| int nfc_frequency; |
| struct nand_timing * timing = pndata->chip->timing; |
| struct ndtr0cs0 reg0; |
| struct ndtr1cs0 reg1; |
| struct ndredel reg2; |
| int max; |
| unsigned tmp; |
| |
| #if 1 |
| //never try to caculate timing!!! |
| pndata->ndtr0cs0 = MV_NAND_NDTR0CS0_VAL; |
| pndata->ndtr1cs0 = MV_NAND_NDTR1CS0_VAL; |
| pndata->ndredel = MV_NAND_NDREDEL_VAL; |
| return; |
| #endif |
| |
| nfc_frequency = GaloisGetFrequency(SOC_FREQ_NFCECC)/2; //MHz |
| debug_printf(("NFC clock frequency %d\n", nfc_frequency)); |
| |
| reg0.u32_ndtr0cs0 = 0; |
| reg1.u32_ndtr1cs0 = 0; |
| reg2.u32_ndredel = 0; |
| //NDTR0CS0 |
| reg0.SelCntr = 1; |
| tmp = nfc_frequency * (timing->tREA - timing->tRP)/1000+ 1; |
| if(tmp > ((1<<4) - 1)) |
| tmp = (1<<4) - 1; |
| reg0.Rd_Cnt_Del = tmp; |
| debug_printf(("Rd_Cnt_Del= %x\n", reg0.Rd_Cnt_Del)); |
| |
| tmp = nfc_frequency * timing->tCH/1000; |
| if(tmp > ((1<<3) - 1)) |
| tmp = (1<<3) - 1; |
| reg0.tCH = tmp; |
| debug_printf(("tCH= %x\n", reg0.tCH)); |
| |
| tmp = nfc_frequency * timing->tCS/1000; |
| if(tmp > ((1<<3) - 1)) |
| tmp = (1<<3) - 1; |
| reg0.tCS = tmp; |
| debug_printf(("tCS= %x\n", reg0.tCS)); |
| |
| tmp = nfc_frequency * timing->tWH/1000; |
| if(tmp > ((1<<3) - 1)) |
| tmp = (1<<3) - 1; |
| reg0.tWH = tmp; |
| debug_printf(("tWH= %x\n", reg0.tWH)); |
| |
| tmp = nfc_frequency * timing->tWP/1000; |
| if(tmp > ((1<<3) - 1)) |
| tmp = (1<<3) - 1; |
| reg0.tWP = tmp; |
| if(reg0.tWP + reg0.tWH < 2) |
| reg0.tWP = 2 - reg0.tWH; |
| debug_printf(("tWP= %x\n", reg0.tWP)); |
| |
| reg0.sel_nRE_edge = 0; |
| |
| tmp = nfc_frequency * timing->tRH/1000; |
| if(tmp > ((1<<3) - 1)) |
| tmp = (1<<3) - 1; |
| reg0.tRH = tmp; |
| debug_printf(("tRH= %x\n", reg0.tRH)); |
| |
| tmp = nfc_frequency * timing->tRP/1000; |
| if(tmp > ((1<<4) - 1)) |
| tmp = (1<<4) - 1; |
| reg0.etRP = tmp>>3; |
| debug_printf(("etRP= %x\n", reg0.etRP)); |
| reg0.tRP = tmp&7; |
| debug_printf(("tRP= %x\n", reg0.tRP)); |
| |
| tmp = nfc_frequency * timing->tADL/1000+ reg0.tWP + 4; |
| if(tmp > ((1<<5) - 1)) |
| tmp = (1<<5) - 1; |
| reg0.tADL = tmp; |
| debug_printf(("tADL= %x\n", reg0.tADL)); |
| |
| //NDTR1CS0 |
| tmp = nfc_frequency * timing->tR/1000- reg0.tCH - 2; |
| if(tmp > ((1<<16) - 1)) |
| tmp = (1<<16) - 1; |
| reg1.tR = tmp; |
| debug_printf(("tR= %x\n", reg1.tR)); |
| |
| reg1.wait_mode = 1; |
| reg1.PRESCALE = 0; |
| |
| tmp = nfc_frequency * timing->tRHW/(1000*16) + 1; |
| if(tmp > ((1<<2) - 1)) |
| tmp = (1<<2) - 1; |
| reg1.tRHW = tmp; |
| debug_printf(("tRHW= %x\n", reg1.tRHW)); |
| |
| max = reg0.tWH > reg0.tCH ? reg0.tWH : reg0.tCH; |
| tmp = nfc_frequency * timing->tWHR/1000+ 1 + max; |
| if(tmp > ((1<<4) - 1)) |
| tmp = (1<<4) - 1; |
| reg1.tWHR = tmp; |
| debug_printf(("tWHR= %x\n", reg1.tWHR)); |
| |
| tmp = nfc_frequency * timing->tAR/1000+ 1; |
| if(tmp > ((1<<4) - 1)) |
| tmp = (1<<4) - 1; |
| reg1.tAR = tmp; |
| debug_printf(("tAR= %x\n", reg1.tAR)); |
| |
| pndata->ndtr0cs0 = reg0.u32_ndtr0cs0; |
| pndata->ndtr1cs0 = reg1.u32_ndtr1cs0; |
| pndata->ndredel = reg2.u32_ndredel; |
| debug_printf(("ndtr0cs0 %08x, ndtr1cs0 %08x, ndredel %08x\n", |
| pndata->ndtr0cs0, pndata->ndtr1cs0, pndata->ndredel)); |
| #if 1 |
| unsigned int i; |
| char temp[8]; |
| mv_nand_read_id(temp); |
| debug_printf(("NAND id ")); |
| for(i=0;i<8;i++) |
| debug_printf(("%02X", temp[i])); |
| debug_printf(("\n")); |
| #endif |
| } |
| #endif /* #if defined(__UBOOT__) */ |
| |
| int init_nand_data(struct mv_nand_data * board) |
| { |
| unsigned int ndcr; |
| int pages_per_block; |
| unsigned int i; |
| |
| board->chunk_size = 2048; |
| if(board->szofpg== 512) { |
| board->addr_cycle = 4; |
| } else { |
| board->addr_cycle = 5; |
| } |
| for(i=0;i<16;i++){ |
| if((unsigned int)(1<<i) == board->szofpg) |
| break; |
| } |
| board->page_shift = i; |
| |
| board->ncs = 0; |
| #ifdef NAND_MODE_MULTI_SPARE |
| board->conspare = MODE_MULTI_SPARE; |
| #else |
| board->conspare = MODE_SINGLE_SPARE; |
| #endif |
| |
| //oob size avaliable for user |
| if(board->szofpg== 512) |
| board->oobsize = 16; |
| else if(board->conspare == MODE_NONE_SPARE) |
| board->oobsize = 0; |
| else if(board->conspare == MODE_SINGLE_SPARE) |
| board->oobsize = 32; |
| else |
| board->oobsize = board->szofpg/board->chunk_size * 32; |
| |
| board->conecc = 1; |
| if(board->szofpg== 512) |
| board->conbch = 0; |
| else |
| board->conbch = 1; |
| board->constp = 0; |
| |
| pages_per_block = board->szofblk/board->szofpg; |
| ndcr = MV_NAND_NDCR_VAL; |
| ndcr &= ~(((1 << MV_NAND_NDCR_PG_PER_BLK_BITS) - 1) << MV_NAND_NDCR_PG_PER_BLK_LSB); |
| if (pages_per_block == 32) |
| ndcr|= (PAGES_PER_BLOCK_32 << MV_NAND_NDCR_PG_PER_BLK_LSB); |
| else if (pages_per_block == 64) |
| ndcr|= (PAGES_PER_BLOCK_64 << MV_NAND_NDCR_PG_PER_BLK_LSB); |
| else if (pages_per_block == 128) |
| ndcr|= (PAGES_PER_BLOCK_128 << MV_NAND_NDCR_PG_PER_BLK_LSB); |
| else if (pages_per_block == 256) |
| ndcr|= (PAGES_PER_BLOCK_256 << MV_NAND_NDCR_PG_PER_BLK_LSB); |
| else { |
| return -1; |
| } |
| ndcr &= ~(((1 << MV_NAND_NDCR_PG_SIZE_BITS) - 1) << MV_NAND_NDCR_PG_SIZE_LSB); |
| if(board->szofpg != 512) |
| ndcr |= 1 << MV_NAND_NDCR_PG_SIZE_LSB; |
| |
| // ndcr &= ~(((1 << MV_NAND_NDCR_ID_BYTE_BITS) - 1) << MV_NAND_NDCR_ID_BYTE_LSB); |
| // ndcr |= 6 << MV_NAND_NDCR_ID_BYTE_LSB; |
| if (board->conecc & 1) |
| ndcr |= MV_NAND_NDCR_ECC_EN; |
| else |
| ndcr &= ~MV_NAND_NDCR_ECC_EN; |
| |
| if (board->constp & 1) |
| ndcr |= MV_NAND_NDCR_STOP_ON_UNCOR; |
| else |
| ndcr &= ~MV_NAND_NDCR_STOP_ON_UNCOR; |
| |
| board->ndeccctrl = (board->conbch & 0x7F) | (board->t << 24); |
| board->ndcr = ndcr; |
| board->ndtr0cs0 = MV_NAND_NDTR0CS0_VAL; |
| board->ndtr1cs0 = MV_NAND_NDTR1CS0_VAL; |
| board->ndredel = MV_NAND_NDREDEL_VAL; |
| #if 0 // It's a quick fix here. To use the new timing setting in bootloader |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDTR0CS0_OFFSET, MV_NAND_NDTR0CS0_VAL); |
| MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDTR1CS0_OFFSET, MV_NAND_NDTR1CS0_VAL); |
| // MV_NAND_REG_WRITE_DWORD(MV_NAND_DFC_REG_BASE, MV_NAND_NDREDEL_OFFSET, MV_NAND_NDREDEL_VAL); |
| #endif |
| mv_nand_chip_reinit(); |
| #ifdef SUPPORT_RANDOMIZATION |
| nand_randomizer_init_by_flash_type(); |
| #endif // SUPPORT_RANDOMIZATION |
| |
| #if defined(CONFIG_NAND_READ_RETRY) |
| nand_read_retry = 0; |
| if(0 == mv_nand_read_id(id_data)){ |
| if(HINIX_H27UAG8T2CTR.maker == id_data[0] && |
| HINIX_H27UAG8T2CTR.dev_id == id_data[1]){ |
| for (i = 2; i < 6; i++){ |
| if(HINIX_H27UAG8T2CTR.id[i-2] != id_data[i]) |
| break; |
| } |
| if (i = 6){ |
| printf("NAND read retry can be supported on this NAND flash.\n"); |
| nand_read_retry = 1; |
| nand_block_count = HINIX_H27UAG8T2CTR.chip_size / |
| (HINIX_H27UAG8T2CTR.block_size / (1<<20)); |
| } |
| }else |
| printf("NAND read retry cannot be supported on this NAND flash.\n"); |
| }else |
| printf("get nand id error!!\n"); |
| #endif |
| |
| #if defined(CONFIG_ENHANCED_SLC) |
| config_enhanced_SLC = 0; |
| if(0 == mv_nand_read_id(id_data)){ |
| if(HINIX_H27UAG8T2CTR.maker == id_data[0] && |
| HINIX_H27UAG8T2CTR.dev_id == id_data[1]){ |
| for (i = 2; i < 6; i++){ |
| if(HINIX_H27UAG8T2CTR.id[i-2] != id_data[i]) |
| break; |
| } |
| if (i = 6){ |
| printf("Enhanced SLC can be supported on this NAND flash.\n"); |
| config_enhanced_SLC = 1; |
| } |
| }else |
| printf("Enhanced SLC cannot be supported on this NAND flash.\n"); |
| }else |
| printf("get nand id error!!\n"); |
| #endif |
| |
| #if defined(CONFIG_SLC) |
| config_SLC = 0; |
| if(0 == mv_nand_read_id(id_data)){ |
| if(HINIX_H27UAG8T2CTR.maker == id_data[0] && |
| HINIX_H27UAG8T2CTR.dev_id == id_data[1]){ |
| for (i = 2; i < 6; i++){ |
| if(HINIX_H27UAG8T2CTR.id[i-2] != id_data[i]) |
| break; |
| } |
| if (i = 6){ |
| printf("SLC can be supported on this NAND flash.\n"); |
| config_SLC = 1; |
| } |
| }else |
| printf("SLC cannot be supported on this NAND flash.\n"); |
| }else |
| printf("get nand id error!!\n"); |
| #endif |
| |
| return 0; |
| } |
| |
| #if defined(__UBOOT__) |
| static int mv_nand_get_flash_type(int busw, int *maf_id,struct mv_nand_flash_dev **rtype) |
| { |
| unsigned int tmp[2]; |
| int i, dev_id, maf_idx; |
| struct mv_nand_flash_dev *type = NULL; |
| struct mv_nand_data *board = &nand_data; |
| struct nand_param **p_nparam = buildin_nand_list; |
| mv_nand_select_chip(0); |
| |
| mv_nand_read_id((char *)&tmp[0]); |
| printf("NAND chip id "); |
| for(i=0;i<8;i++) |
| printf("%02X", ((char *)tmp)[i]); |
| printf("\n"); |
| |
| //new detect method |
| while(*p_nparam) { |
| if( (strncmp((char*)tmp, &((*p_nparam)->maker), 5) == 0) |
| || (strncmp((char*)tmp, &((*p_nparam)->maker), 6) == 0)) { |
| board->chip = *p_nparam; |
| board->szofpg = board->chip->page_size; |
| board->szofblk = board->chip->block_size; |
| board->t = board->chip->t; |
| init_nand_data(board); |
| init_timing(board); |
| real_device_oobsize = board->oobsize; |
| chip_size = (long long)board->chip->chip_size * 1024*1024; |
| return 0; |
| } |
| p_nparam++; |
| } |
| |
| printf("NAND is not in the buildin list. Try best to get its parameter.\n"); |
| //if detect failed, use the old method. |
| *maf_id = tmp[0] & 0xff; |
| dev_id = (tmp[0] >> 8) & 0xff; |
| for (i = 0; nand_flash_ids[i].name != NULL; i++) { |
| if ((dev_id == nand_flash_ids[i].id)) { |
| type = &nand_flash_ids[i]; |
| } |
| } |
| if (!type) |
| return -1; |
| chip_size = (long long)(type->chipsize) << 20; |
| |
| /* Newer devices have all the information in additional id bytes */ |
| if (!type->pagesize) { |
| int extid; |
| |
| /* it is SAMGSUNG chip and support cache program */ |
| if((NAND_MFR_SAMSUNG == *maf_id) && ((tmp[0] >> 16)&0x80)) { |
| /* The 3rd id byte holds MLC / multichip data */ |
| /* The 4th id byte is the important one */ |
| extid = (tmp[0]>>24)&0xff; |
| /* Calc pagesize */ |
| board->szofpg = 2048 << (extid & 0x3); |
| extid >>= 4; |
| |
| /* Calc blocksize. Blocksize is multiples of 64KiB */ |
| board->szofblk = (128 * 1024) << (extid & 0x03); |
| extid >>= 2; |
| busw = 1; |
| } else if ((NAND_MFR_HYNIX == *maf_id) && ((tmp[0]>>16)&0x80)) { |
| /* The 3rd id byte holds MLC / multichip data */ |
| /* The 4th id byte is the important one */ |
| extid = (tmp[0]>>24)&0xff; |
| /* Calc pagesize */ |
| board->szofpg = 2048 << (extid & 0x3); |
| switch (extid & 0xB0) { |
| case 0: |
| board->szofblk = 128 * 1024; |
| break; |
| case 0x10: |
| board->szofblk = 2 * 128 * 1024; |
| break; |
| case 0x20: |
| board->szofblk = 4 * 128 * 1024; |
| break; |
| case 0x30: |
| board->szofblk = 6 * 128 * 1024; |
| break; |
| case 0x80: |
| board->szofblk = 8 * 128 * 1024; |
| break; |
| case 0x90: |
| board->szofblk = 2 * 1024 * 1024; |
| break; |
| } |
| real_device_oobsize = 128; |
| busw = 1; |
| } else if (NAND_MFR_MICRON == *maf_id) { |
| board->szofpg = 4096; |
| board->szofblk = 4096*256; |
| } else { |
| /* The 3rd id byte holds MLC / multichip data */ |
| /* The 4th id byte is the important one */ |
| extid = (tmp[0]>>24)&0xff; |
| /* Calc pagesize */ |
| board->szofpg = 1024 << (extid & 0x3); |
| extid >>= 2; |
| /* Calc oobsize */ |
| extid >>= 2; |
| /* Calc blocksize. Blocksize is multiples of 64KiB */ |
| board->szofblk = (64 * 1024) << (extid & 0x03); |
| extid >>= 2; |
| /* Get buswidth information */ |
| busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; |
| if((NAND_MFR_SAMSUNG == *maf_id) && (dev_id == 0xd5)) |
| chip_size = (long long)((64 << ((tmp[1] >> 4) & 0x7)) << 17) * ( 1 << ((tmp[1] >> 2) & 0x3)); |
| } |
| } else { |
| /* |
| * Old devices have chip data hardcoded in the device id table |
| */ |
| if ((NAND_MFR_SAMSUNG == *maf_id) && (dev_id == 0xd5)) { |
| int extid; |
| /* The 3rd id byte holds MLC / multichip data */ |
| /* The 4th id byte is the important one */ |
| extid = (tmp[0]>>24)&0xff; |
| /* Calc pagesize */ |
| board->szofpg = 1024 << (extid & 0x3); |
| extid >>= 2; |
| /* Calc oobsize */ |
| extid >>= 2; |
| /* Calc blocksize. Blocksize is multiples of 64KiB */ |
| board->szofblk = (64 * 1024) << (extid & 0x03); |
| extid >>= 2; |
| /* Get buswidth information */ |
| busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; |
| chip_size = (long long)((64 << ((tmp[1] >> 4) & 0x7)) << 17) * ( 1 << ((tmp[1] >> 2) & 0x3)); |
| } else { |
| board->szofblk = type->erasesize; |
| board->szofpg = type->pagesize; |
| if(board->conbch && (board->szofpg == 512)) { |
| board->conbch = 0; |
| } |
| } |
| } |
| for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) |
| if ((nand_manuf_ids[maf_idx].id == *maf_id)) |
| break; |
| |
| printf("%s NAND flash.\n", nand_manuf_ids[maf_idx].name ); |
| |
| *rtype = type; |
| |
| #if (defined(BERLIN_SOC_BG2) && !defined(BERLIN_SOC_BG2_Z1)) |
| board->t = 48; |
| #else |
| board->t = 16; |
| #endif |
| init_nand_data(board); |
| real_device_oobsize = board->oobsize; |
| |
| board->chip = buildin_nand_list[0]; |
| init_timing(board); |
| |
| return 0; |
| } |
| |
| static int mv_nand_scan_ident(int maxchips) |
| { |
| struct mv_nand_flash_dev *type; |
| int ret,i, busw = 0, nand_maf_id; |
| unsigned int tmp[2]; |
| ret = mv_nand_get_flash_type(busw, &nand_maf_id ,&type); |
| if(ret) |
| return ret; |
| for (i = 1; i < maxchips; i++) { |
| mv_nand_select_chip(i); |
| mv_nand_read_id((char *)&tmp[0]); |
| if (nand_maf_id != (tmp[0]&0xff) || |
| type->id != ((tmp[0]>>8)&0xff)) |
| break; |
| } |
| |
| printf("Total size %LuB, block size: %uB, page size: %uB\n", |
| mv_nand_chip_size(), mv_nand_block_size(), mv_nand_page_size()); |
| printf("oob size: %uB\n", mv_nand_oob_size()); |
| |
| return 0; |
| } |
| |
| static int mv_nand_scan(int maxchips) |
| { |
| int ret; |
| ret = mv_nand_scan_ident(maxchips); |
| return ret; |
| } |
| |
| |
| /* |
| * Probe for the NAND device. |
| */ |
| int mv_nand_probe(void) |
| { |
| int res = 0; |
| |
| mv_nand_chip_init(); |
| //printf("reset nand flash before proble\n") ; |
| if(mv_nand_reset_chip(0)) { |
| res = -1; |
| goto out; |
| } |
| //printf("reset nand flash before proble\n") ; |
| if (mv_nand_scan(1)) { |
| res = -1; |
| goto out; |
| } |
| if(mv_nand_chip_reinit()) { |
| res = -1; |
| goto out; |
| } |
| out: |
| return res; |
| } |
| #endif /* #if defined(__UBOOT__) */ |
| |
| /* |
| * 100% finished. |
| * Read within one block, data is copied only once. |
| * nand_start: no alignment requirement |
| * data_size: no alignement requirement, but data_size + nand_start |
| * must not exceed the block boundary |
| * return value: |
| * -1 when error |
| * actual data has been read, maybe little than data_size |
| */ |
| int mv_nand_read_block(long long nand_start, char* data_buf, int data_size) |
| { |
| long long pos; |
| int page_size, block_size; |
| char *buf; |
| long long mask; |
| int oddment; |
| int size; |
| int ret; |
| |
| page_size = mv_nand_page_size(); |
| block_size = mv_nand_block_size(); |
| mask = (~(page_size- 1)); |
| |
| //check whether exceed block end |
| if((nand_start & (block_size-1)) + data_size > block_size) |
| data_size = block_size - (nand_start & (block_size - 1)); |
| // |
| size = data_size; |
| buf = data_buf; |
| pos = nand_start & mask; |
| oddment = nand_start & ~mask; |
| if(oddment) { |
| oddment = page_size - (nand_start & ~mask); |
| oddment = oddment < data_size ? oddment : data_size; |
| ret = mv_nand_read_large_page(pos, mvnand_page_buffer, 0); |
| if(ret == 1) |
| printf("Uncorrectable error @ 0x%08x\n", (int)pos); |
| if(ret) |
| return -1; |
| memcpy(buf, &mvnand_page_buffer[nand_start & ~mask], oddment); |
| pos += page_size; |
| buf += oddment; |
| } |
| data_size -= oddment; |
| if(!data_size) |
| return size; |
| while(data_size >= page_size) { |
| ret = mv_nand_read_large_page(pos, buf, 0); |
| if(ret == 1) |
| printf("Uncorrectable error @ 0x%08x\n", (int)pos); |
| if(ret) |
| return -1; |
| pos += page_size; |
| data_size -= page_size; |
| buf += page_size; |
| } |
| |
| if(data_size) { |
| ret = mv_nand_read_large_page(pos, mvnand_page_buffer, 0); |
| if(ret == 1) |
| printf("Uncorrectable error @ 0x%08x\n", (int)pos); |
| if(ret) |
| return -1; |
| memcpy(buf, mvnand_page_buffer, data_size); |
| } |
| |
| return size; |
| } |
| |
| /* |
| * 70% finished. |
| * nand_start: must be page_aligned |
| * data_size: no alignement requirement, but data_size + nand_start |
| * must not exceed the block boundary |
| * return: |
| * 0: sucess |
| * 1: fail |
| * |
| */ |
| int mv_nand_write_block(loff_t nand_start, char* data_buf, int data_size, int verify) |
| { |
| int page_size, block_size; |
| loff_t block_mask; |
| // unsigned check_sum_w, check_sum_r; |
| int size = data_size; |
| char *buf = data_buf; |
| loff_t pos = nand_start; |
| UNUSED(verify); |
| |
| // check_sum_w = crc32(0, (unsigned char*)data_buf, data_size); |
| // printf("nand_start: 0x%09Lx\n", nand_start); |
| page_size = mv_nand_page_size(); |
| block_size = mv_nand_block_size(); |
| block_mask = (~(block_size - 1)); |
| if(pos & (page_size - 1)) |
| return -1; |
| if( (pos & block_mask) != |
| ((pos + size -1) & block_mask) ) |
| return -1; |
| while(size >= page_size) { |
| if(mv_nand_write_large_page(pos, buf, 0)) |
| return -1; |
| pos += page_size; |
| size -= page_size; |
| buf += page_size; |
| } |
| |
| if(size) { |
| memcpy(mvnand_page_buffer, buf, size); |
| memset(&mvnand_page_buffer[size], 0xff, page_size-size); |
| if(mv_nand_write_large_page(pos, mvnand_page_buffer, 0)) |
| return -1; |
| } |
| /* |
| mv_nand_read_block(nand_start, (char*)0x1000000, data_size); |
| |
| compare_buff(data_buf, 0x1000000, data_size); |
| check_sum_r = crc32(0, (unsigned char*)0x1000000, data_size); |
| if(check_sum_w != check_sum_r) { |
| printf("check_sum_w=%08x, check_sum_r=%08x\n", check_sum_w, check_sum_r); |
| return -1; |
| } |
| */ |
| return 0; |
| } |
| |
| /* |
| * 80% finished. start needs to be aligned, dual plane is not supported. |
| * bad block will be skipped automatically |
| * nand_start: must be block aligned |
| * data_size: no requirement |
| * return: |
| * -1: fail |
| * other: current read position. |
| */ |
| loff_t mv_nand_read_skip_bad(loff_t nand_start, char* data_buf, int data_size) |
| { |
| int block_size; |
| loff_t pos, size, block_mask; |
| char *buf; |
| int rdsz=0; |
| UNUSED(block_mask); |
| |
| if(!data_size) |
| return nand_start; |
| |
| block_size = mv_nand_block_size(); |
| block_mask = ~((loff_t)block_size - 1); |
| |
| //force block aligned |
| if(nand_start & (block_size-1)) |
| return -1; |
| |
| pos = nand_start; |
| buf = data_buf; |
| size = data_size; |
| |
| while(size > 0) { |
| if(mv_nand_block_bad(pos, 0)) { |
| pos += block_size; |
| continue; |
| } |
| rdsz = mv_nand_read_block(pos, buf, size); |
| if(rdsz < 0) |
| return -1; |
| size -= rdsz; |
| buf += rdsz; |
| pos += block_size; |
| } |
| |
| return pos-block_size+rdsz; |
| } |
| |
| /* |
| * 100% finished. |
| * suppose image length is [data_size], it is stored in the partition starts from |
| * [start] ends at [end]. This function is to find out the next block bihind this image. |
| */ |
| loff_t get_next_addr(loff_t start, loff_t end, unsigned int data_size) |
| { |
| loff_t i; |
| unsigned int j; |
| unsigned int block_size; |
| |
| block_size = nand_data.szofblk; |
| j = (data_size + block_size - 1) & ~(block_size - 1); |
| for(i=start;i<end;i+=block_size) { |
| if(j==0) |
| break; |
| if(mv_nand_block_bad(i, 0)) { |
| // printf("Bad block found @0x%09Lx.\n", i); |
| continue; |
| } |
| j-=block_size; |
| } |
| if(i >= end) |
| return -1; |
| |
| return i; |
| } |