2410上NAND的初始化及驱动流程

来源:互联网 发布:知也无涯 编辑:程序博客网 时间:2024/04/28 21:54

NAND设备及资源的定义和注册

我们通过MACHINE_START定义了smdk2410machine_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");  /*获取用于nandclock(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 < 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);

       

        /*初始化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 */

 

    /*计算各时钟频率, 这些时钟参数可参考2410datasheet*/

    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 < 0 || twrph0 < 0 || twrph1 < 0) {

        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硬件的操作了, 大家可以对照着2410nand部分的datasheet来看这些代码

 
原创粉丝点击