一、说明

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. Node.js—学习

    一.Node.js 1. Hello World var http = require('http'); http.createServer(function(request, response) { ...

  2. 第十二周Scrum会议

    本次照片 总结上周所达成的工作 做到的工作 1. 将前端页面进行了比较美观的美化 2. 实现了后台的代码的整合,同时将flask项目的整体框架搭建完成 3. 进行了数据库的建表等一些工作 遇到的难点 ...

  3. 【Ribbon篇四】Ribbon介绍(1)

    Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具. 简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法, ...

  4. rss订阅

    其实,本质上和爬虫没区别,只不过这是人家主动给你数据,而且是编排好格式后的数据 按个人主页url更新内容 去重,按照redis去重的方式 按时间保存内容 mysql 保存为时间格式(可以根据时间比较大 ...

  5. 使用puppeteer爬取网页数据实践小结

    简单介绍Puppeteer Puppeteer是一个Node库,它通过DevTools协议提供高级API来控制Chrome或Chromium.Puppeteer默认以无头方式运行,但可以配置为有头方式 ...

  6. 题目:利用Calendar类计算自己的出生日期距今天多少天,再将自己的出生日期利用SimpleDateFormat类设定的格式输出显示

    package cn.exercise; import java.util.Calendar; import java.util.Date; import java.text.SimpleDateFo ...

  7. windows下mysql安装和配置

    历史版本下载地址安装,解压添加环境变量使用cmd中操作mysql进程修改mysql的配置附录:设置mysql随开机自启 TOC 历史版本下载地址 windows的mysql历史版本,推荐使用5.6版本 ...

  8. [转]WPF入门教程系列

    转载自:https://www.cnblogs.com/chillsrc/category/684419.html 谢谢浏览!

  9. Knative 应用在阿里云容器服务上的最佳实践

    作者|元毅 阿里云智能事业群高级开发工程师 相信通过前面几个章节的内容,大家对 Knative 有了初步的体感,那么在云原生时代如何在云上玩转 Knative?本篇内容就给你带来了 Knative 应 ...

  10. ASP.NET Core: BackgroundService停止(StopAsync)后无法重新启动(StartAsync)的问题

    这里的 BackgroundService 是指: Microsoft.Extensions.Hosting.BackgroundService 1. 问题复现 继承该BackgroundServic ...