文章目录

      • 架构
      • sdio控制器驱动

架构

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芯片通信。

linux sdio驱动-编程之家wilc1000wifi芯片,提供sdio slave接口与 sdio host 接口相连。
linux sdio驱动-编程之家

mmc core为host控制器驱动提供注册接口,抽象同一API,提供给功能层驱动使用。
linux sdio驱动-编程之家

先看sdio控制器层驱动

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_datareturn 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); //初始化slotif (ret)dev_dbg(host->dev, "slot %d init failed\n", i);elseinit_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);elsemmc_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);}
#endifreturn 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标准默认为400KHZfor (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 opsmmc_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->rcaerr = 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_typecard->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_typefunc->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。