首页 技术 正文
技术 2022年11月13日
0 收藏 738 点赞 2,523 浏览 17417 个字

点击打开链接

内容来自以下博客:

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 plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  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. 用于从主机控制器向 core 层注册操作函数,从而将core层与具体的主机控制器隔离
  11. 也就是说 core 要操作主机控制器,就是这个 ops 当中给的函数指针操作,不能直接调用具体主控制器的函数
  12. struct mmc_host_ops {   —————–非常重要
  13. int (*enable)(struct mmc_host *host);   //使能和禁止HOST控制器
  14. int (*disable)(struct mmc_host *host);
  15. void    (*request)(struct mmc_host *host, struct mmc_request *req); //核心函数,用于SD卡命令的传输,比如发送和接收命令,CMD0,CMD8,ACMD41诸如此类的都是在这个函数去实现
  16. void    (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); //配置时钟、总线、电源、片选、时序等
  17. int (*get_ro)(struct mmc_host *host);   //用于检测SD卡的写保护是否打开
  18. int (*get_cd)(struct mmc_host *host);   //用于SD卡的检测,是否有卡插入和弹出
  19. void    (*enable_sdio_irq)(struct mmc_host *host, int enable);  //开启sdio中断
  20. }
  21. 控制器对卡的I/O状态
  22. struct mmc_ios {
  23. }
  24. 描述卡
  25. kernel/include/linux/mmc/card.h
  26. struct mmc_card {
  27. }
  28. 读写MMC卡的请求
  29. 包括命令,数据以及请求完成后的回调函数
  30. struct mmc_request {
  31. struct mmc_command  *sbc;       /* SET_BLOCK_COUNT for multiblock */
  32. struct mmc_command  *cmd;
  33. struct mmc_data     *data;
  34. struct mmc_command  *stop;
  35. struct completion   completion;
  36. void  (*done)(struct mmc_request *);/* completion function */
  37. struct mmc_host     *host;
  38. };
  39. MMC卡读写的数据相关信息
  40. 如:请求,操作命令,数据以及状态等
  41. struct mmc_data {
  42. }
  43. MMC卡操作相关命令及数据,状态信息等
  44. 一条指令command共48位,其中command index指代这条具体的指令名称,argument为该指令的参数
  45. struct mmc_command {
  46. u32         opcode;     //对应command index
  47. u32         arg;        // 对应argument
  48. u32         resp[4];    // 对应response
  49. }
  50. 描述mmc卡驱动
  51. struct mmc_driver {
  52. struct device_driver drv;
  53. int (*probe)(struct mmc_card *);
  54. void (*remove)(struct mmc_card *);
  55. int (*suspend)(struct mmc_card *);
  56. int (*resume)(struct mmc_card *);
  57. void (*shutdown)(struct mmc_card *);
  58. };
  59. 总线操作结构
  60. 由于mmc卡支持多种总数据线,如SPI、SDIO、8LineMMC,而不同的总线的操作控制方式不尽相同,所以通过此结构与相应的总线回调函数相关联
  61. struct mmc_bus_ops {
  62. void (*remove)(struct mmc_host *);  //拔出SD卡的回调函数
  63. void (*detect)(struct mmc_host *);  //探测SD卡是否还在SD总线上的回调函数,具体实现是发送CMD13命令,并读回响应,如果响应错误,则依次调用.remove、detach_bus来移除卡及释放总线
  64. };

四、涉及到三种总线[cpp] view plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  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. 2. mmc bus type  //在mmc_init()中被创建的.通过调用 mmc_register_bus() 来注册 MMC 总线
  11. drivers\mmc\core\bus.c
  12. static struct bus_type mmc_bus_type = {
  13. .name        = "mmc",
  14. .dev_attrs    = mmc_dev_attrs,
  15. .match        = mmc_bus_match,
  16. .uevent        = mmc_bus_uevent,
  17. .probe        = mmc_bus_probe,
  18. .remove        = mmc_bus_remove,
  19. .shutdown        = mmc_bus_shutdown,
  20. .pm        = &mmc_bus_pm_ops,
  21. };
  22. 3. sdio bus type    //在mmc_init()中被创建的.通过调用sdio_register_bus() 来注册 SDIO 总线
  23. drivers\mmc\core\sdio_bus.c
  24. static struct bus_type sdio_bus_type = {
  25. .name        = "sdio",
  26. .dev_attrs    = sdio_dev_attrs,
  27. .match        = sdio_bus_match,
  28. .uevent        = sdio_bus_uevent,
  29. .probe        = sdio_bus_probe,
  30. .remove        = sdio_bus_remove,
  31. .pm        = SDIO_PM_OPS_PTR,
  32. };

对于 platform bus 上的设备,通常初始化的流程是:
a. 在 platform bus上注册 platform device.
b. 在 platform bus上注册 platform driver.
c. 如果 platform bus 上的 device 和 driver 相互匹配, 则调用其 probe() 函数进行初始化.

对于SD host controller 设备也是一样.[cpp] view plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  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. kernel\arch\arm\boot\dts\msm9625.dtsi
  7. sdcc2: qcom,sdcc@f98a4000 {
  8. cell-index = <2>; /* SDC2 SD card slot */
  9. compatible = "qcom,msm-sdcc";
  10. }
  11. 2. 注册 platform driver:
  12. driver/mmc/host/msm_sdcc.c
  13. static int __init msmsdcc_init(void)
  14. platform_driver_register(&msmsdcc_driver);

五、总体架构及流程

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

[cpp] view plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  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. mmc/card/block.c
  9. static int __init mmc_blk_init(void)
  10. res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");//注册一个块设备
  11. res = mmc_register_driver(&mmc_driver);//注册一个mmc设备驱动
  12. static struct mmc_driver mmc_driver =
  13. .probe      = mmc_blk_probe,
  14. static int mmc_blk_probe(struct mmc_card *card)
  15. mmc_set_bus_resume_policy(card->host, 1);//*host 该指针指向一个mmc主机实例,块设备中的读写操作就是调用这个mmc主机的操作函数host->ops->request来实现对实际硬件的操作。

然后再挂载 mmc 设备驱动,执行驱动程序中的xx_mmc_probe(),检测host设备中挂载的sd设备。[cpp] view plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

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

此时probe函数会创建一个host设备,然后开启一个延时任务 mmc_rescan()[cpp] view plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  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 plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  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. 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 plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  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. 1a:
  8. int mmc_go_idle(struct mmc_host *host)
  9. struct mmc_command cmd = {0};
  10. cmd.opcode = MMC_GO_IDLE_STATE; //即CMD0
  11. cmd.arg = 0;                    //此命令无参数
  12. err = mmc_wait_for_cmd(host, &cmd, 0)
  13. int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
  14. memset(cmd->resp, 0, sizeof(cmd->resp));  //调用了 mmc_start_request,
  15. cmd->retries = retries;
  16. mrq.cmd = cmd;
  17. mmc_wait_for_req(host, &mrq);
  18. void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)   —-重要函数
  19. __mmc_start_req(host, mrq);
  20. static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
  21. mmc_start_request(host, mrq);
  22. static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
  23. host->ops->request(host, mrq);    //即 msmsdcc_request, MMC 核心与核HOST 层握手了
  24. 1b:
  25. core/mmc.c
  26. int mmc_attach_sd(struct mmc_host *host)                    //完成匹配,和初始化卡的功能
  27. err = mmc_send_app_op_cond(host, 0, &ocr);      —-1b1 //检测是否是支持SD卡
  28. host->ocr = mmc_select_voltage(host, ocr);               //设置MMC电压
  29. err = mmc_init_card(host, host->ocr, NULL);              //对mmc卡进行初始化,主要是读取mmc卡里的一些寄存器信息,且对这些寄存器的值进行设置
  30. err = mmc_sd_init_card(host, host->ocr, NULL);   —-1b2
  31. err = mmc_add_card(host->card);                  —-1b3 //调用 mmc_add_card 来把 mmc_card 挂载到 mmc_bus_type 总线去
  32. 1b1:
  33. int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
  34. cmd.opcode = SD_APP_OP_COND;    //ACMD41,获取 SDcard 的允许电压范围值,保存在 ocr 中. 所有发送它之前需要发送 CMD_55 命令。执行完后 card 状态变为 READY
  35. 1b2:
  36. static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,struct mmc_card *oldcard)
  37. err = mmc_sd_get_cid(host, ocr, cid, &rocr);        //发送 CMD2 ,获取卡的身份信息,进入到身份状态
  38. card = mmc_alloc_card(host, &sd_type);              //分配一张 SD 类型的 card 结构
  39. err = mmc_send_relative_addr(host, &card->rca);      //获取卡的相对地址,注意一前卡和主机通信都采用默认地址,现在有了自己的地址了,进入到 stand_by 状态
  40. err = mmc_sd_get_csd(host, card); —-mmc_send_csd(card, card->raw_csd);//CMD9, 获取 CSD 寄存器的信息,包括 block 长度,卡容量等信息
  41. err = mmc_select_card(card);                        //发送 CMD7, 选中目前 RADD 地址上的卡,任何时候总线上只有一张卡被选中,进入了传输状态
  42. err = mmc_sd_setup_card(host, card, oldcard != NULL);
  43. int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,bool reinit)
  44. mmc_app_send_scr(card, card->raw_scr);   //发送命令 ACMD51 获取 SRC 寄存器的内容,进入到 SENDING-DATA 状态
  45. if (host->ops->get_ro(host) > 0 )      // get_ro(host) 即是 msmsdcc_get_ro
  46. mmc_card_set_readonly(card);        //是否写保护,如果是的,将 card 状态设置为只读状态
  47. 1b3:
  48. core/bus.c
  49. int mmc_add_card(struct mmc_card *card)     //  /sys/devices/msm_sdcc.2/mmc_host/mmc0
  50. ret = device_add(&card->dev);
  51. drivers/base/core.c
  52. int device_add(struct device *dev)
  53. dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); //
  54. bus_probe_device(dev);
  55. void bus_probe_device(struct device *dev)
  56. if (bus->p->drivers_autoprobe)
  57. ret = device_attach(dev);           //这样,在总线 mmc_bus_type 中就有了 mmc 设备 mmc_card 了
  58. ***********
  59. 2:
  60. //完成kobject的注册,并调用 mmc_rescan,目的在于在系统初始化的时候就扫描SD总线查看是否存在SD卡
  61. int mmc_add_host(struct mmc_host *host)
  62. err = device_add(&host->class_dev);//将设备注册进linux设备模型,最终的结果就是在 sys/bus/platform/devices 目录下能见到 mmc 设备节点
  63. mmc_start_host(host);
  64. void mmc_start_host(struct mmc_host *host)
  65. mmc_power_off(host);                —-2a
  66. mmc_detect_change(host, 0);         —-2b
  67. 2a:
  68. void mmc_power_off(struct mmc_host *host)
  69. host->ios.power_mode = MMC_POWER_OFF;    //对 ios 进行了设置
  70. mmc_set_ios(host);
  71. void mmc_set_ios(struct mmc_host *host)
  72. host->ops->set_ios(host, ios);            // set_ios 实际上就是 mmc_host_ops 的 .set_ios  = msmsdcc_set_ios,
  73. 2b:
  74. void mmc_detect_change(struct mmc_host *host, unsigned long delay)
  75. mmc_schedule_delayed_work(&host->detect, delay); //实际上就是调用我们前面说的延时函数 mmc_rescan

关于命令和数据的发送和接收[cpp] view plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  1. struct mmc_host_ops {
  2. //用于SD卡命令的传输,比如发送和接收命令,CMD0,CMD8,ACMD41诸如此类的都是在这个函数去实现
  3. void    (*request)(struct mmc_host *host, struct mmc_request *req);
  4. }
  5. static const struct mmc_host_ops msmsdcc_ops = {
  6. .enable     = msmsdcc_enable,
  7. .disable    = msmsdcc_disable,
  8. .pre_req        = msmsdcc_pre_req,
  9. .post_req       = msmsdcc_post_req,
  10. .request    = msmsdcc_request,
  11. .set_ios    = msmsdcc_set_ios,
  12. .get_ro     = msmsdcc_get_ro,
  13. .enable_sdio_irq = msmsdcc_enable_sdio_irq,
  14. .start_signal_voltage_switch = msmsdcc_switch_io_voltage,
  15. .execute_tuning = msmsdcc_execute_tuning,
  16. .hw_reset = msmsdcc_hw_reset,
  17. .stop_request = msmsdcc_stop_request,
  18. .get_xfer_remain = msmsdcc_get_xfer_remain,
  19. .notify_load = msmsdcc_notify_load,
  20. };
  21. /*这个函数实现了命令和数据的发送和接收,
  22. 当 CORE 部分需要发送命令或者传输数据时,都会调用这个函数,并传递 mrq 请求*/
  23. static void msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq)
  24. mmc_request_done(mmc, mrq);             // 如果卡不存在,就终止请求
  25. msmsdcc_request_start(host, mrq);
  26. static void msmsdcc_request_start (struct msmsdcc_host *host, struct mmc_request *mrq)
  27. if ((mrq->data->flags & MMC_DATA_READ) ||host->curr.use_wr_data_pend)      //判断发送数据还是命令
  28. msmsdcc_start_data(host, mrq->data,mrq->sbc ? mrq->sbc : mrq->cmd,0);   //发送数据
  29. else
  30. msmsdcc_start_command(host,mrq->sbc ? mrq->sbc : mrq->cmd,0);          //发送命令
  31. static void msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,struct mmc_command *cmd, u32 c)
  32. //对某些 寄存器进行设置, 使能某些中断, 如 pio_irqmask
  33. if (is_dma_mode(host) && (datactrl & MCI_DPSM_DMAENABLE))   //采用 DMA 进行数据传输还是采用 FIFO 进行数据传输
  34. msmsdcc_start_command_deferred(host, cmd, &c);          //启动了数据传输模式
  35. else
  36. msmsdcc_start_command(host, cmd, c)
  37. static void msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)
  38. {
  39. msmsdcc_start_command_deferred(host, cmd, &c);
  40. msmsdcc_start_command_exec(host, cmd->arg, c);
  41. }
  42. static void msmsdcc_start_command_deferred(struct msmsdcc_host *host,struct mmc_command *cmd, u32 *c)
  43. cmd->opcode —-对应SD卡命令 ,如 CMD0:复位SD 卡

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

1.中断

在probe 中有三个中断函数:

[cpp] view plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  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 plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  1. //真正的 SD 卡检测中断:
  2. ret = request_threaded_irq(plat->status_irq, NULL,msmsdcc_platform_status_irq, plat->irq_flags, DRIVER_NAME " (slot)", host);
  3. //在 msmsdcc_probe 中调用 msmsdcc_populate_pdata 获取设备树信息:
  4. static struct mmc_platform_data *msmsdcc_populate_pdata(struct device *dev)
  5. msmsdcc_dt_parse_gpio_info(dev, pdata)      //获取设备树种 GPIO 信息
  6. static int msmsdcc_dt_parse_gpio_info(struct device *dev, struct mmc_platform_data *pdata)
  7. msmsdcc_dt_get_cd_wp_gpio(dev, pdata);
  8. static void msmsdcc_dt_get_cd_wp_gpio(struct device *dev, struct mmc_platform_data *pdata)
  9. pdata->status_gpio = of_get_named_gpio_flags(np,"cd-gpios", 0, &flags);  //获取中断的 gpio
  10. pdata->status_irq = platform_get_irq_byname(pdev, "status_irq"); //获取 status_irq 的中断名
  11. //设备树里关于中断的信息:
  12. apps_proc\kernel\arch\arm\boot\dts\msm9625.dtsi
  13. interrupt-names = "core_irq", "bam_irq", "status_irq";
  14. cd-gpios = <&msmgpio 66 0>;
  15. static irqreturn_t msmsdcc_platform_status_irq(int irq, void *dev_id)
  16. msmsdcc_check_status((unsigned long) host);
  17. static void msmsdcc_check_status(unsigned long data)
  18. if (host->plat->status || gpio_is_valid(host->plat->status_gpio))   //检测 GPIO 的状态
  19. mmc_detect_change(host->mmc, 0);
  20. void mmc_detect_change(struct mmc_host *host, unsigned long delay)
  21. mmc_schedule_delayed_work(&host->detect, delay);

2.轮询:[cpp] view plaincopysd 卡驱动–基于高通平台sd 卡驱动–基于高通平台

  1. Msm_sdcc.c (drivers\mmc\host)
  2. static int msmsdcc_probe(struct platform_device *pdev)
  3. mmc->caps |= MMC_CAP_NEEDS_POLL;             //设置轮询标志
  4. core/core.c
  5. void mmc_rescan(struct work_struct *work)
  6. out:
  7. if (host->caps & MMC_CAP_NEEDS_POLL)
  8. mmc_schedule_delayed_work(&host->detect, HZ);//轮询

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

七、开始上图片了

初始化:

sd 卡驱动–基于高通平台

卡识别:

sd 卡驱动–基于高通平台

数据传输:

sd 卡驱动–基于高通平台

request

sd 卡驱动–基于高通平台

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,028
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,518
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,367
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,146
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,781
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,858