uboot中nand详细分析(一)

时间:2021-04-14 16:47:24
一.gpmc初始化 uboot首先执行start.s,初始化cpu并从nand中拷贝其他代码到内存中;最后跳转到start_armboot()函数; 在start_armboot()中,循环初始化外设,其中board_init()函数就调用gpmc_init()初始化了gpmc: void gpmc_init(void){        //通过把gpmc_cfg指向gpmc基地址,使gpmc_cfg中的数据类型所代表的是gpmc寄存器地址
gpmc_cfg = (struct gpmc *)GPMC_BASE;
#if defined(CONFIG_CMD_NAND) || defined(CONFIG_CMD_ONENAND)const u32 *gpmc_config = NULL;u32 base = 0;u32 size = 0;#if defined(CONFIG_ENV_IS_IN_NAND) || defined(CONFIG_ENV_IS_IN_ONENAND)u32 f_off = CONFIG_SYS_MONITOR_LEN;u32 f_sec = 0;#endif/* global settings;初始化gpmc基本设置 */writel(0x00000008, &gpmc_cfg->sysconfig);writel(0x00000100, &gpmc_cfg->irqstatus);writel(0x00000200, &gpmc_cfg->irqenable);writel(0x00000012, &gpmc_cfg->config);/*Disable the GPMC0 config set by ROM code*/writel(0, &gpmc_cfg->cs[0].config7);sdelay(1000);#if defined(CONFIG_CMD_NAND) /* CS 0 */        //gpmc_m_nand是gpmc cs的设置
gpmc_config = gpmc_m_nand;base = PISMO1_NAND_BASE;size = PISMO1_NAND_SIZE;        //根据gpmc_m_nand中的配置设置gpmc_cfg->cs[0]
enable_gpmc_cs_config(gpmc_config, &gpmc_cfg->cs[0], base, size);#endif}
二.nand底层初始化 nand_init()调用nand_init_chip()来初始化寄存器设置和赋值相关操作函数; uboot将nand操作封装成mtd子系统,其中mtd_info结构体表示mtd通用层的数据结构和操作函数;而nand_chip表示nand底层相关的数据结构和操作函数;其中mtd->priv=nand;即把底层的数据结构赋给mtd的priv,方便直接通过mtd获得; 在nand_init_chip()中,通过调用board_nand_init()来初始化基本寄存器和赋值chip相关函数,调用nand_scan()来初始化通用层需要的数据和赋值mtd层相关函数。 1.底层初始化 我们先来看board_nand_init(): int board_nand_init(struct nand_chip *nand){cs = 0;while (cs < GPMC_MAX_CS) {/* Check if NAND type is set;看是否有设备连接上 */if ((readl(&gpmc_cfg->cs[cs].config1) & 0xC00) == 0x800) {/* Found it!! */break;}cs++;}        //赋值io读写地址
nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat;nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd;
nand->cmd_ctrl = ti81xx_nand_hwcontrol;nand->options = NAND_NO_PADDING | NAND_CACHEPRG | NAND_NO_AUTOINCR;/* If we are 16 bit dev, our gpmc config tells us that;判断是否是16位nand */if ((readl(&gpmc_cfg->cs[cs].config1) & 0x3000) == 0x1000) {nand->options |= NAND_BUSWIDTH_16;}/* fallback ecc info(备用ecc信息), this will be overridden by  * ti81xx_nand_switch_ecc() below to 1-bit h/w ecc;            将nand_prib指向bch_priv*/nand->priv = &bch_priv;nand->ecc.mode = NAND_ECC_SOFT;/* required in case of BCH;lem(错误位置模块)提取错误地址 */elm_init();
nand_curr_device = 0;ti81xx_nand_switch_ecc(NAND_ECC_HW, 2);return 0;}
在函数的最后,我们来看看ti81xx_nand_switch_ecc(NAND_ECC_HW,2): /*__ti81xx_nand_switch_ecc - switch the ECC operation ib/w h/w ecc * (i.e. hamming / bch) and s/w ecc. * The default is to come up on s/w ecc * @nand: NAND chip datastructure * @hardware:  NAND_ECC_HW -switch to h/w ecc *  NAND_ECC_SOFT -switch to s/w ecc * @mode:  0 - hamming code *  1 - bch4 * 2 - bch8 * 3 - bch16 * @return: 0 - success, or error */int __ti81xx_nand_switch_ecc(struct nand_chip *nand,nand_ecc_modes_t hardware, int32_t mode){struct nand_bch_priv *bch;
bch = nand->priv;nand->options |= NAND_OWN_BUFFERS;
nand->ecc.mode = hardware;/* Setup the ecc configurations again */if (hardware == NAND_ECC_HW) {if (mode) {bch->mode = NAND_ECC_HW_BCH;/* -1 for converting mode to bch type */bch->type = mode - 1;printf("HW ECC BCH");switch (bch->type) {case ECC_BCH8:default:                                        //ecc校验码数量
nand->ecc.bytes = 14;                                        //清空的nand中使用的校验码
nand->ecc.layout = &hw_bch8_nand_oob;nand->ecc.read_page_raw =ti81xx_nand_read_page_raw_bch;nand->ecc.write_page_raw =ti81xx_nand_write_page_raw_bch;nand->ecc.read_oob =ti81xx_nand_read_oob_bch;nand->ecc.write_oob =ti81xx_nand_write_oob_bch;bch->nibbles = ECC_BCH8_NIBBLES;printf("8 Selected\n");break;}bch->mode = NAND_ECC_HW;nand->ecc.mode = NAND_ECC_HW_SYNDROME;nand->ecc.steps = 4;nand->ecc.size = 512;nand->ecc.total = (nand->ecc.steps * nand->ecc.bytes);nand->ecc.write_page = ti81xx_write_page_bch;nand->ecc.read_page = ti81xx_read_page_bch;nand->ecc.hwctl = ti81xx_enable_ecc_bch;nand->ecc.correct = ti81xx_correct_data_bch;nand->ecc.calculate = ti81xx_calculate_ecc_bch;ti81xx_hwecc_init_bch(nand, NAND_ECC_READ);return ret;}
该函数调用ti81xx_hwecc_init_bch初始化bch 硬件ecc: /* * ti81xx_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in *  GPMC controller * @mtd:       MTD device structure * @mode: Read/Write mode */static void ti81xx_hwecc_init_bch(struct nand_chip *chip, int32_t mode){uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1;uint32_t unused_length = 0;struct nand_bch_priv *bch = chip->priv;
switch(bch->nibbles) {case ECC_BCH8_NIBBLES:unused_length = 2;break;}
/* Clear the ecc result registers, select ecc reg as 1 */writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
switch (mode) {case NAND_ECC_WRITE:/* eccsize1 config */val = ((unused_length + bch->nibbles) << 22);break;
case NAND_ECC_READ:default:/* by default eccsize0 selected for ecc1resultsize *//* eccsize0 config */val  = (bch->nibbles << 12);/* eccsize1 config */val |= (unused_length << 22);break;}/* ecc size configuration */writel(val, &gpmc_cfg->ecc_size_config);/* by default 512bytes sector page is selected *//* set bch mode */val  = (1 << 16);/* bch4 / bch8 / bch16 */val |= (bch->type << 12);/* set wrap mode to 1 */val |= (1 << 8);val |= (dev_width << 7);val |= (cs << 1);/* enable ecc *//* val |= (1); */ /* should not enable ECC just init i.e. config */writel(val, &gpmc_cfg->ecc_config);}
2.mtd层初始化 /*nand_scan_ident - [NAND Interface] Scan for the NAND device * @mtd:      MTD device structure * @maxchips:      Number of chips to scan for * This is the first phase of the normal nand_scan() function. It * reads the flash ID and sets up MTD fields accordingly. * The mtd->owner field must be set to the module of the caller. */int nand_scan_ident(struct mtd_info *mtd, int maxchips){int i, busw, nand_maf_id;struct nand_chip *chip = mtd->priv;struct nand_flash_dev *type;/* Set the default functions;初始化chip层通用函数 */nand_set_defaults(chip, busw);/* Read the flash type ;通过读取id等信息获取nand的大小,页大小,block大小,ecc大小及转换成需要的信息*/type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);/* Check for a chip array;检测系统中的nand */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) ||    type->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;}
最后的nand_scan_tail()主要初始化mtd层函数: /* 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->suspend = nand_suspend;mtd->resume = nand_resume;mtd->block_isbad = nand_block_isbad;mtd->block_markbad = nand_block_markbad;