MMC/SD设备驱动在Linux中的结构层次
在Linux中MMC/SD卡的记忆体都当作块设备。MMC/SD设备驱动代码在drivers\mmc 分别有card、core和host三个文件夹,
card层 存放闪存卡(块设备)的相关驱动,如MMC/SD卡设备驱动
core层 MMC的核心层,抽象了mmc驱动的公共部分,完成不同协议和规范的实现,为host层和设备驱动层提供接口函数
host层 mmc/sd/sdio主机控制器代码
sdio接口用来连接主机和设备,如wifi,gps等。主机中的wifi,gps驱动通过sdio接口和wifi芯片,gps芯片通信。
wilc1000wifi芯片,提供sdio slave接口与 sdio host 接口相连。
mmc core为host控制器驱动提供注册接口,抽象同一API,提供给功能层驱动使用。
先看sdio控制器层驱动
samsung,exynos5250-dw-mshc驱动为例
host控制器设备是以platform_device类型注册到platform总线,同时host控制器驱动以platform_driver注册到到platform总线,无论是platform_device或platform_driver注册到platform总线都进行probe。
[ /include/linux/mmc/host.h ]
struct mmc_host 用来描述卡控制器
struct mmc_card 用来描述卡
struct mmc_driver 用来描述 mmc 卡驱动
struct sdio_func 用来描述 功能设备
struct mmc_host_ops 用来描述卡控制器操作接口函数功能,用于从 主机控制器层向 core 层注册操作函数,从而将core 层与具体的主机控制器隔离。
dts中device描述:
dwmmc_3: dwmmc3@12230000 {
compatible = "samsung,exynos5250-dw-mshc";
reg = <0x12230000 0x1000>;
interrupts = <0 78 0>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&clock 283>, <&clock 142>;
clock-names = "biu", "ciu";
};
/*
* On Snow we've got SIP WiFi and so can keep drive strengths low to
* reduce EMI.
*/
dwmmc3@12230000 {
slot@0 {
pinctrl-names = "default";
pinctrl-0 = <&sd3_clk &sd3_cmd &sd3_bus4>;
};
};
驱动注册
static const struct dw_mci_drv_data exynos_drv_data = {
.caps = exynos_dwmmc_caps,
.init = dw_mci_exynos_priv_init,
.setup_clock = dw_mci_exynos_setup_clock,
.prepare_command = dw_mci_exynos_prepare_command,
.set_ios = dw_mci_exynos_set_ios,
.parse_dt = dw_mci_exynos_parse_dt,
};
static const struct of_device_id dw_mci_exynos_match[] = {
{ .compatible = "samsung,exynos4412-dw-mshc",
.data = &exynos_drv_data, },
{ .compatible = "samsung,exynos5250-dw-mshc",
.data = &exynos_drv_data, },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
static struct platform_driver dw_mci_exynos_pltfm_driver = {
.probe = dw_mci_exynos_probe,
.remove = __exit_p(dw_mci_pltfm_remove),
.driver = {
.name = "dwmmc_exynos",
.of_match_table = dw_mci_exynos_match,
.pm = &dw_mci_pltfm_pmops,
},
};
module_platform_driver(dw_mci_exynos_pltfm_driver); //驱动注册
通过compatible = "samsung,exynos4412-dw-mshc ,在platform bus上匹配成功后,调用dw_mci_exynos_probe;
static int dw_mci_exynos_probe(struct platform_device *pdev)
{
const struct dw_mci_drv_data *drv_data;
const struct of_device_id *match;
match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
drv_data = match->data; //driver data:exynos_drv_data
return dw_mci_pltfm_register(pdev, drv_data);
}
struct dw_mci *host; 主机控制器驱动层定义的管理结构;而struct mmc_host 是core层抽象的主机控制器通用管理结构。
dw_mci_pltfm_register中先分配struct dw_mci *host; 获取dts中定义各种控制器设备资源:io地址,中断号等。
然后调用dw_mci_probe,dw_mci_probe先完成控制器初始化配置,时钟,电压。dw_mci_init_slot初始化slot
int dw_mci_probe(struct dw_mci *host)
{
const struct dw_mci_drv_data *drv_data = host->drv_data;
int width, i, ret = 0;
u32 fifo_size;
int init_slots = 0;
u32 msize;
......
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);//注册tasklet中断上半部,处理各种中断
host->card_workqueue = alloc_workqueue("dw-mci-card",
WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
if (!host->card_workqueue)
goto err_dmaunmap;
INIT_WORK(&host->card_work, dw_mci_work_routine_card); //注册workqueue中断上半部,主要处理卡检测
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, //注册硬件中断
host->irq_flags, "dw-mci", host);
........
/* We need at least one slot to succeed */
for (i = 0; i < host->num_slots; i++) {
ret = dw_mci_init_slot(host, i); //初始化slot
if (ret)
dev_dbg(host->dev, "slot %d init failed\n", i);
else
init_slots++;
}
。。。。
}
dw_mci_init_slot:
主要 mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);分配 struct mmc_host *mmc;
mmc_add_host-》mmc_start_host 控制器初始完毕,开始工作;
其中,mmc_alloc_host中会 INIT_DELAYED_WORK(&host->detect, mmc_rescan); 创建卡检测work;
void mmc_start_host(struct mmc_host *host)
{
host->f_init = max(freqs[0], host->f_min);
host->rescan_disable = 0;
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
mmc_power_off(host);
else
mmc_power_up(host);
mmc_detect_change(host, 0); //控制器开始工作后,主动探测卡是否存在
}
mmc_detect_change--》mmc_schedule_delayed_work(&host->detect, delay); 唤醒mmc_rescan卡扫描,后面再描述
中断处理
发生中断后,会回调dw_mci_interrupt,读取中断状态寄存器,查看中断源,进行不同处理。例如:命令发送完成,数据发送完成,卡连接,错误处理等。
static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
{
struct dw_mci *host = dev_id;
u32 pending;
int i;
pending = mci_readl(host, MINTSTS); /* read-only mask reg */
if (pending) {
/*
* DTO fix - version 2.10a and below, and only if internal DMA
* is configured.
*/
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
if (!pending &&
((mci_readl(host, STATUS) >> 17) & 0x1fff))
pending |= SDMMC_INT_DATA_OVER;
}
if (pending & SDMMC_INT_CMD_DONE) {
u32 cmd = mci_readl(host, CMD) & 0x3f;
if (cmd == SD_SWITCH_VOLTAGE &&
!(mci_readl(host, STATUS) & SDMMC_DATA_BUSY)) {
pending |= SDMMC_INT_RTO;
}
}
if (pending & SDMMC_INT_HLE) {
mci_writel(host, RINTSTS, SDMMC_INT_HLE);
host->cmd_status = pending;
tasklet_schedule(&host->tasklet);
}
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_status = pending;
smp_wmb();
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
}
if (pending & SDMMC_INT_VOLT_SW) {
u32 cmd = mci_readl(host, CMD) & 0x3f;
if (cmd == SD_SWITCH_VOLTAGE) {
mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SW);
dw_mci_cmd_interrupt(host, pending);
}
}
if (pending & DW_MCI_DATA_ERROR_FLAGS) {
/* if there is an error report DATA_ERROR */
mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
host->data_status = pending;
smp_wmb();
set_bit(EVENT_DATA_ERROR, &host->pending_events);
tasklet_schedule(&host->tasklet);
}
if (pending & SDMMC_INT_DATA_OVER) {
if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
del_timer(&host->dto_timer);
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
if (!host->data_status)
host->data_status = pending;
smp_wmb();
if (host->dir_status == DW_MCI_RECV_STATUS) {
if (host->sg != NULL)
dw_mci_read_data_pio(host, true);
}
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
}
if (pending & SDMMC_INT_RXDR) {
mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
if (host->dir_status == DW_MCI_RECV_STATUS && host->sg) {
dw_mci_read_data_pio(host, false);
} else {
if (host->hw_mmc_id == DWMMC_SD_ID && !host->sg) {
printk(KERN_DEBUG"mmc%d:debug error:host.sg=%p.cmd%d\n",
host->hw_mmc_id, host->sg,mci_readl(host, CMD) & 0x3F);
dw_mci_fifo_reset(host->dev, host);
dw_mci_ciu_reset(host->dev, host);
}
}
}
if (pending & SDMMC_INT_TXDR) {
mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
if (host->dir_status == DW_MCI_SEND_STATUS && host->sg)
dw_mci_write_data_pio(host);
}
if (pending & SDMMC_INT_CMD_DONE) {
mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
dw_mci_cmd_interrupt(host, pending);
}
if (pending & SDMMC_INT_CD) { //卡检测中
mci_writel(host, RINTSTS, SDMMC_INT_CD);
queue_work(host->card_workqueue, &host->card_work);//唤醒card_workqueue队列工作
}
/* Handle SDIO Interrupts */
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
if (pending & SDMMC_INT_SDIO(i)) {
mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i));
mmc_signal_sdio_irq(slot->mmc);
}
}
}
#ifdef CONFIG_MMC_DW_IDMAC
/* Handle DMA interrupts */
pending = mci_readl(host, IDSTS);
if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI);
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
host->dma_ops->complete(host);
}
#endif
return IRQ_HANDLED;
}
bit 15:结束位错误/CRC 错误
bit 14:自动命令完成(ACD)
bit 13:起始位错误(SBE) /忙退出
中断 0(BCI0)。
bit 12:硬件锁定写错误(HLE)
bit 11: FIFO 下溢出/上溢出错误
(FRUN)
bit 10:主机超时引起的数据缺乏
(HTO) /电压切换中断
bit 9:数据读超时(DRTO) /Boot 数
据开始(BDS)
bit 8:响应超时(RTO) /Boot 接收
响应(BAR)
bit 7:数据 CRC 错误(DCRC)
bit 6:响应 CRC 错误(RCRC)
bit 5:接收 FIFO 数据请求(RXDR)
bit 4:发送 FIFO 数据请求(TXDR)
bit 3:数据传输结束(DTO)
bit 2:命令完成(CD)
bit 1:响应错误(RE)
bit 0:卡检测(CDT)
卡检测
host->card_workqueue–》
dw_mci_work_routine_card:读取控制器寄存器 present = dw_mci_get_cd(mmc);获取卡状态,插入或拔出;
卡存在:
mmc_detect_change–>mmc_schedule_delayed_work(&host->detect, delay); 唤醒mmc_alloc_host中创建的 INIT_DELAYED_WORK(&host->detect, mmc_rescan);
mmc_rescan()
{
....
//以不同频率扫描卡static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
//MMC标准默认为400KHZ
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
extend_wakelock = true;
break;
}
if (freqs[i] <= host->f_min)
break;
}
.....
}
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
//host上电
mmc_power_up(host);
/*
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
* do a hardware reset if possible.
*/
mmc_hw_reset_for_init(host); //复位硬件
/*
* sdio_reset sends CMD52 to reset card. Since we do not know
* if the card is being re-initialized, just send it. CMD52
* should be ignored by SD/eMMC cards.
*/
//如果目标卡是纯SD卡则目标卡不会应答,一般主机host的寄存器会报错,但是这个无关紧要,可以不理它。如果目标卡是纯SDIO卡,那么这里就是复位SDIO卡,通过命令CMD52来实现的。③如果目标卡是SD卡和SDIO卡的组合卡,则需要先发送CMD52来复位SDIO卡,再复位SD卡,因为CMD52要先于CMD0发送。
sdio_reset(host);
mmc_go_idle(host);//发送CMD0,让设备进入IDLE模式
mmc_send_if_cond(host, host->ocr_avail);//发送CMD8,获取该卡所支持的电压值
/* Order's important: probe SDIO, then SD, then MMC */
识别卡类型
首先发送 CMD5。如果收到一个响应,那么该卡是 SDIO。否则发送 ACMD41;如果收到一个响应,那么该卡是 SD。 否则,该卡是 MMC。
if (!mmc_attach_sdio(host))
return 0;
if (!mmc_attach_sd(host))
return 0;
if (!mmc_attach_mmc(host))
return 0;
mmc_power_off(host);
return -EIO;
}
SDIO类型初始化
/*
* Starting point for SDIO card init.
*/
int mmc_attach_sdio(struct mmc_host *host)
{
int err, i, funcs;
u32 ocr;
struct mmc_card *card;
//发送cmd5,如果收到响应就是sdio卡,否则不是直接返回; 收到响应为R4指示sdio卡使用的电压
err = mmc_send_io_op_cond(host, 0, &ocr);
if (err)
return err;
//设置mmc host中bus_ops为sdio ops
mmc_attach_bus(host, &mmc_sdio_ops);
if (host->ocr_avail_sdio)
host->ocr_avail = host->ocr_avail_sdio;
/*
* Sanity check the voltages that the card claims to
* support.
*/
cmd5时返回电压
if (ocr & 0x7F) {
pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
}
//设置电压值,需要sdio控制器支持的电压和sdio卡匹配
host->ocr = mmc_select_voltage(host, ocr);
//初始化卡,里面先分配card类型结构,struct mmc_card card = mmc_alloc_card(host, NULL);并判断sdio对应device是否为存储还是其它功能(wifi,gps等),设置card->type = MMC_TYPE_SD_COMBO;或card->type = MMC_TYPE_SDIO;并且获取mmc_send_relative_addr(host, &card->rca);设备地址保存到card->rca
err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
card = host->card;
/*
* The number of functions on the card is encoded inside
* the ocr.
*/
funcs = (ocr & 0x70000000) >> 28; //sdio device上具备的功能个数,例如wifi设备
card->sdio_funcs = 0;
/*
* Initialize (but don't add) all present functions.
*/
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
//初始化化sdio 对应卡设备上功能,分配了struct sdio_func,sdio_alloc_func(card);
err = sdio_init_func(host->card, i + 1);
}
/*
* First add the card to the driver model...
*/
mmc_release_host(host);
err = mmc_add_card(host->card); //把card设备注册到系统中
/*
* ...then the SDIO functions.
*/
for (i = 0;i < funcs;i++) {
err = sdio_add_func(host->card->sdio_func[i]); //把func功能注册到设备上
if (err)
goto remove_added;
}
mmc_claim_host(host);
return 0;
}
sdio_init_func(host->card, i + 1);
初始化功能并注册到系统中,在/sys/bus/sdio/devices,
mmc1:0001:1 表示host控制器1:连接的设备1:设备1上功能1
mmc1:0001:2 表示host控制器1:连接的设备1:设备1上功能2
分配struct mmc_card
/*
* Allocate and initialise a new MMC card structure.
*/
struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
{
struct mmc_card *card;
card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);
card->host = host;
device_initialize(&card->dev);
card->dev.parent = mmc_classdev(host);
card->dev.bus = &mmc_bus_type;//设置卡设备bus为mmc_bus_type
card->dev.release = mmc_release_card;
card->dev.type = type;
return card;
}
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,
.pm = &mmc_bus_pm_ops,
};
/*
* This currently matches any MMC driver to any MMC card - drivers
* themselves make the decision whether to drive this card in their
* probe method.
*/
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
return 1; //默认返回1,任何mmc card和mmc驱动都能够匹配
}
mmc_blk_init–>mmc_register_driver(&mmc_driver);–>drv->drv.bus = &mmc_bus_type;–>driver_register(&drv->drv);
注册了mmc_bus_type 类型mmc card驱动会和 sdio card设备 匹配到。
func分配struct sdio_func
/*
* Allocate and initialise a new SDIO function structure.
*/
struct sdio_func *sdio_alloc_func(struct mmc_card *card)
{
struct sdio_func *func;
func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);
func->card = card;
device_initialize(&func->dev);
func->dev.parent = &card->dev;//父节点为card设备
func->dev.bus = &sdio_bus_type; //bus类型为sdio_bus_type
func->dev.release = sdio_release_func;
return func;
}
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,
};
static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
struct sdio_driver *sdrv)
{
const struct sdio_device_id *ids;
ids = sdrv->id_table;
if (ids) {
while (ids->class || ids->vendor || ids->device) { 读取card func中id和driver中id匹配
if (sdio_match_one(func, ids))
return ids;
ids++;
}
}
return NULL;
}
func为sdio_bus_type 注册到sdio bus中,和系统中注册的driver进行匹配。然后调用驱动的probe。