SD卡驱动学习笔记
以TI DM365为例,内核版本2.6.18
一、 MMC/SD简介
1、 概念
(1)MMC:(Multi MediaCard)
(2)SD:(Secure DigitalMemory Card),完全兼容 MMC 标准。
(3)SDIO:(SecureDigital Input and Output Card)安全数字输入输出卡。SDIO 是在SD 标准上定义了一种外设接口,通过 SD 的 I/O 接脚来连接外围设备,并且通过 SD
上的 I/O 数据接位与这些外围设备进行数据传输。
2、 工作模式
工作模式是针对主机控制器来说的,SDI 控制器可以在符合 MMC 的标准下工作,或者可以在符合 SD 的标准下工作,或者可以在符合SDIO 的标准下工作。故就分别简称为:MMC 模式、SD 模式和 SDIO 模式。
3、 传输模式
传输模式也是针对主机控制器来说的,指控制器与卡之间数据的传输模式, SDI 控制器可支持 SPI、1 位和 4 位的三种传输模式
4、 分类
按体积大小,普通SD卡,mini-SD卡,micro-SD卡(TF卡)
按存储大小,普通SD卡(<=2GB,支持FAT12/FAT16),HCSD卡(>2GB,<=32GB,支持FAT32)。
二、 MMC/SD卡协议
1、 总线协议
SD总线通信是基于命令和数据位流方式的,由一个起始位开始,以一个停止位结束。
命令——命令是开始开始操作的标记。命令从主机发送一个卡(寻址命令)或所有
接的卡(广播命令)。命令在CMD线上串行传送。
响应——响应是从寻址卡或所有连接的卡(同步)发送给主机用来响应接受到的命
的标记。命令在CMD线上串行传送。
数据——数据可以通过数据线在卡和主机间双向传送。
2、 传输方式:空操作、写操作、读操作
首先由主机向SD卡发送命令command,等待SD卡的回复response,如果成功收到回复,则进行数据传输。其中,指令线和数据线上传输的指令和数据都要遵循相应的协议格式。
(1) 空操作
(2) 写操作
(3) 读操作
3、 命令格式
一条指令command共48位,其中command index指代这条具体的指令名称,argument为该指令的参数。格式如图:
4、 MMC/SD卡驱动阶段:卡识别阶段和数据传输阶段
(1) 卡识别阶段
在卡识别阶段通过命令使MMC/SD 处于:空闲(idle)、准备(ready)、识别(ident)、等待(stby)、不活动(ina)几种不同的状态。
(2) 数据传输阶段
在数据传输阶段通过命令使MMC/SD 处于:发送(data)、传输(tran)、接(rcv)、程序(prg)、断开连接(dis)几种不同的状态
三、 MMC/SD子系统代码结构
1、 子系统源码目录
在 Linux 中 MMC/SD 卡的记忆体都当作块设备。MMC/SD设备驱动代码在kernel\drivers\mmc 下面,主要包含如下:davinci-mmc.c 、mmc.c、 mmc-block.c、 mmc-queue.c、 mmc-sysfs.c。
2、 子系统划分
MMC子系统可以划分为三个部分:card、 core、 host层,其中前两个层内核提供,host层需要动手实现跟设备关系密切。
(1) card层
(mmc-block.c、 mmc-queue.c、 mmc-sysfs.c):要把操作的数据以块设备的处理方式写到记忆体上或从记忆体上读取,使SD 卡实现为块设备的。
(2) core层(
(mmc.c):将数据以何种格式,何种方式在 MMC/SD 主机控制器与 MMC/SD卡的记忆体(即块设备)之间进行传递。实现不同规范和协议,为host层的驱动层提供接口函数。
(3) host层
(davinci-mmc.c):要动手实现的具体MMC/SD 设备驱动了,包括 RAM 芯片中的SDI控制器(支持对 MMC/SD 卡的控制,俗称MMC/SD 主机控制器)和 SDI控制器与MMC/SD 卡的硬件接口电路。
3、 层次关系
子系统三个部分的层次关系如图所示:
四、 MMC/SD驱动程序分析
1、 重要数据结构
(1) host主控制器structmmc_davinci_host
该结构体位于davinci-mmc.c中,用于存储SD相关的所有数据信息。
structmmc_davinci_host {
. . . .
struct mmc_request *que_mmc_request;
struct mmc_command *cmd;
struct mmc_data *data;
struct mmc_host *mmc;//贯穿三个层的核心结构体
struct device *dev;
struct clk *clk;
. . . .
}
(2) MMC核心结构体struct mmc_host
该结构体位于host.h,可以认为是linux为SD卡控制器专门准备的一个类,该类里面的成员是所有SD卡控制器都需要的,放之四海而皆准的数据结构,而在PXA芯片控制器的驱动程序pxamci.c中,则为该类具体化了一个对象struct mmc_host *mmc。
struct mmc_host {
struct device *dev;
struct class_device class_dev;
const struct mmc_host_ops *ops; // SD卡主控制器的操作函数,即该控制器所具备的驱动能力
struct mmc_ios ios; // 配置时钟、总线、电源、片选、时序
structmmc_card *card_selected;//连接到此主控制器的SD卡设备
. . . .
struct work_struct detect;
unsigned long private[0]____cacheline_aligned;
}
(3) struct mmc_host_ops实现主控制器的基本操作
定义了对 host 主机进行操作的各种方法,其定义在 Core 核心层的,也就是Core核心层对Host主机层提供的接口函数, 他是Core层与Host层进行数据交换的载体。
struct mmc_host_ops {
//核心函数,完成主控器与SD卡设备之间的数据通信
void (*request)(structmmc_host *host, struct mmc_request *req);
//配置时钟、总线、电源、片选、时序
void (*set_ios)(structmmc_host *host, struct mmc_ios *ios);
int (*get_ro)(structmmc_host *host);
};
(4) structmmc_request
structmmc_request {
struct mmc_command *cmd;
struct mmc_data *data;
struct mmc_command *stop;
void *done_data; /* completion data */
void (*done)(struct mmc_request*);/* completion function */
};
(5) struct mmc_command
structmmc_command {
u32 opcode;//对应命令格式中的commandindex
u32 arg;//对应argument
u32 resp[4]; //对应response
. . . .
struct mmc_data *data; /*data segment associated with cmd */
struct mmc_request *mrq; /*associated request */
};
2、 host层驱动
(1) 添加MMC/SD平台设备
在 Linux 中,MMC/SD 设备是被作为平台设备添加到系统的,可以查看内核代码:/arch/arm/mach-davinci/board_dm365_evm.c中为MMC/SD主机控制器SDI定义了平台设备和平台设备资源,然后在系统初始化的时候添加到系统中.
static struct resource mmc1_resources[] = {
[0] = { /*registers */
.start =DM365_MMC_SD1_BASE,
.end =DM365_MMC_SD1_BASE + SZ_1K - 1,
.flags =IORESOURCE_MEM,
},
[1] = { /* interrupt */
.start =IRQ_DM3XX_MMCINT1,
.end =IRQ_DM3XX_MMCINT1,
.flags =IORESOURCE_IRQ,
},
[2] = { /* dma rx */
.start =DM365_DMA_MMC1RXEVT,
.end =DM365_DMA_MMC1RXEVT,
.flags =IORESOURCE_DMA | IORESOURCE_DMA_RX_CHAN,
},
[3] = { /* dma tx */
.start =DM365_DMA_MMC1TXEVT,
.end =DM365_DMA_MMC1TXEVT,
.flags =IORESOURCE_DMA | IORESOURCE_DMA_TX_CHAN,
},
[4] = { /* event queue */
.start = EVENTQ_3,
.end = EVENTQ_3,
.flags = IORESOURCE_DMA |IORESOURCE_DMA_EVENT_Q,
},
};
static struct davinci_mmc_platform_data mmc1_platform_data = {
.mmc_clk ="MMCSDCLK1",
.rw_threshold = 64,
.use_4bit_mode = 1,
.use_8bit_mode = 0,
.max_frq = 50000000,
.pio_set_dmatrig = 1,
};
static struct platform_device mmc1_device= {
.name ="davinci-mmc",
.id = 1,
.dev = {
.platform_data= &mmc1_platform_data,
},
.num_resources = ARRAY_SIZE(mmc1_resources),
.resource =mmc1_resources,
};
static struct platform_device *dm365_evm_devices[]__initdata = {
&serial_device,
&mmc0_device,
&mmc1_device,
&nor_device,
&rtc_device,
&dm365_kp_device,
};
static __init void dm365_evm_init(void)
{
. . . .
platform_add_devices(dm365_evm_devices,ARRAY_SIZE(dm365_evm_devices));
}
(2) 注册设备
相应结构体初始化
staticstruct platform_driver davinci_mmcsd_driver = {
.probe =davinci_mmc_probe,
.remove =davinci_mmcsd_remove,
.suspend =davinci_mmcsd_suspend,
.resume =davinci_mmcsd_resume,
.driver ={
.name = DRIVER_NAME,
},
};
staticint davinci_mmcsd_init(void)
{
returnplatform_driver_register(&davinci_mmcsd_driver);
}
staticvoid __exit davinci_mmcsd_exit(void)
{
platform_driver_unregister(&davinci_mmcsd_driver);
}
(3) 探针函数davinci_mmc_probe
在驱动的接口函数中davinci_mmc_probe()函数,用于分配 mmc_host ,mmc_davinci_host结构体,并对结构体进行设置,在 SDI 主机控制器操作接口函数 mmc_host_ops 中会调用mmc_davinci_host结构体,申请中断并设置中断服务函数,将结构体 mmc_host添加到主机。函数原型及部分关键代码如下:
static int davinci_mmc_probe(struct platform_device *pdev)
{
. . . .
/* 获取资源,该资源在MMC/SD平台添加时定义好 */
res =platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev,0);
. . .
/* 分配 mmc_host ,mmc_davinci_host结构体, 里面包含mmc_rescan(),该函数很重要*/
mmc =mmc_alloc_host(sizeof(struct mmc_davinci_host), &pdev->dev);
. . . .
init_mmcsd_host(host);
. . . .
mmc->ops= &mmc_davinci_ops;
. . . .
ret = request_irq(host->irq, mmc_davinci_irq,0, DRIVER_NAME, host);
platform_set_drvdata(pdev,host);
mmc_add_host(mmc);
. . . .
}
(4) MMC操作接口函数
static struct mmc_host_ops mmc_davinci_ops= {
.request = mmc_davinci_request,
.set_ios = mmc_davinci_set_ios,
.get_ro = mmc_davinci_get_ro
};
(5) 申请中断
davinci_mmc_probe (struct platform_device *pdev)中有个中断,为SD主控制器芯片内电路固有的内部中断。当调用(*request),即host->ops->request(host, mrq),即上文中的mmc_davinci_request ()后,控制器与SD卡之间开始进行一次指令或数据传输,通信完毕后,主控芯片将产生一个内部中断,以告知此次指令或数据传输完毕。
3、 core层驱动
core层提供了一系列SD卡的接口服务函数,其中,最为核心的一个函数便是之前提到的位于mmc.c的mmc_rescan()。
(1) mmc_rescan()
mmc_rescan扫描SD总线上是否存在SD卡,具体的实现方法就是通过向SD卡上电,看是否能成功;如果上电成功,则返回0,进行总线与SD卡的绑定。如果上电失败,则返回非0值,尝试其他上电的方法。
函数原型及部分关键代码:
static voidmmc_rescan(void *data)
{
. . . .
/*扫描SD卡 */
mmc_setup(host);
.. . .
/*注册SD卡, 即进行总线与SD卡的绑定*/
list_for_each_safe(l,n, &host->cards) {
structmmc_card *card = mmc_list_to_card(l);
/*
* If this is a new and good card, register it.
*/
if(!mmc_card_present(card) && !mmc_card_dead(card)) {
if(mmc_register_card(card))
mmc_card_set_dead(card);
else
mmc_card_set_present(card);
}
/*
* If this card is dead, destroy it.
*/
if(mmc_card_dead(card)) {
list_del(&card->node);
mmc_remove_card(card);
}
}
. . . .
}
(2) 识别卡函数mmc_setup()
函数原型及关键代码:
static void mmc_setup(struct mmc_host*host)
{
. . .
/* 将总线和host联系起来 */
mmc_discover_cards(host);
. . .
}
(3) 注册SD卡mmc_register_card()
函数原型及关键代码:
int mmc_register_card(struct mmc_card *card)
{
. . . .
ret = device_add(&card->dev);
. . . .
}
4、 card层驱动
(1) 总线驱动数据结构struct bus_type
该结构体主要是包含总线属性,挂载到该总线的设备属性和驱动属性,及总线操作接口。其部分代码如下:
struct bus_type {
. . . .
structbus_attribute * bus_attrs;
structdevice_attribute * dev_attrs;
structdriver_attribute * drv_attrs;
int (*match)(struct device * dev, structdevice_driver * drv);
int (*uevent)(struct device *dev, char**envp,
intnum_envp, char *buffer, int buffer_size);
int (*probe)(struct device * dev);
int (*remove)(struct device * dev);
void (*shutdown)(struct device * dev);
int (*suspend)(struct device * dev,pm_message_t state);
int (*resume)(struct device * dev);
};
(2) 总线驱动注册
相应结构体初始化:
staticstruct 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,
.suspend =mmc_bus_suspend,
.resume =mmc_bus_resume,
};
staticstruct class mmc_host_class = {
.name ="mmc_host",
.release =mmc_host_classdev_release,
};
注册和注销总线:
staticint __init mmc_init(void)
{
int ret;
workqueue =create_singlethread_workqueue("kmmcd");
if(!workqueue)
return -ENOMEM;
ret = bus_register(&mmc_bus_type);
if (ret == 0) {
ret =class_register(&mmc_host_class);
if (ret)
bus_unregister(&mmc_bus_type);
}
return ret;
}
staticvoid __exit mmc_exit(void)
{
class_unregister(&mmc_host_class);
bus_unregister(&mmc_bus_type);
destroy_workqueue(workqueue);
}
(3) 块驱动结构体
structmmc_driver为MMC设备驱动结构体,它提供了MMC块设备的基本操作接口。其代码原型如下:
structmmc_driver {
struct device_driver drv;
int (*probe)(struct mmc_card *);
void (*remove)(struct mmc_card *);
int (*suspend)(struct mmc_card *,pm_message_t);
int (*resume)(struct mmc_card *);
};
structblock_device_operations为通用块设备基本操作接口,其代码原型如下:struct block_device_operations {
int (*open) (struct inode*, struct file *);
int (*release) (structinode *, struct file *);
int (*ioctl) (struct inode*, struct file *, unsigned, unsigned long);
long (*unlocked_ioctl)(struct file *, unsigned, unsigned long);
long (*compat_ioctl)(struct file *, unsigned, unsigned long);
int (*direct_access)(struct block_device *, sector_t, unsigned long *);
int (*media_changed)(struct gendisk *);
int (*revalidate_disk)(struct gendisk *);
int (*getgeo)(structblock_device *, struct hd_geometry *);
struct module *owner;
};
(4) 块驱动注册
相应结构体初始化:
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmcblk",
},
.probe = mmc_blk_probe,
.remove = mmc_blk_remove,
.suspend = mmc_blk_suspend,
.resume = mmc_blk_resume,
};
static struct block_device_operations mmc_bdops = {
.open = mmc_blk_open,
.release = mmc_blk_release,
.getgeo = mmc_blk_getgeo,
.owner = THIS_MODULE,
};
其中接口函数mmc_blk_probe()中的mmc_blk_alloc()调用了mmc_bdops。
注册和注销设备:
static int __init mmc_blk_init(void)
{
int res = -ENOMEM;
res =register_blkdev(major, "mmc");
. . . .
returnmmc_register_driver(&mmc_driver);
}
static void __exit mmc_blk_exit(void)
{
mmc_unregister_driver(&mmc_driver);
unregister_blkdev(major,"mmc");
}
其中的mmc_register_driver()将块设备和总线联系在一起:
int mmc_register_driver(struct mmc_driver *drv)
{
drv->drv.bus =&mmc_bus_type;
returndriver_register(&drv->drv);
}
5、 各个驱动联系流程图
(1) host底层驱动流程图
平台设备初始化,平台驱动初始化如图所示。
(2) core层mmc_rescan()函数执行流程
(3) 块和总线驱动注册
(4) request及数据传输实现
五、 总体架构
1、 总体启动架构
kernel启动时,先后执行mmc_init()及mmc_blk_init(),以对mmc设备及mmc块模块进行初始化。然后在挂载mmc设备驱动时,执行驱动程序中的davinci_mmc_probe (),检测host设备中挂载的sd设备。此时probe函数会创建一个host设备,然后开启一个延时任务mmc_rescan()。驱动挂载成功后,mmc_rescan()函数被执行,然后对卡进行初始化(步骤后面详细讲述)。确定当前插入的卡是一张有效、可识别的存储卡。然后调用mmc_add_host ()把存储卡加到系统中。正式与系统驱动连接在一起。
卡设备加到系统中后,通知mmc块设备驱动。块设备驱动此时调用probe函数,即mmc_blk_probe()函数,mmc_blk_probe()首先分配一个新的mmc_blk_data结构变量,然后调用mmc_init_queue,初始化blk队列。然后建立一个线程 mmc_queue_thread()。
2、 卡初始化流程
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都不能通过,那这张卡恐怕就是无效卡了,初始化失败。
3、 核心任务
MMC/SD卡的驱动整个架构由三个文件夹组成,其实一共就做两件事:
卡的检测和卡数据的读取。
(1) 卡的检测涉及到的函数
xxx_mmc_probe()(host/xxx_mmc.c底层驱动)
mmc_alloc_host()(core/core.c)
mmc_rescan()(core/core.c)
mmc_attach_mmc()(core/mmc.c)
mmc_init_card()(core/mmc.c)
mmc_add_card()(core/bus.c)
device_add()
mmc_bus_match()(core/bus.c)
mmc_bus_probe()(core/bus.c)
alloc_disk/add_disk()
mmc_blk_probe()(card/block.c)
(2) 卡中数据读写涉及到的函数
mmc_blk_issue_rq()(card/block.c)
mmc_wait_for_req()(core/core.c)
mmc_start_request()(core/core.c)
host->ops->request(host,mrq)(底层驱动host中的xxx_mmc_request)。