备注:
1. Kernel版本:5.4
2. 使用工具:Source Insight 4.0
3. 参考博客:
6. [mmc subsystem] mmc core(第六章)——mmc core主模块
- Linux驱动——mmc core浅析(三)
- mmc core概述
- API总览
- mmc core初始化相关
- mmc host的管理和维护相关
- mmc card的操作相关(包括card状态的获取)
- 总线io setting相关
- host的mmc总线相关
- mmc请求相关
- card检测相关
- mmc core初始化相关
- mmc_init的实现
- mmc host的管理和维护相关
- mmc_claim_host & mmc_release_host
- mmc_power_up & mmc_power_off
- mmc_start_host & mmc_stop_host
- card检测相关
- mmc_detect_change
- mmc_rescan
- mmc_rescan_try_freq
- 总线io setting相关
- mmc_set_ios
- mmc_set_bus_mode & mmc_set_bus_width
- mmc_set_clock
- host的mmc总线相关
- mmc_attach_bus & mmc_detach_bus
- mmc_bus_get & mmc_bus_put
- mmc请求相关
- 同步的mmc请求
- 异步的mmc请求
- mmc_wait_for_req
- __mmc_start_req
- mmc_start_request
- __mmc_start_request
- mmc_wait_for_req_done
- mmc_request_done
- mmc_wait_for_cmd
本章主要介绍mmc core主模块,是mmc的实现核心,其对应的代码位置为:drivers/mmc/core/core.c。
本模块的主要功能有以下几个:
-
mmc core初始化,如:mmc bus、sdio bus、mmc host class等等。
-
mmc host的管理和维护,及为其他模块提供mmc_host的操作接口,如下:
host的启动与停止
host的占用与释放
host的电源状态的控制
host总线的的配置,如:bus_width、clock等
host的卡状态检测 -
为其他模块提供的mmc_card的操作接口,如下:
card的休眠与唤醒
card擦除
card的属性获取 -
为其他模块提供总线io setting的接口
-
为其他模块提供mmc请求接口
-
card检测接口
static int __init mmc_init(void); // 注册bus、host class static void __exit mmc_exit(void);mmc host的管理和维护相关
static inline void mmc_claim_host(struct mmc_host *host); // 申请占用host void mmc_release_host(struct mmc_host *host); // 释放占用的host void mmc_power_up(struct mmc_host *host, u32 ocr); // host上电 void mmc_power_off(struct mmc_host *host); // host掉电 void mmc_start_host(struct mmc_host *host); // 启动host void mmc_stop_host(struct mmc_host *host); // 关闭hostmmc card的操作相关(包括card状态的获取)
int mmc_hw_reset(struct mmc_host *host); // host硬件reset int mmc_sw_reset(struct mmc_host *host); // host软件reset int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, unsigned int arg); int mmc_can_erase(struct mmc_card *card); int mmc_can_trim(struct mmc_card *card); int mmc_can_discard(struct mmc_card *card); int mmc_can_sanitize(struct mmc_card *card); int mmc_can_secure_erase_trim(struct mmc_card *card); int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from, unsigned int nr);总线io setting相关
static inline void mmc_set_ios(struct mmc_host *host); // 模块内部使用,配置io属性 void mmc_set_chip_select(struct mmc_host *host, int mode); // spi mode配置cs void mmc_set_clock(struct mmc_host *host, unsigned int hz); // 配置时钟频率 void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); // 配置IO模式:开漏/上拉 void mmc_set_bus_width(struct mmc_host *host, unsigned int width); // 配置IO数据宽度 u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); // 配置电压 int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); void mmc_set_timing(struct mmc_host *host, unsigned int timing); //配置时序模式 void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); // 配置驱动类型host的mmc总线相关
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); // 绑定host的bus void mmc_detach_bus(struct mmc_host *host);mmc请求相关
void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq); // cmd done void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq); // mrq done int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq); // 直接发送mrq void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq); //等待mrq done void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq); //发送mrq,并等待mrq done int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries); //发送cmd, 并等待cmd done void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card); // 配置data传输超时时间card检测相关
void mmc_detect_change(struct mmc_host *host, unsigned long delay); //启动card状态检测 int mmc_detect_card_removed(struct mmc_host *host); //card移除 void mmc_rescan(struct work_struct *work); //card状态检测taskmmc core初始化相关 mmc_init的实现
负责初始化整个mmc core。主要工作:
- 注册mmc bus
- 注册mmc host class
- 注册sdio bus
static int __init mmc_init(void)
{
int ret;
ret = mmc_register_bus();
if (ret)
return ret;
ret = mmc_register_host_class();
if (ret)
goto unregister_bus;
ret = sdio_register_bus();
if (ret)
goto unregister_host_class;
return 0;
unregister_host_class:
mmc_unregister_host_class();
unregister_bus:
mmc_unregister_bus();
return ret;
}
static void __exit mmc_exit(void)
{
sdio_unregister_bus();
mmc_unregister_host_class();
mmc_unregister_bus();
}
subsys_initcall(mmc_init);
module_exit(mmc_exit);
MODULE_LICENSE("GPL");
mmc host的管理和维护相关
mmc_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, NULL); // 调用__mmc_claim_host来获取host
}
int __mmc_claim_host(struct mmc_host *host, struct mmc_ctx *ctx,
atomic_t *abort)
{
struct task_struct *task = ctx ? NULL : current;
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int stop;
bool pm = false;
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 || mmc_ctx_matches(host, ctx, task))// 当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
mmc_ctx_set_claimer(host, ctx, task);// 设置占用者为当前进程
host->claim_cnt += 1; // 占用计数加1
if (host->claim_cnt == 1)
pm = true;
} else
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait); // 将当前进程从等待队列中退出
if (pm)
pm_runtime_get_sync(mmc_dev(host));
return stop;
}
void mmc_release_host(struct mmc_host *host)
{
unsigned long flags;
WARN_ON(!host->claimed);
spin_lock_irqsave(&host->lock, flags);
if (--host->claim_cnt) {// 如果减一之后计数还不为0,说明当前进程需要继续占用该host,不做其他操作
spin_unlock_irqrestore(&host->lock, flags);
} else {// 以下需要释放该host
host->claimed = 0; // 设置占用标志claimed为0
host->claimer->task = NULL;// 清空占用者(进程)
host->claimer = NULL;
spin_unlock_irqrestore(&host->lock, flags);
wake_up(&host->wq); // 唤醒host的等待队列,让那些调用mmc_claim_host睡眠等待host资源的进程被唤醒
pm_runtime_mark_last_busy(mmc_dev(host));
if (host->caps & MMC_CAP_SYNC_RUNTIME_PM)
pm_runtime_put_sync_suspend(mmc_dev(host));
else
pm_runtime_put_autosuspend(mmc_dev(host));
}
}
会调用mmc_host->struct mmc_host_ops->enable和mmc_host->struct mmc_host_ops->disable来使能和禁用host。
mmc_power_up & mmc_power_offmmc host的上电操作和关电操作。
- mmc的power状态
#define MMC_POWER_OFF 0 // 掉电状态 #define MMC_POWER_UP 1 // 正在上电的状态 #define MMC_POWER_ON 2 // 供电正常的状态 #define MMC_POWER_UNDEFINED 3 // 未定义的状态
- 主要工作
主要工作就是初始化host的总线设置、总线时钟以及工作电压、信号电压。
void mmc_power_up(struct mmc_host *host, u32 ocr)
{
if (host->ios.power_mode == MMC_POWER_ON)
return;
mmc_pwrseq_pre_power_on(host); // 上电前的准备工作
host->ios.vdd = fls(ocr) - 1; // 选择一个ocr配置设置为host的工作电压
host->ios.power_mode = MMC_POWER_UP;
mmc_set_initial_state(host);//初始化状态,及调用mmc_set_ios设置总线的io setting
mmc_set_initial_signal_voltage(host);
mmc_delay(host->ios.power_delay_ms);
mmc_pwrseq_post_power_on(host);
host->ios.clock = host->f_init;// 设置总线的时钟频率为初始化频率
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
mmc_delay(host->ios.power_delay_ms);
}
void mmc_power_off(struct mmc_host *host)
{
if (host->ios.power_mode == MMC_POWER_OFF)
return;
//使host处于MMC_POWER_OFF的状态
mmc_pwrseq_power_off(host);
host->ios.clock = 0;
host->ios.vdd = 0;
host->ios.power_mode = MMC_POWER_OFF;
mmc_set_initial_state(host);
mmc_delay(1);
}
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_remove_host中调用mmc_stop_host停止host。
void mmc_start_host(struct mmc_host *host)
{
host->f_init = max(freqs[0], host->f_min);// 通过最小频率要设置初始化频率
host->rescan_disable = 0; // 设置rescan_disable标志为0,说明已经可以进行card检测了
host->ios.power_mode = MMC_POWER_UNDEFINED;
// 如果mmc属性没有设置了MMC_CAP2_NO_PRESCAN_POWERUP,也就是在rescan前需要进行power up操作时,则进行上电
if (!(host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)) {
mmc_claim_host(host); // 因为上电操作涉及到对host的使用和设置,需要先占用host
mmc_power_up(host, host->ocr_avail);
mmc_release_host(host); // 完成上电操作,释放host
}
//申请 cd_gpio
mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false); // 调用mmc_detect_change检测card变化
}
void mmc_stop_host(struct mmc_host *host)
{
if (host->slot.cd_irq >= 0) { //关闭cd wakeup功能
mmc_gpio_set_cd_wake(host, false);
disable_irq(host->slot.cd_irq);
}
host->rescan_disable = 1;
cancel_delayed_work_sync(&host->detect);
host->pm_flags = 0;
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
host->bus_ops->remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_power_off(host);
mmc_release_host(host);
mmc_bus_put(host);
return;
}
mmc_bus_put(host);
mmc_claim_host(host);
mmc_power_off(host); //host掉电
mmc_release_host(host);
}
card检测相关
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, bool cd_irq)
{
if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) &&
device_can_wakeup(mmc_dev(host)))
pm_wakeup_event(mmc_dev(host), 5000);
host->detect_change = 1; // 检测到card状态发生变化的标识
mmc_schedule_delayed_work(&host->detect, delay); // 间隔delay jiffies之后调用host->detect的工作(mmc_rescan)
}
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
_mmc_detect_change(host, delay, true);
}
EXPORT_SYMBOL(mmc_detect_change);
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);
int i;
if (host->rescan_disable)
return;
if (!mmc_card_is_removable(host) && host->rescan_entered)
return;
host->rescan_entered = 1;
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;
}
mmc_bus_get(host);// 获取host对应的bus
if (host->bus_ops && !host->bus_dead)
host->bus_ops->detect(host);
host->detect_change = 0;
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);
if (host->bus_ops != NULL) {
mmc_bus_put(host);
goto out;
}
mmc_bus_put(host);
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;
}
// 调用mmc_rescan_try_freq,以支持的最低频率作为工作频率尝试搜索card
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
break;
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host);
out:
// 当host设置了MMC_CAP_NEEDS_POLL属性时,需要每隔HZ的时间轮询检测host的卡槽状态,
// 调度了host->detect工作,对应就是mmc_rescan
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
}
会调用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。
以一定频率搜索host bus上的card。
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 Hzn",
mmc_hostname(host), __func__, host->f_init);
mmc_power_up(host, host->ocr_avail); // 给host做上电操作
mmc_hw_reset_for_init(host); // 硬件复位和初始化
// sdio card需发送cmd52,使card reset
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
sdio_reset(host);
mmc_go_idle(host);//发送card IDLE命令cmd0
//非sd card需发送cmd8,获取card的可用频率,存储到host->ocr_avail中
if (!(host->caps2 & MMC_CAP2_NO_SD))
mmc_send_if_cond(host, host->ocr_avail);
if (!(host->caps2 & MMC_CAP2_NO_SDIO)) // 未配置no-sdio,则先假设card是sdio type card,
if (!mmc_attach_sdio(host)) // 尝试绑定到host bus上,失败则说明不是sdio type card,
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;
mmc_power_off(host);
return -EIO;
}
总线io setting相关
mmc_set_ios
统一设置mmc总线的io设置(io setting)。
static inline void mmc_set_ios(struct mmc_host *host)
{
struct mmc_ios *ios = &host->ios;
pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u "
"width %u timing %un",
mmc_hostname(host), ios->clock, ios->bus_mode,
ios->power_mode, ios->chip_select, ios->vdd,
1 << ios->bus_width, ios->timing);
// 调用host->ops->set_ios来对mmc总线的io setting进行设置,核心函数,由host自行实现
host->ops->set_ios(host, ios);
}
会调用host->ops->set_ios来对mmc总线的io setting进行设置,核心函数。对于sdhci类型的host,对应就是sdhci_set_ios。
mmc_set_bus_mode & mmc_set_bus_width- mmc_set_bus_mode用于设置总线模式,有如下模式
#define MMC_BUSMODE_OPENDRAIN 1 //(开漏模式) #define MMC_BUSMODE_PUSHPULL 2 //(上拉模式)
- mmc_set_bus_width用于设置总线宽度,有如下模式
#define MMC_BUS_WIDTH_1 0 #define MMC_BUS_WIDTH_4 2 #define MMC_BUS_WIDTH_8 3
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
{
host->ios.bus_mode = mode;
mmc_set_ios(host);
}
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
host->ios.bus_width = width;
mmc_set_ios(host);
}
mmc_set_clock
void mmc_set_clock(struct mmc_host *host, unsigned int hz)
{
WARN_ON(hz && hz < host->f_min);
if (hz > host->f_max)
hz = host->f_max;
host->ios.clock = hz;
mmc_set_ios(host);
}
host的mmc总线相关
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;
WARN_ON(!host->claimed);
spin_lock_irqsave(&host->lock, flags);
WARN_ON(host->bus_ops); // 不允许重复设置host的mmc总线操作集
WARN_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;
WARN_ON(!host->claimed); // 不允许在占用时被释放
WARN_ON(!host->bus_ops);
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
}
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)
__mmc_release_bus(host); // 说明host的mmc总线当前并没有使用,调用__mmc_release_bus进行实际的释放操作
spin_unlock_irqrestore(&host->lock, flags);
}
mmc请求相关
同步的mmc请求
mmc_wait_for_req
|--->__mmc_start_req
| |--->mmc_start_request
| |--->mmc_mrq_prep
| |--->__mmc_start_request
|--->mmc_wait_for_req_done
|--->wait_for_completion
异步的mmc请求
并不是说调用这个接口并不会阻塞,而是不会为了等待当前请求处理完成而阻塞,但是可能会等待上一次请求处理完成而阻塞。
mmc_start_request
|--->mmc_mrq_prep
|--->__mmc_start_request
mmc_wait_for_req
发起mmc_request请求并且等待其处理完成。由其他需要发起mmc请求的模块调用。
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
__mmc_start_req(host, mrq); // 开始发起mmc_request请求
if (!mrq->cap_cmd_during_tfr)
mmc_wait_for_req_done(host, mrq); // 开始发起mmc_request请求
}
EXPORT_SYMBOL(mmc_wait_for_req);
__mmc_start_req
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
mmc_wait_ongoing_tfr_cmd(host);
init_completion(&mrq->completion); // 初始化完成量,在mmc_wait_for_req_done中会去等待这个完成量
// 设置mmc_request处理完成的回调函数,会调用complete(&mrq->completion);来设置完成量
// host controller会调用mmc_request_done来执行这个回调函数
mrq->done = mmc_wait_done;
err = mmc_start_request(host, mrq); // 开始处理mmc_request请求
if (err) { // mmc_request出错后的异常处理
mrq->cmd->error = err;
mmc_complete_cmd(mrq);
complete(&mrq->completion);
}
return err;
}
mmc_start_request
int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
// 初始化cmd 完成量
init_completion(&mrq->cmd_completion);
mmc_retune_hold(host);
if (mmc_card_removed(host->card))
return -ENOMEDIUM;
mmc_mrq_pr_debug(host, mrq, false);
WARN_ON(!host->claimed);
// mmc_request发送前的初始化
err = mmc_mrq_prep(host, mrq);
if (err)
return err;
led_trigger_event(host->led, LED_FULL);
__mmc_start_request(host, mrq);
return 0;
}
__mmc_start_request
static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
err = mmc_retune(host);
if (err) {
mrq->cmd->error = err;
mmc_request_done(host, mrq);
return;
}
// sdio card需检查 I/O 是否为busy
if (sdio_is_io_busy(mrq->cmd->opcode, mrq->cmd->arg) &&
host->ops->card_busy) {
int tries = 500;
while (host->ops->card_busy(host) && --tries)
mmc_delay(1);
if (tries == 0) {
mrq->cmd->error = -EBUSY;
mmc_request_done(host, mrq);
return;
}
}
if (mrq->cap_cmd_during_tfr) {
host->ongoing_mrq = mrq;
reinit_completion(&mrq->cmd_completion);
}
trace_mmc_request_start(host, mrq);
if (host->cqe_on)
host->cqe_ops->cqe_off(host);
host->ops->request(host, mrq);
}
mmc_wait_for_req_done
void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd;
while (1) {
wait_for_completion(&mrq->completion);
cmd = mrq->cmd; // 获取对应的command
if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) {
if (!mmc_interrupt_hpi(host->card)) {
pr_warn("%s: %s: Interrupted sanitizen",
mmc_hostname(host), __func__);
cmd->error = 0;
break;
} else {
pr_err("%s: %s: Failed to interrupt sanitizen",
mmc_hostname(host), __func__);
}
}
// 如果command正常处理完成,或者失败重复尝试次数为0,或者card被移除了,直接退出循环返回
if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card))
break;
mmc_retune_recheck(host);
// 以下处理失败重复尝试的情况
pr_debug("%s: req failed (CMD%u): %d, retrying...n",
mmc_hostname(host), cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
__mmc_start_request(host, mrq);
}
mmc_retune_release(host);
}
mmc_request_done
通知mmc core某个mmc_request已经处理完成,由host controller调用。
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
int err = cmd->error;
if (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 &&
!host->retune_crc_disable &&
(err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) ||
(mrq->data && mrq->data->error == -EILSEQ) ||
(mrq->stop && mrq->stop->error == -EILSEQ)))
mmc_retune_needed(host);
// command执行出错,如果还需要重复尝试的话,只是通知mmc core
if (err && cmd->retries && mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
cmd->retries = 0;
}
if (host->ongoing_mrq == mrq)
host->ongoing_mrq = NULL;
mmc_complete_cmd(mrq);
trace_mmc_request_done(host, mrq);
if (!err || !cmd->retries || mmc_card_removed(host->card)) {
mmc_should_fail_request(host, mrq);
if (!host->ongoing_mrq)
led_trigger_event(host->led, LED_OFF);
if (mrq->sbc) {
pr_debug("%s: req done : %d: %08x %08x %08x %08xn",
mmc_hostname(host), mrq->sbc->opcode,
mrq->sbc->error,
mrq->sbc->resp[0], mrq->sbc->resp[1],
mrq->sbc->resp[2], mrq->sbc->resp[3]);
}
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08xn",
mmc_hostname(host), cmd->opcode, err,
cmd->resp[0], cmd->resp[1],
cmd->resp[2], cmd->resp[3]);
if (mrq->data) {
pr_debug("%s: %d bytes transferred: %dn",
mmc_hostname(host),
mrq->data->bytes_xfered, mrq->data->error);
}
if (mrq->stop) {
pr_debug("%s: (CMD%u): %d: %08x %08x %08x %08xn",
mmc_hostname(host), mrq->stop->opcode,
mrq->stop->error,
mrq->stop->resp[0], mrq->stop->resp[1],
mrq->stop->resp[2], mrq->stop->resp[3]);
}
}
// 执行mmc_request的回调函数来通知mmc core,
// 对于__mmc_start_req发起的request来说,就是mmc_wait_done
// 对于__mmc_start_data_req发起的request来说,就是mmc_wait_data_done
if (mrq->done)
mrq->done(mrq);
}
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 = {};
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; // 返回错误码
}



