MTD原始设备与NANDFLASH硬件驱动交互

好久没写博客了,今天再次分析NANDFLASH驱动程序,每一次读源码总有一点的收获

1、首先从入口函数开始

probe函数将是我们遇到的第一个与具体硬件打交道,同时也相对复杂的函数对于很多外设的driver来说,只要能成功实现probe函数,那基本上完成这个外设的driver也就成功了一多半,基于MTD的NAND driver就是一个典型的例子。稍后就可以看到,在NAND driver的probe函数中,就已经涉及到了对NAND芯片的读写。
 
在基于MTD的NAND driver的probe函数中,主要可以分为两部分内容,其一是与很多外设driver类似的一些工作,如申请地址,中断,DMA等资源,kzalloc及初始化一些结构体,分配DMA用的内存等等;其二就是与MTD相关的一

static int s3c24xx_nand_probe(struct platform_device *pdev)
{
    /***获取平台数据信息return dev->dev.platform_data;**********/
    struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
    enum s3c_cpu_type cpu_type;
    struct s3c2410_nand_info *info;//NANDFLASH控制器信息,在分析驱动源码的时候我发现了一个问题,好像每一个驱动程序都有一个这样类似的结构体,里面封装着各种与该驱动相关的信息**/
    struct s3c2410_nand_mtd *nmtd;
    struct s3c2410_nand_set *sets;
    struct resource *res;
    int err = 0;
    int size;
    int nr_sets;
    int setno;
    /*****获取芯片CPU类型唯一标识符**/
    cpu_type = platform_get_device_id(pdev)->driver_data;

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

    info = kzalloc(sizeof(*info), GFP_KERNEL);
    if (info == NULL) {
        dev_err(&pdev->dev, "no memory for flash info\n");
        err = -ENOMEM;
        goto exit_error;
    }
    /******把驱动相关的数据信息设置到内核里面*********/
    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");/***获取时钟**/
    if (IS_ERR(info->clk)) {
        dev_err(&pdev->dev, "failed to get clock\n");
        err = -ENOENT;
        goto exit_error;
    }

    clk_enable(info->clk);//使能时钟

    /* allocate and map the resource */
    /****获取平台设备所需要的资源***在arch/arm/plat-samsung/dev-nand.c***********/
    /* currently we assume we have the one resource */
    res  = pdev->resource;
    size = resource_size(res);//计算资源的大小
    /**为平台设备资源分配空间**/
    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;
    }
    /**填充s3c2410_nand_info结构***/
    info->device     = &pdev->dev;
    info->platform   = plat; /*保存 s3c2410_platform_nand结构信息*/
    info->regs       = ioremap(res->start, size);/**映射IO**/
    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);//硬件初始化 主要是配置NFCONF寄存器
    if (err != 0)
        goto exit_error;

    sets = (plat != NULL) ? plat->sets : NULL;
    nr_sets = (plat != NULL) ? plat->nr_sets : 1;

    info->mtd_count = nr_sets;

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

    /* initialise all possible chips */
    
    nmtd = info->mtds;

    for (setno = 0; setno < nr_sets; setno++, nmtd++) {
        pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
      /***填充nand_chip结构**/
        s3c2410_nand_init_chip(info, nmtd, sets);
        /**扫描nand芯片读取芯片ID等**/
        nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
                         (sets) ? sets->nr_chips : 1,
                         NULL);

        if (nmtd->scan_res == 0) {
            s3c2410_nand_update_chip(info, nmtd);
            nand_scan_tail(&nmtd->mtd);
            s3c2410_nand_add_partition(info, nmtd, sets);
        }

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

    err = s3c2410_nand_cpufreq_register(info);
    if (err < 0) {
        dev_err(&pdev->dev, "failed to init cpufreq support\n");
        goto exit_error;
    }

    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:
    s3c24xx_nand_remove(pdev);

    if (err == 0)
        err = -EINVAL;
    return err;
}

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       = set->options;
    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;
        chip->read_buf  = s3c2440_nand_read_buf;
        chip->write_buf    = s3c2440_nand_write_buf;
        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;

        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:
              chip->ecc.hwctl     = s3c2412_nand_enable_hwecc;
              chip->ecc.calculate = s3c2412_nand_calculate_ecc;
            break;

        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;
    }

    if (set->ecc_layout != NULL)
        chip->ecc.layout = set->ecc_layout;

    if (set->disable_ecc)
        chip->ecc.mode    = NAND_ECC_NONE;

    switch (chip->ecc.mode) {
    case NAND_ECC_NONE:
        dev_info(info->device, "NAND ECC disabled\n");
        break;
    case NAND_ECC_SOFT:
        dev_info(info->device, "NAND soft ECC\n");
        break;
    case NAND_ECC_HW:
        dev_info(info->device, "NAND hardware ECC\n");
        break;
    default:
        dev_info(info->device, "NAND ECC UNKNOWN\n");
        break;
    }

    /* If you use u-boot BBT creation code, specifying this flag will
     * let the kernel fish out the BBT from the NAND, and also skip the
     * full NAND scan that can take 1/2s or so. Little things... */
    if (set->flash_bbt)
        chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值