uboot中nand详细分析(一)

来源:互联网 发布:mysql更新字段值 编辑:程序博客网 时间:2024/05/17 09:28
一.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;
原创粉丝点击