一、說明 1、mmc core概述 mmc core主模塊是mmc core的實現核心。也是本章的重點內容。 對應代碼位置 。 其主要負責如下功能: mmc core初始化,包括註冊mmc bus、mm host class等等 mmc host的管理和維護,包括為其他模塊提供mmc_host的操作 ...
一、說明
1、mmc core概述
mmc core主模塊是mmc core的實現核心。也是本章的重點內容。
對應代碼位置drivers/mmc/core/core.c
。
其主要負責如下功能:
- mmc core初始化,包括註冊mmc bus、mm host class等等
- mmc host的管理和維護,包括為其他模塊提供mmc_host的操作介面,如下
- host的啟動和停止
- host的占用和釋放
- host電源狀態的保存和恢復
- host匯流排操作集的綁定和解綁
- host上卡狀態檢測
- 為其他模塊提供mmc_card的操作介面,如下
- card的喚醒和休眠
- card擦除
- card屬性的獲取
- 為其他模塊提供匯流排io setting的介面
- 為其他模塊提供mmc請求介面
- card檢測介面
- bkops操作介面
- regulator操作介面
- clock操作介面
- mmc core電源管理操作介面
2、操作集說明
在mmc_host中有兩個操作集成員,需要理解一下,以免在代碼中產生誤會:
- mmc_host->struct mmc_host_ops *ops,這個是host的操作集,由host controller驅動決定。對於sdhci類host來說,就是sdhci_ops(sdhci.c中設置)。
- mmc_host->struct mmc_bus_ops *bus_ops,這個是mmc匯流排的操作集(也可以理解為host的mmc bus handler,host的匯流排處理方法),由匯流排上的card type決定。對於mmc card type來說,就是mmc_ops_unsafe或者mmc_ops(mmc_attach_bus_ops中設置)。
二、API總覽
1、mmc core初始化相關
mmc_init & mmc_exit (模塊內使用)
2、mmc host的管理和維護相關
- mmc_claim_host & mmc_try_claim_host & mmc_release_host (模塊內使用)
- mmc_power_up & mmc_power_off
- mmc_start_host & mmc_stop_host
- mmc_power_save_host & mmc_power_restore_host
- mmc_resume_host & mmc_suspend_host
mmc_pm_notify
3、mmc card的操作相關(包括card狀態的獲取)
- mmc_hw_reset & mmc_hw_reset_check &
- mmc_card_awake & mmc_card_sleep
- mmc_card_is_prog_state
- mmc_can_erase
- mmc_can_trim
- mmc_can_discard
- mmc_can_sanitize
- mmc_can_secure_erase_trim
- mmc_erase_group_aligned
4、匯流排io setting相關
- mmc_set_ios
- mmc_set_chip_select
- mmc_set_clock
- mmc_set_bus_mode
- mmc_set_bus_width
- mmc_select_voltage
- mmc_set_signal_voltage(特殊)
- mmc_set_timing
- mmc_set_driver_type
- mmc_get_max_frequency & mmc_get_min_frequency
5、host的mmc匯流排相關
- mmc_resume_bus
- mmc_attach_bus & mmc_detach_bus
6、mmc請求相關
- mmc_request_done
- mmc_wait_for_req
- mmc_wait_for_cmd
- mmc_set_data_timeout
- mmc_align_data_size
7、card檢測相關
mmc_detect_change
mmc_rescan
mmc_detect_card_removed
8、bkops操作相關
- mmc_blk_init_bkops_statistics
- mmc_start_delayed_bkops
- mmc_start_bkops & mmc_stop_bkops
- mmc_start_idle_time_bkops
- mmc_read_bkops_status
9、regulator操作相關
- mmc_regulator_get_ocrmask
- mmc_regulator_set_ocr
- mmc_regulator_get_supply
10、card擦除操作相關
- mmc_init_erase
- mmc_erase
11、clock操作介面
- mmc_init_clk_scaling & mmc_exit_clk_scaling
- mmc_can_scale_clk
- mmc_disable_clk_scaling
12、mmc core電源管理操作
- mmc_rpm_hold & mmc_rpm_release
二、介面代碼說明——mmc core初始化相關
1、mmc_init實現
負責初始化整個mmc core。
主要工作:
- 分配一個workqueue,用於專門處理mmc core的執行的工作
- 註冊mmc bus
- 註冊mmc host class
代碼如下:
static int __init mmc_init(void)
{
int ret;
/* 分配一個workqueue,用於專門處理mmc core的執行的工作 */
workqueue = alloc_ordered_workqueue("kmmcd", 0);
/* 註冊mmc bus */
ret = mmc_register_bus(); // 調用mmc_register_bus註冊mmc bus,具體參考《mmc core——bus模塊說明》
// 會生成/sys/bus/mmc目錄
/* 註冊mmc host class */
ret = mmc_register_host_class(); // 調用mmc_register_host_class註冊mmc host class,具體參考《mmc core——host模塊說明》
// 會生成/sys/class/mmc_host目錄
/* 註冊sdio bus */
ret = sdio_register_bus();
return 0;
}
subsys_initcall(mmc_init);
三、介面代碼說明——mmc host的管理和維護相關
1、mmc_claim_host & mmc_try_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);·// 調用__mmc_claim_host來獲取host
}
int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
{
/////只考慮abort為NULL的情況,在mmc core中的mmc_claim_host也是將其設置為NULL
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int stop;
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 || host->claimer == current) // 當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
host->claimer = current; // 設置占用者為當前進程
host->claim_cnt += 1; // 占用計數加1
} else
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait); // 將當前進程從等待隊列中退出
if (host->ops->enable && !stop && host->claim_cnt == 1)
host->ops->enable(host); // 調用host操作集中的enable方法來占用該host,對應sdhci類host即為sdhci_enable
return stop;
}
void mmc_release_host(struct mmc_host *host)
{
unsigned long flags;
WARN_ON(!host->claimed);
if (host->ops->disable && host->claim_cnt == 1) // 當前claim_cnt為1(馬上要變為0),調用釋放host了
host->ops->disable(host); // 調用host操作集中的disable方法來釋放該host,對應sdhci類host即為sdhci_disable
spin_lock_irqsave(&host->lock, flags);
if (--host->claim_cnt) {
/* Release for nested claim */
spin_unlock_irqrestore(&host->lock, flags); // 如果減一之後計數還不為0,說明當前進程需要繼續占用該host,不做其他操作
} else { // 以下需要釋放該host
host->claimed = 0; // 設置占用標誌claimed為0
host->claimer = NULL; // 清空占用者(進程)
spin_unlock_irqrestore(&host->lock, flags);
wake_up(&host->wq); // 喚醒host的等待隊列,讓那些調用mmc_claim_host睡眠等待host資源的進程被喚醒
}
}
int mmc_try_claim_host(struct mmc_host *host)
{
// 和mmc_claim_host的主要區別在於進程不會休眠,獲取失敗直接返回
int claimed_host = 0;
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
if (!host->claimed || host->claimer == current) {
host->claimed = 1;
host->claimer = current;
host->claim_cnt += 1;
claimed_host = 1;
}
spin_unlock_irqrestore(&host->lock, flags);
if (host->ops->enable && claimed_host && host->claim_cnt == 1)
host->ops->enable(host);
return claimed_host;
}
會調用mmc_host->struct mmc_host_ops->enable和mmc_host->struct mmc_host_ops->disable來使能和禁用host。
對於sdhci類的host,相應就是sdhci_enable和sdhci_disable。
2、mmc_power_up & mmc_power_off
mmc host的上電操作和關電操作。
mmc的power狀態
- MMC_POWER_OFF:掉電狀態
- MMC_POWER_UP:正在上電的狀態
- MMC_POWER_ON:供電正常的狀態
主要工作
主要工作就是初始化host的匯流排設置、匯流排時鐘以及工作電壓、信號電壓。
代碼如下
void mmc_power_up(struct mmc_host *host)
{
int bit;
/* 判斷是否已經處於MMC_POWER_ON,是的話不進行後續操作 */
if (host->ios.power_mode == MMC_POWER_ON)
return;
/* 第一階段,先設置對應的io setting使host處於MMC_POWER_UP的狀態(匯流排工作頻率沒有設置) */
mmc_host_clk_hold(host); // 先獲取host時鐘
/* If ocr is set, we use it */
if (host->ocr)
bit = ffs(host->ocr) - 1; // 選擇一個ocr配置設置為host的工作電壓
else
bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
if (mmc_host_is_spi(host))
host->ios.chip_select = MMC_CS_HIGH;
else {
host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; // 設置匯流排模式
}
host->ios.power_mode = MMC_POWER_UP;
host->ios.bus_width = MMC_BUS_WIDTH_1; // 設置匯流排寬度為1
host->ios.timing = MMC_TIMING_LEGACY; // 串口時序
mmc_set_ios(host); // 調用mmc_set_ios設置匯流排的io setting,後面會說明
/*
* This delay should be sufficient to allow the power supply
* to reach the minimum voltage.
*/
mmc_delay(10);
/* 第二階段,以host的初始化工作頻率再次設置io setting,使host處於MMC_POWER_ON狀態 */
host->ios.clock = host->f_init; // 設置匯流排的時鐘頻率
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host); // 調用mmc_set_ios設置匯流排的io setting,後面會說明
/*
* This delay must be at least 74 clock sizes, or 1 ms, or the
* time required to reach a stable voltage.
*/
mmc_delay(10);
/* 設置信號的電壓 */
/* Set signal voltage to 3.3V */
__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
mmc_host_clk_release(host); // 釋放host時鐘
}
3、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 core——host模塊說明》。
相對應的,會在mmc_remove_host中調用mmc_stop_host停止host。
void mmc_start_host(struct mmc_host *host)
{
mmc_claim_host(host); // 因為上電操作涉及到對host的使用和設置,需要先占用host
host->f_init = max(freqs[0], host->f_min); // 通過最小頻率要設置初始化頻率
host->rescan_disable = 0; // 設置rescan_disable標誌為0,說明已經可以進行card檢測了
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) // 如果mmc屬性設置了MMC_CAP2_NO_PRESCAN_POWERUP,也就是在rescan前不需要進行power up操作時,則進行關電
mmc_power_off(host);
else
mmc_power_up(host); // 否則,調用mmc_power_up對host進行上電操作。這裡也是mmc core中啟動host的核心函數。
mmc_release_host(host); // 完成上電操作,釋放host
/* 到這裡host已經可以工作了,可以開始進行後續的card操作了 */
mmc_detect_change(host, 0); // 調用mmc_detect_change檢測card變化,後續會繼續說明
}
四、介面代碼說明——card檢測相關
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)
{
#ifdef CONFIG_MMC_DEBUG
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
WARN_ON(host->removed);
spin_unlock_irqrestore(&host->lock, flags);
#endif
host->detect_change = 1; // 檢測到card狀態發生變化的標識
mmc_schedule_delayed_work(&host->detect, delay); // 間隔delay jiffies之後調用host->detect的工作
}
在《host模塊說明》已經知道了在mmc_alloc_host中預設將host->detect工作設置為mmc_rescan(card重新掃描)函數, INIT_DELAYED_WORK(&host->detect, mmc_rescan)。
當然,host也可以自己另外設置,但是一般都是使用mmc core提供的mmc_rescan作為detect工作來搜索card。下麵說明。
2、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);
bool extend_wakelock = false;
/* 如果rescan_disable被設置,說明host此時還禁止rescan */
if (host->rescan_disable)
return;
/* 對於設備不可移除的host來說,只能rescan一次 */
/* If there is a non-removable card registered, only scan once */
if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
return;
host->rescan_entered = 1;
mmc_bus_get(host); // 獲取host對應的bus
mmc_rpm_hold(host, &host->class_dev); // 使host處於rpm resume的狀態
/* 以下判斷原來的card是否已經被移除,移除了則需要做相應的操作 */
/*
* if there is a _removable_ card registered, check whether it is
* still present
*/
if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
&& !(host->caps & MMC_CAP_NONREMOVABLE))
host->bus_ops->detect(host);
// host->bus_ops存在的話說明之前是有card插入的狀態
// 需要調用host->bus_ops->detect檢測card是否被移除,是的話在host->bus_ops->detect中做相應的處理
// 對於mmc type card來說,對應就是mmc_detect,具體參考《card相關模塊》
host->detect_change = 0;
/* If the card was removed the bus will be marked
* as dead - extend the wakelock so userspace
* can respond */
if (host->bus_dead)
extend_wakelock = 1; // 需要設置一個wakelock鎖,使用戶空間可以及時做出相應
/*
* 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);
// 再獲取host bus
/* if there still is a card present, stop here */
if (host->bus_ops != NULL) { // 說明此時還有card插入,退出後續的操作
mmc_rpm_release(host, &host->class_dev);
mmc_bus_put(host);
goto out;
}
mmc_rpm_release(host, &host->class_dev);
/*
* Only we can add a new handler, so it's safe to
* release the lock here.
*/
mmc_bus_put(host);
/* 檢測當前卡槽狀態,根據卡槽狀態做相應的操作 */
if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
// 調用host->ops->get_cd來判斷host的卡槽的當前card插入狀態
// 對應sdhci類型的host來說,就是sdhci_get_cd
// 為0的時候,表示沒有card插入,對應host進行power off操作之後進行退出
// 為1的時候,表示當前有card插入,跳到後續的操作
mmc_claim_host(host);
mmc_power_off(host);
mmc_release_host(host);
goto out;
}
mmc_rpm_hold(host, &host->class_dev);
mmc_claim_host(host);
if (!mmc_rescan_try_freq(host, host->f_min)) // 調用mmc_rescan_try_freq,以支持的最低頻率作為工作頻率嘗試搜索card,後續繼續說明
extend_wakelock = true;
mmc_release_host(host);
mmc_rpm_release(host, &host->class_dev);
out:
/* only extend the wakelock, if suspend has not started yet */
if (extend_wakelock && !host->rescan_disable)
wake_lock_timeout(&host->detect_wake_lock, HZ / 2); // 占用wakelock,使系統在HZ/2的時間內不會休眠
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
// 當host設置了MMC_CAP_NEEDS_POLL屬性時,需要每隔HZ的時間輪詢檢測host的卡槽狀態,
// 調度了host->detect工作,對應就是mmc_rescan
}
會調用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。
3、mmc_rescan_try_freq
以一定頻率搜索host bus上的card。
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
mmc_power_up(host); // 給host做上電操作
mmc_hw_reset_for_init(host); // 硬體複位和初始化
mmc_go_idle(host);
mmc_send_if_cond(host, host->ocr_avail); // 獲取card的可用頻率,存儲到host->ocr_avail中
/* Order's important: probe SDIO, then SD, then MMC */
/* 用於綁定card到host bus上(也就是card和host的綁定)。 */
if (!mmc_attach_sdio(host)) // 先假設card是sdio type card,嘗試綁定到host bus上,失敗則說明不是sdio type card,繼續後面的操作,否則返回
return 0;
if (!mmc_attach_sd(host)) // 先假設card是sd type card,嘗試綁定到host bus上,失敗則說明不是sd type card,繼續後面的操作,否則返回
return 0;
if (!mmc_attach_mmc(host)) // 先假設card是mmc type card,嘗試綁定到host bus上,失敗則說明不是mmc type card,繼續後面的操作,否則返回
// mmc_attach_mmc通過mmc_host獲取mmc type card信息,初始化mmc_card,併進行部分驅動,最後將其註冊到mmc_bus上。
// 具體參考《card相關模塊說明》
return 0;
mmc_power_off(host);
return -EIO;
}
五、介面代碼說明——匯流排io setting相關
0、mmc_ios說明
struct mmc_ios 由mmc core定義的規範的結構,用來維護mmc匯流排相關的一些io setting。如下:
struct mmc_ios {
unsigned int clock; /* clock rate */ // 當前工作頻率
unsigned int old_rate; /* saved clock rate */ // 上一次的工作頻率
unsigned long clk_ts; /* time stamp of last updated clock */ // 上一次更新工作頻率的時間戳
unsigned short vdd;/* vdd stores the bit number of the selected voltage range from below. */ // 支持的電壓表
unsigned char bus_mode; /* command output mode */ // 匯流排輸出模式,包括開漏模式和上拉模式
unsigned char chip_select; /* SPI chip select */ // spi片選
unsigned char power_mode; /* power supply mode */ // 電源狀態模式
unsigned char bus_width; /* data bus width */ // 匯流排寬度
unsigned char timing; /* timing specification used */ // 時序類型
unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ // 信號的工作電壓
unsigned char drv_type; /* driver type (A, B, C, D) */ // 驅動類型
};
在設置匯流排io setting的過程中,就是要設置mmc_host->mmc_ios中的這些成員。
然後通過調用mmc_set_ios進行統一設置。後續會繼續說明。
1、mmc_set_ios
統一設置mmc匯流排的io設置(io setting)。
void mmc_set_ios(struct mmc_host *host)
{
struct mmc_ios *ios = &host->ios;
if (ios->clock > 0)
mmc_set_ungated(host); // 關閉clock的門控
host->ops->set_ios(host, ios); // 調用host->ops->set_ios來對mmc匯流排的io setting進行設置,核心函數
// 對於sdhci類型的host,對應就是sdhci_set_ios
}
會調用host->ops->set_ios來對mmc匯流排的io setting進行設置,核心函數。對於sdhci類型的host,對應就是sdhci_set_ios
2、mmc_set_bus_mode & mmc_set_bus_width
- mmc_set_bus_mode用於設置匯流排模式,有如下模式
- MMC_BUSMODE_OPENDRAIN(開漏模式)
- MMC_BUSMODE_PUSHPULL(上拉模式)
- mmc_set_bus_width用於設置匯流排寬度,有如下模式
- MMC_BUS_WIDTH_1
- MMC_BUS_WIDTH_4
- MMC_BUS_WIDTH_8
代碼如下:
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
{
mmc_host_clk_hold(host);
host->ios.bus_mode = mode;
mmc_set_ios(host);
mmc_host_clk_release(host);
}
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
mmc_host_clk_hold(host);
host->ios.bus_width = width;
mmc_set_ios(host);
mmc_host_clk_release(host);
}
其他設置io setting的函數類似,不多說明。
六、介面代碼說明——host的mmc匯流排相關
1、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;
spin_lock_irqsave(&host->lock, flags);
BUG_ON(host->bus_ops); // 不允許重覆設置host的mmc匯流排操作集
BUG_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;
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
}
在《card相關模塊》中可以看到mmc_attach_mmc->mmc_attach_bus_ops調用mmc_attach_bus來綁定了host的mmc匯流排操作集為mmc_ops_unsafe或者mmc_ops
2、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) // 說明host的mmc匯流排當前並沒有使用,調用__mmc_release_bus進行實際的釋放操作
__mmc_release_bus(host);
spin_unlock_irqrestore(&host->lock, flags);
}
static void __mmc_release_bus(struct mmc_host *host)
{
host->bus_ops = NULL; // 清空host的mmc匯流排操作集
}
七、介面代碼說明——mmc請求相關
分成同步的mmc請求和非同步的mmc請求。差別如下:
1、流程上的差別:
(1)會阻塞的處理流程:
mmc_wait_for_req
——》__mmc_start_req // 發起請求
————》init_completion(&mrq->completion);
————》mrq->done = mmc_wait_done
————》mmc_start_request(host, mrq); // 實際發起請求的操作
——》mmc_wait_for_req_done // 阻塞等待請求處理完成
——》返回
(2)不阻塞等待該命令的處理流程:
(註意:並不是說調用這個介面並不會阻塞,而是不會為了等待當前請求處理完成而阻塞,但是可能會等待上一次請求處理完成而阻塞)
mmc_start_req
——》mmc_wait_for_data_req_done // 阻塞等待上一次的請求處理
——》__mmc_start_data_req // 發起非同步請求
————》mrq->done = mmc_wait_data_done
————》mmc_start_request // 實際發起請求的操作
——》返回
最後都是調用了mmc_start_request使host向MMC發起請求。
0、數據結構說明
一個mmc請求分成兩部分內容,分別是命令部分和數據部分。
- mmc_command
struct mmc_command {
u32 opcode; // 命令的操作碼,如MMC_GO_IDLE_STATE、MMC_SEND_OP_COND等等
u32 arg; // 命令的參數
u32 resp[4]; // response值
unsigned int flags; /* expected response type */ // 期待的response的類型
#define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
/*
* These are the command types.
*/
#define mmc_cmd_type(cmd) ((cmd)->flags & MMC_CMD_MASK)
unsigned int retries; /* max number of retries */ // 失敗時的重覆嘗試次數
unsigned int error; /* command error */ // 命令的錯誤碼
/*
* Standard errno values are used for errors, but some have specific
* meaning in the MMC layer:
*
* ETIMEDOUT Card took too long to respond
* EILSEQ Basic format problem with the received or sent data
* (e.g. CRC check failed, incorrect opcode in response
* or bad end bit)
* EINVAL Request cannot be performed because of restrictions
* in hardware and/or the driver
* ENOMEDIUM Host can determine that the slot is empty and is
* actively failing requests
*/
unsigned int cmd_timeout_ms; /* in milliseconds */ // 命令執行的等待超時事件
struct mmc_data *data; /* data segment associated with cmd */ // 和該命令關聯在一起的數據段
struct mmc_request *mrq; /* associated request */ // 該命令關聯到哪個request
};
- mmc_data
struct mmc_data {
unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */ // 超時時間,以ns為單位
unsigned int timeout_clks; /* data timeout (in clocks) */ // 超時時間,以clock為單位
unsigned int blksz; /* data block size */ // 塊大小
unsigned int blocks; /* number of blocks */ // 塊數量
unsigned int error; /* data error */ // 傳輸的錯誤碼
unsigned int flags; // 傳輸標識
#define MMC_DATA_WRITE (1 << 8)
#define MMC_DATA_READ (1 << 9)
#define MMC_DATA_STREAM (1 << 10)
unsigned int bytes_xfered;
struct mmc_command *stop; /* stop command */ // 結束傳輸的命令
struct mmc_request *mrq; /* associated request */ // 該命令關聯到哪個request
unsigned int sg_len; /* size of scatter list */
struct scatterlist *sg; /* I/O scatter list */
s32 host_cookie; /* host private data */
bool fault_injected; /* fault injected */
};
- mmc_request
struct mmc_request是mmc core向host controller發起命令請求的處理單位。
struct mmc_request {
struct mmc_command *sbc; /* SET_BLOCK_COUNT for multiblock */ // 設置塊數量的命令,怎麼用的後續再補充
struct mmc_command *cmd; // 要傳輸的命令
struct mmc_data *data; // 要傳輸的數據
struct mmc_command *stop; // 結束命令,怎麼用的後續再補充
struct completion completion; // 完成量
void (*done)(struct mmc_request *);/* completion function */ // 傳輸結束後的回調函數
struct mmc_host *host; // 所屬host
};
- mmc_async_req
struct mmc_async_req {
/* active mmc request */
struct mmc_request *mrq;
unsigned int cmd_flags; /* copied from struct request */
/*
* Check error status of completed mmc request.
* Returns 0 if success otherwise non zero.
*/
int (*err_check) (struct mmc_card *, struct mmc_async_req *);
/* Reinserts request back to the block layer */
void (*reinsert_req) (struct mmc_async_req *);
/* update what part of request is not done (packed_fail_idx) */
int (*update_interrupted_req) (struct mmc_card *,
struct mmc_async_req *);
};
1、mmc_wait_for_req
發起mmc_request請求並且等待其處理完成。由其他需要發起mmc請求的模塊調用。
可以結合後面的mmc_request_done來看。
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
if (mmc_bus_needs_resume(host))
mmc_resume_bus(host);
#endif
__mmc_start_req(host, mrq); // 開始發起mmc_request請求
mmc_wait_for_req_done(host, mrq); // 等待mmc_request處理完成
}
//-----------------------------------__mmc_start_req說明,開始發起mmc_request請求
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
/* 發起mmc_request前的一些初始化工作,包括完成量和處理完成的回調函數的設置 */
init_completion(&mrq->completion); // 初始化完成量,在mmc_wait_for_req_done中會去等待這個完成量
mrq->done = mmc_wait_done;
// 設置mmc_request處理完成的回調函數,會調用complete(&mrq->completion);來設置完成量
// host controller會調用mmc_request_done來執行這個回調函數,具體在後面分析
if (mmc_card_removed(host->card)) { // 檢測card是否存在
mrq->cmd->error = -ENOMEDIUM;
complete(&mrq->completion);
return -ENOMEDIUM;
}
/* 調用mmc_start_request發起mmc請求 */
mmc_start_request(host, mrq); // 開始處理mmc_request請求
return 0;
}
static void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
WARN_ON(!host->claimed);
/* 以下對mmc_request的各個成員,包括cmd、data、stop做驗證操作和關聯操作 */
mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
if (mrq->data) {
BUG_ON(mrq->data->blksz > host->max_blk_size);
BUG_ON(mrq->data->blocks > host->max_blk_count);
BUG_ON(mrq->data->blocks * mrq->data->blksz >
host->max_req_size);
mrq->cmd->data = mrq->data; // 也就是說mmc_request的data和其cmd中的data是一一樣的
mrq->data->error = 0;
mrq->data->mrq = mrq;
if (mrq->stop) {
mrq->data->stop = mrq->stop;
mrq->stop->error = 0;
mrq->stop->mrq = mrq;
}
#ifdef CONFIG_MMC_PERF_PROFILING
if (host->perf_enable)
host->perf.start = ktime_get();
#endif
}
/* 獲取時鐘 */
mmc_host_clk_hold(host);
/* 調用host controller的request方法來處理mmc_request請求 */
host->ops->request(host, mrq);
// host->ops->request也就是host controller的request方法,對於sdhci類型的host來說,就是sdhci_request
}
//-----------------------------------mmc_wait_for_req_done說明,等待mmc_request處理完成
static void mmc_wait_for_req_done(struct mmc_host *host,
struct mmc_request *mrq)
{
struct mmc_command *cmd;
while (1) {
wait_for_completion_io(&mrq->completion); // 在這裡休眠,等待mrq->completion完成量,在__mmc_start_req中初始化的
cmd = mrq->cmd; // 獲取對應的command
/*
* If host has timed out waiting for the commands which can be
* HPIed then let the caller handle the timeout error as it may
* want to send the HPI command to bring the card out of
* programming state.
*/
if (cmd->ignore_timeout && cmd->error == -ETIMEDOUT)
break;
if (!cmd->error || !cmd->retries || mmc_card_removed(host->card))
// 如果command正常處理完成,或者失敗重覆嘗試次數為0,或者card被移除了,直接退出迴圈返回
break;
// 以下處理失敗重覆嘗試的情況
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host), cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
host->ops->request(host, mrq);
}
}
會調用host->ops->request來對mmc_request進行處理,對於sdhci類型的host,對應就是sdhci_request。
這個方法就是mmc_request實際被處理的核心。
2、mmc_request_done
通知mmc core某個mmc_request已經處理完成,由host controller調用。
以sdhci類型的host為例,處理完一個mmc_request之後,會執行sdhci_tasklet_finish,而在sdhci_tasklet_finish中會調用mmc_request_done來通知host某個mmc_request已經處理完成了。
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
int err = cmd->error;
if (host->card)
mmc_update_clk_scaling(host);
if (err && cmd->retries && !mmc_card_removed(host->card)) {
// command執行出錯,如果還需要重覆嘗試的話,這裡不釋放clock,只是通知mmc core
if (mrq->done)
mrq->done(mrq);
// 執行mmc_request的回調函數來通知mmc core,
// 對於__mmc_start_req發起的request來說,就是mmc_wait_done,上面已經說明過了
// 對於__mmc_start_data_req發起的request來說,就是mmc_wait_data_done,後面會說明
} else {
mmc_should_fail_request(host, mrq);
// 用於模擬data傳輸概率出錯的情況
// 具體參考http://blog.csdn.net/luckywang1103/article/details/52224160
if (mrq->done)
mrq->done(mrq);
// 執行mmc_request的回調函數來通知mmc core,對於__mmc_start_req發起的request來說,就是mmc_wait_done,上面已經說明過了
mmc_host_clk_release(host);
}
}
通過上述,mrq->done被調度,mmc_wait_done被執行,mrq->completion被設置。
然後等待mrq->completion的mmc_wait_for_req_done就會繼續往下執行。
3、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 = {NULL};
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; // 返回錯誤碼
}
4、mmc_start_req(重要)
機制說明如下:mmc_start_req會先判斷上一次的asycn_req是否處理完成,如果沒有處理完成,則會等待其處理完成。
如果處理完成了,為當前要處理的asycn_req發起請求,但是並不會等待,而是直接返回。
註意:並不是說調用這個介面並不會阻塞,而是不會為了等待當前請求處理完成而阻塞,但是可能會等待上一次請求處理完成而阻塞。這樣,可以利用等待的一部分時間來做其他操作。
為了方便理解這個函數,需要看一下其函數註釋。
- 要註意,在函數裡面有兩個非同步請求:
- areq:表示新的非同步請求
- host->areq:表示上一次發起的、正在處理、等待完成的非同步請求
代碼如下(為了方便理解,對代碼進行了簡化):
/**
* mmc_start_req - start a non-blocking request // 該函數用來發起一個不阻塞的請求
* @host: MMC host to start command // 要發起對應請求的host
* @areq: async request to start // 要發起的非同步請求
* @error: out parameter returns 0 for success, otherwise non zero // 返回值,返回0表示成功,返回非零表示失敗
*
* Start a new MMC custom command request for a host. // 為host發起的一個新的mmc命令請求
* If there is on ongoing async request wait for completion // 如果host已經有一個正在處理、等待完成的非同步請求,那麼會等待這個請求完成!!!
* of that request and start the new one and return. // 然後發起新的請求,然後返回!!!
* Does not wait for the new request to complete. // 並不會等待這個新的請求完成!!!
*
* Returns the completed request, NULL in case of none completed. // 會返回被完成的mmc請求(而不是新的mmc請求。)空表示沒有mmc請求被完成。
* Wait for the an ongoing request (previoulsy started) to complete and
* return the completed request. If there is no ongoing request, NULL
* is returned without waiting. NULL is not an error condition.
// 等待上一次發起的mmc請求完成,然後把這個mmc請求返回。如果沒有mmc請求正在處理,那麼就直接返回而不會等待。空並不是錯誤條件。
*/
struct mmc_async_req *mmc_start_req(struct mmc_host *host,
struct mmc_async_req *areq, int *error)
{
int err = 0;
int start_err = 0;
struct mmc_async_req *data = host->areq;
unsigned long flags;
bool is_urgent;
/* Prepare a new request */
/* 為新的非同步請求做準備處理 */
if (areq) {
/*
* start waiting here for possible interrupt
* because mmc_pre_req() taking long time
*/
mmc_pre_req(host, areq->mrq, !host->areq);
}
/* 對上一次發起的、正在處理、等待完成的非同步請求進行處理、等待操作 */
if (host->areq) {
err = mmc_wait_for_data_req_done(host, host->areq->mrq, areq); // 在這裡等待正在處理的非同步請求處理完成
//.......以下過濾了錯誤處理的部分
}
/* 對新的非同步請求進行發起操作 */
if (!err && areq) {
/* urgent notification may come again */
spin_lock_irqsave(&host->context_info.lock, flags);
is_urgent = host->context_info.is_urgent;
host->context_info.is_urgent = false;
spin_unlock_irqrestore(&host->context_info.lock, flags);
if (!is_urgent || (areq->cmd_flags & REQ_URGENT)) {
start_err = __mmc_start_data_req(host, areq->mrq); // 調用__mmc_start_data_req發起新的非同步請求
} else {
/* previous request was done */
err = MMC_BLK_URGENT_DONE;
if (host->areq) {
mmc_post_req(host, host->areq->mrq, 0);
host->areq = NULL;
}
areq->reinsert_req(areq);
mmc_post_req(host, areq->mrq, 0);
goto exit;
}
}
if (host->areq)
mmc_post_req(host, host->areq->mrq, 0);
/* Cancel a prepared request if it was not started. */
if ((err || start_err) && areq)
mmc_post_req(host, areq->mrq, -EINVAL);
if (err)
host->areq = NULL;
else
host->areq = areq;
exit:
if (error)
*error = err;
return data; // 反正上一次正常處理的非同步請求
}
//-----------------------------------------------------------------------------------------------------------------------------
static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
{
mrq->done = mmc_wait_data_done;
// 設置mmc_request處理完成的回調函數,會喚醒正在等待請求被完成的進程,後面說明
// host controller會調用mmc_request_done來執行這個回調函數,具體前面分析過了
mrq->host = host;
mmc_start_request(host, mrq); // 開始處理mmc_request請求,前面已經說明過了
return 0;
}
static void mmc_wait_data_done(struct mmc_request *mrq)
{
unsigned long flags;
struct mmc_context_info *context_info = &mrq->host->context_info;
spin_lock_irqsave(&context_info->lock, flags);
mrq->host->context_info.is_done_rcv = true; // 設置is_done_rcv標識
wake_up_interruptible(&mrq->host->context_info.wait); // 喚醒context_info上的等待進程
spin_unlock_irqrestore(&context_info->lock, flags);
}
//-----------------------------------------------------------------------------------------------------------------------------
static int mmc_wait_for_data_req_done(struct mmc_host *host,
struct mmc_request *mrq,
struct mmc_async_req *next_req)
{
// struct mmc_request *mrq:表示正在等待完成的請求
// struct mmc_async_req *next_req:表示下一次要執行的非同步請求
struct mmc_command *cmd;
struct mmc_context_info *context_info = &host->context_info;
bool pending_is_urgent = false;
bool is_urgent = false;
bool is_done_rcv = false;
int err, ret;
unsigned long flags;
while (1) {
/* 在這裡等待正在進行的請求完成,會在mmc_wait_data_done中被喚醒 */
/* 有幾種情況會喚醒等待進程 */
ret = wait_io_event_interruptible(context_info->wait,(context_info->is_done_rcv || context_info->is_new_req || context_info->is_urgent));
spin_lock_irqsave(&context_info->lock, flags);
is_urgent = context_info->is_urgent;
is_done_rcv = context_info->is_done_rcv;
context_info->is_waiting_last_req = false;
spin_unlock_irqrestore(&context_info->lock, flags);
/* 對請求處理完成的處理 */
if (is_done_rcv) {
context_info->is_done_rcv = false;
context_info->is_new_req = false;
cmd = mrq->cmd;
if (!cmd->error || !cmd->retries || mmc_card_removed(host->card)) {
/* 請求正常處理完成,或者失敗但是不需要重覆嘗試的情況的處理 */
err = host->areq->err_check(host->card, host->areq);
//.......
break; /* return err */
} else {
/* 對請求處理出錯並且需要重覆嘗試的情況的處理 */
//.......
}
}
}
return err;
}