一、说明

1、mmc core概述

mmc core主模块是mmc core的实现核心。也是本章的重点内容。

对应代码位置drivers/mmc/core/core.c

其主要负责如下功能:

  • mmc core初始化,包括注册mmc bus、mm host class等等
  • mmc host的管理和维护,包括为其他模块提供mmc_host的操作接口,如下
    • host的启动和停止
    • host的占用和释放
    • host电源状态的保存和恢复
    • host总线操作集的绑定和解绑
    • host上卡状态检测
  • 为其他模块提供mmc_card的操作接口,如下
    • card的唤醒和休眠
    • card擦除
    • card属性的获取
  • 为其他模块提供总线io setting的接口
  • 为其他模块提供mmc请求接口
  • card检测接口
  • bkops操作接口
  • regulator操作接口
  • clock操作接口
  • mmc core电源管理操作接口

2、操作集说明

在mmc_host中有两个操作集成员,需要理解一下,以免在代码中产生误会:

  • mmc_host->struct mmc_host_ops *ops,这个是host的操作集,由host controller驱动决定。对于sdhci类host来说,就是sdhci_ops(sdhci.c中设置)。
  • mmc_host->struct mmc_bus_ops *bus_ops,这个是mmc总线的操作集(也可以理解为host的mmc bus handler,host的总线处理方法),由总线上的card type决定。对于mmc card type来说,就是mmc_ops_unsafe或者mmc_ops(mmc_attach_bus_ops中设置)。

二、API总览

1、mmc core初始化相关

  • mmc_init & mmc_exit (模块内使用)

2、mmc host的管理和维护相关

  • mmc_claim_host & mmc_try_claim_host & mmc_release_host (模块内使用)
  • mmc_power_up & mmc_power_off
  • mmc_start_host & mmc_stop_host
  • mmc_power_save_host & mmc_power_restore_host
  • mmc_resume_host & mmc_suspend_host
  • mmc_pm_notify

3、mmc card的操作相关(包括card状态的获取)

  • mmc_hw_reset & mmc_hw_reset_check &
  • mmc_card_awake & mmc_card_sleep
  • mmc_card_is_prog_state
  • mmc_can_erase
  • mmc_can_trim
  • mmc_can_discard
  • mmc_can_sanitize
  • mmc_can_secure_erase_trim
  • mmc_erase_group_aligned

4、总线io setting相关

  • mmc_set_ios
  • mmc_set_chip_select
  • mmc_set_clock
  • mmc_set_bus_mode
  • mmc_set_bus_width
  • mmc_select_voltage
  • mmc_set_signal_voltage(特殊)
  • mmc_set_timing
  • mmc_set_driver_type
  • mmc_get_max_frequency & mmc_get_min_frequency

5、host的mmc总线相关

  • mmc_resume_bus
  • mmc_attach_bus & mmc_detach_bus

6、mmc请求相关

  • mmc_request_done
  • mmc_wait_for_req
  • mmc_wait_for_cmd
  • mmc_set_data_timeout
  • mmc_align_data_size

7、card检测相关

mmc_detect_change

mmc_rescan

mmc_detect_card_removed

8、bkops操作相关

  • mmc_blk_init_bkops_statistics
  • mmc_start_delayed_bkops
  • mmc_start_bkops & mmc_stop_bkops
  • mmc_start_idle_time_bkops
  • mmc_read_bkops_status

9、regulator操作相关

  • mmc_regulator_get_ocrmask
  • mmc_regulator_set_ocr
  • mmc_regulator_get_supply

10、card擦除操作相关

  • mmc_init_erase
  • mmc_erase

11、clock操作接口

  • mmc_init_clk_scaling & mmc_exit_clk_scaling
  • mmc_can_scale_clk
  • mmc_disable_clk_scaling

12、mmc core电源管理操作

  • mmc_rpm_hold & mmc_rpm_release

二、接口代码说明——mmc core初始化相关

1、mmc_init实现

负责初始化整个mmc core。

  • 主要工作:

    • 分配一个workqueue,用于专门处理mmc core的执行的工作
    • 注册mmc bus
    • 注册mmc host class

代码如下:

static int __init mmc_init(void)
{
int ret; /* 分配一个workqueue,用于专门处理mmc core的执行的工作 */
workqueue = alloc_ordered_workqueue("kmmcd", 0); /* 注册mmc bus */
ret = mmc_register_bus(); // 调用mmc_register_bus注册mmc bus,具体参考《mmc core——bus模块说明》
// 会生成/sys/bus/mmc目录 /* 注册mmc host class */
ret = mmc_register_host_class(); // 调用mmc_register_host_class注册mmc host class,具体参考《mmc core——host模块说明》
// 会生成/sys/class/mmc_host目录 /* 注册sdio bus */
ret = sdio_register_bus(); return 0; } subsys_initcall(mmc_init);

三、接口代码说明——mmc host的管理和维护相关

1、mmc_claim_host & mmc_try_claim_host & mmc_release_host

host被使能之后就不能再次被使能,并且只能被某个进程独自占用。

可以简单地将host理解为一种资源,同时只能被一个进程获取,但是在占用进程里面可以重复占用。

在对host进行操作之前(包括发起mmc请求),必须使用mmc_claim_host和mmc_release_host来进行获取和释放。

  • 变量说明

    • mmc_host->claimed用来表示host是否被占用
    • mmc_host->claimer用来表示host的占用者(进程)
    • mmc_host->claim_cnt用来表示host的占用者的占用计数,为0时则会释放这个host
  • 代码如下

static inline void mmc_claim_host(struct mmc_host *host)
{
__mmc_claim_host(host, NULL);·// 调用__mmc_claim_host来获取host
} int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
{
/////只考虑abort为NULL的情况,在mmc core中的mmc_claim_host也是将其设置为NULL
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int stop; might_sleep(); // 说明这个函数可能导致进程休眠 add_wait_queue(&host->wq, &wait); // 把当前进程加入到等待队列中 spin_lock_irqsave(&host->lock, flags);
while (1) { // 以下尝试获取host,如果host正在被占用,会进入休眠
set_current_state(TASK_UNINTERRUPTIBLE); // 设置进程状态为TASK_UNINTERRUPTIBLE状态
stop = abort ? atomic_read(abort) : 0;
if (stop || !host->claimed || host->claimer == current) // 当host的占用标志claimed为0,或者占用者是当前进程的时候,说明可以占用了,退出
break;
spin_unlock_irqrestore(&host->lock, flags);
schedule(); // 否则,进行调度进入休眠
spin_lock_irqsave(&host->lock, flags);
}
set_current_state(TASK_RUNNING); // 设置进程为运行状态
if (!stop) {
host->claimed = 1; // 设置占用标志claimed
host->claimer = current; // 设置占用者为当前进程
host->claim_cnt += 1; // 占用计数加1
} else
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait); // 将当前进程从等待队列中退出
if (host->ops->enable && !stop && host->claim_cnt == 1)
host->ops->enable(host); // 调用host操作集中的enable方法来占用该host,对应sdhci类host即为sdhci_enable
return stop;
} void mmc_release_host(struct mmc_host *host)
{
unsigned long flags; WARN_ON(!host->claimed); if (host->ops->disable && host->claim_cnt == 1) // 当前claim_cnt为1(马上要变为0),调用释放host了
host->ops->disable(host); // 调用host操作集中的disable方法来释放该host,对应sdhci类host即为sdhci_disable spin_lock_irqsave(&host->lock, flags);
if (--host->claim_cnt) {
/* Release for nested claim */
spin_unlock_irqrestore(&host->lock, flags); // 如果减一之后计数还不为0,说明当前进程需要继续占用该host,不做其他操作
} else { // 以下需要释放该host
host->claimed = 0; // 设置占用标志claimed为0
host->claimer = NULL; // 清空占用者(进程)
spin_unlock_irqrestore(&host->lock, flags);
wake_up(&host->wq); // 唤醒host的等待队列,让那些调用mmc_claim_host睡眠等待host资源的进程被唤醒
}
} int mmc_try_claim_host(struct mmc_host *host)
{
// 和mmc_claim_host的主要区别在于进程不会休眠,获取失败直接返回
int claimed_host = 0;
unsigned long flags; spin_lock_irqsave(&host->lock, flags);
if (!host->claimed || host->claimer == current) {
host->claimed = 1;
host->claimer = current;
host->claim_cnt += 1;
claimed_host = 1;
}
spin_unlock_irqrestore(&host->lock, flags);
if (host->ops->enable && claimed_host && host->claim_cnt == 1)
host->ops->enable(host);
return claimed_host;
}

会调用mmc_host->struct mmc_host_ops->enable和mmc_host->struct mmc_host_ops->disable来使能和禁用host。

对于sdhci类的host,相应就是sdhci_enable和sdhci_disable。

2、mmc_power_up & mmc_power_off

mmc host的上电操作和关电操作。

  • mmc的power状态

    • MMC_POWER_OFF:掉电状态
    • MMC_POWER_UP:正在上电的状态
    • MMC_POWER_ON:供电正常的状态
  • 主要工作

    主要工作就是初始化host的总线设置、总线时钟以及工作电压、信号电压。

代码如下

void mmc_power_up(struct mmc_host *host)
{
int bit; /* 判断是否已经处于MMC_POWER_ON,是的话不进行后续操作 */
if (host->ios.power_mode == MMC_POWER_ON)
return; /* 第一阶段,先设置对应的io setting使host处于MMC_POWER_UP的状态(总线工作频率没有设置) */
mmc_host_clk_hold(host); // 先获取host时钟 /* If ocr is set, we use it */
if (host->ocr)
bit = ffs(host->ocr) - 1; // 选择一个ocr配置设置为host的工作电压
else
bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
if (mmc_host_is_spi(host))
host->ios.chip_select = MMC_CS_HIGH;
else {
host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; // 设置总线模式
}
host->ios.power_mode = MMC_POWER_UP;
host->ios.bus_width = MMC_BUS_WIDTH_1; // 设置总线宽度为1
host->ios.timing = MMC_TIMING_LEGACY; // 串口时序
mmc_set_ios(host); // 调用mmc_set_ios设置总线的io setting,后面会说明 /*
* This delay should be sufficient to allow the power supply
* to reach the minimum voltage.
*/
mmc_delay(10); /* 第二阶段,以host的初始化工作频率再次设置io setting,使host处于MMC_POWER_ON状态 */
host->ios.clock = host->f_init; // 设置总线的时钟频率
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host); // 调用mmc_set_ios设置总线的io setting,后面会说明 /*
* This delay must be at least 74 clock sizes, or 1 ms, or the
* time required to reach a stable voltage.
*/
mmc_delay(10); /* 设置信号的电压 */
/* Set signal voltage to 3.3V */
__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); mmc_host_clk_release(host); // 释放host时钟
}

3、mmc_start_host & mmc_stop_host

mmc_start_host 用来启动一个host,mmc_stop_host用来停止一个host。

当底层host controller调用mmc_add_host来注册host时,在mmc_add_host中就会调用mmc_start_host来启动一个host了。具体参考《mmc core——host模块说明》。

相对应的,会在mmc_remove_host中调用mmc_stop_host停止host。

void mmc_start_host(struct mmc_host *host)
{
mmc_claim_host(host); // 因为上电操作涉及到对host的使用和设置,需要先占用host host->f_init = max(freqs[0], host->f_min); // 通过最小频率要设置初始化频率
host->rescan_disable = 0; // 设置rescan_disable标志为0,说明已经可以进行card检测了
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) // 如果mmc属性设置了MMC_CAP2_NO_PRESCAN_POWERUP,也就是在rescan前不需要进行power up操作时,则进行关电
mmc_power_off(host);
else
mmc_power_up(host); // 否则,调用mmc_power_up对host进行上电操作。这里也是mmc core中启动host的核心函数。 mmc_release_host(host); // 完成上电操作,释放host /* 到这里host已经可以工作了,可以开始进行后续的card操作了 */
mmc_detect_change(host, 0); // 调用mmc_detect_change检测card变化,后续会继续说明
}

四、接口代码说明——card检测相关

1、mmc_detect_change

在上述中我们知道在启动host的函数mmc_start_host 中最后调用了mmc_detect_change来开始检测card(也就是检测mmc卡槽的状态变化情况)。

其实mmc_detect_change是在driver发现mmc卡槽状态发生变化时,调用mmc_detect_change来进行确认和处理。

void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
#ifdef CONFIG_MMC_DEBUG
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
WARN_ON(host->removed);
spin_unlock_irqrestore(&host->lock, flags);
#endif
host->detect_change = 1; // 检测到card状态发生变化的标识 mmc_schedule_delayed_work(&host->detect, delay); // 间隔delay jiffies之后调用host->detect的工作
}

在《host模块说明》已经知道了在mmc_alloc_host中默认将host->detect工作设置为mmc_rescan(card重新扫描)函数, INIT_DELAYED_WORK(&host->detect, mmc_rescan)。

当然,host也可以自己另外设置,但是一般都是使用mmc core提供的mmc_rescan作为detect工作来搜索card。下面说明。

2、mmc_rescan

用于检测host的卡槽状态,并对状态变化做相应的操作。

有card插入时,重新扫描mmc card。

void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
bool extend_wakelock = false; /* 如果rescan_disable被设置,说明host此时还禁止rescan */
if (host->rescan_disable)
return; /* 对于设备不可移除的host来说,只能rescan一次 */
/* If there is a non-removable card registered, only scan once */
if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
return;
host->rescan_entered = 1; mmc_bus_get(host); // 获取host对应的bus
mmc_rpm_hold(host, &host->class_dev); // 使host处于rpm resume的状态 /* 以下判断原来的card是否已经被移除,移除了则需要做相应的操作 */
/*
* if there is a _removable_ card registered, check whether it is
* still present
*/
if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
&& !(host->caps & MMC_CAP_NONREMOVABLE))
host->bus_ops->detect(host);
// host->bus_ops存在的话说明之前是有card插入的状态
// 需要调用host->bus_ops->detect检测card是否被移除,是的话在host->bus_ops->detect中做相应的处理
// 对于mmc type card来说,对应就是mmc_detect,具体参考《card相关模块》 host->detect_change = 0;
/* If the card was removed the bus will be marked
* as dead - extend the wakelock so userspace
* can respond */
if (host->bus_dead)
extend_wakelock = 1; // 需要设置一个wakelock锁,使用户空间可以及时做出相应 /*
* Let mmc_bus_put() free the bus/bus_ops if we've found that
* the card is no longer present.
*/
mmc_bus_put(host);
// 因为在这个函数的前面已经获取了一次host,可能导致host->bus_ops->detect中检测到card拔出之后,没有真正释放到host的bus,所以这里先put一次
// host bus的计数(bus_refs)为0的时候,会调用__mmc_release_bus清空host bus的信息
mmc_bus_get(host);
// 再获取host bus /* if there still is a card present, stop here */
if (host->bus_ops != NULL) { // 说明此时还有card插入,退出后续的操作
mmc_rpm_release(host, &host->class_dev);
mmc_bus_put(host);
goto out;
} mmc_rpm_release(host, &host->class_dev); /*
* Only we can add a new handler, so it's safe to
* release the lock here.
*/
mmc_bus_put(host); /* 检测当前卡槽状态,根据卡槽状态做相应的操作 */
if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
// 调用host->ops->get_cd来判断host的卡槽的当前card插入状态
// 对应sdhci类型的host来说,就是sdhci_get_cd
// 为0的时候,表示没有card插入,对应host进行power off操作之后进行退出
// 为1的时候,表示当前有card插入,跳到后续的操作
mmc_claim_host(host);
mmc_power_off(host);
mmc_release_host(host);
goto out;
} mmc_rpm_hold(host, &host->class_dev);
mmc_claim_host(host);
if (!mmc_rescan_try_freq(host, host->f_min)) // 调用mmc_rescan_try_freq,以支持的最低频率作为工作频率尝试搜索card,后续继续说明
extend_wakelock = true;
mmc_release_host(host);
mmc_rpm_release(host, &host->class_dev);
out:
/* only extend the wakelock, if suspend has not started yet */
if (extend_wakelock && !host->rescan_disable)
wake_lock_timeout(&host->detect_wake_lock, HZ / 2); // 占用wakelock,使系统在HZ/2的时间内不会休眠 if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
// 当host设置了MMC_CAP_NEEDS_POLL属性时,需要每隔HZ的时间轮询检测host的卡槽状态,
// 调度了host->detect工作,对应就是mmc_rescan
}

会调用host->ops->get_cd来判断host的卡槽的当前card插入状态,对应sdhci类型的host就是sdhci_get_cd。

会调用host->bus_ops->detect检测card是否被移除,是的话在host->bus_ops->detect中做相应的处理。对于mmc type card,就是mmc_detect。

3、mmc_rescan_try_freq

以一定频率搜索host bus上的card。

static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq; mmc_power_up(host); // 给host做上电操作 mmc_hw_reset_for_init(host); // 硬件复位和初始化
mmc_go_idle(host); mmc_send_if_cond(host, host->ocr_avail); // 获取card的可用频率,存储到host->ocr_avail中 /* Order's important: probe SDIO, then SD, then MMC */
/* 用于绑定card到host bus上(也就是card和host的绑定)。 */
if (!mmc_attach_sdio(host)) // 先假设card是sdio type card,尝试绑定到host bus上,失败则说明不是sdio type card,继续后面的操作,否则返回
return 0;
if (!mmc_attach_sd(host)) // 先假设card是sd type card,尝试绑定到host bus上,失败则说明不是sd type card,继续后面的操作,否则返回
return 0;
if (!mmc_attach_mmc(host)) // 先假设card是mmc type card,尝试绑定到host bus上,失败则说明不是mmc type card,继续后面的操作,否则返回
// mmc_attach_mmc通过mmc_host获取mmc type card信息,初始化mmc_card,并进行部分驱动,最后将其注册到mmc_bus上。
// 具体参考《card相关模块说明》
return 0; mmc_power_off(host);
return -EIO;
}

五、接口代码说明——总线io setting相关

0、mmc_ios说明

struct mmc_ios 由mmc core定义的规范的结构,用来维护mmc总线相关的一些io setting。如下:

struct mmc_ios {
unsigned int clock; /* clock rate */ // 当前工作频率
unsigned int old_rate; /* saved clock rate */ // 上一次的工作频率
unsigned long clk_ts; /* time stamp of last updated clock */ // 上一次更新工作频率的时间戳
unsigned short vdd;/* vdd stores the bit number of the selected voltage range from below. */ // 支持的电压表
unsigned char bus_mode; /* command output mode */ // 总线输出模式,包括开漏模式和上拉模式
unsigned char chip_select; /* SPI chip select */ // spi片选
unsigned char power_mode; /* power supply mode */ // 电源状态模式
unsigned char bus_width; /* data bus width */ // 总线宽度
unsigned char timing; /* timing specification used */ // 时序类型
unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ // 信号的工作电压
unsigned char drv_type; /* driver type (A, B, C, D) */ // 驱动类型
};

在设置总线io setting的过程中,就是要设置mmc_host->mmc_ios中的这些成员。

然后通过调用mmc_set_ios进行统一设置。后续会继续说明。

1、mmc_set_ios

统一设置mmc总线的io设置(io setting)。

void mmc_set_ios(struct mmc_host *host)
{
struct mmc_ios *ios = &host->ios; if (ios->clock > 0)
mmc_set_ungated(host); // 关闭clock的门控
host->ops->set_ios(host, ios); // 调用host->ops->set_ios来对mmc总线的io setting进行设置,核心函数
// 对于sdhci类型的host,对应就是sdhci_set_ios
}

会调用host->ops->set_ios来对mmc总线的io setting进行设置,核心函数。对于sdhci类型的host,对应就是sdhci_set_ios

2、mmc_set_bus_mode & mmc_set_bus_width

  • mmc_set_bus_mode用于设置总线模式,有如下模式

    • MMC_BUSMODE_OPENDRAIN(开漏模式)
    • MMC_BUSMODE_PUSHPULL(上拉模式)
  • mmc_set_bus_width用于设置总线宽度,有如下模式
    • MMC_BUS_WIDTH_1
    • MMC_BUS_WIDTH_4
    • MMC_BUS_WIDTH_8

代码如下:

void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
{
mmc_host_clk_hold(host);
host->ios.bus_mode = mode;
mmc_set_ios(host);
mmc_host_clk_release(host);
} void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
mmc_host_clk_hold(host);
host->ios.bus_width = width;
mmc_set_ios(host);
mmc_host_clk_release(host);
}

其他设置io setting的函数类似,不多说明。

六、接口代码说明——host的mmc总线相关

1、mmc_attach_bus & mmc_detach_bus

  • 主要功能

    • mmc_attach_bus用于将分配一个mmc总线操作集给host。
    • mmc_detach_bus用于释放和host相关联的mmc总线操作集。
  • 一些变量
    • mmc_host->bus_ops,表示host的mmc总线操作集
    • mmc_host->bus_refs,表示host的mmc总线的使用者计数
    • mmc_host->bus_dead,表示host的mmc总线是否被激活,如果设置了bus_ops,那么就会被激活了
  • 代码如下
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
{
unsigned long flags; spin_lock_irqsave(&host->lock, flags); BUG_ON(host->bus_ops); // 不允许重复设置host的mmc总线操作集
BUG_ON(host->bus_refs); // 当mmc总线的使用者计数还存在时,不允许设置host的mmc总线操作集 host->bus_ops = ops; // 设置host的mmc总线操作集
host->bus_refs = 1; // host的mmc总线的使用者计数设置为1,相当于调用了mmc_bus_get
host->bus_dead = 0; // 总线被激活了 spin_unlock_irqrestore(&host->lock, flags);
} void mmc_detach_bus(struct mmc_host *host)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
host->bus_dead = 1; // host的mmc总线设置为dead状态
spin_unlock_irqrestore(&host->lock, flags);
mmc_bus_put(host); // 调用mmc_bus_put释放host的mmc总线,也就是对host的mmc总线的使用者计数-1
}

在《card相关模块》中可以看到mmc_attach_mmc->mmc_attach_bus_ops调用mmc_attach_bus来绑定了host的mmc总线操作集为mmc_ops_unsafe或者mmc_ops

2、mmc_bus_get & mmc_bus_put

static inline void mmc_bus_get(struct mmc_host *host)
{
unsigned long flags; spin_lock_irqsave(&host->lock, flags);
host->bus_refs++; // 对host的mmc总线的使用者计数+1
spin_unlock_irqrestore(&host->lock, flags);
} static inline void mmc_bus_put(struct mmc_host *host)
{
unsigned long flags; spin_lock_irqsave(&host->lock, flags);
host->bus_refs--; // 对host的mmc总线的使用者计数-1
if ((host->bus_refs == 0) && host->bus_ops) // 说明host的mmc总线当前并没有使用,调用__mmc_release_bus进行实际的释放操作
__mmc_release_bus(host);
spin_unlock_irqrestore(&host->lock, flags);
} static void __mmc_release_bus(struct mmc_host *host)
{
host->bus_ops = NULL; // 清空host的mmc总线操作集
}

七、接口代码说明——mmc请求相关

分成同步的mmc请求和异步的mmc请求。差别如下:

1、流程上的差别:
(1)会阻塞的处理流程:
mmc_wait_for_req
——》__mmc_start_req // 发起请求
————》init_completion(&mrq->completion);
————》mrq->done = mmc_wait_done
————》mmc_start_request(host, mrq); // 实际发起请求的操作
——》mmc_wait_for_req_done // 阻塞等待请求处理完成
——》返回 (2)不阻塞等待该命令的处理流程:
(注意:并不是说调用这个接口并不会阻塞,而是不会为了等待当前请求处理完成而阻塞,但是可能会等待上一次请求处理完成而阻塞)
mmc_start_req
——》mmc_wait_for_data_req_done // 阻塞等待上一次的请求处理
——》__mmc_start_data_req // 发起异步请求
————》mrq->done = mmc_wait_data_done
————》mmc_start_request // 实际发起请求的操作
——》返回

最后都是调用了mmc_start_request使host向MMC发起请求。

0、数据结构说明

一个mmc请求分成两部分内容,分别是命令部分和数据部分。

  • mmc_command
struct mmc_command {
u32 opcode; // 命令的操作码,如MMC_GO_IDLE_STATE、MMC_SEND_OP_COND等等
u32 arg; // 命令的参数
u32 resp[4]; // response值
unsigned int flags; /* expected response type */ // 期待的response的类型
#define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE)) /*
* These are the command types.
*/
#define mmc_cmd_type(cmd) ((cmd)->flags & MMC_CMD_MASK) unsigned int retries; /* max number of retries */ // 失败时的重复尝试次数
unsigned int error; /* command error */ // 命令的错误码 /*
* Standard errno values are used for errors, but some have specific
* meaning in the MMC layer:
*
* ETIMEDOUT Card took too long to respond
* EILSEQ Basic format problem with the received or sent data
* (e.g. CRC check failed, incorrect opcode in response
* or bad end bit)
* EINVAL Request cannot be performed because of restrictions
* in hardware and/or the driver
* ENOMEDIUM Host can determine that the slot is empty and is
* actively failing requests
*/ unsigned int cmd_timeout_ms; /* in milliseconds */ // 命令执行的等待超时事件 struct mmc_data *data; /* data segment associated with cmd */ // 和该命令关联在一起的数据段
struct mmc_request *mrq; /* associated request */ // 该命令关联到哪个request
};
  • mmc_data
struct mmc_data {
unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */ // 超时时间,以ns为单位
unsigned int timeout_clks; /* data timeout (in clocks) */ // 超时时间,以clock为单位
unsigned int blksz; /* data block size */ // 块大小
unsigned int blocks; /* number of blocks */ // 块数量
unsigned int error; /* data error */ // 传输的错误码
unsigned int flags; // 传输标识 #define MMC_DATA_WRITE (1 << 8)
#define MMC_DATA_READ (1 << 9)
#define MMC_DATA_STREAM (1 << 10) unsigned int bytes_xfered; struct mmc_command *stop; /* stop command */ // 结束传输的命令
struct mmc_request *mrq; /* associated request */ // 该命令关联到哪个request unsigned int sg_len; /* size of scatter list */
struct scatterlist *sg; /* I/O scatter list */
s32 host_cookie; /* host private data */
bool fault_injected; /* fault injected */
};
  • mmc_request

struct mmc_request是mmc core向host controller发起命令请求的处理单位。

struct mmc_request {
struct mmc_command *sbc; /* SET_BLOCK_COUNT for multiblock */ // 设置块数量的命令,怎么用的后续再补充
struct mmc_command *cmd; // 要传输的命令
struct mmc_data *data; // 要传输的数据
struct mmc_command *stop; // 结束命令,怎么用的后续再补充 struct completion completion; // 完成量
void (*done)(struct mmc_request *);/* completion function */ // 传输结束后的回调函数
struct mmc_host *host; // 所属host
};
  • mmc_async_req
struct mmc_async_req {
/* active mmc request */
struct mmc_request *mrq;
unsigned int cmd_flags; /* copied from struct request */ /*
* Check error status of completed mmc request.
* Returns 0 if success otherwise non zero.
*/
int (*err_check) (struct mmc_card *, struct mmc_async_req *);
/* Reinserts request back to the block layer */
void (*reinsert_req) (struct mmc_async_req *);
/* update what part of request is not done (packed_fail_idx) */
int (*update_interrupted_req) (struct mmc_card *,
struct mmc_async_req *);
};

1、mmc_wait_for_req

发起mmc_request请求并且等待其处理完成。由其他需要发起mmc请求的模块调用。

可以结合后面的mmc_request_done来看。

void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
if (mmc_bus_needs_resume(host))
mmc_resume_bus(host);
#endif
__mmc_start_req(host, mrq); // 开始发起mmc_request请求
mmc_wait_for_req_done(host, mrq); // 等待mmc_request处理完成
} //-----------------------------------__mmc_start_req说明,开始发起mmc_request请求
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
/* 发起mmc_request前的一些初始化工作,包括完成量和处理完成的回调函数的设置 */
init_completion(&mrq->completion); // 初始化完成量,在mmc_wait_for_req_done中会去等待这个完成量
mrq->done = mmc_wait_done;
// 设置mmc_request处理完成的回调函数,会调用complete(&mrq->completion);来设置完成量
// host controller会调用mmc_request_done来执行这个回调函数,具体在后面分析
if (mmc_card_removed(host->card)) { // 检测card是否存在
mrq->cmd->error = -ENOMEDIUM;
complete(&mrq->completion);
return -ENOMEDIUM;
} /* 调用mmc_start_request发起mmc请求 */
mmc_start_request(host, mrq); // 开始处理mmc_request请求
return 0;
} static void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{ WARN_ON(!host->claimed); /* 以下对mmc_request的各个成员,包括cmd、data、stop做验证操作和关联操作 */
mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
if (mrq->data) {
BUG_ON(mrq->data->blksz > host->max_blk_size);
BUG_ON(mrq->data->blocks > host->max_blk_count);
BUG_ON(mrq->data->blocks * mrq->data->blksz >
host->max_req_size);
mrq->cmd->data = mrq->data; // 也就是说mmc_request的data和其cmd中的data是一一样的
mrq->data->error = 0;
mrq->data->mrq = mrq;
if (mrq->stop) {
mrq->data->stop = mrq->stop;
mrq->stop->error = 0;
mrq->stop->mrq = mrq;
}
#ifdef CONFIG_MMC_PERF_PROFILING
if (host->perf_enable)
host->perf.start = ktime_get();
#endif
} /* 获取时钟 */
mmc_host_clk_hold(host); /* 调用host controller的request方法来处理mmc_request请求 */
host->ops->request(host, mrq);
// host->ops->request也就是host controller的request方法,对于sdhci类型的host来说,就是sdhci_request
} //-----------------------------------mmc_wait_for_req_done说明,等待mmc_request处理完成
static void mmc_wait_for_req_done(struct mmc_host *host,
struct mmc_request *mrq)
{
struct mmc_command *cmd; while (1) {
wait_for_completion_io(&mrq->completion); // 在这里休眠,等待mrq->completion完成量,在__mmc_start_req中初始化的 cmd = mrq->cmd; // 获取对应的command /*
* If host has timed out waiting for the commands which can be
* HPIed then let the caller handle the timeout error as it may
* want to send the HPI command to bring the card out of
* programming state.
*/
if (cmd->ignore_timeout && cmd->error == -ETIMEDOUT)
break; if (!cmd->error || !cmd->retries || mmc_card_removed(host->card))
// 如果command正常处理完成,或者失败重复尝试次数为0,或者card被移除了,直接退出循环返回
break; // 以下处理失败重复尝试的情况
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host), cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
host->ops->request(host, mrq);
}
}

会调用host->ops->request来对mmc_request进行处理,对于sdhci类型的host,对应就是sdhci_request。

这个方法就是mmc_request实际被处理的核心。

2、mmc_request_done

通知mmc core某个mmc_request已经处理完成,由host controller调用。

以sdhci类型的host为例,处理完一个mmc_request之后,会执行sdhci_tasklet_finish,而在sdhci_tasklet_finish中会调用mmc_request_done来通知host某个mmc_request已经处理完成了。

void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
int err = cmd->error; if (host->card)
mmc_update_clk_scaling(host); if (err && cmd->retries && !mmc_card_removed(host->card)) {
// command执行出错,如果还需要重复尝试的话,这里不释放clock,只是通知mmc core
if (mrq->done)
mrq->done(mrq);
// 执行mmc_request的回调函数来通知mmc core,
// 对于__mmc_start_req发起的request来说,就是mmc_wait_done,上面已经说明过了
// 对于__mmc_start_data_req发起的request来说,就是mmc_wait_data_done,后面会说明
} else {
mmc_should_fail_request(host, mrq);
// 用于模拟data传输概率出错的情况
// 具体参考http://blog.csdn.net/luckywang1103/article/details/52224160 if (mrq->done)
mrq->done(mrq);
// 执行mmc_request的回调函数来通知mmc core,对于__mmc_start_req发起的request来说,就是mmc_wait_done,上面已经说明过了 mmc_host_clk_release(host);
}
}

通过上述,mrq->done被调度,mmc_wait_done被执行,mrq->completion被设置。

然后等待mrq->completion的mmc_wait_for_req_done就会继续往下执行。

3、mmc_wait_for_cmd

mmc_wait_for_cmd用于处理一个不带数据请求的命令。

会被封装到mmc_request中,通过调用mmc_wait_for_req来发起请求。

int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{
struct mmc_request mrq = {NULL}; WARN_ON(!host->claimed);
memset(cmd->resp, 0, sizeof(cmd->resp)); // 清空command的response
cmd->retries = retries; // 失败时的重复尝试次数
mrq.cmd = cmd; // 封装到mmc_request中
cmd->data = NULL; // 不带数据包的命令,故清空data
mmc_wait_for_req(host, &mrq); // 调用mmc_wait_for_req发起mmc请求并且等待其处理完成 return cmd->error; // 返回错误码
}

4、mmc_start_req(重要)

机制说明如下:mmc_start_req会先判断上一次的asycn_req是否处理完成,如果没有处理完成,则会等待其处理完成。

如果处理完成了,为当前要处理的asycn_req发起请求,但是并不会等待,而是直接返回。

注意:并不是说调用这个接口并不会阻塞,而是不会为了等待当前请求处理完成而阻塞,但是可能会等待上一次请求处理完成而阻塞。这样,可以利用等待的一部分时间来做其他操作。

为了方便理解这个函数,需要看一下其函数注释。

  • 要注意,在函数里面有两个异步请求:

    • areq:表示新的异步请求
    • host->areq:表示上一次发起的、正在处理、等待完成的异步请求

代码如下(为了方便理解,对代码进行了简化):

/**
* mmc_start_req - start a non-blocking request // 该函数用来发起一个不阻塞的请求
* @host: MMC host to start command // 要发起对应请求的host
* @areq: async request to start // 要发起的异步请求
* @error: out parameter returns 0 for success, otherwise non zero // 返回值,返回0表示成功,返回非零表示失败
*
* Start a new MMC custom command request for a host. // 为host发起的一个新的mmc命令请求
* If there is on ongoing async request wait for completion // 如果host已经有一个正在处理、等待完成的异步请求,那么会等待这个请求完成!!!
* of that request and start the new one and return. // 然后发起新的请求,然后返回!!!
* Does not wait for the new request to complete. // 并不会等待这个新的请求完成!!!
*
* Returns the completed request, NULL in case of none completed. // 会返回被完成的mmc请求(而不是新的mmc请求。)空表示没有mmc请求被完成。
* Wait for the an ongoing request (previoulsy started) to complete and
* return the completed request. If there is no ongoing request, NULL
* is returned without waiting. NULL is not an error condition.
// 等待上一次发起的mmc请求完成,然后把这个mmc请求返回。如果没有mmc请求正在处理,那么就直接返回而不会等待。空并不是错误条件。
*/
struct mmc_async_req *mmc_start_req(struct mmc_host *host,
struct mmc_async_req *areq, int *error)
{
int err = 0;
int start_err = 0;
struct mmc_async_req *data = host->areq;
unsigned long flags;
bool is_urgent; /* Prepare a new request */
/* 为新的异步请求做准备处理 */
if (areq) {
/*
* start waiting here for possible interrupt
* because mmc_pre_req() taking long time
*/
mmc_pre_req(host, areq->mrq, !host->areq);
} /* 对上一次发起的、正在处理、等待完成的异步请求进行处理、等待操作 */
if (host->areq) {
err = mmc_wait_for_data_req_done(host, host->areq->mrq, areq); // 在这里等待正在处理的异步请求处理完成
//.......以下过滤了错误处理的部分
} /* 对新的异步请求进行发起操作 */
if (!err && areq) {
/* urgent notification may come again */
spin_lock_irqsave(&host->context_info.lock, flags);
is_urgent = host->context_info.is_urgent;
host->context_info.is_urgent = false;
spin_unlock_irqrestore(&host->context_info.lock, flags);
if (!is_urgent || (areq->cmd_flags & REQ_URGENT)) {
start_err = __mmc_start_data_req(host, areq->mrq); // 调用__mmc_start_data_req发起新的异步请求
} else {
/* previous request was done */
err = MMC_BLK_URGENT_DONE;
if (host->areq) {
mmc_post_req(host, host->areq->mrq, 0);
host->areq = NULL;
}
areq->reinsert_req(areq);
mmc_post_req(host, areq->mrq, 0);
goto exit;
}
} if (host->areq)
mmc_post_req(host, host->areq->mrq, 0); /* Cancel a prepared request if it was not started. */
if ((err || start_err) && areq)
mmc_post_req(host, areq->mrq, -EINVAL); if (err)
host->areq = NULL;
else
host->areq = areq; exit:
if (error)
*error = err;
return data; // 反正上一次正常处理的异步请求
} //-----------------------------------------------------------------------------------------------------------------------------
static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
{
mrq->done = mmc_wait_data_done;
// 设置mmc_request处理完成的回调函数,会唤醒正在等待请求被完成的进程,后面说明
// host controller会调用mmc_request_done来执行这个回调函数,具体前面分析过了 mrq->host = host;
mmc_start_request(host, mrq); // 开始处理mmc_request请求,前面已经说明过了 return 0;
} static void mmc_wait_data_done(struct mmc_request *mrq)
{
unsigned long flags;
struct mmc_context_info *context_info = &mrq->host->context_info; spin_lock_irqsave(&context_info->lock, flags);
mrq->host->context_info.is_done_rcv = true; // 设置is_done_rcv标识
wake_up_interruptible(&mrq->host->context_info.wait); // 唤醒context_info上的等待进程
spin_unlock_irqrestore(&context_info->lock, flags);
} //-----------------------------------------------------------------------------------------------------------------------------
static int mmc_wait_for_data_req_done(struct mmc_host *host,
struct mmc_request *mrq,
struct mmc_async_req *next_req)
{
// struct mmc_request *mrq:表示正在等待完成的请求
// struct mmc_async_req *next_req:表示下一次要执行的异步请求
struct mmc_command *cmd;
struct mmc_context_info *context_info = &host->context_info;
bool pending_is_urgent = false;
bool is_urgent = false;
bool is_done_rcv = false;
int err, ret;
unsigned long flags; while (1) {
/* 在这里等待正在进行的请求完成,会在mmc_wait_data_done中被唤醒 */
/* 有几种情况会唤醒等待进程 */
ret = wait_io_event_interruptible(context_info->wait,(context_info->is_done_rcv || context_info->is_new_req || context_info->is_urgent));
spin_lock_irqsave(&context_info->lock, flags);
is_urgent = context_info->is_urgent;
is_done_rcv = context_info->is_done_rcv;
context_info->is_waiting_last_req = false;
spin_unlock_irqrestore(&context_info->lock, flags); /* 对请求处理完成的处理 */
if (is_done_rcv) {
context_info->is_done_rcv = false;
context_info->is_new_req = false;
cmd = mrq->cmd; if (!cmd->error || !cmd->retries || mmc_card_removed(host->card)) {
/* 请求正常处理完成,或者失败但是不需要重复尝试的情况的处理 */
err = host->areq->err_check(host->card, host->areq);
//.......
break; /* return err */
} else {
/* 对请求处理出错并且需要重复尝试的情况的处理 */
//.......
}
}
}
return err;
}

6. [mmc subsystem] mmc core(第六章)——mmc core主模块的更多相关文章

  1. 10. [mmc subsystem] host(第四章)——host实例(sdhci-msm说明)

    一.说明 sdhci-msm是指高通的mmc host,其使用了标准SDHC标准.故可以使用前面说的<host(第二章)--sdhci>和<host(第三章)--sdhci-pltf ...

  2. 9. [mmc subsystem] host(第三章)——sdhci-pltfm说明

    一.sdhci-pltfm说明 sdhci-pltfm并不是实际某个host的driver. sdhci-pltfm是指在sdhci core的基础上,提供了统一对sdhci_host的必要属性进行解 ...

  3. 5. [mmc subsystem] mmc core(第五章)——card相关模块(mmc type card)

    零.说明(重要,需要先搞清楚概念有助于后面的理解) 1.mmc core card相关模块为对应card实现相应的操作,包括初始化操作.以及对应的总线操作集合.负责和对应card协议层相关的东西. 这 ...

  4. 4. [mmc subsystem] mmc core(第四章)——host模块说明

    零.说明 对应代码drivers/mmc/core/host.c,drivers/mmc/core/host.h. 为底层host controller driver实现mmc host的申请以及注册 ...

  5. 3. [mmc subsystem] mmc core(第三章)——bus模块说明

    零.说明 对应代码drivers/mmc/core/bus.c. 抽象出虚拟mmc bus,实现mmc bus的操作. 一.API总览 1.mmc bus相关 mmc_register_bus &am ...

  6. 8. [mmc subsystem] host(第二章)——sdhci

    一.sdhci core说明 1.sdhci说明 具体参考<host(第一章)--概述> SDHC:Secure Digital(SD) Host Controller,是指一套sd ho ...

  7. 7. [mmc subsystem] host(第一章)——概述

    一.host简单说明 host,也可以理解为host controller,是指mmc总线上的主机端,mmc总线的控制器,每个host controller对应一条mmc总线. host contro ...

  8. 2. [mmc subsystem] mmc core数据结构和宏定义说明

    一.host相关 1.struct mmc_host struct mmc_host是mmc core由host controller抽象出来的结构体,用于代表一个mmc host控制器. 数据结构如 ...

  9. 1. [mmc subsystem] 概念与框架

    一.概念 1.mmc的概念 mmc有很多种意义,具体如下: mmc MultiMedia Card,多媒体存储卡, 但后续泛指一个接口协定(一种卡式),能符合这接口的内存器都可称作mmc储存体. 主要 ...

随机推荐

  1. centos 的系统管理命令 service systemctl

    centos 的 systemctl 命令 systemctl is-enabled *.service     #查询服务是否开机启动 systemctl enable *.service    # ...

  2. 运行java程序

    使用方式: java类名 硬盘上有HelloWorld.class,那么类名就是HelloWorld java HelloWorld[运行先到class路径下] 一定要注意:java命令后面跟的不是文 ...

  3. git clone克隆代码显示“无权限或者确认存储库是否存在”

    今天我用公司的电脑要继续完成我自己的git上的小项目的时候,发现git clone失败,提示 然后我在公司电脑上生成公钥感觉又太麻烦了 网上找了个好方法就是把自己电脑上的.ssh文件夹拷贝到公司电脑上 ...

  4. 安装Ubuntu时出现Intel VT-X没有开启

    试了很多种方法,最后原来是只需要进入bosi系统,然后进入Intel Virtual Technology 使之成为Enabled即可,就这么简单粗暴.

  5. vue_03day

    目录 作业: vue组件操作页面渲染: 组件渲染: 作业: vue组件操作页面渲染: 1.有以下广告数据(实际数据命名可以略做调整) ad_data = { tv: [ {img: 'img/tv/0 ...

  6. Python 中的时间处理包datetime和arrow

    Python 中的时间处理包datetime和arrow 在获取贝壳分的时候用到了时间处理函数,想要获取上个月时间包括年.月.日等 # 方法一: today = datetime.date.today ...

  7. Module Code: CMT107

    Cardiff School of Computer Science and InformaticsCoursework Assessment Pro-formaModule Code: CMT107 ...

  8. Python 下载图片的三种方法

    import os os.makedirs('./image/', exist_ok=True) IMAGE_URL = "http://image.nationalgeographic.c ...

  9. CentOS 7.6 安装htop

    yum -y install epel-release.noarch yum -y install htop htop 上面左上角显示CPU.内存.交换区的使用情况,右边显示任务.负载.开机时间,下面 ...

  10. 一个简单的利用 WebClient 异步下载的示例(三)

    继续上一篇 一个简单的利用 WebClient 异步下载的示例(二) 后,继续优化它. 1. 直接贴代码了: DownloadEntry: public class DownloadEntry { p ...