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

 

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