sd 卡驱动--基于高通平台

点击打开链接

内容来自下面博客:

http://blog.csdn.net/qianjin0703/article/details/5918041 Linux设备驱动子系统第二弹 - SD卡 (有介绍SD卡硬件)
http://blog.csdn.net/wavemcu/article/details/7366852     linux2.6内核SD Card Driver详细解析之中的一个
http://blog.chinaunix.net/uid-147915-id-3063162.html      基于S3C2410的SD卡linux驱动工作原理
http://www.cnblogs.com/autum/archive/2012/08/16/SD.html  SD卡驱动分析(一)(系列关于 SD 卡文章)
http://blog.163.com/fenglang_2006/blog/static/133662318201011183912576/ linux sd卡驱动分析,基于mini2440。sdio mmc sd卡驱动编4


一、先来一些简单硬件知识:

MMC:MMC就是 MultiMediaCard 的缩写。即多媒体卡
SD:SD卡为Secure Digital Memory Card, 即安全数码卡,(另TF卡又称microSD)
SDIO:SDIO是在SD标准上定义了一种外设接口
MCI:MCI是Multimedia Card Interface的简称,即多媒体卡接口。

上述的MMC,SD,SDI卡定义的接口都属于MCI接口

SD卡引脚:

一根指令线CMD。4根数据线DAT0~DAT3,一个 SDCARD_DET_N 检測引脚


SD卡内部有7个寄存器
OCR,CID,CSD和SCR寄存器保存卡的配置信息
RCA寄存器保存着通信过程中卡当前临时分配的地址(仅仅适合SD模式)
卡状态(Card Status)和SD状态(SD Status)寄存器保存着卡的状态


OCR寄存器保存着SD/MMC卡的供电电同意范围
CID为一个16个字节的寄存器。该寄存器包含一个独特的卡标识号
CSD寄存器(卡特殊数据寄存器)包含訪问卡存储时须要的相关信息


SD卡命令共分为12类,分别为class0到Class11
CMD0:复位SD 卡
CMD1:读OCR寄存器
...

二、好,開始软件部分了。

Linux相关MMC的代码分布。主要有两个文件夹。一个头文件文件夹和一个源码文件夹
头文件文件夹:include/linux/mmc
源码文件夹:drivers/mmc

mmc驱动共分为三个文件夹:card/、core/、host/
card:块设备的驱动程序。这部分就是实现了将SD卡怎样实现为块设备的
core:总线驱动程序。

这是整个MMC的核心层,这部分完毕了不同协议和规范的实现,而且为HOST层的驱动提供接口函数
host:通讯接口驱动。针对不同主机的驱动程序。这一部分须要根据自己的特定平台来完毕

MMC/SD卡的驱动被分为:卡识别阶段和传输数据阶段
卡识别阶段:空暇(idle)、准备(ready)、识别(ident)、等待(stby)、不活动(ina)
传输数据阶段:发送(data)、传输(tran)、接收(rcv)、程序(prg)、断开连接(dis)

在实际驱动开发中。仅仅须要在host文件夹下实现你详细的MMC/SD设备驱动部分代码,
也就是控制器(支持对MMC/SD卡的控制,俗称MMC/SD主机控制器)和SDI控制器与MMC/SD卡的硬件接口电路


事实上SD驱动一共就做了两件事件:
1).卡的检測。

(初始化sd卡)
2).卡数据的读取:
写sd卡:POLL、中断、DMA
读sd卡:POLL、中断、DMA


能够从下面几个方面理解驱动:
1、 msm_sdcc.c代码初始化过程;
2、 SD卡块设备注冊过程;
3、 request及传输数据的实现


SD 传输模式有下面 3 种:
    SPI mode (required )
    1-bit mode
    4-bit mode


開始上代码

三、重要的结构体

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 卡控制器  
  2. kernel/include/linux/mmc/host.h  
  3. struct mmc_host {  
  4.     const struct mmc_host_ops *ops;     // SD卡主控制器的操作函数,即该控制器所具备的驱动能力  
  5.     struct mmc_ios      ios;            // 配置时钟、总线、电源、片选、时序等  
  6.     truct mmc_card      *card;          // 连接到此主控制器的SD卡设备  
  7.     const struct mmc_bus_ops *bus_ops;  // SD总线驱动的操作函数,即SD总线所具备的驱动能力  
  8.     ...  
  9.       
  10. }  
  11.   
  12. 卡控制器操作集  
  13. 用于从主机控制器向 core 层注冊操作函数。从而将core层与详细的主机控制器隔离  
  14. 也就是说 core 要操作主机控制器,就是这个 ops 其中给的函数指针操作,不能直接调用详细主控制器的函数  
  15. struct mmc_host_ops {   -----------------非常重要  
  16.       
  17.     int (*enable)(struct mmc_host *host);   //使能和禁止HOST控制器  
  18.     int (*disable)(struct mmc_host *host);  
  19.       
  20.     void    (*request)(struct mmc_host *host, struct mmc_request *req); //核心函数,用于SD卡命令的传输,比方发送和接收命令,CMD0,CMD8,ACMD41诸如此类的都是在这个函数去实现   
  21.       
  22.     void    (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); //配置时钟、总线、电源、片选、时序等   
  23.     int (*get_ro)(struct mmc_host *host);   //用于检測SD卡的写保护是否打开     
  24.     int (*get_cd)(struct mmc_host *host);   //用于SD卡的检測,是否有卡插入和弹出  
  25.     void    (*enable_sdio_irq)(struct mmc_host *host, int enable);  //开启sdio中断  
  26.     ...  
  27. }  
  28.   
  29. 控制器对卡的I/O状态  
  30. struct mmc_ios {  
  31.       
  32. }  
  33.   
  34. 描写叙述卡  
  35. kernel/include/linux/mmc/card.h  
  36. struct mmc_card {  
  37.       
  38. }  
  39.   
  40. 读写MMC卡的请求  
  41. 包含命令,数据以及请求完毕后的回调函数  
  42. struct mmc_request {  
  43.     struct mmc_command  *sbc;       /* SET_BLOCK_COUNT for multiblock */  
  44.     struct mmc_command  *cmd;  
  45.     struct mmc_data     *data;  
  46.     struct mmc_command  *stop;  
  47.   
  48.     struct completion   completion;  
  49.     void  (*done)(struct mmc_request *);/* completion function */  
  50.     struct mmc_host     *host;  
  51. };  
  52.   
  53. MMC卡读写的数据相关信息  
  54. 如:请求,操作命令,数据以及状态等  
  55. struct mmc_data {  
  56.       
  57. }  
  58.   
  59. MMC卡操作相关命令及数据。状态信息等  
  60. 一条指令command共48位,其中command index指代这条详细的指令名称。argument为该指令的參数  
  61. struct mmc_command {  
  62.     u32         opcode;     //相应command index  
  63.     u32         arg;        // 相应argument  
  64.     u32         resp[4];    // 相应response  
  65.     ...  
  66. }  
  67.   
  68. 描写叙述mmc卡驱动  
  69. struct mmc_driver {  
  70.     struct device_driver drv;  
  71.     int (*probe)(struct mmc_card *);  
  72.     void (*remove)(struct mmc_card *);  
  73.     int (*suspend)(struct mmc_card *);  
  74.     int (*resume)(struct mmc_card *);  
  75.     void (*shutdown)(struct mmc_card *);  
  76. };  
  77.   
  78. 总线操作结构  
  79. 由于mmc卡支持多种总数据线,如SPI、SDIO、8LineMMC。而不同的总线的操作控制方式不尽同样。所以通过此结构与相应的总线回调函数相关联  
  80. struct mmc_bus_ops {  
  81.     void (*remove)(struct mmc_host *);  //拔出SD卡的回调函数  
  82.     void (*detect)(struct mmc_host *);  //探測SD卡是否还在SD总线上的回调函数,详细实现是发送CMD13命令。并读回响应,假设响应错误,则依次调用.remove、detach_bus来移除卡及释放总线  
  83.     ...  
  84. };  

四、涉及到三种总线

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 1. platform bus //MMC host controller 作为一种 platform device, 它是须要注冊到 platform bus上 的  
  2. driver/base/platform.c  
  3. struct bus_type platform_bus_type = {  
  4.     .name        = "platform",  
  5.     .dev_attrs    = platform_dev_attrs,  
  6.     .match        = platform_match,  
  7.     .uevent        = platform_uevent,  
  8.     .pm        = &platform_dev_pm_ops,  
  9. };  
  10.   
  11. 2. mmc bus type  //在mmc_init()中被创建的.通过调用 mmc_register_bus() 来注冊 MMC 总线  
  12. drivers\mmc\core\bus.c  
  13. static struct bus_type mmc_bus_type = {  
  14.     .name        = "mmc",  
  15.     .dev_attrs    = mmc_dev_attrs,  
  16.     .match        = mmc_bus_match,  
  17.     .uevent        = mmc_bus_uevent,  
  18.     .probe        = mmc_bus_probe,  
  19.     .remove        = mmc_bus_remove,  
  20.     .shutdown        = mmc_bus_shutdown,  
  21.     .pm        = &mmc_bus_pm_ops,  
  22. };  
  23.   
  24. 3. sdio bus type    //在mmc_init()中被创建的.通过调用sdio_register_bus() 来注冊 SDIO 总线  
  25. drivers\mmc\core\sdio_bus.c  
  26. static struct bus_type sdio_bus_type = {  
  27.     .name        = "sdio",  
  28.     .dev_attrs    = sdio_dev_attrs,  
  29.     .match        = sdio_bus_match,  
  30.     .uevent        = sdio_bus_uevent,  
  31.     .probe        = sdio_bus_probe,  
  32.     .remove        = sdio_bus_remove,  
  33.     .pm        = SDIO_PM_OPS_PTR,  
  34. };  
对于 platform bus 上的设备,通常初始化的流程是:
a. 在 platform bus上注冊 platform device.
b. 在 platform bus上注冊 platform driver.
c. 假设 platform bus 上的 device 和 driver 相互匹配, 则调用其 probe() 函数进行初始化.

对于SD host controller 设备也是一样.

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 1. 注冊 platform device  
  2. board-9625.c (arch\arm\mach-msm)  
  3. void __init msm9625_init(void)  
  4.     board_dt_populate(msm9625_auxdata_lookup)  
  5.         of_platform_populate(of_find_node_by_path("/soc"),of_default_bus_match_table, adata, NULL);  
  6.           
  7. kernel\arch\arm\boot\dts\msm9625.dtsi  
  8.     sdcc2: qcom,sdcc@f98a4000 {  
  9.         cell-index = <2>; /* SDC2 SD card slot */  
  10.         compatible = "qcom,msm-sdcc";  
  11.         ...  
  12.     }  
  13.   
  14.   
  15. 2. 注冊 platform driver:  
  16. driver/mmc/host/msm_sdcc.c  
  17. static int __init msmsdcc_init(void)  
  18.     platform_driver_register(&msmsdcc_driver);  

五、整体架构及流程

kernel启动时。先后运行 mmc_init() 及 mmc_blk_init() 。以对mmc设备及mmc块模块进行初始化

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. mmc/core/core.c  
  2. static int __init mmc_init(void)  
  3.     workqueue = alloc_ordered_workqueue("kmmcd", 0);//建立了一个工作队列workqueue,这个工作队列的作用主要是用来支持热插拔  
  4.     ret = mmc_register_bus();//注冊一个mmc总线  
  5.     ret = mmc_register_host_class();//注冊了一个 mmc_host 类  
  6.     ret = sdio_register_bus();//注冊了一个 sdio_bus_type  
  7.       
  8. *******   
  9. mmc/card/block.c  
  10. static int __init mmc_blk_init(void)  
  11.     res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");//注冊一个块设备  
  12.     res = mmc_register_driver(&mmc_driver);//注冊一个mmc设备驱动  
  13.   
  14. static struct mmc_driver mmc_driver =  
  15.     .probe      = mmc_blk_probe,  
  16.       
  17. static int mmc_blk_probe(struct mmc_card *card)  
  18.     mmc_set_bus_resume_policy(card->host, 1);//*host 该指针指向一个mmc主机实例,块设备中的读写操作就是调用这个mmc主机的操作函数host->ops->request来实现对实际硬件的操作。

      

然后再挂载 mmc 设备驱动,运行驱动程序中的xx_mmc_probe()。检測host设备中挂载的sd设备。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. kernel\arch\arm\configs\msm9625_defconfig  
  2. CONFIG_MMC_MSM=y  
  3.   
  4. kernel\drivers\mmc\host\Makefile  
  5. obj-$(CONFIG_MMC_MSM)        += msm_sdcc.o      
  6.   
  7. msm_sdcc.c (drivers\mmc\host)  
  8. //系统初始化时扫描 platform 总线上是否有名为该SD主控制器名字"msm_sdcc"的设备,假设有, 驱动程序将主控制器挂载到 platform 总线上,并注冊该驱动程序  
  9. static int __init msmsdcc_init(void)  
  10.     platform_driver_register(&msmsdcc_driver);    //注冊 platform driver  
  11.       
  12. static struct platform_driver msmsdcc_driver = {  
  13.     .probe        = msmsdcc_probe,  
  14.     .remove        = msmsdcc_remove,  
  15.     .driver        = {  
  16.         .name    = "msm_sdcc",  
  17.         .pm    = &msmsdcc_dev_pm_ops,  
  18.         .of_match_table = msmsdcc_dt_match,  
  19.     },  
  20. };      
  21.       
  22. //整个设备驱动的 probe()函数。其本质就是是为设备建立起数据结构并对其赋初值  
  23. //msmsdcc_probe 全部赋值中。我们重点关注从 platform_device *pdev里得到的数据,即设备树里的数据  
  24. //platform_device *pdev是在系统初始化的时候扫描 platform 总线发现SD主控制器后所得到的数据  
  25. static int msmsdcc_probe(struct platform_device *pdev)      
  26. {      
  27.     //初始化设备的数据结构  
  28.     if (pdev->dev.of_node) {  
  29.     plat = msmsdcc_populate_pdata(&pdev->dev);        //获取设备树信息  
  30.     of_property_read_u32((&pdev->dev)->of_node,"cell-index", &pdev->id);  
  31.     } else {  
  32.         plat = pdev->dev.platform_data;  
  33.     }  
  34.     //为主设备控制器建立数据结构,建立kobject,并初始化等待队列。工作队列,以及一些控制器的配置  
  35.     mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev);            ---- 1  
  36.     //实现设备驱动的功能函数,如mmc->ops = &pxamci_ops;  
  37.     mmc->ops = &msmsdcc_ops;  
  38.     //申请中断函数 request_irq()  
  39.     ret = request_irq(core_irqres->start, msmsdcc_irq, IRQF_SHARED,DRIVER_NAME " (cmd)", host);  
  40.     //注冊设备,即注冊kobject,建立sys文件,发送uevent等  
  41.     mmc_add_host(mmc);                                                        ---- 2  
  42.     //其它需求,如在/proc/driver下建立用户交互文件等  
  43.     ret = device_create_file(&pdev->dev, &host->auto_cmd21_attr);  
  44. }      
此时probe函数会创建一个host设备。然后开启一个延时任务 mmc_rescan()

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 1:    
  2. core/host.c   
  3. //重要函数mmc_alloc_host , 用于分配mmc_host结构体指针的内存空间大小  
  4. struct mmc_host *mmc_alloc_host(int extra, struct device *dev)----创建一个 mmc_host 和 mmc_spi_host ,且mmc_host的最后一个成员指针private指向mmc_spi_host  
  5.     //建立数据结构  
  6.     struct mmc_host *host;    
  7.     host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);  
  8.     //建立kobject  
  9.     host->parent = dev;  
  10.     host->class_dev.parent = dev;  
  11.     host->class_dev.class = &mmc_host_class;  
  12.     device_initialize(&host->class_dev);  
  13.     //初始化等待队列,工作队列  
  14.     init_waitqueue_head(&host->wq);  
  15.     INIT_DELAYED_WORK(&host->detect, mmc_rescan);    //建立了一个工作队列任务 structdelayed_work detect。

    工作队列任务运行的函数为mmc_rescan

      
  16.     //配置控制器  
  17.     host->max_segs = 1;  
  18.     host->max_seg_size = PAGE_CACHE_SIZE;  
  19.     return host;  

驱动挂载成功后, mmc_rescan()函数被运行。然后对卡进行初始化

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. core/core.c  
  2. //mmc_rescan 函数是须要重点关注的,由于SD卡协议中的检測,以及卡识别等都是在此函数中实现  
  3. void mmc_rescan(struct work_struct *work)  
  4.     if (host->bus_ops && host->bus_ops->detect && !host->bus_dead && !(host->caps & MMC_CAP_NONREMOVABLE))    //存在热插拔卡。不包含emmc,调用探測函数  
  5.     host->bus_ops->detect(host);  
  6.     mmc_bus_put(host);    //降低引用技术,就释放  
  7.     mmc_bus_get(host);    //添加bus引用计数  
  8.     if (host->bus_ops != NULL) {  
  9.         mmc_bus_put(host);    //假设卡仍然存在。降低引用计数,不必探測了  
  10.         goto out;  
  11.     }  
  12.     if (host->ops->get_cd && host->ops->get_cd(host) == 0)  //有卡,退出  
  13.     goto out;  
  14.     mmc_claim_host(host);                   //用于检測host是否被占用,占用则退出,否则标记成占用  
  15.       
  16.     if (!mmc_rescan_try_freq(host, host->f_min))  

初始化卡按下面流程初始化(后面会附图):
a、发送CMD0使卡进入IDLE状态
b、发送CMD8,检查卡是否 SD2.0。

SD1.1是不支持CMD8的。因此在SD2.0 Spec中提出了先发送CMD8,如响应为无效命令,则卡为SD1.1,否则就是SD2.0(请參考SD2.0 Spec)。
c、发送CMD5读取OCR寄存器。
d、发送ACMD55、CMD41,使卡进入工作状态。MMC卡并不支持ACMD55、CMD41,假设这步通过了。则证明这张卡是SD卡。
e、假设d步骤错误,则发送CMD1推断卡是否为MMC。SD卡不支持CMD1,而MMC卡支持,这就是SD和MMC类型的推断根据。
f、假设ACMD41和CMD1都不能通过,那这张卡恐怕就是无效卡了,初始化失败。


[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)  
  2.     host->f_init = freq;                 //设置某一个时钟频率  
  3.     mmc_power_up(host);                     //与 mmc_power_off 相似,只是设置了启动时须要的 ios  
  4.     mmc_go_idle(host);          ----1a      //CMD0 ,SD卡从 inactive 到 idle          
  5.     mmc_send_if_cond(host, host->ocr_avail);//检測SD卡是否支持SD2.0       
  6.     if (!mmc_attach_sd(host))   ----1b      //然后对mmc或者sd发送一些命令进行探測,这里以 sd 为例  
  7.   
  8. 1a:  
  9. int mmc_go_idle(struct mmc_host *host)    
  10.     struct mmc_command cmd = {0};  
  11.     cmd.opcode = MMC_GO_IDLE_STATE; //即CMD0  
  12.     cmd.arg = 0;                    //此命令无參数  
  13.     err = mmc_wait_for_cmd(host, &cmd, 0)  
  14.       
  15. int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)  
  16.     memset(cmd->resp, 0, sizeof(cmd->resp));  //调用了 mmc_start_request,   
  17.     cmd->retries = retries;  
  18.     mrq.cmd = cmd;                                
  19.     mmc_wait_for_req(host, &mrq);  
  20.       
  21. void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)   ----重要函数  
  22.     __mmc_start_req(host, mrq);  
  23.   
  24. static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)  
  25.     mmc_start_request(host, mrq);  
  26.           
  27. static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)  
  28.     host->ops->request(host, mrq);    //即 msmsdcc_request, MMC 核心与核HOST 层握手了  
  29.   
  30.       
  31. 1b:   
  32. core/mmc.c  
  33. int mmc_attach_sd(struct mmc_host *host)                    //完毕匹配。和初始化卡的功能  
  34.     err = mmc_send_app_op_cond(host, 0, &ocr);      ----1b1 //检測是否是支持SD卡  
  35.     host->ocr = mmc_select_voltage(host, ocr);               //设置MMC电压  
  36.     err = mmc_init_card(host, host->ocr, NULL);              //对mmc卡进行初始化,主要是读取mmc卡里的一些寄存器信息,且对这些寄存器的值进行设置  
  37.     err = mmc_sd_init_card(host, host->ocr, NULL);   ----1b2  
  38.     err = mmc_add_card(host->card);                  ----1b3 //调用 mmc_add_card 来把 mmc_card 挂载到 mmc_bus_type 总线去  
  39.       
  40.       
  41. 1b1:  
  42. int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)  
  43.     cmd.opcode = SD_APP_OP_COND;    //ACMD41,获取 SDcard 的同意电压范围值,保存在 ocr 中. 全部发送它之前须要发送 CMD_55 命令。运行完后 card 状态变为 READY  
  44.   
  45.       
  46.       
  47. 1b2:  
  48. static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,struct mmc_card *oldcard)  
  49.     err = mmc_sd_get_cid(host, ocr, cid, &rocr);        //发送 CMD2 。获取卡的身份信息,进入到身份状态  
  50.     card = mmc_alloc_card(host, &sd_type);              //分配一张 SD 类型的 card 结构  
  51.     err = mmc_send_relative_addr(host, &card->rca);      //获取卡的相对地址。注意一前卡和主机通信都採用默认地址,如今有了自己的地址了,进入到 stand_by 状态  
  52.     err = mmc_sd_get_csd(host, card); ----mmc_send_csd(card, card->raw_csd);//CMD9, 获取 CSD 寄存器的信息。包含 block 长度,卡容量等信息  
  53.     err = mmc_select_card(card);                        //发送 CMD7, 选中眼下 RADD 地址上的卡,不论什么时候总线上仅仅有一张卡被选中。进入了传输状态   
  54.     err = mmc_sd_setup_card(host, card, oldcard != NULL);     
  55.   
  56. int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,bool reinit)  
  57.     mmc_app_send_scr(card, card->raw_scr);   //发送命令 ACMD51 获取 SRC 寄存器的内容,进入到 SENDING-DATA 状态  
  58.     if (host->ops->get_ro(host) > 0 )      // get_ro(host) 即是 msmsdcc_get_ro   
  59.         mmc_card_set_readonly(card);        //是否写保护,假设是的,将 card 状态设置为仅仅读状态  
  60.       
  61. 1b3:  
  62. core/bus.c  
  63. int mmc_add_card(struct mmc_card *card)     //  /sys/devices/msm_sdcc.2/mmc_host/mmc0  
  64.     ret = device_add(&card->dev);  
  65.   
  66. drivers/base/core.c  
  67. int device_add(struct device *dev)  
  68.     dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); //  
  69.     bus_probe_device(dev);  
  70.   
  71. void bus_probe_device(struct device *dev)  
  72.         if (bus->p->drivers_autoprobe)   
  73.         ret = device_attach(dev);           //这样,在总线 mmc_bus_type 中就有了 mmc 设备 mmc_card 了      
  74.       
  75.       
  76. ***********   
  77. 2:  
  78. //完毕kobject的注冊,并调用 mmc_rescan,目的在于在系统初始化的时候就扫描SD总线查看是否存在SD卡  
  79. int mmc_add_host(struct mmc_host *host)  
  80.     err = device_add(&host->class_dev);//将设备注冊进linux设备模型。终于的结果就是在 sys/bus/platform/devices 文件夹下能见到 mmc 设备节点  
  81.     mmc_start_host(host);  
  82.       
  83.       
  84. void mmc_start_host(struct mmc_host *host)    
  85.     mmc_power_off(host);                ----2a  
  86.     mmc_detect_change(host, 0);         ----2b  
  87.   
  88. 2a:  
  89. void mmc_power_off(struct mmc_host *host)     
  90.     host->ios.power_mode = MMC_POWER_OFF;    //对 ios 进行了设置  
  91.     ...  
  92.     mmc_set_ios(host);  
  93.   
  94. void mmc_set_ios(struct mmc_host *host)  
  95.     host->ops->set_ios(host, ios);            // set_ios 实际上就是 mmc_host_ops 的 .set_ios  = msmsdcc_set_ios,  
  96.   
  97. 2b:  
  98. void mmc_detect_change(struct mmc_host *host, unsigned long delay)  
  99.         mmc_schedule_delayed_work(&host->detect, delay); //实际上就是调用我们前面说的延时函数 mmc_rescan  

关于命令和数据的发送和接收

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. struct mmc_host_ops {         
  2.     //用于SD卡命令的传输,比方发送和接收命令,CMD0,CMD8,ACMD41诸如此类的都是在这个函数去实现  
  3.     void    (*request)(struct mmc_host *host, struct mmc_request *req);  
  4.   
  5. }  
  6.   
  7. static const struct mmc_host_ops msmsdcc_ops = {  
  8.     .enable     = msmsdcc_enable,  
  9.     .disable    = msmsdcc_disable,  
  10.     .pre_req        = msmsdcc_pre_req,  
  11.     .post_req       = msmsdcc_post_req,  
  12.     .request    = msmsdcc_request,  
  13.     .set_ios    = msmsdcc_set_ios,  
  14.     .get_ro     = msmsdcc_get_ro,  
  15.     .enable_sdio_irq = msmsdcc_enable_sdio_irq,  
  16.     .start_signal_voltage_switch = msmsdcc_switch_io_voltage,  
  17.     .execute_tuning = msmsdcc_execute_tuning,  
  18.     .hw_reset = msmsdcc_hw_reset,  
  19.     .stop_request = msmsdcc_stop_request,  
  20.     .get_xfer_remain = msmsdcc_get_xfer_remain,  
  21.     .notify_load = msmsdcc_notify_load,  
  22. };  
  23.   
  24. /*这个函数实现了命令和数据的发送和接收, 
  25. 当 CORE 部分须要发送命令或者传输数据时,都会调用这个函数。并传递 mrq 请求*/  
  26. static void msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq)  
  27.     mmc_request_done(mmc, mrq);             // 假设卡不存在,就终止请求  
  28.     msmsdcc_request_start(host, mrq);         
  29.   
  30. static void msmsdcc_request_start (struct msmsdcc_host *host, struct mmc_request *mrq)  
  31.     if ((mrq->data->flags & MMC_DATA_READ) ||host->curr.use_wr_data_pend)      //推断发送数据还是命令  
  32.         msmsdcc_start_data(host, mrq->data,mrq->sbc ?

     mrq->sbc : mrq->cmd,0);   //发送数据  

  33.     else  
  34.         msmsdcc_start_command(host,mrq->sbc ? mrq->sbc : mrq->cmd,0);          //发送命令  
  35.   
  36.   
  37. static void msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,struct mmc_command *cmd, u32 c)  
  38.     //对某些 寄存器进行设置, 使能某些中断, 如 pio_irqmask  
  39.     ...  
  40.     if (is_dma_mode(host) && (datactrl & MCI_DPSM_DMAENABLE))   //採用 DMA 进行传输数据还是採用 FIFO 进行传输数据  
  41.         msmsdcc_start_command_deferred(host, cmd, &c);          //启动了传输数据模式  
  42.     else      
  43.         msmsdcc_start_command(host, cmd, c)  
  44.   
  45. static void msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)  
  46. {  
  47.     msmsdcc_start_command_deferred(host, cmd, &c);  
  48.     msmsdcc_start_command_exec(host, cmd->arg, c);  
  49. }  
  50.   
  51. static void msmsdcc_start_command_deferred(struct msmsdcc_host *host,struct mmc_command *cmd, u32 *c)  
  52.     cmd->opcode ----相应SD卡命令 ,如 CMD0:复位SD 卡  

六、SD卡热插拔检測的两种方法

1.中断

在probe 中有三个中断函数:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ret = request_irq(core_irqres->start, msmsdcc_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);    //命令中断  
  2. ret = request_irq(core_irqres->start, msmsdcc_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host);//IO中断            
  3. ret = request_irq(plat->sdiowakeup_irq,msmsdcc_platform_sdiowakeup_irq,IRQF_SHARED | IRQF_TRIGGER_LOW, DRIVER_NAME "sdiowakeup", host);   
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //真正的 SD 卡检測中断:   
  2.     ret = request_threaded_irq(plat->status_irq, NULL,msmsdcc_platform_status_irq, plat->irq_flags, DRIVER_NAME " (slot)", host);  
  3.   
  4. //在 msmsdcc_probe 中调用 msmsdcc_populate_pdata 获取设备树信息:  
  5. static struct mmc_platform_data *msmsdcc_populate_pdata(struct device *dev)  
  6.     msmsdcc_dt_parse_gpio_info(dev, pdata)      //获取设备树种 GPIO 信息  
  7.   
  8. static int msmsdcc_dt_parse_gpio_info(struct device *dev, struct mmc_platform_data *pdata)  
  9.     msmsdcc_dt_get_cd_wp_gpio(dev, pdata);  
  10.   
  11. static void msmsdcc_dt_get_cd_wp_gpio(struct device *dev, struct mmc_platform_data *pdata)  
  12.     pdata->status_gpio = of_get_named_gpio_flags(np,"cd-gpios", 0, &flags);  //获取中断的 gpio  
  13.     pdata->status_irq = platform_get_irq_byname(pdev, "status_irq"); //获取 status_irq 的中断名  
  14.   
  15. //设备树里关于中断的信息:  
  16. apps_proc\kernel\arch\arm\boot\dts\msm9625.dtsi  
  17.     interrupt-names = "core_irq""bam_irq""status_irq";  
  18.     cd-gpios = <&msmgpio 66 0>;  
  19.       
  20. static irqreturn_t msmsdcc_platform_status_irq(int irq, void *dev_id)  
  21.     msmsdcc_check_status((unsigned long) host);  
  22.   
  23. static void msmsdcc_check_status(unsigned long data)  
  24.     if (host->plat->status || gpio_is_valid(host->plat->status_gpio))   //检測 GPIO 的状态  
  25.     mmc_detect_change(host->mmc, 0);  
  26.   
  27. void mmc_detect_change(struct mmc_host *host, unsigned long delay)  
  28.     mmc_schedule_delayed_work(&host->detect, delay);  
2.轮询:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Msm_sdcc.c (drivers\mmc\host)  
  2. static int msmsdcc_probe(struct platform_device *pdev)  
  3.     mmc->caps |= MMC_CAP_NEEDS_POLL;             //设置轮询标志  
  4.       
  5. core/core.c  
  6. void mmc_rescan(struct work_struct *work)  
  7.     out:  
  8.         if (host->caps & MMC_CAP_NEEDS_POLL)           
  9.         mmc_schedule_delayed_work(&host->detect, HZ);//轮询  

以上仅是SD卡驱动部分信息。其它关于块设备。总线知识。linux驱动中已经非常成熟了,临时也就没有了解。

 七、開始上图片了

初始化:


卡识别:

传输数据:


request


转载于:https://www.cnblogs.com/jzdwajue/p/6970973.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值