loader\u-boot-2011.12\drivers\mtd\nand\nand_base.c
可以从mtd的结构体初始化关联出来,每一个mtd设备关联chip指针,最终调用flash硬件接口
mtd->read = nand_erase; -> nand_erase_nand(mtd, instr, 0); -> chip->erase_cmd(mtd, page & chip->pagemask);
#ifdef CONFIG_SPI_NAND_FLASH_INIT_FIRST
#if __DEVICE_USING_QIO
__SECTION_INIT_PHASE_DATA
spi_nand_cmd_info_t toshiba_x4_cmd_info = {
.w_cmd = PROGRAM_LOAD_OP,
.w_addr_io = SIO_WIDTH,
.w_data_io = SIO_WIDTH,
.r_cmd = FAST_READ_X4_OP,
.r_addr_io = SIO_WIDTH,
.r_data_io = QIO_WIDTH,
.r_dummy_cycles = 8,
};
#endif
#if defined(NSU_DRIVER_IN_ROM) || (defined(CONFIG_SPI_NAND_FLASH_INIT_FIRST) && defined(CONFIG_UNDER_UBOOT))
__SECTION_INIT_PHASE_DATA
spi_nand_model_info_t toshiba_general_model = {
._pio_write = snaf_pio_write_data,
._pio_read = snaf_pio_read_data,
._page_read = snaf_page_read,
._page_write = snaf_page_write,
._page_read_ecc = snaf_page_read_with_ondie_ecc,
._page_write_ecc = snaf_page_write_with_ondie_ecc,
._block_erase = nsc_block_erase,
._wait_spi_nand_ready = nsc_wait_spi_nand_oip_ready,
};
#endif
__SECTION_INIT_PHASE_DATA
spi_nand_flash_info_t toshiba_chip_info[] = {
{
.man_id = MID_TOSHIBA,
.dev_id = DID_TC58CVG0S3HRAIG,
._num_block = SNAF_MODEL_NUM_BLK_1024,
._num_page_per_block = SNAF_MODEL_NUM_PAGE_64,
._page_size = SNAF_MODEL_PAGE_SIZE_2048B,
._spare_size = SNAF_MODEL_SPARE_SIZE_64B,
._oob_size = SNAF_MODEL_OOB_SIZE(24),
._ecc_ability = ECC_USE_ODE,
#if __DEVICE_REASSIGN
._ecc_encode = VZERO,
._ecc_decode = VZERO,
._reset = VZERO,
._cmd_info = VZERO,
._model_info = VZERO,
#elif __DEVICE_USING_SIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &nsc_sio_cmd_info,
._model_info = &toshiba_general_model,
#elif __DEVICE_USING_DIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &nsc_x2_cmd_info,
._model_info = &toshiba_general_model,
#elif __DEVICE_USING_QIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &toshiba_x4_cmd_info,
._model_info = &toshiba_general_model,
#endif
},
{
.man_id = MID_TOSHIBA,
.dev_id = DID_TC58CVG1S3HRAIG,
._num_block = SNAF_MODEL_NUM_BLK_2048,
._num_page_per_block = SNAF_MODEL_NUM_PAGE_64,
._page_size = SNAF_MODEL_PAGE_SIZE_2048B,
._spare_size = SNAF_MODEL_SPARE_SIZE_64B,
._oob_size = SNAF_MODEL_OOB_SIZE(24),
._ecc_ability = ECC_USE_ODE,
#if __DEVICE_REASSIGN
._ecc_encode = VZERO,
._ecc_decode = VZERO,
._reset = VZERO,
._cmd_info = VZERO,
._model_info = VZERO,
#elif __DEVICE_USING_SIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &nsc_sio_cmd_info,
._model_info = &toshiba_general_model,
#elif __DEVICE_USING_DIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &nsc_x2_cmd_info,
._model_info = &toshiba_general_model,
#elif __DEVICE_USING_QIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &toshiba_x4_cmd_info,
._model_info = &toshiba_general_model,
#endif
},
{
.man_id = MID_KIOXIA,
.dev_id = DID_TC58CVG0S3HRAIJ,
._num_block = SNAF_MODEL_NUM_BLK_1024,
._num_page_per_block = SNAF_MODEL_NUM_PAGE_64,
._page_size = SNAF_MODEL_PAGE_SIZE_2048B,
._spare_size = SNAF_MODEL_SPARE_SIZE_64B,
._oob_size = SNAF_MODEL_OOB_SIZE(24),
._ecc_ability = ECC_USE_ODE,
#if __DEVICE_REASSIGN
._ecc_encode = VZERO,
._ecc_decode = VZERO,
._reset = VZERO,
._cmd_info = VZERO,
._model_info = VZERO,
#elif __DEVICE_USING_SIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &nsc_sio_cmd_info,
._model_info = &toshiba_general_model,
#elif __DEVICE_USING_DIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &nsc_x2_cmd_info,
._model_info = &toshiba_general_model,
#elif __DEVICE_USING_QIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &toshiba_x4_cmd_info,
._model_info = &toshiba_general_model,
#endif
},
{
.man_id = MID_KIOXIA,
.dev_id = DID_TC58CVG1S3HRAIJ,
._num_block = SNAF_MODEL_NUM_BLK_2048,
._num_page_per_block = SNAF_MODEL_NUM_PAGE_64,
._page_size = SNAF_MODEL_PAGE_SIZE_2048B,
._spare_size = SNAF_MODEL_SPARE_SIZE_64B,
._oob_size = SNAF_MODEL_OOB_SIZE(24),
._ecc_ability = ECC_USE_ODE,
#if __DEVICE_REASSIGN
._ecc_encode = VZERO,
._ecc_decode = VZERO,
._reset = VZERO,
._cmd_info = VZERO,
._model_info = VZERO,
#elif __DEVICE_USING_SIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &nsc_sio_cmd_info,
._model_info = &toshiba_general_model,
#elif __DEVICE_USING_DIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &nsc_x2_cmd_info,
._model_info = &toshiba_general_model,
#elif __DEVICE_USING_QIO
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &toshiba_x4_cmd_info,
._model_info = &toshiba_general_model,
#endif
},
{//This is for Default
.man_id = MID_TOSHIBA,
.dev_id = DEFAULT_DATA_BASE,
._num_block = SNAF_MODEL_NUM_BLK_1024,
._num_page_per_block = SNAF_MODEL_NUM_PAGE_64,
._page_size = SNAF_MODEL_PAGE_SIZE_2048B,
._spare_size = SNAF_MODEL_SPARE_SIZE_64B,
._oob_size = SNAF_MODEL_OOB_SIZE(24),
._ecc_ability = ECC_MODEL_6T,
#if __DEVICE_REASSIGN
._ecc_encode = VZERO,
._ecc_decode = VZERO,
._reset = VZERO,
._cmd_info = VZERO,
._model_info = VZERO,
#else
._ecc_encode = toshiba_ecc_encode,
._ecc_decode = toshiba_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._cmd_info = &nsc_sio_cmd_info,
._model_info = &toshiba_general_model,
#endif
},
};
#endif // CONFIG_SPI_NAND_FLASH_INIT_FIRST
__SECTION_INIT_PHASE_DATA
spi_nand_flash_info_t winbond_chip_info[] = {
{
.man_id = MID_WINBOND,
.dev_id = DID_W25N01GV,
._num_block = SNAF_MODEL_NUM_BLK_1024,
._num_page_per_block = SNAF_MODEL_NUM_PAGE_64,
._page_size = SNAF_MODEL_PAGE_SIZE_2048B,
._spare_size = SNAF_MODEL_SPARE_SIZE_64B,
._oob_size = SNAF_MODEL_OOB_SIZE(24),
._ecc_ability = ECC_MODEL_6T,
#if __DEVICE_REASSIGN
._ecc_encode = VZERO,
._ecc_decode = VZERO,
._reset = VZERO,
._model_info = VZERO,
._cmd_info = VZERO,
#else
._ecc_encode = ecc_encode_bch,
._ecc_decode = ecc_decode_bch,
._reset = nsu_reset_spi_nand_chip,
._model_info = &snaf_rom_general_model,
#if __DEVICE_USING_QIO
._cmd_info = &winbond_qio_cmd_info,
#elif __DEVICE_USING_DIO
._cmd_info = &nsc_dio_cmd_info,
#else
._cmd_info = &nsc_sio_cmd_info,
#endif
#endif
},{
.man_id = MID_WINBOND,
.dev_id = DID_W25N02KV,
._num_block = SNAF_MODEL_NUM_BLK_2048,
._num_page_per_block = SNAF_MODEL_NUM_PAGE_64,
._page_size = SNAF_MODEL_PAGE_SIZE_2048B,
._spare_size = SNAF_MODEL_SPARE_SIZE_64B, //64B for internal ECC
._oob_size = SNAF_MODEL_OOB_SIZE(62),
._ecc_ability = ECC_USE_ODE,
#if __DEVICE_REASSIGN
._ecc_encode = VZERO,
._ecc_decode = VZERO,
._reset = VZERO,
._model_info = VZERO,
._cmd_info = VZERO,
#else
._ecc_encode = winbond_ecc_encode,
._ecc_decode = winbond_ecc_decode,
._reset = nsu_reset_spi_nand_chip,
._model_info = &winbond_ode_model,
#if __DEVICE_USING_QIO
._cmd_info = &winbond_qio_cmd_info,
#elif __DEVICE_USING_DIO
._cmd_info = &nsc_dio_cmd_info,
#else
._cmd_info = &nsc_sio_cmd_info,
#endif
#endif
},{//This is for Default
.man_id = MID_WINBOND,
.dev_id = DEFAULT_DATA_BASE,
._num_block = SNAF_MODEL_NUM_BLK_1024,
._num_page_per_block = SNAF_MODEL_NUM_PAGE_64,
._page_size = SNAF_MODEL_PAGE_SIZE_2048B,
._spare_size = SNAF_MODEL_SPARE_SIZE_64B,
._oob_size = SNAF_MODEL_OOB_SIZE(24),
._ecc_ability = ECC_MODEL_6T,
#if __DEVICE_REASSIGN
._ecc_encode = VZERO,
._ecc_decode = VZERO,
._reset = VZERO,
._model_info = VZERO,
._cmd_info = VZERO,
#else
._ecc_encode = ecc_encode_bch,
._ecc_decode = ecc_decode_bch,
._reset = nsu_reset_spi_nand_chip,
._model_info = &snaf_rom_general_model,
._cmd_info = &nsc_sio_cmd_info,
#endif
}
};
#endif // CONFIG_SPI_NAND_FLASH_INIT_FIRST
./arch/otto40/plr/src/soc/spi_nand_gen2/spi_nand_toshiba.c:128: ._block_erase = nsc_block_erase,
./arch/otto40/plr/src/soc/spi_nand_gen2/spi_nand_winbond.c:112: ._block_erase = nsc_block_erase,
./drivers/mtd/spi/luna_spi_nand.c:278: chip->_spi_nand_info->_model_info->_block_erase(chip->_spi_nand_info, page_addr);
------------------------------------------------
init_fnc_t *init_sequence[] = {
board_early_init_f,
timer_init,
env_init, /* initialize environment */
//init_baudrate, /* initialize baudrate settings */
//serial_init, /* serial communications setup */
//console_init_f,
display_banner, /* say that we are here */
checkboard,
init_func_ram,
NULL,
};
//loader\u-boot-2011.12\arch\otto40\lib\board.c
void board_init_f(ulong bootflag)
{
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0)
hang();
}
./config.in:102:hex 'U-Boot Text Base' CONFIG_SYS_TEXT_BASE 0x83F00000
board_init_r(id, CONFIG_SYS_TEXT_BASE);
}
loader\u-boot-2011.12\arch\otto40\lib\board.c
PATCH_REG(swp_flash_init, 1);
PATCH_REG(swp_env, 3);
PATCH_REG(swp_uart, 5);
#ifdef CONFIG_OTTO_FLASH_LAYOUT
PATCH_REG(swp_gen_fl, 5);
#endif /* #ifdef CONFIG_OTTO_FLASH_LAYOUT */
PATCH_REG(swp_pci, 7);
PATCH_REG(swp_studio, 9);
PATCH_REG(swp_console_r, 11);
PATCH_REG(swp_env_set, 13);
PATCH_REG(swp_spi, 15);
PATCH_REG(swp_misc, 17);
PATCH_REG(swp_post, 19);
PATCH_REG(swp_eth, 21);
loader\u-boot-2011.12\arch\otto40\lib\board.c
void board_init_r(gd_t *id, ulong dest_addr)
{
sw_patch_t **sw_patch = &LS_sw_patch_start;
while (sw_patch!=&LS_bootm_stack) {
(*sw_patch)();
++sw_patch;
}
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;)
main_loop();
}
loader\u-boot-2011.12\common\main.c
void main_loop (void)
/*flash init , level 1*/
void swp_flash_init(void){
#ifndef CONFIG_SYS_NO_FLASH
ulong size;
#endif
bd_t *bd;
bd = gd->bd;
#ifndef CONFIG_SYS_NO_FLASH
size = flash_init();
display_flash_config(size);
bd->bi_flashsize = size;
#endif
#ifdef CONFIG_CMD_SF
puts("SPI-F: ");
spi_flash_init();
bd->bi_flashstart = CONFIG_SYS_FLASH_BASE;
#endif
#if CONFIG_SYS_MONITOR_BASE == CONFIG_SYS_FLASH_BASE
bd->bi_flashoffset = monitor_flash_len; /* reserved area for U-Boot */
#else
bd->bi_flashoffset = 0;
#endif
#if defined (CONFIG_CMD_SPI_NAND)
spi_nand_init();
#endif
}
loader\u-boot-2011.12\arch\otto40\lib\otto_spi_nand_flash.c
void spi_nand_init(void) {
#if 0
#if USE_BBT_SKIP
u32_t _bbt_table[NUM_WORD(MAX_BLOCKS)];
#endif
#endif
spi_nand_flash_info_t *fi;
uint32_t count=1;
/* fill spi nand info */
fi = spi_nand_probe(&count);#检索nand flash芯片信息
if(NULL==fi) return;
#关于OOB区,是每个Page都有的。Page大小是512字节的NAND每页分配16字节的OOB;如果NAND物理上
#是2K的Page,则每个Page分配64字节的OOB。不同型号的flash一个page的大小也不同, pagesize或
#者为512+16字节, 或者为2048+64字节;
#以pagesize=2048+64的为例:2048字节是数据区,也被称为main area; 后面的64字节是OOB(out
#of band), 也可以被成为spare area;
#if 0
Device:
SPI-NAND, ID:c212, size:128 MB*1
each chip has 1024 block(s)
each block has 64 page(s)
page size 2048 byte(s)
erase size 131072 byte(s)
spare size 64 byte(s)
oob size 24 byte(s)
others 40 byte(s)
#nand-flash 擦除以一个块的单位大小擦除,写以一个页的单位大小读写
#endif
_info.id = (fi->man_id << DEV_ID_LEN_BITS(fi->dev_id)) | fi->dev_id; #厂家身份ID
_info.cs_count=count; #总共=有几块flash芯片
_info.chip_size=SNAF_NUM_OF_BLOCK(fi) #芯片总大小
*SNAF_NUM_OF_PAGE_PER_BLK(fi)
*SNAF_PAGE_SIZE(fi);
_info.block_size=SNAF_NUM_OF_PAGE_PER_BLK(fi) #每个块大小
*SNAF_PAGE_SIZE(fi);
_info.page_size=SNAF_PAGE_SIZE(fi); #每页大小,一页大小等于 = 页大小 + obb size;
_info.oob_size=SNAF_OOB_SIZE(fi);
_info.spare_size=SNAF_SPARE_SIZE(fi);
_info.block_count=SNAF_NUM_OF_BLOCK(fi); #一块flash总共有多少块
_info.page_count=SNAF_NUM_OF_PAGE_PER_BLK(fi); #每个块包含多少页
_info.flash_info = fi;
printf("SPI-NAND Flash: %X/Mode%d %dx%uMB\n", _info.id, fi->_cmd_info->r_data_io, count, spi_nand_chip_size()>>20);
#if 0
memset(_bbt_table, '\0', sizeof(_bbt_table));
if(_info.block_count > MAX_BLOCKS){
printf("SPI-NAND Flash: Create Bad Block Table Error!\n Block Number is More than %d!\n", MAX_BLOCKS);
}else{
create_bbt(_bbt_table);
#if USE_BBT_SKIP
create_skip_table(_bbt_table);
#endif
}
#endif
#if defined(CONFIG_MTD_DEVICE) && defined(CONFIG_MTD_PARTITIONS)
#ifdef CONFIG_SPI_NAND_FLASH
./drivers/mtd/spi/luna_spi_nand.c:610:int rtk_spi_nand_init (void)
rtk_spi_nand_init ();
#endif
#endif
}
struct mtd_info *rtk_mtd[2];
#define MTDSIZE (sizeof (struct mtd_info) + sizeof (struct luna_nand_t))
int rtk_spi_nand_init (void)
{
struct nand_chip *this = NULL;
//spi_nand_flash_info_t *fi;
int rc = 0;
//uint32_t count=1;
#申请一个mtd设备大小空间:mtd_info size + nand-info size;
rtk_mtd[0] = kmalloc (MTDSIZE, GFP_KERNEL); //mtd_info struct + nand_chip struct
if (!rtk_mtd[0]) {
printk ("%s: Error, no enough memory for rtk_mtd\n", __FUNCTION__);
rc = -1;//ENOMEM;
}
memset ((char *)rtk_mtd[0], 0, MTDSIZE);
#if 1
#nand-flash info信息来自硬件探测得到的信息
rtk_mtd[0]->priv = this = (struct nand_chip *)(rtk_mtd[0] + 1);
#endif
chip_count = spi_nand_cs_count(); # 1块nand-flash芯片
/* fill mtd info */
rtk_mtd[0]->name = "nand0";
rtk_mtd[0]->size = spi_nand_chip_size()*chip_count; #128*1M
rtk_mtd[0]->erasesize = spi_nand_block_size(); #64*2048
rtk_mtd[0]->writesize = spi_nand_page_size(); #2048
rtk_mtd[0]->oobsize = spi_nand_oob_size(); #24 =64(spare-size)=24(oob-sieze)+40(others)
/* init spi nand flash */
loader\u-boot-2011.12\drivers\mtd\spi\luna_spi_nand.c
if (rtk_spi_nand_profile (rtk_mtd[0]) < 0) {
rc = -1;
}
return rc;
}
loader\u-boot-2011.12\drivers\mtd\spi\luna_spi_nand.c
static int rtk_spi_nand_profile (struct mtd_info *mtd)
{
//char *ptype;
//int pnum = 0;
//struct mtd_partition *mtd_parts;
register struct luna_nand_t *chip = (struct luna_nand_t *)mtd->priv;
#loader\u-boot-2011.12\arch\otto40\cpu\mips1004kc\project\9300_nand_demo\platform\spi_nand\spi_nand_struct.h:struct spi_nand_flash_info_s
chip->_spi_nand_info = spi_nand_flash_info();#填充来自硬件nand-flash探测得到的信息
#if 1
#if 0 注释
#loader\u-boot-2011.12\arch\otto40\cpu\mips1004kc\project\9300_nand_demo\platform\spi_nand\spi_nand_struct.h
static int
rtk_mtd_create_buffer(struct mtd_info *mtd)
{
struct luna_nand_t *chip = (struct luna_nand_t *)mtd->priv;
//const int bbt_table_len = (sizeof(*_bbt_table))*(NUM_WORD(SNAF_NUM_OF_BLOCK(chip->_spi_nand_info)));
if (RTK_NAND_PAGE_BUF_SIZE < chip->_writesize + chip->_spare_size) {
RTK_NAND_PAGE_BUF_SIZE = (chip->_writesize + chip->_spare_size);
}
static unsigned char *_ecc_buf = NULL;
static unsigned char *_page_buf = NULL;
(sizeof(*_ecc_buf) == 1
(sizeof(*_page_buf) == 1
#./arch/otto40/include/asm/arch-rtl9300/soc.h:61: #define MAX_ECC_BUF_SIZE 32
_ecc_buf = kmalloc((sizeof(*_ecc_buf))*MAX_ECC_BUF_SIZE + 32, GFP_KERNEL);
_page_buf = kmalloc((sizeof(*_page_buf))*RTK_NAND_PAGE_BUF_SIZE + 32, GFP_KERNEL);
// align
chip->_ecc_buf = (__typeof__(*_ecc_buf)*)(((uintptr_t)_ecc_buf+31) & ~ (uintptr_t)0x1F);
chip->_page_buf = (__typeof__(*_page_buf)*)(((uintptr_t)_page_buf+31) & ~ (uintptr_t)0x1F);
if (!_ecc_buf || !_page_buf) {
printk ("%s: Error, no enough memory for buffer\n", __FUNCTION__);
return -12;//ENOMEM;
} else {
return 0;
}
}
#endif
if (rtk_mtd_create_buffer(mtd)) {
return -1;
}
#endif
#if 0 注释
static void
rtk_luna_nand_set(struct mtd_info *mtd)
{
struct luna_nand_t *chip = (struct luna_nand_t *)mtd->priv;
chip->_writesize = mtd->writesize;//2048bytes
chip->_oob_poi = (chip->_page_buf) + (chip->_writesize); //4096bytes
chip->_spare_size = spi_nand_spare_size();//64bytes
}
rtk_luna_nand_set(mtd);
#endif
#if 0
static void
rtk_nand_set(struct mtd_info *mtd)
{
register struct nand_chip *chip = (struct nand_chip *)mtd->priv;
if (!chip->chip_delay)
chip->chip_delay = 20;
chip->cmdfunc = rtk_nand_cmdfunc;
chip->waitfunc = rtk_nand_waitfunc;
chip->read_byte = rtk_nand_read_byte;
chip->read_word = rtk_nand_read_word;
chip->select_chip = rtk_nand_select_chip;
chip->write_buf = rtk_nand_write_buf;
chip->read_buf = rtk_nand_read_buf;
}
#endif
rtk_nand_set(mtd);
#if 1
#if 0
static int
rtk_nand_ecc_set(struct mtd_info *mtd)
{
struct luna_nand_t *chip = (struct luna_nand_t *)mtd->priv;
register struct nand_ecc_ctrl *ecc = &(((struct nand_chip *)mtd->priv)->ecc);
ecc->mode = NAND_ECC_HW;
ecc->size = 512;
ecc->bytes = 10;
if (2048 == chip->_writesize) {
ecc->layout = &nand_bch_oob_64;
} else if (4096 == chip->_writesize ) {
ecc->layout = &nand_bch_oob_128;
} else {
printk("%s: Error! unsupported page size %d\n", __FUNCTION__, chip->_writesize);
return -1;
}
ecc->mode = NAND_ECC_HW;
ecc->calculate = (void*)rtk_nand_bug;
ecc->correct = (void*)rtk_nand_bug;
#loader\u-boot-2011.12\drivers\mtd\nand\nand_base.c
ecc->read_page = rtk_nand_read_page;
ecc->write_page = rtk_nand_write_page;
ecc->read_page_raw = rtk_nand_read_page_raw;
ecc->write_page_raw = rtk_nand_write_page_raw;
ecc->read_subpage = (void*)rtk_nand_bug;
ecc->strength = 6;
return 0;
}
#endif
if (rtk_nand_ecc_set(mtd))
return -1;
#if 0
rtk_nand_ids(struct mtd_info *mtd)
{
struct luna_nand_t *chip = (struct luna_nand_t *)mtd->priv;
nand_flash_ids[0].name = "ids0";
nand_flash_ids[0].id = chip->_spi_nand_info->dev_id >> ((chip->_spi_nand_info->dev_id&0xFF00)?8:0);
nand_flash_ids[0].chipsize = (mtd->size) >> 20;
nand_flash_ids[0].pagesize = mtd->writesize;
nand_flash_ids[0].erasesize = mtd->erasesize;
nand_flash_ids[0].options = LUNA_SPI_OPTION;
}
#endif
rtk_nand_ids(mtd);
#endif
#if 1
#loader\u-boot-2011.12\drivers\mtd\nand\nand_base.c:nand_scan
#if 0
int nand_scan(struct mtd_info *mtd, int maxchips)
{
int ret;
#if 0
int i, busw, nand_maf_id, nand_dev_id;
struct nand_chip *chip = mtd->priv;
const struct nand_flash_dev *type;
/* Get buswidth to select the correct functions */
busw = chip->options & NAND_BUSWIDTH_16;
/* Set the default functions */
nand_set_defaults(chip, busw);
#if 1
loader\u-boot-2011.12\drivers\mtd\nand\nand_base.c
static void single_erase_cmd(struct mtd_info *mtd, int page)
{
struct nand_chip *chip = mtd->priv;
/* Send commands to erase a block */
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
}
loader\u-boot-2011.12\drivers\mtd\nand\nand_base.c
static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
int busw,
int *maf_id, int *dev_id,
const struct nand_flash_dev *type)
{
int ret, maf_idx;
int tmp_id, tmp_manf;
/* Select the device */
chip->select_chip(mtd, 0);
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up
*/
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd);
/* Try again to make sure, as some systems the bus-hold or other
* interface concerns can cause random data which looks like a
* possibly credible NAND flash to appear. If the two results do
* not match, ignore the device completely.
*/
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
tmp_manf = chip->read_byte(mtd);
tmp_id = chip->read_byte(mtd);
if (tmp_manf != *maf_id || tmp_id != *dev_id) {
printk(KERN_INFO "%s: second ID read did not match "
"%02x,%02x against %02x,%02x\n", __func__,
*maf_id, *dev_id, tmp_manf, tmp_id);
return ERR_PTR(-ENODEV);
}
if (!type)
type = nand_flash_ids;
for (; type->name != NULL; type++)
if (*dev_id == type->id)
break;
if (!type->name) {
/* supress warning if there is no nand */
if (*maf_id != 0x00 && *maf_id != 0xff &&
*dev_id != 0x00 && *dev_id != 0xff)
printk(KERN_INFO "%s: unknown NAND device: "
"Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
__func__, *maf_id, *dev_id);
return ERR_PTR(-ENODEV);
}
if (!mtd->name)
mtd->name = type->name;
chip->chipsize = (uint64_t)type->chipsize << 20;
chip->onfi_version = 0;
ret = nand_flash_detect_onfi(mtd, chip, &busw);
if (!ret)
nand_flash_detect_non_onfi(mtd, chip, type, &busw);
/* Get chip options, preserve non chip based options */
chip->options &= ~NAND_CHIPOPTIONS_MSK;
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
/*
* Set chip as a default. Board drivers can override it, if necessary
*/
chip->options |= NAND_NO_AUTOINCR;
#if 0
loader\u-boot-2011.12\drivers\mtd\nand\nand_ids.c
const 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"},
{NAND_MFR_AMD, "AMD"},
{0x0, "Unknown"}
};
#endif
/* Try to identify manufacturer */
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
if (nand_manuf_ids[maf_idx].id == *maf_id)
break;
}
/*
* Check, if buswidth is correct. Hardware drivers should set
* chip correct !
*/
if (busw != (chip->options & NAND_BUSWIDTH_16)) {
printk(KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
*dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
busw ? 16 : 8);
return ERR_PTR(-EINVAL);
}
/* Calculate the address shift from the page size */
chip->page_shift = ffs(mtd->writesize) - 1;
/* Convert chipsize to number of pages per chip -1. */
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
chip->bbt_erase_shift = chip->phys_erase_shift =
ffs(mtd->erasesize) - 1;
if (chip->chipsize & 0xffffffff)
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
else
chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 31;
/* Set the bad block position */
chip->badblockpos = mtd->writesize > 512 ?
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
/* Check if chip is a not a samsung device. Do not clear the
* options for chips which are not having an extended id.
*/
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
/* Check for AND chips with 4 page planes */
if (chip->options & NAND_4PAGE_ARRAY)
chip->erase_cmd = multi_erase_cmd;
else
chip->erase_cmd = single_erase_cmd;
/* Do not replace user supplied command function ! */
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp;
MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
nand_manuf_ids[maf_idx].name, type->name);
return type;
}
/* Read the flash type */
type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id, table);
#endif
if (IS_ERR(type)) {
#ifndef CONFIG_SYS_NAND_QUIET_TEST
printk(KERN_WARNING "No NAND device found!!!\n");
#endif
chip->select_chip(mtd, -1);
return PTR_ERR(type);
}
/* Check for a chip array */
for (i = 1; i < maxchips; i++) {
chip->select_chip(mtd, i);
/* See comment in nand_get_flash_type for reset */
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
if (nand_maf_id != chip->read_byte(mtd) ||
nand_dev_id != chip->read_byte(mtd))
break;
}
/* Store the number of chips and calc total size for mtd */
chip->numchips = i;
mtd->size = i * chip->chipsize;
return 0;
}
#endif
ret = nand_scan_ident(mtd, maxchips, NULL);
if (!ret)
ret = nand_scan_tail(mtd);
return ret;
}
#endif
if(nand_scan(mtd, 1)){
printk ("Warning: rtk nand scan error!\n");
return -1;
}
#endif
rtk_luna_nand_chk(mtd);
#if 1
/* check page size(write size) is 512/2048/4096.. must 512byte align */
if (!(mtd->writesize & (0x200 - 1)))
;//rtk_writel( rtk_mtd->oobblock >> 9, REG_PAGE_LEN);
else {
printk ("Error: pagesize is not 512Byte Multiple");
return -1;
}
#endif
#ifdef CONFIG_MTD_CMDLINE_PARTS
/* partitions from cmdline */
ptype = (char *)ptypes[0];
pnum = parse_mtd_partitions (rtk_mtd, ptypes, &mtd_parts, 0);
if (pnum <= 0) {
printk (KERN_NOTICE "RTK: using the whole nand as a partitoin\n");
if (add_mtd_device (rtk_mtd)) {
printk (KERN_WARNING "RTK: Failed to register new nand device\n");
return -EAGAIN;
}
} else {
printk (KERN_NOTICE "RTK: using dynamic nand partition\n");
if (add_mtd_partitions (rtk_mtd, mtd_parts, pnum)) {
printk("%s: Error, cannot add %s device\n",
__FUNCTION__, rtk_mtd->name);
rtk_mtd->size = 0;
return -EAGAIN;
}
}
#else
/* fixed partition ,modify rtl8686_parts table*/
printk("RTK: Register new nand device\n");
#if 0
struct mtd_info *mtd_table[MAX_MTD_DEVICES];
int add_mtd_device(struct mtd_info *mtd)
{
int i;
BUG_ON(mtd->writesize == 0);
for (i = 0; i < MAX_MTD_DEVICES; i++)
if (!mtd_table[i]) {
mtd_table[i] = mtd;
mtd->index = i;
mtd->usecount = 0;
return 0;
}
return 1;
}
#./drivers/mtd/mtdcore.c:16:int add_mtd_device(struct mtd_info *mtd)
if (add_mtd_device (mtd)) {
printk(KERN_WARNING "RTK: Failed to register new nand device\n");
//return -EAGAIN;
return -11;
}
#endif
#endif
return 0;
}
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
if (!chip->block_bad)
chip->block_bad = nand_block_bad;
if (!chip->block_markbad)
chip->block_markbad = nand_default_block_markbad;
if (!chip->verify_buf)
chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
if (!chip->scan_bbt)
chip->scan_bbt = nand_default_bbt;
if (!chip->controller)
chip->controller = &chip->hwcontrol;
}
int nand_scan_tail(struct mtd_info *mtd)
{
int i;
struct nand_chip *chip = mtd->priv;
if (!(chip->options & NAND_OWN_BUFFERS))
chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
if (!chip->buffers)
return -ENOMEM;
/* Set the internal oob buffer location, just after the page data */
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
if (!chip->write_page)
chip->write_page = nand_write_page;
/*
* check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
*/
switch (chip->ecc.mode) {
case NAND_ECC_HW:
if (!chip->ecc.read_oob)
chip->ecc.read_oob = nand_read_oob_std;
if (!chip->ecc.write_oob)
chip->ecc.write_oob = nand_write_oob_std;
default:
printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
chip->ecc.mode);
BUG();
}
/*
* The number of bytes available for a client to place data into
* the out of band area
*/
chip->ecc.layout->oobavail = 0;
for (i = 0; chip->ecc.layout->oobfree[i].length
&& i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
chip->ecc.layout->oobavail +=
chip->ecc.layout->oobfree[i].length;
mtd->oobavail = chip->ecc.layout->oobavail;
/*
* Set the number of read / write steps for one page depending on ECC
* mode
*/
chip->ecc.steps = mtd->writesize / chip->ecc.size;
if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {
printk(KERN_WARNING "Invalid ecc parameters\n");
BUG();
}
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
/*
* Allow subpage writes up to ecc.steps. Not possible for MLC
* FLASH.
*/
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
!(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
switch(chip->ecc.steps) {
case 2:
mtd->subpage_sft = 1;
break;
case 4:
case 8:
case 16:
mtd->subpage_sft = 2;
break;
}
}
chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
/* Initialize state */
chip->state = FL_READY;
/* De-select the device */
chip->select_chip(mtd, -1);
/* Invalidate the pagebuffer reference */
chip->pagebuf = -1;
loader\u-boot-2011.12\drivers\mtd\nand\nand_base.c
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
mtd->sync = nand_sync;
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;
/* propagate ecc.layout to mtd_info */
mtd->ecclayout = chip->ecc.layout; #&nand_bch_oob_64;
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
chip->options |= NAND_BBT_SCANNED;
return 0;
}
#ifdef CONFIG_SPI_NAND_FLASH_INIT_F
IRST
extern spi_nand_flash_info_t ub_spi_nand_flash_info;
#define _plr_spi_nand_info (&ub_spi_nand_flash_info)
#else // CONFIG_SPI_NAND_FLASH_INIT_FIRST
#define _plr_soc_t (*(soc_t *)(OTTO_SRAM_START+OTTO_HEADER_OFFSET))
#define _plr_spi_nand_info _plr_soc_t.flash_info.spi_nand_info
#endif // CONFIG_SPI_NAND_FLASH_INIT_FIRST
================================================================
nand-flash分区
kernel\uClinux\linux-4.4.x\drivers\mtd\mtdpart.c
struct nand_oobinfo {
uint32_t useecc;
uint32_t eccbytes;
uint32_t oobfree[8][2];
uint32_t eccpos[48];
};
struct nand_oobfree {
uint32_t offset;
uint32_t length;
};
#define MTD_MAX_OOBFREE_ENTRIES 8
/*
* ECC layout control structure. Exported to userspace for
* diagnosis and to allow creation of raw images
*/
struct nand_ecclayout {
uint32_t eccbytes;
uint32_t eccpos[128];
uint32_t oobavail;
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
};
/**
* struct mtd_ecc_stats - error correction stats
*
* @corrected: number of corrected bits
* @failed: number of uncorrectable errors
* @badblocks: number of bad blocks in this partition
* @bbtblocks: number of blocks reserved for bad block tables
*/
struct mtd_ecc_stats {
uint32_t corrected;
uint32_t failed;
uint32_t badblocks;
uint32_t bbtblocks;
};
#if 0
#ifdef CONFIG_MTD_CMDLINE_PARTS
/* for fixed partition */
const char *ptypes[] = {"cmdlinepart", NULL};
//const char *ptypes[] = {"mtdparts=rtk_nand:640k@0(boot),6M@0x180000(linux),-(rootfs)", NULL};
#else
//eric, use cmdlinepart now
struct mtd_partition {
char *name; /* identifier string */
uint64_t size; /* partition size */
uint64_t offset; /* offset within the master MTD space */
u_int32_t mask_flags; /* master MTD flags to mask out for this partition */
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/
struct mtd_info **mtdp; /* pointer to store the MTD object */
};
static struct mtd_partition def_parts[] = {
{ name: "boot", offset: 0, size: 0x100000, mask_flags:0},
{ name: "env", offset: 0x100000, size: 0x20000, mask_flags:0},
{ name: "env", offset: 0x120000, size: 0x20000, mask_flags:0},
};
#endif
#endif
#ifdef CONFIG_MTD_CMDLINE_PARTS
/* partitions from cmdline */
ptype = (char *)ptypes[0];
struct mtd_info *rtk_mtd[2]; rtk_mtd[0]
pnum = parse_mtd_partitions (rtk_mtd, ptypes, &mtd_parts, 0);
if (pnum <= 0) {
printk (KERN_NOTICE "RTK: using the whole nand as a partitoin\n");
if (add_mtd_device (rtk_mtd)) {
printk (KERN_WARNING "RTK: Failed to register new nand device\n");
return -EAGAIN;
}
} else {
printk (KERN_NOTICE "RTK: using dynamic nand partition\n");
if (add_mtd_partitions (rtk_mtd, mtd_parts, pnum)) {
printk("%s: Error, cannot add %s device\n",
__FUNCTION__, rtk_mtd->name);
rtk_mtd->size = 0;
return -EAGAIN;
}
}
#else
/* fixed partition ,modify rtl8686_parts table*/
printk("RTK: Register new nand device\n");
if (add_mtd_device (mtd)) {
printk(KERN_WARNING "RTK: Failed to register new nand device\n");
//return -EAGAIN;
return -11;
}
#endif
/*
* Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
* are changing this array!
*/
static const char * const default_mtd_part_types[] = {
"cmdlinepart",
"ofpart",
NULL
};
int add_mtd_partitions(struct mtd_info *master,
const struct mtd_partition *parts,
int nbparts)
{
struct mtd_part *slave;
uint64_t cur_offset = 0;
int i;
/*
* Need to init the list here, since LIST_INIT() does not
* work on platforms where relocation has problems (like MIPS
* & PPC).
*/
if (mtd_partitions.next == NULL)
INIT_LIST_HEAD(&mtd_partitions);
printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
for (i = 0; i < nbparts; i++) {
slave = add_one_partition(master, parts + i, i, cur_offset);
if (!slave)
return -ENOMEM;
cur_offset = slave->offset + slave->mtd.size;
}
return 0;
}
static struct mtd_part *add_one_partition(struct mtd_info *master,
const struct mtd_partition *part, int partno,
uint64_t cur_offset)
{
struct mtd_part *slave;
/* allocate the partition structure */
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
if (!slave) {
printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
master->name);
del_mtd_partitions(master);
return NULL;
}
#总要节点
list_add(&slave->list, &mtd_partitions);
/* set up the MTD object for this partition */
slave->mtd.type = master->type;
slave->mtd.flags = master->flags & ~part->mask_flags;
slave->mtd.size = part->size;
slave->mtd.writesize = master->writesize;
slave->mtd.oobsize = master->oobsize;
slave->mtd.oobavail = master->oobavail;
slave->mtd.subpage_sft = master->subpage_sft;
slave->mtd.name = part->name;
slave->mtd.owner = master->owner;
slave->mtd.read = part_read;
slave->mtd.write = part_write;
if (master->panic_write)
slave->mtd.panic_write = part_panic_write;
if (master->read_oob)
slave->mtd.read_oob = part_read_oob;
if (master->write_oob)
slave->mtd.write_oob = part_write_oob;
if (master->read_user_prot_reg)
slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
if (master->read_fact_prot_reg)
slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
if (master->write_user_prot_reg)
slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
if (master->lock_user_prot_reg)
slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
if (master->get_user_prot_info)
slave->mtd.get_user_prot_info = part_get_user_prot_info;
if (master->get_fact_prot_info)
slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
if (master->sync)
slave->mtd.sync = part_sync;
if (master->lock)
slave->mtd.lock = part_lock;
if (master->unlock)
slave->mtd.unlock = part_unlock;
if (master->block_isbad)
slave->mtd.block_isbad = part_block_isbad;
if (master->block_markbad)
slave->mtd.block_markbad = part_block_markbad;
slave->mtd.erase = part_erase;
slave->master = master;
slave->offset = part->offset;
slave->index = partno;
if (slave->mtd.size == MTDPART_SIZ_FULL)
slave->mtd.size = master->size - slave->offset;
printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
/* let's do some sanity checks */
if (slave->offset >= master->size) {
/* let's register it anyway to preserve ordering */
slave->offset = 0;
slave->mtd.size = 0;
printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
part->name);
goto out_register;
}
if (slave->offset + slave->mtd.size > master->size) {
slave->mtd.size = master->size - slave->offset;
printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
part->name, master->name, (unsigned long long)slave->mtd.size);
}
if (master->numeraseregions > 1) {
/* Deal with variable erase size stuff */
int i, max = master->numeraseregions;
u64 end = slave->offset + slave->mtd.size;
struct mtd_erase_region_info *regions = master->eraseregions;
/* Find the first erase regions which is part of this
* partition. */
for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
;
/* The loop searched for the region _behind_ the first one */
i--;
/* Pick biggest erasesize */
for (; i < max && regions[i].offset < end; i++) {
if (slave->mtd.erasesize < regions[i].erasesize) {
slave->mtd.erasesize = regions[i].erasesize;
}
}
BUG_ON(slave->mtd.erasesize == 0);
} else {
/* Single erase size */
slave->mtd.erasesize = master->erasesize;
}
if ((slave->mtd.flags & MTD_WRITEABLE) &&
mtd_mod_by_eb(slave->offset, &slave->mtd)) {
/* Doesn't start on a boundary of major erase size */
/* FIXME: Let it be writable if it is on a boundary of
* _minor_ erase size though */
slave->mtd.flags &= ~MTD_WRITEABLE;
printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
part->name);
}
if ((slave->mtd.flags & MTD_WRITEABLE) &&
mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
slave->mtd.flags &= ~MTD_WRITEABLE;
printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
part->name);
}
slave->mtd.ecclayout = master->ecclayout;
if (master->block_isbad) {
uint64_t offs = 0;
while (offs < slave->mtd.size) {
if (master->block_isbad(master,
offs + slave->offset))
slave->mtd.ecc_stats.badblocks++;
offs += slave->mtd.erasesize;
}
}
out_register:
if (part->mtdp) {
/* store the object pointer (caller may or may not register it*/
*part->mtdp = &slave->mtd;
slave->registered = 0;
} else {
/* register our partition */
./include/linux/mtd/mtd.h:16:#define MAX_MTD_DEVICES 32
./drivers/mtd/mtdcore.c:16:int add_mtd_device(struct mtd_info *mtd)
add_mtd_device(&slave->mtd);
slave->registered = 1;
}
return slave;
}
int add_mtd_device(struct mtd_info *mtd)
{
int i;
BUG_ON(mtd->writesize == 0);
for (i = 0; i < MAX_MTD_DEVICES; i++)
if (!mtd_table[i]) {
mtd_table[i] = mtd;
mtd->index = i;
mtd->usecount = 0;
return 0;
}
return 1;
}
loader\u-boot-2011.12\common\cmd_nvedit.c
U_BOOT_CMD_COMPLETE(
setenv, CONFIG_SYS_MAXARGS, 0, do_env_set,
"set environment variables",
"name value ...\n"
" - set environment variable 'name' to 'value ...'\n"
"setenv name\n"
" - delete environment variable 'name'",
var_complete
);
关于mtd->write_oob()和chip->ecc.write_oob(),具体有何区别和联系,
自己之前无意间注意到,也迷惑过,但并没有去弄懂,这次看到别人有提问,所以,专门去看了源码,基本算是搞清楚了。
mtd->write_oob,是在nand_scan()->nand_scan_tail()中被赋值的:
mtd->write_oob = nand_write_oob;
那我们就去看看nand_write_oob(),它主要是根据输入参数,决定具体做什么事情:
1.当输入参数中的页数据缓存是空的话,那么就去只是去写oob的数据,也就是单纯的写oob,而不写整个页的数据,也就只调用
nand_do_write_oob()去真正写oob的数据。
2.当输入参数中的页数据缓存pagebuf非空的话,那么不仅写页数据,而且还写oob。
具体调用的函数nand_do_write_ops()去实现既写整页数据,又写oob信息。
而此函数的功能,其实从函数功能的注释中,也可以看得很清楚:NAND write data and/or out-of-band。
下面就分别来看看上面两个数据的具体执行过程。
1.nand_do_write_oob
此函数简单说就是,先填充oob信息,即根据ecc的layout,调用nand_fill_oob,把ecc填充到buffer中;
然后调用chip->ecc.write_oob()去真正实现将oob写入到nand flash中。
至此,mtd->write_oob和ecc.write_oob的其中一种联系,就已经很清楚了:
mtd->write_oob中,如果只是写oob,那么调用nand_do_write_oob()->ecc.write_oob(),
完成oob的实际的写操作。
2.nand_do_write_ops
简单点说就是,先根据设置决定是否去调用nand_fill_oob填充oob,然后再去调用
chip->write_page实现真正页数据的写操作。
对于chip->write_page再解释一下,如果你没有自己实现此函数的话,
也是在nand_scan()->nand_scan_tail()中被赋值成系统默认的:
chip->write_page = nand_write_page;
而nand_write_page()中,根据是否是是raw,去调用不同的函数:
(1)chip->ecc.write_page_raw
关于raw:也就是raw原始数据以及oob,没有在oob中添加ecc信息的。
nand_scan_tail()中:
chip->ecc.write_page_raw = nand_write_page_raw;
nand_write_page_raw()中直接就是先写页数据,再写oob,没有具体去计算ecc并写入,
所以,和我们此处讨论的ecc没啥关系。
(2)chip->ecc.write_page
对于nand 的ecc类型是最常见的NAND_ECC_HW的话,
nand_scan_tail()中:
chip->ecc.write_page = nand_write_page_hwecc;
nand_write_page_hwecc()中,循环ecc的step次
(常见的是,页大小是2K的nand,每512字节产生6位或8位ecc值,ecc的step值就是,
页大小/一次ecc对应数据大小=2k/512=4),
先写启用硬件ecc校验,再写页数据,
然后把得到的ecc校验值组织好,放到oob中对应位置,然后再调用底层的写函数,去写ecc。
这部分,和我们上面的ecc.write_oob,也没有直接关系。
只不过,无论是写页数据,还是写oob数据,底层具体去写的动作,都是调用chip->write_buf()去实现的。
而nand_write_page_hwecc()中后面的写oob的部分,其实和ecc.write_oob是一样的,只不过没单独再去分开而已。
【总结】
mtd->write_oob()和chip->ecc.write_oob()的联系:
1.mtd->write_oob中,如果只是写oob,那么调用nand_do_write_oob()->ecc.write_oob(),
完成oob的实际的写操作。
2.mtd->write_oob中,如果即写页数据又写oob数据,并且是带ecc的写页数据的话,调用
chip->ecc.write_page去实现具体的先写页数据,再写含ecc的oob数据,具体最后的写含ecc的oob的过程,
本质上和ecc.write_oob,底层调用函数实现机制,都是一样的。
【题外话】
1.底层如何定位到oob的起始位置?
中间有个细节,本来很简单清楚的,差点把自己搞晕了,那就是:
对于写oob,到底底层是怎么传入参数使得定位到一个页的oob的起始位置的,然后再开始写oob数据的。
结果就是,再nand_write_oob_std()中,发命令的时候,送的就是定位的起始地址:
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
其中,mtd->writesize就是页内地址,此处就是一个页大小,比如2K,页结束地址=oob开始的位置,
就可以定位到oob起始处了,后面就可以写oob数据了:
chip->write_buf(mtd, buf, length);