注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

Linux MTD子系统学习5—s3c24xx_nand_probe()函数之二  

2012-01-05 20:18:08|  分类: LINUX内核驱动 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
 

Linux MTD子系统学习4—s3c24xx_nand_probe()函数之二

从头介绍,在给info=kmalloc(sizeof(*info),GFP_KERNEL)分配好内存后,会调用platform_set_drvdata这个宏,其又会调用下面的函数,目的是将info和dev联接起来,下面会有很多这样的变化,在看内核的时候儿要认真。

void dev_set_drvdata(struct device *dev, void *data)

{

    int error;

 

    if (!dev)

        return;

    if (!dev->p) {

        error = device_private_init(dev);

        if (error)

            return;

    }

    dev->p->driver_data = data;

}

当然里面还有一些调用设备初始化的函数,里面又会初始化链表,大家可以细往下看,但这个不是重点。接着向下,就是一片为info赋值的代码,略过,直接走到s3c2410_nand_inithw()这个函数,今天就从这儿开刀,详细的讲一下。

static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)

{

    int ret;

 

    ret = s3c2410_nand_setrate(info);

    if (ret < 0)

        return ret;

 

    switch (info->cpu_type) {

    case TYPE_S3C2410:

    default:

        break;

 

    case TYPE_S3C2440:

    case TYPE_S3C2412:

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

 

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

    }

 

    return 0;

}

在arch\arm\plat-s3c24xx\common-smdk.c这个文件中定义了:

static struct s3c2410_platform_nand smdk_nand_info = {

    .tacls      = 20,

    .twrph0     = 60,

    .twrph1     = 20,

    .nr_sets    = ARRAY_SIZE(smdk_nand_sets),

    .sets       = smdk_nand_sets,

};

而这个被MACHINE_START宏中间的函数定义所调用,具体的用法,这里不细讲,不然就不是讲MTD了。那个是默认的,而下面可以对其进行重新的设置。此函数的主要作用是对总线进行设置。

 

其中又调用了下面这个函数:

static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)

{

    struct s3c2410_platform_nand *plat = info->platform;

    int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;

    int tacls, twrph0, twrph1;

    unsigned long clkrate = clk_get_rate(info->clk);

    unsigned long uninitialized_var(set), cfg, uninitialized_var(mask);

    unsigned long flags;

 

    /* calculate the timing information for the controller */

 

    info->clk_rate = clkrate;

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

 

    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:

        mask = (S3C2410_NFCONF_TACLS(3) |

            S3C2410_NFCONF_TWRPH0(7) |

            S3C2410_NFCONF_TWRPH1(7));

        set = S3C2410_NFCONF_EN;

        set |= S3C2410_NFCONF_TACLS(tacls - 1);

        set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);

        set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);

        break;

 

    case TYPE_S3C2440:

    case TYPE_S3C2412:

        mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) |

            S3C2440_NFCONF_TWRPH0(7) |

            S3C2440_NFCONF_TWRPH1(7));

 

        set = S3C2440_NFCONF_TACLS(tacls - 1);

        set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);

        set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);

        break;

 

    default:

        BUG();

    }

 

    local_irq_save(flags);

 

    cfg = readl(info->regs + S3C2410_NFCONF);

    cfg &= ~mask;

    cfg |= set;

    writel(cfg, info->regs + S3C2410_NFCONF);

 

    local_irq_restore(flags);

 

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

 

    return 0;

}

注意:这里参考了“arm-linux东东之nand”这篇博文,http://blog.csdn.net/qiuhuafeng/article/details/4018371

 

 2012年01月05日 - 还东国 - 还东国的博客

 

Tacls是当CLE/ALE有效时过了多少时间后nwe才有效.

TWRPH0是nwe的有效时间.

TWRPH1是当nWE无效后DATA的保持时间.

 2012年01月05日 - 还东国 - 还东国的博客2012年01月05日 - 还东国 - 还东国的博客

这里tcs,twp,tclh就是我们要的

    .tacls     = 20,

    .twrph0       = 60,

    .twrph1       = 20,

注意单位是ns.

好回到我们的s3c2410_nand_setrate中来.

tacls_max是怎么算的呢?还不是S3C2440 的TACLS只占了二位.二位就是4.

clkrate 是NAND的源时钟./1000把它转换成KHZ

plat是不为NULL的,于是

#define NS_IN_KHZ 1000000

 

其中一些参数的计算又调用了:

static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)

{

    int result;

 

    result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ);

 

    pr_debug("result %d from %ld, %d\n", result, clk, wanted);

 

    if (result > max) {

        printk("%d ns is too big for current clock rate %ld\n", wanted, clk);

        return -1;

    }

 

    if (result < 1)

        result = 1;

 

    return result;

}

这里怎么算呢.举个例子:(1/HZ)*n=20ns

于是n=HZ*(20ns)由于这里的单位是ns 所于除了NS_IN_KHZ纯数学问题very simple.

Result 算出来以后就返回了.

好返回s3c2410_nand_inithw中来:

twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);

twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);

这两个也是一样的.

来看一下switch(info->cpu_type)这个句子

 

    case TYPE_S3C2440:

    case TYPE_S3C2412:

       cfg = S3C2440_NFCONF_TACLS(tacls - 1);

       cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);

       cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);

这里为什么减一呢.原来是这样的

 

 2012年01月05日 - 还东国 - 还东国的博客

 

 

 

算的时候它自动加1了.三星的东西很多都是这样的.这里算的时候也不是很严的时间限制.有没有注意到上面result++.不管怎么样误差不是太大就行了.

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

 

这个info->regs在platform_device中的资源里,整个操作流程从:

MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch

                  * to SMDK2410 */

    /* Maintainer: Jonas Dietsche */

    .phys_io   = S3C2410_PA_UART,

    .io_pg_offst  = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

    .boot_params  = S3C2410_SDRAM_PA + 0x100,

    .map_io       = smdk2410_map_io,

    .init_irq  = s3c24xx_init_irq,

    .init_machine = smdk2410_init,

    .timer     = &s3c24xx_timer,

MACHINE_END

这个宏中的定义始,大家有兴趣可以自己去看。

#define S3C2440_NFCONT_ENABLE      (1<<0)

NFCONF有个开始/禁止控制位.这里开启使nand控制器跑起来.

s3c2410_nand_inithw完了.

注意:2.6.32的内核和其前面的一些内核有些不同,明显的是把s3c2410_nand_inithw()这个函数拆出来了一个s3c2410_nand_setrate()函数,不过具体的东西没有什么变化,所以可能只是为了代码更简洁美观这样写的吧。

最后的一句话,越是到了年关,越要努力学习,坚持就是胜利。

  评论这张
 
阅读(994)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017