免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 645 | 回复: 0
打印 上一主题 下一主题

第五讲:2410上NAND的初始化及驱动流程 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-04-25 18:07 |只看该作者 |倒序浏览
一 NAND设备及资源的定义和注册
我们通过MACHINE_START定义了smdk2410的machine_desc对象,这个对象里面有个init_machine的函数指针, 这里指向smdk_machine_init(), 我们的NAND设备就是在这个函数里注册到系统的.
void __init smdk_machine_init(void)
{
   ….
   s3c_device_nand.dev.platform_data = &smdk_nand_info;
   platform_add_device(smdk_devs, ARRAY_SIZE(smdk_devs));  //这里就把设备注册到系统里去了
   …
}
Static struct platform_device __initdata *smdk_devs[] =
{
   &s3c_device_nand,    //这样在上面的函数里我们的nand设备就注册好了.
   ...   
}
其他设备我们也可以在这里注册进系统.
struct platform_device s3c_device_nand =
{
   .name = “s3c2410-nand”,  /*名字很重要*/
   .id = -1,
   . num_resources = ARRAY_SIZE(s3c_nand_resource),
   .resource = s3c_nand_resource,  //这个是NAND占用的资源.
};
Static struct s3c2410_platform_nand smdk_nand_info = {
     .tacks = 20,      /*在datasheet上有描述*/
     .twrph0 = 60,    /*在datasheet上有描述*/
     .twrph1 = 20,    /*在datasheet上有描述*/
     .nr_sets = ARRAY_SIZE(smdk_nand_sets),
     .sets = smdk_nand_sets  
}
static struct s3c2410_nand_set smdk_nand_sets[] = {
    [0] = {
        .name       = "NAND",
        .nr_chips   = 1,
        .nr_partitions  = ARRAY_SIZE(smdk_default_nand_part),
        .partitions = smdk_default_nand_part,   /*nand的分区信息*/
    },
};
/*分区信息, 我们可以在这里修改分区内容*/
static struct mtd_partition smdk_default_nand_part[] = {
    [0] = {
        .name   = "Boot Agent",
        .size   = SZ_16K,
        .offset = 0,
    },
    [1] = {
        .name   = "S3C2410 flash partition 1",
        .offset = 0,
        .size   = SZ_2M,
    },
    [2] = {
        .name   = "S3C2410 flash partition 2",
        .offset = SZ_4M,
        .size   = SZ_4M,
    },
    [3] = {
        .name   = "S3C2410 flash partition 3",
        .offset = SZ_8M,
        .size   = SZ_2M,
    },
    [4] = {
        .name   = "S3C2410 flash partition 4",
        .offset = SZ_1M * 10,
        .size   = SZ_4M,
    },
    [5] = {
        .name   = "S3C2410 flash partition 5",
        .offset = SZ_1M * 14,
        .size   = SZ_1M * 10,
    },
    [6] = {
        .name   = "S3C2410 flash partition 6",
        .offset = SZ_1M * 24,
        .size   = SZ_1M * 24,
    },
    [7] = {
        .name   = "S3C2410 flash partition 7",
        .offset = SZ_1M * 48,
        .size   = SZ_16M,
    }
};
这样NAND设备(连同设备的详细信息)就注册进了系统, 以后在nand的驱动注册后就会probe到并使用这里定义的资源信息.

二 NAND驱动流程
   2410的驱动实现在driver\mtd\nand\S3c2410.c里
   首先和其他驱动一样先通过module_init(), module_exit()注册一个初始化/卸载函数,
module_init(s3c2410_nand_init);
module_exit(s3c2410_nand_exit);
系统初始化时该函数被调用
static int __init s3c2410_nand_init(void)
{
    printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");

    platform_driver_register(&s3c2412_nand_driver);  /*注册nand驱动*/
    platform_driver_register(&s3c2440_nand_driver);  /*注册nand驱动*/
    return platform_driver_register(&s3c2410_nand_driver);  /*注册nand驱动*/
}
从上面可以看到我们注册了3个驱动程序, 但在系统probe时它只会匹配到s3c2410_nand_driver的驱动, 因为各个驱动的名字是不一样的, 而系统是安名字来probe的.
static struct platform_driver s3c2410_nand_driver = {
    .probe      = s3c2410_nand_probe,
    .remove     = s3c2410_nand_remove,
    .suspend    = s3c24xx_nand_suspend,
    .resume     = s3c24xx_nand_resume,
    .driver     = {
        .name   = "s3c2410-nand",  /*这里的名字一定要与设备定义的名字相同*/
        .owner  = THIS_MODULE,
    },
};
当系统probe到我们刚才注册的nand设备后即调用s3c2410_nand_probe函数
static int s3c2410_nand_probe(struct platform_device *dev)
{
    return s3c24xx_nand_probe(dev, TYPE_S3C2410);
}
static int s3c24xx_nand_probe(struct platform_device *pdev, /*nand设备,前面已经列出*/
                              enum s3c_cpu_type cpu_type /*TYPE_S3C2410*/)
{
    struct s3c2410_platform_nand *plat = to_nand_plat(pdev);/*对照前面列出的设备定义来看*/
    struct s3c2410_nand_info *info;
    struct s3c2410_nand_mtd *nmtd;
    struct s3c2410_nand_set *sets;
    struct resource *res;
    int err = 0;
    int size;
    int nr_sets;
    int setno;

    pr_debug("s3c2410_nand_probe(%p)\n", pdev);

/*该变量用来保存nand详细信息,以后访问nand信息都将从这个变量里得到*/
    info = kmalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
        dev_err(&pdev->dev, "no memory for flash info\n");
        err = -ENOMEM;
        goto exit_error;
    }

    memzero(info, sizeof(*info));
    platform_set_drvdata(pdev, info);

    spin_lock_init(&info->controller.lock);  /*自选锁初始化*/
    init_waitqueue_head(&info->controller.wq);  /*等待队列初始化*/

    /* get the clock source and enable it */
    info->clk = clk_get(&pdev->dev, "nand");  /*获取用于nand的clock(nand也要时钟信号的哦)*/
    if (IS_ERR(info->clk)) {
        dev_err(&pdev->dev, "failed to get clock");
        err = -ENOENT;
        goto exit_error;
    }

    clk_enable(info->clk); /*使能该clock,实际上就是设置CLKCON的第四位(详见2410datasheet)*/

    /* allocate and map the resource */
    /* currently we assume we have the one resource */
    res  = pdev->resource;  /*nand资源,见前面的定义*/
    size = res->end - res->start + 1;

     /*
* 请求指定的memory区域(实际上是nand的寄存器区域), 这是物理内存, 实际上只是检测该区域
* 是否空闲的
*/
    info->area = request_mem_region(res->start, size, pdev->name);

    if (info->area == NULL) {
        dev_err(&pdev->dev, "cannot reserve register region\n");
        err = -ENOENT;
        goto exit_error;
    }

    /*保存nand信息*/
    info->device     = &pdev->dev;
    info->platform   = plat;
/*虚实地址映射,以后程序里就可以直接访问nand的寄存器了*/
    info->regs       = ioremap(res->start, size);  
    info->cpu_type   = cpu_type;

    if (info->regs == NULL) {
        dev_err(&pdev->dev, "cannot reserve register region\n");
        err = -EIO;
        goto exit_error;
    }

    dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);

    /* initialise the hardware */

    err = s3c2410_nand_inithw(info, pdev);  /*初始化nand硬件设备*/
    if (err != 0)
        goto exit_error;

    /*接下来是MTD方面的初始化*/
    sets = (plat != NULL) ? plat->sets : NULL;
    nr_sets = (plat != NULL) ? plat->nr_sets : 1;

    info->mtd_count = nr_sets;

    /* allocate our information */
    size = nr_sets * sizeof(*info->mtds);
    info->mtds = kmalloc(size, GFP_KERNEL);
    if (info->mtds == NULL) {
        dev_err(&pdev->dev, "failed to allocate mtd storage\n");
        err = -ENOMEM;
        goto exit_error;
    }

    memzero(info->mtds, size);

    /* initialise all possible chips */

    nmtd = info->mtds;

    for (setno = 0; setno
        pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
         
         /*初始化nand chip实例*/
        s3c2410_nand_init_chip(info, nmtd, sets);
        
        /*初始化mtd及相关信息,详情可参考MTD的源码*/
        nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1) ;

        if (nmtd->scan_res == 0) {
            s3c2410_nand_add_partition(info, nmtd, sets);  /*添加分区,详情参看MTD*/
        }

        if (sets != NULL)
            sets++;
    }

    if (allow_clk_stop(info)) {
        dev_info(&pdev->dev, "clock idle support enabled\n");
        clk_disable(info->clk);
    }

    pr_debug("initialised ok\n");
    return 0;

exit_error:
    s3c2410_nand_remove(pdev);

    if (err == 0)
        err = -EINVAL;
    return err;
}
这个函数是最关键的, 要要给nand设备进行很多初始化的工作,并保存nand的所有详细信息,便于以后使用, 详细功能已在函数里描述了. clock方面的内容会有专门的文章讲解
/* nand控制器的初始化 */
static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
                   struct platform_device *pdev)
{
    struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
    unsigned long clkrate = clk_get_rate(info->clk); /*nand时钟主频*/
    int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
    int tacls, twrph0, twrph1;
    unsigned long cfg = 0;

    /* calculate the timing information for the controller */

    clkrate /= 1000;   /* turn clock into kHz for ease of use */

    /*计算各时钟频率, 这些时钟参数可参考2410的datasheet*/
    if (plat != NULL) {
        tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max);
        twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);
        twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);
    } else {
        /* default timings */
        tacls = tacls_max;
        twrph0 = 8;
        twrph1 = 8;
    }

    if (tacls
        dev_err(info->device, "cannot get suitable timings\n");
        return -EINVAL;
    }

    dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
           tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));

    switch (info->cpu_type) {
    case TYPE_S3C2410:
        cfg = S3C2410_NFCONF_EN;  /*使能*/
        cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
        cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
        cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
        break;

    case TYPE_S3C2440:
    case TYPE_S3C2412:
        cfg = S3C2440_NFCONF_TACLS(tacls - 1);
        cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
        cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);

        /* enable the controller and de-assert nFCE */

        writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
    }

    dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);

    /*把使能参数,时钟参数设到寄存器NFCONF里*/
    writel(cfg, info->regs + S3C2410_NFCONF);   
    return 0;
}
/*
* 为每个nand chip初始化一个实例.
* 比较简单,就是初始化各种参数和函数指针,以后会用到
*/
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
                   struct s3c2410_nand_mtd *nmtd,
                   struct s3c2410_nand_set *set)
{
    struct nand_chip *chip = &nmtd->chip;
    void __iomem *regs = info->regs;
   
    chip->write_buf    = s3c2410_nand_write_buf;
    chip->read_buf     = s3c2410_nand_read_buf;
    chip->select_chip  = s3c2410_nand_select_chip;
    chip->chip_delay   = 50;
    chip->priv     = nmtd;
    chip->options      = 0;
    chip->controller   = &info->controller;

    switch (info->cpu_type) {
    case TYPE_S3C2410:
        chip->IO_ADDR_W = regs + S3C2410_NFDATA;
        info->sel_reg   = regs + S3C2410_NFCONF;
        info->sel_bit   = S3C2410_NFCONF_nFCE;
        chip->cmd_ctrl  = s3c2410_nand_hwcontrol;
        chip->dev_ready = s3c2410_nand_devready;
        break;

    case TYPE_S3C2440:
        chip->IO_ADDR_W = regs + S3C2440_NFDATA;
        info->sel_reg   = regs + S3C2440_NFCONT;
        info->sel_bit   = S3C2440_NFCONT_nFCE;
        chip->cmd_ctrl  = s3c2440_nand_hwcontrol;
        chip->dev_ready = s3c2440_nand_devready;
        break;

    case TYPE_S3C2412:
        chip->IO_ADDR_W = regs + S3C2440_NFDATA;
        info->sel_reg   = regs + S3C2440_NFCONT;
        info->sel_bit   = S3C2412_NFCONT_nFCE0;
        chip->cmd_ctrl  = s3c2440_nand_hwcontrol;
        chip->dev_ready = s3c2412_nand_devready;

        if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT)
            dev_info(info->device, "System booted from NAND\n");

        break;
    }

    chip->IO_ADDR_R = chip->IO_ADDR_W;

    nmtd->info     = info;
    nmtd->mtd.priv     = chip;
    nmtd->mtd.owner    = THIS_MODULE;
    nmtd->set      = set;

    if (hardware_ecc) {
        chip->ecc.calculate = s3c2410_nand_calculate_ecc;
        chip->ecc.correct   = s3c2410_nand_correct_data;
        chip->ecc.mode      = NAND_ECC_HW;
        chip->ecc.size      = 512;
        chip->ecc.bytes     = 3;
        chip->ecc.layout    = &nand_hw_eccoob;

        switch (info->cpu_type) {
        case TYPE_S3C2410:
            chip->ecc.hwctl     = s3c2410_nand_enable_hwecc;
            chip->ecc.calculate = s3c2410_nand_calculate_ecc;
            break;

        case TYPE_S3C2412:
        case TYPE_S3C2440:
            chip->ecc.hwctl     = s3c2440_nand_enable_hwecc;
            chip->ecc.calculate = s3c2440_nand_calculate_ecc;
            break;

        }
    } else {
        chip->ecc.mode      = NAND_ECC_SOFT;
    }
}
Nand驱动剩下的就是实现上面这个函数里初始化的各个函数指针了.而这些函数就是实实在在的对nand硬件的操作了, 大家可以对照着2410的nand部分的datasheet来看这些代码
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/66024/showart_585872.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP