Linux驱动——mmc core浅析(三)

Linux驱动——mmc core浅析(三)

备注:
  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 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检测接口

API总览

mmc core初始化相关

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); // 关闭host

mmc 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状态检测task

mmc core初始化相关

mmc_init的实现

负责初始化整个mmc core。主要工作:

  • 注册mmc bus
  • 注册mmc host class
  • 注册sdio bus
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");

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
}

/**
 *	__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_power_up & mmc_power_off

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

  • mmc的power状态
#define MMC_POWER_OFF		0        // 掉电状态
#define MMC_POWER_UP		1        // 正在上电的状态
#define MMC_POWER_ON		2       // 供电正常的状态
#define MMC_POWER_UNDEFINED	3      // 未定义的状态

  

  • 主要工作
    主要工作就是初始化host的总线设置、总线时钟以及工作电压、信号电压。
/*
 * 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 & 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);

	/* 到这里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);
}

card检测相关

Linux驱动——mmc core浅析(三)_第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, 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);

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;

	/* 如果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。

mmc_rescan_try_freq

以一定频率搜索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;
}

总线io setting相关

mmc_set_ios

  统一设置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。

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
/*
 * 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);
}

mmc_set_clock

/*
 * 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);
}

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,那么就会被激活了
/*
 * 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
}

mmc_bus_get & mmc_bus_put

/*
 * 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请求相关

同步的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请求的模块调用。

/**
 *	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);

__mmc_start_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;
}

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;

	/* 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);
}

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 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_request_done

  通知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_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; // 返回错误码
}

你可能感兴趣的:(#,Linux,MMC子系统,linux,c语言,运维)