eMMC Sampling Tuning and linux mmc driver support

目录

1. eMMC 总线 Sampling Tuning

1.1 Sampling Tuning 流程

1.2 Tuning Block 数据

1.3 retuning mode

2. linux mmc driver中的retuning触发机制

2.1 设备初始化时tuning

2.2 timer retuning(software)

2.3  Auto-Tuning(hardware managed)


Tuning flow based on CMD21 (eMMC) or CMD19 (SD)

SD 4.2 and eMMMC 5.1 protocols and both these protocols have speed
modes in which the incoming data must be sampled on a clock with a programmable sampling

1. eMMC 总线 Sampling Tuning

由于芯片制造工艺、PCB 走线、电压、温度等因素的影响,数据信号从 eMMC Device 到达 Host 端的时间是存在差异的,Host 接收数据时采样的时间点也需要相应的进行调整。而 Host 端最佳采样时间点,则是通过 Sampling Tuning 流程得到。

NOTE:
不同 eMMC Device 最佳的采样点可能不同,同一 eMMC Device 在不同的环境下运作时的最佳采样点也可能不同。
在 eMMC 标准中,定义了在 HS200 模式下可以进行 Sampling Tuning。

1.1 Sampling Tuning 流程

Sampling Tuning 是用于计算 Host 最佳采样时间点的流程,大致的流程如下:
1. Host 将采样时间点重置为默认值
2. Host 向 eMMC Device 发送 Send Tuning Block 命令
3. eMMC Device 向 Host 发送固定的 Tuning Block 数据
4. Host 接收到 Tuning Block 并进行校验
5. Host 修改采样时点,重新从第 2 步开始执行,直到 Host 获取到一个有效采样时间点区间
6. Host 取有效采样时间点区间的中间值作为采样时间点,并推出 Tuning 流程

NOTE:
上述流程仅仅是一个示例。Tuning 流程执行的时机、频率和具体的步骤是由 Host 端的 eMMC Controller 具体实现而定的。

1.2 Tuning Block 数据

Tuning Block 是专门为了 Tuning 而设计的一组特殊数据。相对于普通的数据,这组特殊数据在传输过程中,会更高概率的出现 high SSO noise、deterministic jitter、ISI、timing errors 等问题。这组数据的具体内容如下所示:

 

 

NOTE: 总线宽度为 1 时,只发送 DAT0 上的数据,总线宽度为 4 时,则只发送 DAT0-3 上的数据

 

1.3 retuning mode

Mode 1 (software assisted) Re-tuning flow (system Timer assisted)

MODE2: Timer and Re-Tuning Request

Mode 3 (hardware managed) re-tuning (that is, Auto-Tuning)

2. linux mmc driver中的retuning触发机制

mmc_host 数据结构中关于tuning的成员如下:

\include\linux\mmc\host.h

struct mmc_host {
	struct device		*parent;
	struct device		class_dev;
	int			index;
...
	unsigned int		can_retune:1;	/* re-tuning can be used */
	unsigned int		doing_retune:1;	/* re-tuning in progress */
	unsigned int		retune_now:1;	/* do re-tuning at next req */
	unsigned int		retune_paused:1; /* re-tuning is temporarily disabled */

	int			rescan_disable;	/* disable card detection */
	int			rescan_entered;	/* used with nonremovable devices */

	int			need_retune;	/* re-tuning is needed */
	int			hold_retune;	/* hold off re-tuning */
	unsigned int		retune_period;	/* re-tuning period in secs */
	struct timer_list	retune_timer;	/* for periodic re-tuning */
...
};

2.1 设备初始化时tuning

初始化emmc卡的过程中会根据controller和卡支持的最高速率,执行相应的tuning流程。

注意hs400的tuning需要先切到hs200下,按照hs200来执行tuning。

\drivers\mmc\core\mmc.c

/*
 * Handle the detection and initialisation of a card.
 *
 * In the case of a resume, "oldcard" will contain the card
 * we're trying to reinitialise.
 */
static int mmc_init_card(struct mmc_host *host, u32 ocr,
	struct mmc_card *oldcard)
{
	struct mmc_card *card;
	int err;
	u32 cid[4];
	u32 rocr;
...
	/*
	 * Select timing interface
	 */
	err = mmc_select_timing(card);
	if (err)
		goto free_card;

	if (mmc_card_hs200(card)) {
		err = mmc_hs200_tuning(card);
		if (err)
			goto free_card;

		err = mmc_select_hs400(card);
		if (err)
			goto free_card;
	} else if (!mmc_card_hs400es(card)) {
		/* Select the desired bus width optionally */
		err = mmc_select_bus_width(card);
		if (err > 0 && mmc_card_hs(card)) {
			err = mmc_select_hs_ddr(card);
			if (err)
				goto free_card;
		}
	}
...
}

具体mmc卡tuning的实现 在mmc_hs200_tuning中.

mmc_hs200_tuning往下继续调用mmc_execute_tuning->host->ops->execute_tuning(host, opcode);->controller驱动实现的tuning

/*
 * Execute tuning sequence to seek the proper bus operating
 * conditions for HS200 and HS400, which sends CMD21 to the device.
 */
static int mmc_hs200_tuning(struct mmc_card *card)
{
	struct mmc_host *host = card->host;

	/*
	 * Timing should be adjusted to the HS400 target
	 * operation frequency for tuning process
	 */
	if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
	    host->ios.bus_width == MMC_BUS_WIDTH_8)
		if (host->ops->prepare_hs400_tuning)
			host->ops->prepare_hs400_tuning(host, &host->ios);

	return mmc_execute_tuning(card);
}

2.2 timer retuning(software)

timer tuning并不是简单地周期tuning,在没有与emmc卡交互时,是不需要定时去做tuning的。

linux mmc驱动框架提供了基于定时的tuning触发机制。对应mmc_host ->retune_timer 作为定时的timer

timer初始化

\drivers\mmc\core\host.c

struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
       int err;
       struct mmc_host *host;

....
       init_waitqueue_head(&host->wq);
       INIT_DELAYED_WORK(&host->detect, mmc_rescan);
       INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
       timer_setup(&host->retune_timer, mmc_retune_timer, 0);//初始化软件timer定时触发Re-tuning

...

       return host;
}

 timer 回调函数,注意这里timer触发后并不会立即执行tuning,只是把host->need_retune 置一。

static void mmc_retune_timer(unsigned long data)
{
	struct mmc_host *host = (struct mmc_host *)data;
	mmc_retune_needed(host);
}

//host.h
static inline void mmc_retune_needed(struct mmc_host *host)
{
	if (host->can_retune)
		host->need_retune = 1;
}

 具体执行tuning会在发起下一次request的时候,在发送命令之前,调用mmc_retune中判断need_retune的值。

\drivers\mmc\core\core.c

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

...
}

mmc_retune中判断need_retune的值。来确定是否需要执行tuning,最终还是会调用mmc_execute_tuning。

int mmc_retune(struct mmc_host *host)
{
	bool return_to_hs400 = false;
	int err;

	if (host->retune_now)
		host->retune_now = 0;
	else
		return 0;

	if (!host->need_retune || host->doing_retune || !host->card)
		return 0;

	host->need_retune = 0;

	host->doing_retune = 1;

	if (host->ios.timing == MMC_TIMING_MMC_HS400) {
		err = mmc_hs400_to_hs200(host->card);
		if (err)
			goto out;

		return_to_hs400 = true;

		if (host->ops->prepare_hs400_tuning)
			host->ops->prepare_hs400_tuning(host, &host->ios);
	}

	err = mmc_execute_tuning(host->card);
	if (err)
		goto out;

	if (return_to_hs400)
		err = mmc_hs200_to_hs400(host->card);
out:
	host->doing_retune = 0;

	return err;
}

 

2.3  Auto-Tuning(hardware managed)

对于硬件管理的tuning,指的是controller来告诉系统软件什么时候需要tuning了。

例如通过中断通知系统软件,需要执行tuning,再由软件来调用tuning的流程。

对于驱动来说,需要在controller的tuning-event中断处理函数中调用mmc_retune_needed来标记需要执行tuning。

后续的处理和timer tuning的流程相同,会等到下一次发起请求的时候来执行tuning。

static irqreturn_t sdhci_irq(int irq, void *dev_id) //中断服务函数
{
       irqreturn_t result = IRQ_NONE;
       struct sdhci_host *host = dev_id;
       u32 intmask, mask, unexpected = 0;
       int max_loops = 16;
...
       intmask = sdhci_readl(host, SDHCI_INT_STATUS);
....

       do {
....

              if (intmask & SDHCI_INT_RETUNE)// 接收到Re-tuning Event 中断
                     mmc_retune_needed(host->mmc);

....

       return result;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章