备注:
1. Kernel版本:5.4
2. 使用工具:Source Insight 4.0
3. 参考博客:
6. [mmc subsystem] mmc core(第六章)——mmc core主模块
本章主要介绍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);
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); // 关闭host
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);
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); // 配置驱动类型
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); // 绑定host的bus
void mmc_detach_bus(struct mmc_host *host);
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传输超时时间
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状态检测task
负责初始化整个mmc core。主要工作:
static int __init mmc_init(void)
{
int ret;
/* 注册mmc bus */
ret = mmc_register_bus();
if (ret)
return ret;
/* 注册mmc host class */
ret = mmc_register_host_class();
if (ret)
goto unregister_bus;
/* 注册sdio 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");
host被使能之后就不能再次被使能,并且只能被某个进程独自占用。
可以简单地将host理解为一种资源,同时只能被一个进程获取,但是在占用进程里面可以重复占用。
在对host进行操作之前(包括发起mmc请求),必须使用mmc_claim_host和mmc_release_host来进行获取和释放。
变量说明:
static inline void mmc_claim_host(struct mmc_host *host)
{
__mmc_claim_host(host, NULL, NULL); // 调用__mmc_claim_host来获取host
}
/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
* @ctx: context that claims the host or NULL in which case the default
* context will be used
* @abort: whether or not the operation should be aborted
*
* Claim a host for a set of operations. If @abort is non null and
* dereference a non-zero value then this will return prematurely with
* that non-zero value without acquiring the lock. Returns zero
* with the lock held otherwise.
*/
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;
}
/**
* mmc_release_host - release a host
* @host: mmc host to release
*
* Release a MMC host, allowing others to claim the host
* for their operations.
*/
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,不做其他操作
/* Release for nested claim */
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 host的上电操作和关电操作。
#define MMC_POWER_OFF 0 // 掉电状态
#define MMC_POWER_UP 1 // 正在上电的状态
#define MMC_POWER_ON 2 // 供电正常的状态
#define MMC_POWER_UNDEFINED 3 // 未定义的状态
/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
* We then wait a bit for the power to stabilise. Finally,
* enable the bus drivers and clock to the card.
*
* We must _NOT_ enable the clock prior to power stablising.
*
* If a host does all the power sequencing itself, ignore the
* initial MMC_POWER_UP stage.
*/
void mmc_power_up(struct mmc_host *host, u32 ocr)
{
if (host->ios.power_mode == MMC_POWER_ON)
return;
/* 第一阶段,先设置对应的io setting使host处于MMC_POWER_UP的状态(总线工作频率没有设置) */
mmc_pwrseq_pre_power_on(host); // 上电前的准备工作
host->ios.vdd = fls(ocr) - 1; // 选择一个ocr配置设置为host的工作电压
host->ios.power_mode = MMC_POWER_UP;
/* Set initial state and call mmc_set_ios */
mmc_set_initial_state(host);//初始化状态,及调用mmc_set_ios设置总线的io setting
mmc_set_initial_signal_voltage(host);
/*
* This delay should be sufficient to allow the power supply
* to reach the minimum voltage.
*/
mmc_delay(host->ios.power_delay_ms);
/* 第二阶段,以host的初始化工作频率再次设置io setting,使host处于MMC_POWER_ON状态 */
mmc_pwrseq_post_power_on(host);
host->ios.clock = host->f_init;// 设置总线的时钟频率为初始化频率
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
/*
* This delay must be at least 74 clock sizes, or 1 ms, or the
* time required to reach a stable voltage.
*/
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;
/* Set initial state and call mmc_set_ios */
mmc_set_initial_state(host);
/*
* Some configurations, such as the 802.11 SDIO card in the OLPC
* XO-1.5, require a short delay after poweroff before the card
* can be successfully turned on again.
*/
mmc_delay(1);
}
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);
/* 到这里host已经可以工作了,可以开始进行后续的card操作了 */
_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);
/* clear pm flags now and let card drivers set them as needed */
host->pm_flags = 0;
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
/* Calling bus_ops->remove() with a claimed host can deadlock */
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);
}
在上述中我们知道在启动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 the device is configured as wakeup, we prevent a new sleep for
* 5 s to give provision for user space to consume the event.
*/
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)
}
/**
* mmc_detect_change - process change of state on a MMC socket
* @host: host which changed state.
* @delay: optional delay to wait before detection (jiffies)
*
* MMC drivers should call this when they detect a card has been
* inserted or removed. The MMC layer will confirm that any
* present card is still functional, and initialize any newly
* inserted.
*/
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
_mmc_detect_change(host, delay, true);
}
EXPORT_SYMBOL(mmc_detect_change);
用于检测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;
/* 如果rescan_disable被设置,说明host此时还禁止rescan */
if (host->rescan_disable)
return;
/* If there is a non-removable card registered, only scan once */
/* 对于设备不可移除的host来说,只能rescan一次 */
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
/* Verify a registered card to be functional, else remove it. */
if (host->bus_ops && !host->bus_dead)
host->bus_ops->detect(host);
host->detect_change = 0;
/*
* 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);
/* if there still is a card present, stop here */
if (host->bus_ops != NULL) {
mmc_bus_put(host);
goto out;
}
/*
* Only we can add a new handler, so it's safe to
* release the lock here.
*/
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 Hz\n",
mmc_hostname(host), __func__, host->f_init);
mmc_power_up(host, host->ocr_avail); // 给host做上电操作
/*
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
* do a hardware reset if possible.
*/
mmc_hw_reset_for_init(host); // 硬件复位和初始化
/*
* sdio_reset sends CMD52 to reset card. Since we do not know
* if the card is being re-initialized, just send it. CMD52
* should be ignored by SD/eMMC cards.
* Skip it if we already know that we do not support SDIO commands
*/
// 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);
/* 用于绑定card到host bus上(也就是card和host的绑定)。 */
/* Order's important: probe SDIO, then SD, then MMC */
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;
}
统一设置mmc总线的io设置(io setting)。
/*
* Internal function that does the actual ios call to the host driver,
* optionally printing some debug output.
*/
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 %u\n",
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。
#define MMC_BUSMODE_OPENDRAIN 1 //(开漏模式)
#define MMC_BUSMODE_PUSHPULL 2 //(上拉模式)
#define MMC_BUS_WIDTH_1 0
#define MMC_BUS_WIDTH_4 2
#define MMC_BUS_WIDTH_8 3
/*
* Change the bus mode (open drain/push-pull) of a host.
*/
/*
* mmc_set_bus_mode用于设置总线模式,有如下模式
* MMC_BUSMODE_OPENDRAIN(开漏模式)
* MMC_BUSMODE_PUSHPULL(上拉模式)
*/
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
{
host->ios.bus_mode = mode;
mmc_set_ios(host);
}
/*
* Change data bus width of a host.
*/
/*
* mmc_set_bus_width用于设置总线宽度,有如下模式
* MMC_BUS_WIDTH_1
* MMC_BUS_WIDTH_4
* MMC_BUS_WIDTH_8
*/
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
host->ios.bus_width = width;
mmc_set_ios(host);
}
/*
* Sets the host clock to the highest possible frequency that
* is below "hz".
*/
/*
* mmc_set_clock用于设置总线时钟,时钟范围为:f_min <= hz <= f_max
*/
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);
}
/*
* Assign a mmc bus handler to a host. Only one bus handler may control a
* host at any given time.
*/
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);
}
/*
* Remove the current bus handler from a host.
*/
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
}
/*
* Increase reference count of bus operator
*/
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);
}
/*
* Decrease reference count of bus operator and free it if
* it is the last reference.
*/
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_wait_for_req
|--->__mmc_start_req
| |--->mmc_start_request
| |--->mmc_mrq_prep
| |--->__mmc_start_request
|--->mmc_wait_for_req_done
|--->wait_for_completion
并不是说调用这个接口并不会阻塞,而是不会为了等待当前请求处理完成而阻塞,但是可能会等待上一次请求处理完成而阻塞。
mmc_start_request
|--->mmc_mrq_prep
|--->__mmc_start_request
发起mmc_request请求并且等待其处理完成。由其他需要发起mmc请求的模块调用。
/**
* mmc_wait_for_req - start a request and wait for completion
* @host: MMC host to start command
* @mrq: MMC request to start
*
* Start a new MMC custom command request for a host, and wait
* for the command to complete. In the case of 'cap_cmd_during_tfr'
* requests, the transfer is ongoing and the caller can issue further
* commands that do not use the data lines, and then wait by calling
* mmc_wait_for_req_done().
* Does not attempt to parse the response.
*/
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);
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
mmc_wait_ongoing_tfr_cmd(host);
/* 发起mmc_request前的一些初始化工作,包括完成量和处理完成的回调函数的设置 */
init_completion(&mrq->completion); // 初始化完成量,在mmc_wait_for_req_done中会去等待这个完成量
// 设置mmc_request处理完成的回调函数,会调用complete(&mrq->completion);来设置完成量
// host controller会调用mmc_request_done来执行这个回调函数
mrq->done = mmc_wait_done;
/* 调用mmc_start_request发起mmc请求 */
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;
}
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;
}
static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
/* Assumes host controller has been runtime resumed by mmc_claim_host */
err = mmc_retune(host);
if (err) {
mrq->cmd->error = err;
mmc_request_done(host, mrq);
return;
}
/*
* For sdio rw commands we must wait for card busy otherwise some
* sdio devices won't work properly.
* And bypass I/O abort, reset and bus suspend operations.
*/
// sdio card需检查 I/O 是否为busy
if (sdio_is_io_busy(mrq->cmd->opcode, mrq->cmd->arg) &&
host->ops->card_busy) {
int tries = 500; /* Wait aprox 500ms at maximum */
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;
/*
* Retry path could come through here without having waiting on
* cmd_completion, so ensure it is reinitialised.
*/
reinit_completion(&mrq->cmd_completion);
}
trace_mmc_request_start(host, mrq);
if (host->cqe_on)
host->cqe_ops->cqe_off(host);
/* 调用host controller的request方法来处理mmc_request请求 */
host->ops->request(host, mrq);
}
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 host has timed out waiting for the sanitize
* to complete, card might be still in programming state
* so let's try to bring the card out of programming
* state.
*/
if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) {
if (!mmc_interrupt_hpi(host->card)) {
pr_warn("%s: %s: Interrupted sanitize\n",
mmc_hostname(host), __func__);
cmd->error = 0;
break;
} else {
pr_err("%s: %s: Failed to interrupt sanitize\n",
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 core某个mmc_request已经处理完成,由host controller调用。
/**
* mmc_request_done - finish processing an MMC request
* @host: MMC host which completed request
* @mrq: MMC request which request
*
* MMC drivers should call this function when they have completed
* their processing of a request.
*/
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
int err = cmd->error;
/* Flag re-tuning needed on CRC errors */
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);
/*
* We list various conditions for the command to be considered
* properly done:
*
* - There was no error, OK fine then
* - We are not doing some kind of retry
* - The card was removed (...so just complete everything no matter
* if there are errors or retries)
*/
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 %08x\n",
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 %08x\n",
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: %d\n",
mmc_hostname(host),
mrq->data->bytes_xfered, mrq->data->error);
}
if (mrq->stop) {
pr_debug("%s: (CMD%u): %d: %08x %08x %08x %08x\n",
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]);
}
}
/*
* Request starter must handle retries - see
* mmc_wait_for_req_done().
*/
// 执行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_request中,通过调用mmc_wait_for_req来发起请求。
/**
* mmc_wait_for_cmd - start a command and wait for completion
* @host: MMC host to start command
* @cmd: MMC command to start
* @retries: maximum number of retries
*
* Start a new MMC command for a host, and wait for the command
* to complete. Return any error that occurred while the command
* was executing. Do not attempt to parse the response.
*/
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; // 返回错误码
}