新聞中心

        EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > Linux內(nèi)核的Nand驅(qū)動流程分析

        Linux內(nèi)核的Nand驅(qū)動流程分析

        作者: 時間:2016-11-28 來源:網(wǎng)絡(luò) 收藏
        1. s3c_nand_set_platdata(&mini2440_nand_info);
        這就是上面platform_data的來源,找到mini2440_nand_info的定義也就找到了上面用到的platform_data
        1. /*NANDFlashonMINI2440board*/
        2. staticstructmtd_partitionmini2440_default_nand_part[]__initdata={
        3. [0]={
        4. .name="u-boot",
        5. .size=SZ_256K,
        6. .offset=0,
        7. },
        8. [1]={
        9. .name="u-boot-env",
        10. .size=SZ_128K,
        11. .offset=SZ_256K,
        12. },
        13. [2]={
        14. .name="kernel",
        15. /*5megabytes,forakernelwithnomodules
        16. *orauImagewitharamdiskattached*/
        17. .size=0x00500000,
        18. .offset=SZ_256K+SZ_128K,
        19. },
        20. [3]={
        21. .name="root",
        22. .offset=SZ_256K+SZ_128K+0x00500000,
        23. .size=MTDPART_SIZ_FULL,
        24. },
        25. };
        26. staticstructs3c2410_nand_setmini2440_nand_sets[]__initdata={
        27. [0]={
        28. .name="nand",
        29. .nr_chips=1,
        30. .nr_partitions=ARRAY_SIZE(mini2440_default_nand_part),
        31. .partitions=mini2440_default_nand_part,
        32. .flash_bbt=1,/*weuseu-boottocreateaBBT*/
        33. },
        34. };
        35. staticstructs3c2410_platform_nandmini2440_nand_info__initdata={
        36. .tacls=0,
        37. .twrph0=25,
        38. .twrph1=15,
        39. .nr_sets=ARRAY_SIZE(mini2440_nand_sets),
        40. .sets=mini2440_nand_sets,
        41. .ignore_unset_ecc=1,
        42. };
        在該文件中,我們找到了三個相互嵌套的結(jié)構(gòu),最下面一個就是最終賦值給platform_data的變量mini2440_nand_info,mini2440_nand_info除了指定了Nand的時序外還有兩個成員,明顯是匹配出現(xiàn)的,就是nr_sets和sets,分別指定了sets數(shù)組指針和sets數(shù)組長度,而上面的結(jié)構(gòu)體就是sets的定以,從結(jié)構(gòu)體名字可知,這時nand_flash芯片,也就是內(nèi)核可以同時支持多個Nand,mini2440開發(fā)板中只有一塊Nand,所以這個數(shù)組只有一個元素,mini2440_nand_sets指定了芯片的名稱,chip數(shù)目,另外還有兩個變量,也是成對出現(xiàn)的,就是nr_partitions和partitions,這兩個就是上面的mtd_partition結(jié)構(gòu),也就是Nand分區(qū)表,這樣就清楚了plartform_data中的數(shù)據(jù),然后我們繼續(xù)閱讀s3c24xx_nand_probe函數(shù)(drivers/mtd/nand/s3c2410.c中),還是循環(huán)處,我們追蹤進(jìn)入s3c2410_nand_init_chip,瀏覽代碼可以知道,這個函數(shù)實際上完成了下面幾件事情

        本文引用地址:http://www.104case.com/article/201611/322799.htm

        (1)初始化了chip中的各種操作函數(shù)指針并賦值給了nmtd->mtd.priv。

        (2)初始化了info的sel_*成員,顯然是Nand片選所用

        (3)初始化了nmtd的幾個成員

        nmtd,info,set是該函數(shù)的三個參數(shù),理解了這幾個參數(shù)也就理解了這個函數(shù)的作用。info顯然就是s3c24xx_nand_init中的s3c2410_nand_info,nmtd是info->mtds,而info->mtds是kzmalloc開辟的大小為size的內(nèi)核空間,kzmalloc是kernel zero malloc,也就是開辟了size大小的空間清全部設(shè)置為0,也就是nmtds就是空的mtd數(shù)組,sets來就前面我定義的mini2440_nand_sets,這樣三個參數(shù)都知道什么意思了,再去看代碼就很簡單了。(剛才去打了半小時電話,思路有點亂,不過大體上看了下,這個函數(shù)里面沒有復(fù)雜的操作,相信大家很容易看懂)。

        執(zhí)行完s3c2410_nand_init之后就執(zhí)行了nand_scan_ident,這是內(nèi)核函數(shù)我就不做分析了,大家自己跟一下就可以知道,這個函數(shù)完成了nand_chip其他未指定函數(shù)指針的初始化,并獲取了Nand的ID信息等,接下來又s3c2410_nand_update_chip,nand_scan_tail,s3c2410_nand_add_partitions,其中nand_scan_tail是通用的內(nèi)核函數(shù),而s3c2410_nand_update_chip是ecc相關(guān)的操作,我們只分析s3c2410_nand_add_partitions,從名字上講,s3c2410開頭的函數(shù)肯定不是內(nèi)核通用函數(shù),也就是說,這實際上是我們需要自行完成的函數(shù),當(dāng)然,也是可以借鑒的函數(shù),追蹤進(jìn)入s3c2410_nand_add_partitions,看看內(nèi)核是如何知道分區(qū)信息的。

        1. staticints3c2410_nand_add_partition(structs3c2410_nand_info*info,
        2. structs3c2410_nand_mtd*mtd,
        3. structs3c2410_nand_set*set)
        4. {
        5. if(set)
        6. mtd->mtd.name=set->name;
        7. returnmtd_device_parse_register(&mtd->mtd,NULL,NULL,
        8. set->partitions,set->nr_partitions);
        9. }

        這個函數(shù)也很簡單,僅設(shè)置了下mtd的nand然后就調(diào)用和mtd_core.c中的mtd_device_parse_register函數(shù),從參數(shù)可以知道,該函數(shù)向內(nèi)核注冊了Nand分區(qū)信息。這樣我們就基本上看完了Linux內(nèi)核Nand驅(qū)動部分的結(jié)構(gòu)。

        在結(jié)尾之前我還要提到一個問題,就是內(nèi)核驅(qū)動的匹配問題,在platform_device定義時內(nèi)核指定的名稱是s3c2410-nand

        1. structplatform_devices3c_device_nand={
        2. .name="s3c2410-nand",
        3. .id=-1,
        4. .num_resources=ARRAY_SIZE(s3c_nand_resource),
        5. .resource=s3c_nand_resource,
        6. };
        但是我們的開發(fā)版是s3c2440的核,兩者的Nand控制器是不相同的,內(nèi)核又怎么正確加載到s3c2440-nand的呢?答案在arch/arm/mach-s3c24xx/s3c2440.c中
        1. void__inits3c244x_map_io(void)
        2. {
        3. /*registerourio-tables*/
        4. iotable_init(s3c244x_iodesc,ARRAY_SIZE(s3c244x_iodesc));
        5. /*renameanyperipheralsuseddifferingfromthes3c2410*/
        6. s3c_device_sdi.name="s3c2440-sdi";
        7. s3c_device_i2c0.name="s3c2440-i2c";
        8. s3c_nand_setname("s3c2440-nand");
        9. s3c_device_ts.name="s3c2440-ts";
        10. s3c_device_usbgadget.name="s3c2440-usbgadget";
        11. }
        從這里就可以看到答案了,原來s3c_nand_setname("s3c2440-nand")將platform_device的name更改了,具體的調(diào)用關(guān)系是這樣的,mini2440_map_io()----->s3c24xx_init_io()---->s3c_init_cpu()---->cpu->map_io()----->s3c2440_map_io()---->s3c_device_nand.name="s3c2440-nand",這樣就不難理解了,至少內(nèi)核加載驅(qū)動時是要查找與s3c2440-nand重名的驅(qū)動,但是我們的s3c24xx_nand_driver中指定的驅(qū)動名稱為s3c24xx-nand,難道內(nèi)核這么智能,能將s3c24xx-nand自動匹配到s3c2440-nand?當(dāng)然這不可能,自己查看s3c24xx_nand_driver的各個變量就可以知道答案了,答案在drivers/mtd/nand/s3c2410.c中,原來s3c24xx_nand_driver有個成員id_table,其具體定義為
        1. staticstructplatform_device_ids3c24xx_driver_ids[]={
        2. {
        3. .name="s3c2410-nand",
        4. .driver_data=TYPE_S3C2410,
        5. },{
        6. .name="s3c2440-nand",
        7. .driver_data=TYPE_S3C2440,
        8. },{
        9. .name="s3c2412-nand",
        10. .driver_data=TYPE_S3C2412,
        11. },{
        12. .name="s3c6400-nand",
        13. .driver_data=TYPE_S3C2412,/*compatiblewith2412*/
        14. },
        15. {}
        16. };
        這就可以猜到了,原來內(nèi)核設(shè)備匹配驅(qū)動時并不是或者說不僅僅是匹配s3c24xx_nand_driver.driver.name與設(shè)備的name,也會跟s3c24xx_nand_driver中id_table的各個元素的name進(jìn)行匹配,只要匹配了其中任何一個則認(rèn)為驅(qū)動匹配成功,繼而執(zhí)行驅(qū)動的probe函數(shù)對設(shè)備進(jìn)行初始化。如果還不確定這里的關(guān)系可以追蹤一下s3c24xx_nand_init中的注冊函數(shù),原來platform_driver_register調(diào)用driver_register之前對s3c24xx_nand_driver.driver.bus指針進(jìn)行的初始化
        1. structbus_typeplatform_bus_type={
        2. .name="platform",
        3. .dev_attrs=platform_dev_attrs,
        4. .match=platform_match,
        5. .uevent=platform_uevent,
        6. .pm=&platform_dev_pm_ops,
        7. };
        8. intplatform_driver_register(structplatform_driver*drv)
        9. {
        10. drv->driver.bus=&platform_bus_type;
        11. if(drv->probe)
        12. drv->driver.probe=platform_drv_probe;
        13. if(drv->remove)
        14. drv->driver.remove=platform_drv_remove;
        15. if(drv->shutdown)
        16. drv->driver.shutdown=platform_drv_shutdown;
        17. returndriver_register(&drv->driver);
        18. }
        從platform_bus_type的成員可知,match函數(shù)就是進(jìn)行驅(qū)動匹配的,我們來看一下這個函數(shù)的定義,追蹤platfrom_match
        1. staticintplatform_match(structdevice*dev,structdevice_driver*drv)
        2. {
        3. structplatform_device*pdev=to_platform_device(dev);
        4. structplatform_driver*pdrv=to_platform_driver(drv);
        5. /*AttemptanOFstylematchfirst*/
        6. if(of_driver_match_device(dev,drv))
        7. return1;
        8. /*Thentrytomatchagainsttheidtable*/
        9. if(pdrv->id_table)
        10. returnplatform_match_id(pdrv->id_table,pdev)!=NULL;
        11. /*fall-backtodrivernamematch*/
        12. return(strcmp(pdev->name,drv->name)==0);
        13. }
        顯然,第二個if就是判斷的id_table,下面的這個函數(shù)肯定是循環(huán)比較的id_table中每個元素的.name跟device.name
        1. staticconststructplatform_device_id*platform_match_id(
        2. conststructplatform_device_id*id,
        3. structplatform_device*pdev)
        4. {
        5. while(id->name[0]){
        6. if(strcmp(pdev->name,id->name)==0){
        7. pdev->id_entry=id;
        8. returnid;
        9. }
        10. id++;
        11. }
        12. returnNULL;
        13. }
        果然,這里的確是比較的設(shè)備名稱跟id_table中的名稱,我們的設(shè)備名稱是s3c2440-nand,我們的id_table中定義了與之對應(yīng)的元素,所以驅(qū)動匹配成功繼而運行了改驅(qū)動的probe函數(shù)。好了,到這里我遇到的絕大多數(shù)問題已經(jīng)解決了,最后,我們需要總結(jié)一下添加Nand驅(qū)動的步驟:
        1. (1)定義resource,保證可以以物理地址方式正確訪問Nand寄存器。(默認(rèn)有)
        2. (2)定義platform_device,這是內(nèi)核記錄的硬件信息,要注冊到內(nèi)核設(shè)備列表。
        3. (3)定義mtd_partition,設(shè)置Nand分區(qū)。
        4. (4)定義s3c2410_nand_set,枚舉所有Nand芯片信息。
        5. (5)定義s3c2410_platform_nand,這是驅(qū)動程序初始化Nand是需要的數(shù)據(jù),包括分區(qū)信息的和芯片時序等必要信息。
        6. (6)將s3c2410_platform_nand賦值給mtd_device->dev.platform_data。
        7. (7)將Nand設(shè)備結(jié)構(gòu)注冊到設(shè)備列表。
        這樣,就可以實現(xiàn)Nand驅(qū)動的安裝,到這里,我們就理解了Nand驅(qū)動結(jié)構(gòu),也就知道了移植教程中的各個步驟,為什么設(shè)置分區(qū)表,為什么要定義s3c2410開頭的幾個結(jié)構(gòu)。其實這種移植是基于s3c2410的移植,從軟件結(jié)構(gòu)的角度來講這種移植破壞了Linux內(nèi)核本身的結(jié)構(gòu),跟Linux內(nèi)核本身的結(jié)構(gòu)有所背離,但是這里我們做的是Nand驅(qū)動原理的分析,就不討論這個問題了,如果以后有機(jī)會會將我認(rèn)為合理的代碼貼出來請大家指點。

        上一頁 1 2 3 下一頁

        評論


        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 积石山| 石首市| 中山市| 永平县| 乡宁县| 奉化市| 攀枝花市| 长岛县| 来凤县| 朝阳区| 南乐县| 仙桃市| 江口县| 达日县| 温宿县| 夹江县| 亚东县| 浪卡子县| 若尔盖县| 盈江县| 沁阳市| 明水县| 奈曼旗| 石泉县| 罗源县| 泽库县| 永兴县| 尼勒克县| 高安市| 邳州市| 宜兰市| 全南县| 盐池县| 永嘉县| 雷州市| 兴海县| 容城县| 大连市| 乐平市| 横峰县| 湘乡市|