新闻中心

EEPW首页>嵌入式系统>设计应用> Linux内核的Nand驱动流程分析

Linux内核的Nand驱动流程分析

作者: 时间:2016-11-28 来源:网络 收藏
  1. staticints3c24xx_nand_probe(structplatform_device*pdev)
  2. {
  3. structs3c2410_platform_nand*plat=to_nand_plat(pdev);
  4. enums3c_cpu_typecpu_type;
  5. structs3c2410_nand_info*info;
  6. structs3c2410_nand_mtd*nmtd;
  7. structs3c2410_nand_set*sets;
  8. structresource*res;
  9. interr=0;
  10. intsize;
  11. intnr_sets;
  12. intsetno;
  13. cpu_type=platform_get_device_id(pdev)->driver_data;
  14. pr_debug("s3c2410_nand_probe(%p)",pdev);
  15. info=kzalloc(sizeof(*info),GFP_KERNEL);
  16. if(info==NULL){
  17. dev_err(&pdev->dev,"nomemoryforflashinfo");
  18. err=-ENOMEM;
  19. gotoexit_error;
  20. }
  21. platform_set_drvdata(pdev,info);
  22. spin_lock_init(&info->controller.lock);
  23. init_waitqueue_head(&info->controller.wq);
  24. /*gettheclocksourceandenableit*/
  25. info->clk=clk_get(&pdev->dev,"nand");
  26. if(IS_ERR(info->clk)){
  27. dev_err(&pdev->dev,"failedtogetclock");
  28. err=-ENOENT;
  29. gotoexit_error;
  30. }
  31. s3c2410_nand_clk_set_state(info,CLOCK_ENABLE);
  32. /*allocateandmaptheresource*/
  33. /*currentlyweassumewehavetheoneresource*/
  34. res=pdev->resource;
  35. size=resource_size(res);
  36. info->area=request_mem_region(res->start,size,pdev->name);
  37. if(info->area==NULL){
  38. dev_err(&pdev->dev,"cannotreserveregisterregion");
  39. err=-ENOENT;
  40. gotoexit_error;
  41. }
  42. info->device=&pdev->dev;
  43. info->platform=plat;
  44. info->regs=ioremap(res->start,size);
  45. info->cpu_type=cpu_type;
  46. if(info->regs==NULL){
  47. dev_err(&pdev->dev,"cannotreserveregisterregion");
  48. err=-EIO;
  49. gotoexit_error;
  50. }
  51. dev_dbg(&pdev->dev,"mappedregistersat%p",info->regs);
  52. /*initialisethehardware*/
  53. err=s3c2410_nand_inithw(info);
  54. if(err!=0)
  55. gotoexit_error;
  56. sets=(plat!=NULL)?plat->sets:NULL;
  57. nr_sets=(plat!=NULL)?plat->nr_sets:1;
  58. info->mtd_count=nr_sets;
  59. /*allocateourinformation*/
  60. size=nr_sets*sizeof(*info->mtds);
  61. info->mtds=kzalloc(size,GFP_KERNEL);
  62. if(info->mtds==NULL){
  63. dev_err(&pdev->dev,"failedtoallocatemtdstorage");
  64. err=-ENOMEM;
  65. gotoexit_error;
  66. }
  67. /*initialiseallpossiblechips*/
  68. nmtd=info->mtds;
  69. for(setno=0;setno
  70. pr_debug("initialisingset%d(%p,info%p)",setno,nmtd,info);
  71. s3c2410_nand_init_chip(info,nmtd,sets);
  72. nmtd->scan_res=nand_scan_ident(&nmtd->mtd,
  73. (sets)?sets->nr_chips:1,
  74. NULL);
  75. if(nmtd->scan_res==0){
  76. s3c2410_nand_update_chip(info,nmtd);
  77. nand_scan_tail(&nmtd->mtd);
  78. s3c2410_nand_add_partition(info,nmtd,sets);
  79. }
  80. if(sets!=NULL)
  81. sets++;
  82. }
  83. err=s3c2410_nand_cpufreq_register(info);
  84. if(err<0){
  85. dev_err(&pdev->dev,"failedtoinitcpufreqsupport");
  86. gotoexit_error;
  87. }
  88. if(allow_clk_suspend(info)){
  89. dev_info(&pdev->dev,"clockidlesupportenabled");
  90. s3c2410_nand_clk_set_state(info,CLOCK_SUSPEND);
  91. }
  92. pr_debug("initialisedok");
  93. return0;
  94. exit_error:
  95. s3c24xx_nand_remove(pdev);
  96. if(err==0)
  97. err=-EINVAL;
  98. returnerr;
  99. }
对于我们的Nand驱动来说,调用这个函数的参数当然是s3c_device_nand,阅读代码就可以知道前面定义每个变量的原理了。我看到函数开头定义的res就想到了我们前面定义的s3c_nand_resource,往下看能看到
  1. res=pdev->resource;
  2. size=resource_size(res);
也就是说,这里引用了我们前面定义的s3c_device_nand,我们看下他如何使用的(如果前面的已经忘记了,没关系,退回去看一下),紧接着下面几行代码
  1. info->area=request_mem_region(res->start,size,pdev->name);
  2. if(info->area==NULL){
  3. dev_err(&pdev->dev,"cannotreserveregisterregion");
  4. err=-ENOENT;
  5. gotoexit_error;
  6. }
显然,这里的request_mem_region用到的参数实际上就是我们前面定义的s3c_device_nand中的start,size当然就是end-start得到的,还有就是设备的名字,我们前面定义的是"s3c2410-nand",从函数名称可以看出,这里是通过res来申请mem资源,具体的可以自己阅读下代码,实际上request_mem_region是个宏,它调用了另外一个函数,这里我就不作分析了。继续往下看,又看到一行
  1. info->regs=ioremap(res->start,size);
ioremap函数的作用是将物理地址影射到虚拟地址,这里就是将s3c_device_nand中记录的Nand寄存器首地址开始的1M空间作了映射,这也就理解为什么是1M空间了,因为内核的一级页表是以1M为单位的,现在就清楚为什么要定义这个s3c_nand_resource了,因为Linux内核使用的地址空间是启动MMU后的虚拟地址空间,而我们给出的寄存器地址是物理地址,内核需要将寄存器的物理地址映射到虚拟地址才能正确访问寄存器,到这里我们知道驱动程序已经可以正确访问Nand寄存器了,前面的疑惑解开了。

本文引用地址://m.amcfsurvey.com/article/201611/322799.htm

继续往下看代码,到for循环处停下来,我们需要注意一下这部分代码,因为我们看到了s3c2410_nand_init_chip,从函数名称上很容易可以看出,这就是Nand的初始化代码,但是这里为什么要使用一个for循环呢?我们看到循环控制变量是nr_sets,往上可以找到

  1. sets=(plat!=NULL)?plat->sets:NULL;
  2. nr_sets=(plat!=NULL)?plat->nr_sets:1;
也就是说sets和nr_sets是从plat中获取的,再往上找plat
  1. structs3c2410_platform_nand*plat=to_nand_plat(pdev);
在函数的开头部分我们找到了plat的定义,看来plat是pdev中获取到的,我们跟踪进入这个to_nand_plat函数看个究竟
  1. staticstructs3c2410_platform_nand*to_nand_plat(structplatform_device*dev)
  2. {
  3. returndev->dev.platform_data;
  4. }
这个函数很简单,就是直接返回了s3c_nand_device中的dev成员的platform_data,而前面我们看到的代码中没有出现这个变量,从plat定义处指出的类型可知,这个platform_data的类型是s3c2410_platform_nand,这时,我们可以回到最开始的文件,arch/arm/mach-s3c24xx/mach-mini2440.h,可以找到mini2440_init函数中有这样一行代码


评论


技术专区

关闭