前言
前面聊了MMC子系统《Linux驱动分析之MMC子系统框架》,接下来我们拿个MMC Host驱动分析一下,来看看大致流程是怎样的。文章很长,代码很多,看个流程即可。
Host驱动分析
Linux版本:5.15
芯片平台:RK3288
部分源码路径:
drivers/mmc/host/dw_mmc-rockchip.c
drivers/mmc/host/dw_mmc_pltfm.c
drivers/mmc/host/dw_mmc.c
1.装载和卸载函数
//驱动数据
static const struct dw_mci_drv_data rk3288_drv_data = {
.caps = dw_mci_rk3288_dwmmc_caps,
.num_caps = ARRAY_SIZE(dw_mci_rk3288_dwmmc_caps),
.set_ios = dw_mci_rk3288_set_ios,
.execute_tuning = dw_mci_rk3288_execute_tuning,
.parse_dt = dw_mci_rk3288_parse_dt,
.init = dw_mci_rockchip_init,
};
//dts匹配表
static const struct of_device_id dw_mci_rockchip_match[] = {
{ .compatible = "rockchip,rk3288-dw-mshc",
.data = &rk3288_drv_data },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match);
//host控制器使用platform_driver
static struct platform_driver dw_mci_rockchip_pltfm_driver = {
.probe = dw_mci_rockchip_probe,
.remove = dw_mci_rockchip_remove,
.driver = {
.name = "dwmmc_rockchip",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_rockchip_match, //dts匹配
.pm = &dw_mci_rockchip_dev_pm_ops,
},
};
//封装module_init和module_exit
module_platform_driver(dw_mci_rockchip_pltfm_driver);
2.probe()函数
static int dw_mci_rockchip_probe(struct platform_device *pdev)
{
const struct dw_mci_drv_data *drv_data;
const struct of_device_id *match;
int ret;
if (!pdev->dev.of_node)
return -ENODEV;
match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
drv_data = match->data; //获取驱动数据
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_use_autosuspend(&pdev->dev);
ret = dw_mci_pltfm_register(pdev, drv_data); //调用dw的注册函数进行注册
if (ret) {
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
return ret;
}
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
}
真正的host注册是在dw_mci_pltfm_register()中,这是dw提供的统一注册接口。
int dw_mci_pltfm_register(struct platform_device *pdev,
const struct dw_mci_drv_data *drv_data)
{
struct dw_mci *host;
struct resource *regs;
//分配dw_mci, 对mmc_host的封装
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
if (!host)
return -ENOMEM;
host->irq = platform_get_irq(pdev, 0); //中断号
if (host->irq < 0)
return host->irq;
host->drv_data = drv_data; //驱动数据
host->dev = &pdev->dev;
host->irq_flags = 0;
host->pdata = pdev->dev.platform_data; //平台数据
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取IO地址, 即寄存器地址
host->regs = devm_ioremap_resource(&pdev->dev, regs); //将IO地址映射为虚拟地址
if (IS_ERR(host->regs))
return PTR_ERR(host->regs);
//获取寄存器的物理基地址
host->phy_regs = regs->start;
platform_set_drvdata(pdev, host);
return dw_mci_probe(host); //调用dw probe
}
int dw_mci_probe(struct dw_mci *host)
{
//省略....(各种寄存器配置,时钟,中断等)
//卡槽初始化
ret = dw_mci_init_slot(host);
if (ret) {
dev_dbg(host->dev, "slot %d init failed\n", i);
goto err_dmaunmap;
}
/* Now that slots are all setup, we can enable card detect */
dw_mci_enable_cd(host);
return 0;
//省略.....
}
static int dw_mci_init_slot(struct dw_mci *host)
{
struct mmc_host *mmc;
struct dw_mci_slot *slot;
int ret;
//分配mmc_host
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
if (!mmc)
return -ENOMEM;
slot = mmc_priv(mmc);
slot->id = 0;
slot->sdio_id = host->sdio_id0 + slot->id;
slot->mmc = mmc;
slot->host = host;
host->slot = slot;
mmc->ops = &dw_mci_ops; //mmc_host_ops, 操作函数集
/*if there are external regulators, get them*/
ret = mmc_regulator_get_supply(mmc);
if (ret)
goto err_host_allocated;
if (!mmc->ocr_avail)
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
ret = mmc_of_parse(mmc);
if (ret)
goto err_host_allocated;
//设置卡槽特性
ret = dw_mci_init_slot_caps(slot);
if (ret)
goto err_host_allocated;
/* Useful defaults if platform data is unset. */
if (host->use_dma == TRANS_MODE_IDMAC) {
mmc->max_segs = host->ring_size;
mmc->max_blk_size = 65535;
mmc->max_seg_size = 0x1000;
mmc->max_req_size = mmc->max_seg_size * host->ring_size;
mmc->max_blk_count = mmc->max_req_size / 512;
} else if (host->use_dma == TRANS_MODE_EDMAC) {
mmc->max_segs = 64;
mmc->max_blk_size = 65535;
mmc->max_blk_count = 65535;
mmc->max_req_size =
mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size;
} else {
/* TRANS_MODE_PIO */
mmc->max_segs = 64;
mmc->max_blk_size = 65535; /* BLKSIZ is 16 bits */
mmc->max_blk_count = 512;
mmc->max_req_size = mmc->max_blk_size *
mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size;
}
//检查卡是否存在
dw_mci_get_cd(mmc);
//注册host
ret = mmc_add_host(mmc);
//省略.....
}
probe调用流程:
dw_mci_rockchip_probe
--》dw_mci_pltfm_register
--》dw_mci_probe //寄存器设置(时钟,速率,中断等)
--》dw_mci_init_slot //注册mmc_host
3.热插拔
首先看一下中断的注册流程:
mmc_add_host
--》mmc_start_host
--》mmc_gpiod_request_cd_irq //中断处理函数mmc_gpio_cd_irqt
当卡插入时,产生中断,就会调用中断处理函数mmc_gpio_cd_irqt
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
{
/* Schedule a card detection after a debounce timeout */
struct mmc_host *host = dev_id;
struct mmc_gpio *ctx = host->slot.handler_priv;
host->trigger_card_event = true;
mmc_detect_change(host, msecs_to_jiffies(ctx->cd_debounce_delay_ms)); //调用mmc_detect_change
return IRQ_HANDLED;
}
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
_mmc_detect_change(host, delay, true);
}
void _mmc_detect_change(struct mmc_host *host, unsigned long delay, bool cd_irq)
{
if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL))
__pm_wakeup_event(host->ws, 5000);
host->detect_change = 1;
mmc_schedule_delayed_work(&host->detect, delay); //中断下半部使用workqueue
}
为了消抖,中断最终在下半部进行处理,里面使用了workqueue。所以接下来来看一下host->detect这个工作队列。
mmc_alloc_host中初始化了host->detect
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
所以下半部最终会调用mmc_rescan
void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
int i;
if (host->rescan_disable)
return;
//不可移除的卡只扫描一次
if (!mmc_card_is_removable(host) && host->rescan_entered)
return;
host->rescan_entered = 1;
//处理卡事件
//host->ops在dw_mci_init_slot中初始化
if (host->trigger_card_event && host->ops->card_event) {
mmc_claim_host(host);
host->ops->card_event(host);
mmc_release_host(host);
host->trigger_card_event = false;
}
//验证已注册卡的功能,否则将其删除
if (host->bus_ops)
host->bus_ops->detect(host);
host->detect_change = 0;
//如果有卡存在的话则退出
if (host->bus_ops != NULL)
goto out;
mmc_claim_host(host);
if (mmc_card_is_removable(host) && host->ops->get_cd &&
host->ops->get_cd(host) == 0) {
mmc_power_off(host);
mmc_release_host(host);
goto out;
}
/* If an SD express card is present, then leave it as is. */
if (mmc_card_sd_express(host)) {
mmc_release_host(host);
goto out;
}
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
unsigned int freq = freqs[i];
if (freq > host->f_max) {
if (i + 1 < ARRAY_SIZE(freqs))
continue;
freq = host->f_max;
}
//进行卡初始化,卡初始化时时钟频率不能超过400KHz
if (!mmc_rescan_try_freq(host, max(freq, host->f_min)))
break;
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host);
out:
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ); //轮询的话会再重新启动
}
(1)host->ops在dw_mci_init_slot中初始化
static const struct mmc_host_ops dw_mci_ops = {
.request = dw_mci_request,
.pre_req = dw_mci_pre_req,
.post_req = dw_mci_post_req,
.set_ios = dw_mci_set_ios,
.get_ro = dw_mci_get_ro,
.get_cd = dw_mci_get_cd,
.hw_reset = dw_mci_hw_reset,
.enable_sdio_irq = dw_mci_enable_sdio_irq,
.ack_sdio_irq = dw_mci_ack_sdio_irq,
.execute_tuning = dw_mci_execute_tuning,
.card_busy = dw_mci_card_busy,
.start_signal_voltage_switch = dw_mci_switch_voltage,
.init_card = dw_mci_init_card,
.prepare_hs400_tuning = dw_mci_prepare_hs400_tuning,
};
mmc->ops = &dw_mci_ops;
(2)host->bus_ops根据不同类型(sd,mmc,sdio)分别定义在sd.c/mmc.c/sdio.c中
(3) 假如是SD类型,host->bus_ops->detect对应mmc_sd_detect()函数
static void mmc_sd_detect(struct mmc_host *host)
{
int err;
mmc_get_card(host->card, NULL);
//检查卡是否移除
err = _mmc_detect_card_removed(host);
mmc_put_card(host->card, NULL);
//移除后则释放资源
if (err) {
mmc_sd_remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_power_off(host);
mmc_release_host(host);
}
}
(4) 卡插入的情况,这会调用mmc_rescan_try_freq对卡进行初始化
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
pr_debug("%s: %s: trying to init card at %u Hz\n",
mmc_hostname(host), __func__, host->f_init);
//上电
mmc_power_up(host, host->ocr_avail);
//硬件复位
mmc_hw_reset_for_init(host);
//SDIO需要发送CMD52进行卡复位, SD/eMMC卡会忽略
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
sdio_reset(host);
//进入IDLE状态
mmc_go_idle(host);
if (!(host->caps2 & MMC_CAP2_NO_SD)) {
if (mmc_send_if_cond_pcie(host, host->ocr_avail))
goto out;
if (mmc_card_sd_express(host))
return 0;
}
//调用attach, 优先级SDIO > SD > MMC
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
if (!mmc_attach_sdio(host))
return 0;
if (!(host->caps2 & MMC_CAP2_NO_SD))
if (!mmc_attach_sd(host))
return 0;
if (!(host->caps2 & MMC_CAP2_NO_MMC))
if (!mmc_attach_mmc(host))
return 0;
out:
mmc_power_off(host);
return -EIO;
}
不同类型设备的attach对应如下:
SDIO -》mmc_attach_sdio
SD -》mmc_attach_sd
mmc -》mmc_attach_mmc
接下来以SDIO卡为例:
int mmc_attach_sdio(struct mmc_host *host)
{
int err, i, funcs;
u32 ocr, rocr;
struct mmc_card *card;
WARN_ON(!host->claimed);
//发送CMD5,获取ocr寄存器
err = mmc_send_io_op_cond(host, 0, &ocr);
mmc_attach_bus(host, &mmc_sdio_ops); //设置mmc_bus_ops
if (host->ocr_avail_sdio)
host->ocr_avail = host->ocr_avail_sdio;
//根据ocr寄存器值选择电压
rocr = mmc_select_voltage(host, ocr);
//检查并初始化卡
err = mmc_sdio_init_card(host, rocr, NULL);
card = host->card;
//省略.....
//确定功能(function)数量
funcs = (ocr & 0x70000000) >> 28; //Response R4
card->sdio_funcs = 0;
//初始化所有function
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
err = sdio_init_func(host->card, i + 1);
if (err)
goto remove;
/*
* Enable Runtime PM for this func (if supported)
*/
if (host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_enable(&card->sdio_func[i]->dev);
}
//添加卡到驱动模型
mmc_release_host(host);
err = mmc_add_card(host->card);
if (err)
goto remove_added;
//添加所有function
for (i = 0;i < funcs;i++) {
err = sdio_add_func(host->card->sdio_func[i]);
if (err)
goto remove_added;
}
if (host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put(&card->dev);
mmc_claim_host(host);
return 0;
//省略.....
}
sdio_add_func会在/sys/bus/sdio/devices中生成功能节点文件
mmc1:0001:1 表示host控制器1:连接的设备1:设备1上功能1
mmc1:0001:2 表示host控制器1:连接的设备1:设备1上功能2
4.SDIO卡初始化
static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
struct mmc_card *card;
int err;
int retries = 10;
u32 rocr = 0;
u32 ocr_card = ocr;
WARN_ON(!host->claimed);
//查询卡是否支持1.8V
if (mmc_host_uhs(host))
ocr |= R4_18V_PRESENT;
try_again:
if (!retries) {
pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));
ocr &= ~R4_18V_PRESENT;
}
//查询电压范围
err = mmc_send_io_op_cond(host, ocr, &rocr);
if (err)
return err;
//对于SPI,可以使能CRC
if (mmc_host_is_spi(host)) {
err = mmc_spi_set_crc(host, use_spi_crc);
if (err)
return err;
}
//分配mmc_card
card = mmc_alloc_card(host, &sdio_type);
if (IS_ERR(card))
return PTR_ERR(card);
//确定是组合卡还是SDIO卡
if ((rocr & R4_MEMORY_PRESENT) &&
mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
card->type = MMC_TYPE_SD_COMBO;
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
err = -ENOENT;
goto mismatch;
}
} else {
card->type = MMC_TYPE_SDIO;
if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
err = -ENOENT;
goto mismatch;
}
}
//调用host的init_card
if (host->ops->init_card)
host->ops->init_card(host, card);
//如果卡支持UHS-I模式则请求切换为1.8V信号电平
if (rocr & ocr & R4_18V_PRESENT) {
err = mmc_set_uhs_voltage(host, ocr_card);
if (err == -EAGAIN) {
mmc_sdio_pre_init(host, ocr_card, card);
retries--;
goto try_again;
} else if (err) {
ocr &= ~R4_18V_PRESENT;
}
}
/*
* For native busses: set card RCA and quit open drain mode.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_send_relative_addr(host, &card->rca);
if (err)
goto remove;
if (oldcard)
oldcard->rca = card->rca;
}
//选择卡之前,读取CSD
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
err = mmc_sd_get_csd(card);
if (err)
goto remove;
mmc_decode_cid(card);
}
//选择卡
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card);
if (err)
goto remove;
}
if (card->quirks & MMC_QUIRK_NONSTD_SDIO) {
//非标准SDIO设备,没有CIA寄存器,host在init_card()中填充cccr和cis结构体。
mmc_set_clock(host, card->cis.max_dtr);
if (card->cccr.high_speed) {
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
}
if (oldcard)
mmc_remove_card(card);
else
host->card = card;
return 0;
}
//读取cccr寄存器
err = sdio_read_cccr(card, ocr);
if (err) {
mmc_sdio_pre_init(host, ocr_card, card);
if (ocr & R4_18V_PRESENT) {
/* Retry init sequence, but without R4_18V_PRESENT. */
retries = 0;
goto try_again;
}
return err;
}
//读取CIS tuple
err = sdio_read_common_cis(card);
if (err)
goto remove;
if (oldcard) {
if (card->cis.vendor == oldcard->cis.vendor &&
card->cis.device == oldcard->cis.device) {
mmc_remove_card(card);
card = oldcard;
} else {
err = -ENOENT;
goto mismatch;
}
}
card->ocr = ocr_card;
mmc_fixup_device(card, sdio_fixup_methods);
if (card->type == MMC_TYPE_SD_COMBO) { //组合卡
err = mmc_sd_setup_card(host, card, oldcard != NULL);
//如果存储初始化失败,则当成仅有SDIO的卡
if (err) {
mmc_go_idle(host);
if (mmc_host_is_spi(host))
/* should not fail, as it worked previously */
mmc_spi_set_crc(host, use_spi_crc);
card->type = MMC_TYPE_SDIO;
} else
card->dev.type = &sd_type;
}
//断开卡检测的上拉电阻
err = sdio_disable_cd(card);
if (err)
goto remove;
//初始化UHS-I的卡
if ((ocr & R4_18V_PRESENT) && card->sw_caps.sd3_bus_mode) {
err = mmc_sdio_init_uhs_card(card);
if (err)
goto remove;
} else {
//切为高速
err = sdio_enable_hs(card);
if (err > 0)
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
else if (err)
goto remove;
//设置卡的最大速率
mmc_set_clock(host, mmc_sdio_get_max_clock(card));
//设置线宽
err = sdio_enable_4bit_bus(card);
if (err)
goto remove;
}
if (host->caps2 & MMC_CAP2_AVOID_3_3V &&
host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
pr_err("%s: Host failed to negotiate down from 3.3V\n",
mmc_hostname(host));
err = -EINVAL;
goto remove;
}
host->card = card;
return 0;
//省略.....
}
这些初始化就跟SDIO规范相关了,可以参考官方规范。
5.发送命令/数据
在sd_ops.c/sdio_ops.c/mmc_ops.c中封装了一些命令的请求函数。比如SDIO中的mmc_send_io_op_cond, 其调用关系如下:
mmc_send_io_op_cond
--》mmc_wait_for_cmd
--》mmc_wait_for_req
--》mmc_wait_for_req
--》__mmc_start_req
--》mmc_start_request
--》__mmc_start_request
static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
//省略......
//mmc_request包含了命令和数据,如果只有命令,data域则为NULL
host->ops->request(host, mrq); //调用mmc_host_ops中的request函数
}
这里host->ops->request对应的就是dw_mci_request
static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
WARN_ON(slot->mrq);
//检查卡是否存在
if (!dw_mci_get_cd(mmc)) {
mrq->cmd->error = -ENOMEDIUM;
mmc_request_done(mmc, mrq);
return;
}
spin_lock_bh(&host->lock);
//发送请求
dw_mci_queue_request(host, slot, mrq);
spin_unlock_bh(&host->lock);
}
static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
struct mmc_request *mrq)
{
dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
host->state);
slot->mrq = mrq;
//省略......
//处于IDLE状态,可以直接发,非IDLE则放入链表中
if (host->state == STATE_IDLE) {
host->state = STATE_SENDING_CMD;
dw_mci_start_request(host, slot);
} else {
list_add_tail(&slot->queue_node, &host->queue);
}
}
static void dw_mci_start_request(struct dw_mci *host,
struct dw_mci_slot *slot)
{
struct mmc_request *mrq = slot->mrq;
struct mmc_command *cmd;
cmd = mrq->sbc ? mrq->sbc : mrq->cmd;
__dw_mci_start_request(host, slot, cmd);
}
static void dw_mci_start_request(struct dw_mci *host,
struct dw_mci_slot *slot)
{
struct mmc_request *mrq = slot->mrq;
struct mmc_command *cmd;
cmd = mrq->sbc ? mrq->sbc : mrq->cmd;
__dw_mci_start_request(host, slot, cmd);
}
static void __dw_mci_start_request(struct dw_mci *host,
struct dw_mci_slot *slot,
struct mmc_command *cmd)
{
struct mmc_request *mrq;
struct mmc_data *data;
u32 cmdflags;
mrq = slot->mrq;
host->mrq = mrq;
host->pending_events = 0;
host->completed_events = 0;
host->cmd_status = 0;
host->data_status = 0;
host->dir_status = 0;
data = cmd->data;
if (data) { //携带有数据
//配置寄存器
mci_writel(host, TMOUT, 0xFFFFFFFF);
mci_writel(host, BYTCNT, data->blksz*data->blocks);
mci_writel(host, BLKSIZ, data->blksz);
}
cmdflags = dw_mci_prepare_command(slot->mmc, cmd);
/* this is the first command, send the initialization clock */
if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))
cmdflags |= SDMMC_CMD_INIT;
if (data) {
dw_mci_submit_data(host, data);
wmb(); /* drain writebuffer */
}
//开始发送命令,里面也是对寄存器的配置
dw_mci_start_command(host, cmd, cmdflags);
//省略......
}
发送数据最终就是回到寄存器的配置,然后通过DMA进行数据发送。
DW接口调用流程:
dw_mci_request
--》dw_mci_queue_request
--》dw_mci_start_request
--》__dw_mci_start_request