点击打开链接
内容来自以下博客:
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 plaincopy
卡控制器 kernel/include/linux/mmc/host.h struct mmc_host { const struct mmc_host_ops *ops; struct mmc_ios ios; truct mmc_card *card; const struct mmc_bus_ops *bus_ops; ... } 卡控制器操作集 用于从主机控制器向 core 层注册操作函数,从而将core层与具体的主机控制器隔离 也就是说 core 要操作主机控制器,就是这个 ops 当中给的函数指针操作,不能直接调用具体主控制器的函数 struct mmc_host_ops { -----------------非常重要 int (*enable)(struct mmc_host *host); int (*disable)(struct mmc_host *host); void (*request)(struct mmc_host *host, struct mmc_request *req); void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); int (*get_ro)(struct mmc_host *host); int (*get_cd)(struct mmc_host *host); void (*enable_sdio_irq)(struct mmc_host *host, int enable); ... } 控制器对卡的I/O状态 struct mmc_ios { } 描述卡 kernel/include/linux/mmc/card.h struct mmc_card { } 读写MMC卡的请求 包括命令,数据以及请求完成后的回调函数 struct mmc_request { struct mmc_command *sbc; struct mmc_command *cmd; struct mmc_data *data; struct mmc_command *stop; struct completion completion; void (*done)(struct mmc_request *); struct mmc_host *host; }; MMC卡读写的数据相关信息 如:请求,操作命令,数据以及状态等 struct mmc_data { } MMC卡操作相关命令及数据,状态信息等 一条指令command共48位,其中command index指代这条具体的指令名称,argument为该指令的参数 struct mmc_command { u32 opcode; u32 arg; u32 resp[4]; ... } 描述mmc卡驱动 struct mmc_driver { struct device_driver drv; int (*probe)(struct mmc_card *); void (*remove)(struct mmc_card *); int (*suspend)(struct mmc_card *); int (*resume)(struct mmc_card *); void (*shutdown)(struct mmc_card *); }; 总线操作结构 由于mmc卡支持多种总数据线,如SPI、SDIO、8LineMMC,而不同的总线的操作控制方式不尽相同,所以通过此结构与相应的总线回调函数相关联 struct mmc_bus_ops { void (*remove)(struct mmc_host *); void (*detect)(struct mmc_host *); ... }; 四、涉及到三种总线
[cpp] view plaincopy
1. platform bus driver/base/platform.c struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, }; 2. mmc bus type drivers\mmc\core\bus.c static struct bus_type mmc_bus_type = { .name = "mmc", .dev_attrs = mmc_dev_attrs, .match = mmc_bus_match, .uevent = mmc_bus_uevent, .probe = mmc_bus_probe, .remove = mmc_bus_remove, .shutdown = mmc_bus_shutdown, .pm = &mmc_bus_pm_ops, }; 3. sdio bus type drivers\mmc\core\sdio_bus.c static struct bus_type sdio_bus_type = { .name = "sdio", .dev_attrs = sdio_dev_attrs, .match = sdio_bus_match, .uevent = sdio_bus_uevent, .probe = sdio_bus_probe, .remove = sdio_bus_remove, .pm = SDIO_PM_OPS_PTR, }; 对于 platform bus 上的设备,通常初始化的流程是:
a. 在 platform bus上注册 platform device.
b. 在 platform bus上注册 platform driver.
c. 如果 platform bus 上的 device 和 driver 相互匹配, 则调用其 probe() 函数进行初始化.
对于SD host controller 设备也是一样.
[cpp] view plaincopy
1. 注册 platform device board-9625.c (arch\arm\mach-msm) void __init msm9625_init(void) board_dt_populate(msm9625_auxdata_lookup) of_platform_populate(of_find_node_by_path("/soc"),of_default_bus_match_table, adata, NULL); kernel\arch\arm\boot\dts\msm9625.dtsi sdcc2: qcom,sdcc@f98a4000 { cell-index = <2>; compatible = "qcom,msm-sdcc"; ... } 2. 注册 platform driver: driver/mmc/host/msm_sdcc.c static int __init msmsdcc_init(void) platform_driver_register(&msmsdcc_driver); 五、总体架构及流程
kernel启动时,先后执行 mmc_init() 及 mmc_blk_init() ,以对mmc设备及mmc块模块进行初始化
[cpp] view plaincopy
mmc/core/core.c static int __init mmc_init(void) workqueue = alloc_ordered_workqueue("kmmcd", 0); ret = mmc_register_bus(); ret = mmc_register_host_class(); ret = sdio_register_bus(); ******* mmc/card/block.c static int __init mmc_blk_init(void) res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); res = mmc_register_driver(&mmc_driver); static struct mmc_driver mmc_driver = .probe = mmc_blk_probe, static int mmc_blk_probe(struct mmc_card *card) mmc_set_bus_resume_policy(card->host, 1); 然后再挂载 mmc 设备驱动,执行驱动程序中的xx_mmc_probe(),检测host设备中挂载的sd设备。
[cpp] view plaincopy
kernel\arch\arm\configs\msm9625_defconfig CONFIG_MMC_MSM=y kernel\drivers\mmc\host\Makefile obj-$(CONFIG_MMC_MSM) += msm_sdcc.o msm_sdcc.c (drivers\mmc\host) static int __init msmsdcc_init(void) platform_driver_register(&msmsdcc_driver); static struct platform_driver msmsdcc_driver = { .probe = msmsdcc_probe, .remove = msmsdcc_remove, .driver = { .name = "msm_sdcc", .pm = &msmsdcc_dev_pm_ops, .of_match_table = msmsdcc_dt_match, }, }; static int msmsdcc_probe(struct platform_device *pdev) { if (pdev->dev.of_node) { plat = msmsdcc_populate_pdata(&pdev->dev); of_property_read_u32((&pdev->dev)->of_node,"cell-index", &pdev->id); } else { plat = pdev->dev.platform_data; } mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev); ---- 1 mmc->ops = &msmsdcc_ops; ret = request_irq(core_irqres->start, msmsdcc_irq, IRQF_SHARED,DRIVER_NAME " (cmd)", host); mmc_add_host(mmc); ---- 2 ret = device_create_file(&pdev->dev, &host->auto_cmd21_attr); } 此时probe函数会创建一个host设备,然后开启一个延时任务 mmc_rescan()
[cpp] view plaincopy
1: core/host.c struct mmc_host *mmc_alloc_host(int extra, struct device *dev)----创建一个 mmc_host 和 mmc_spi_host ,且mmc_host的最后一个成员指针private指向mmc_spi_host struct mmc_host *host; host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); host->parent = dev; host->class_dev.parent = dev; host->class_dev.class = &mmc_host_class; device_initialize(&host->class_dev); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); host->max_segs = 1; host->max_seg_size = PAGE_CACHE_SIZE; return host; 驱动挂载成功后, mmc_rescan()函数被执行,然后对卡进行初始化
[cpp] view plaincopy
core/core.c void mmc_rescan(struct work_struct *work) if (host->bus_ops && host->bus_ops->detect && !host->bus_dead && !(host->caps & MMC_CAP_NONREMOVABLE)) host->bus_ops->detect(host); mmc_bus_put(host); mmc_bus_get(host); if (host->bus_ops != NULL) { mmc_bus_put(host); goto out; } if (host->ops->get_cd && host->ops->get_cd(host) == 0) goto out; mmc_claim_host(host); 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 plaincopy
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) host->f_init = freq; mmc_power_up(host); mmc_go_idle(host); ----1a mmc_send_if_cond(host, host->ocr_avail); if (!mmc_attach_sd(host)) ----1b 1a: int mmc_go_idle(struct mmc_host *host) struct mmc_command cmd = {0}; cmd.opcode = MMC_GO_IDLE_STATE; cmd.arg = 0; err = mmc_wait_for_cmd(host, &cmd, 0) int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries) memset(cmd->resp, 0, sizeof(cmd->resp)); cmd->retries = retries; mrq.cmd = cmd; mmc_wait_for_req(host, &mrq); void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) ----重要函数 __mmc_start_req(host, mrq); static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) mmc_start_request(host, mrq); static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) host->ops->request(host, mrq); 1b: core/mmc.c int mmc_attach_sd(struct mmc_host *host) err = mmc_send_app_op_cond(host, 0, &ocr); ----1b1 host->ocr = mmc_select_voltage(host, ocr); err = mmc_init_card(host, host->ocr, NULL); err = mmc_sd_init_card(host, host->ocr, NULL); ----1b2 err = mmc_add_card(host->card); ----1b3 1b1: int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) cmd.opcode = SD_APP_OP_COND; 1b2: static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,struct mmc_card *oldcard) err = mmc_sd_get_cid(host, ocr, cid, &rocr); card = mmc_alloc_card(host, &sd_type); err = mmc_send_relative_addr(host, &card->rca); err = mmc_sd_get_csd(host, card); ----mmc_send_csd(card, card->raw_csd); err = mmc_select_card(card); err = mmc_sd_setup_card(host, card, oldcard != NULL); int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,bool reinit) mmc_app_send_scr(card, card->raw_scr); if (host->ops->get_ro(host) > 0 ) mmc_card_set_readonly(card); 1b3: core/bus.c int mmc_add_card(struct mmc_card *card) ret = device_add(&card->dev); drivers/base/core.c int device_add(struct device *dev) dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); bus_probe_device(dev); void bus_probe_device(struct device *dev) if (bus->p->drivers_autoprobe) ret = device_attach(dev); *********** 2: int mmc_add_host(struct mmc_host *host) err = device_add(&host->class_dev); mmc_start_host(host); void mmc_start_host(struct mmc_host *host) mmc_power_off(host); ----2a mmc_detect_change(host, 0); ----2b 2a: void mmc_power_off(struct mmc_host *host) host->ios.power_mode = MMC_POWER_OFF; ... mmc_set_ios(host); void mmc_set_ios(struct mmc_host *host) host->ops->set_ios(host, ios); 2b: void mmc_detect_change(struct mmc_host *host, unsigned long delay) mmc_schedule_delayed_work(&host->detect, delay); 关于命令和数据的发送和接收
[cpp] view plaincopy
struct mmc_host_ops { void (*request)(struct mmc_host *host, struct mmc_request *req); } static const struct mmc_host_ops msmsdcc_ops = { .enable = msmsdcc_enable, .disable = msmsdcc_disable, .pre_req = msmsdcc_pre_req, .post_req = msmsdcc_post_req, .request = msmsdcc_request, .set_ios = msmsdcc_set_ios, .get_ro = msmsdcc_get_ro, .enable_sdio_irq = msmsdcc_enable_sdio_irq, .start_signal_voltage_switch = msmsdcc_switch_io_voltage, .execute_tuning = msmsdcc_execute_tuning, .hw_reset = msmsdcc_hw_reset, .stop_request = msmsdcc_stop_request, .get_xfer_remain = msmsdcc_get_xfer_remain, .notify_load = msmsdcc_notify_load, }; static void msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) mmc_request_done(mmc, mrq); msmsdcc_request_start(host, mrq); static void msmsdcc_request_start (struct msmsdcc_host *host, struct mmc_request *mrq) if ((mrq->data->flags & MMC_DATA_READ) ||host->curr.use_wr_data_pend) msmsdcc_start_data(host, mrq->data,mrq->sbc ? mrq->sbc : mrq->cmd,0); else msmsdcc_start_command(host,mrq->sbc ? mrq->sbc : mrq->cmd,0); static void msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,struct mmc_command *cmd, u32 c) ... if (is_dma_mode(host) && (datactrl & MCI_DPSM_DMAENABLE)) msmsdcc_start_command_deferred(host, cmd, &c); else msmsdcc_start_command(host, cmd, c) static void msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c) { msmsdcc_start_command_deferred(host, cmd, &c); msmsdcc_start_command_exec(host, cmd->arg, c); } static void msmsdcc_start_command_deferred(struct msmsdcc_host *host,struct mmc_command *cmd, u32 *c) cmd->opcode ----对应SD卡命令 ,如 CMD0:复位SD 卡 六、SD卡热插拔检测的两种方法
1.中断
在probe 中有三个中断函数:
[cpp] view plaincopy
ret = request_irq(core_irqres->start, msmsdcc_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); ret = request_irq(core_irqres->start, msmsdcc_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host); ret = request_irq(plat->sdiowakeup_irq,msmsdcc_platform_sdiowakeup_irq,IRQF_SHARED | IRQF_TRIGGER_LOW, DRIVER_NAME "sdiowakeup", host); [cpp] view plaincopy
ret = request_threaded_irq(plat->status_irq, NULL,msmsdcc_platform_status_irq, plat->irq_flags, DRIVER_NAME " (slot)", host); static struct mmc_platform_data *msmsdcc_populate_pdata(struct device *dev) msmsdcc_dt_parse_gpio_info(dev, pdata) static int msmsdcc_dt_parse_gpio_info(struct device *dev, struct mmc_platform_data *pdata) msmsdcc_dt_get_cd_wp_gpio(dev, pdata); static void msmsdcc_dt_get_cd_wp_gpio(struct device *dev, struct mmc_platform_data *pdata) pdata->status_gpio = of_get_named_gpio_flags(np,"cd-gpios", 0, &flags); pdata->status_irq = platform_get_irq_byname(pdev, "status_irq"); apps_proc\kernel\arch\arm\boot\dts\msm9625.dtsi interrupt-names = "core_irq", "bam_irq", "status_irq"; cd-gpios = <&msmgpio 66 0>; static irqreturn_t msmsdcc_platform_status_irq(int irq, void *dev_id) msmsdcc_check_status((unsigned long) host); static void msmsdcc_check_status(unsigned long data) if (host->plat->status || gpio_is_valid(host->plat->status_gpio)) mmc_detect_change(host->mmc, 0); void mmc_detect_change(struct mmc_host *host, unsigned long delay) mmc_schedule_delayed_work(&host->detect, delay); 2.轮询:
[cpp] view plaincopy
Msm_sdcc.c (drivers\mmc\host) static int msmsdcc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_NEEDS_POLL; core/core.c void mmc_rescan(struct work_struct *work) out: if (host->caps & MMC_CAP_NEEDS_POLL) mmc_schedule_delayed_work(&host->detect, HZ); 以上仅是SD卡驱动部分信息,其他关于块设备,总线知识,linux驱动中已经很成熟了,暂时也就没有了解。
七、开始上图片了
初始化:
卡识别:
数据传输:
request
总结
以上是生活随笔为你收集整理的sd 卡驱动--基于高通平台的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。