零、說明 對應代碼drivers/mmc/core/host.c,drivers/mmc/core/host.h。 為底層host controller driver實現mmc host的申請以及註冊的API等等,以及host相關屬性的實現。 一、API總覽 1、mmc host分配、註冊相關 mm ...
零、說明
對應代碼drivers/mmc/core/host.c,drivers/mmc/core/host.h。
為底層host controller driver實現mmc host的申請以及註冊的API等等,以及host相關屬性的實現。
一、API總覽
1、mmc host分配、註冊相關
- mmc_alloc_host & mmc_free_host
底層host controller驅動調用,用來分配或者釋放一個struct mmc_host結構體,將其於mmc_host_class關聯,並且做部分初始化操作。
原型:struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
參數說明:extra——》mmc_host的私有數據的長度,會和mmc_host結構體一起分配,
dev——》底層host controller的device結構體,用於作為mmc_host的device的父設備
原型:void mmc_free_host(struct mmc_host *host)
- mmc_add_host & mmc_remove_host
底層host controller驅動調用,註冊或者卸載mmc_host到設備驅動中,添加到sys類下麵,並設置相應的debug目錄。然後啟動mmc_host。
原型:int mmc_add_host(struct mmc_host *host)
原型:void mmc_remove_host(struct mmc_host *host)
2、mmc host class相關
- mmc_register_host_class & mmc_unregister_host_class
註冊或者卸載mmc_host類。
原型:int mmc_register_host_class(void)
原型:void mmc_unregister_host_class(void)
3、mmc host屬性解析相關
- mmc_of_parse
底層host controller驅動調用,解析mmc_host的dtsi節點的部分屬性。
原型:void mmc_of_parse(struct mmc_host *host)
4、mmc host時鐘相關
mmc_host_clk_hold & mmc_host_clk_release
mmc core主模塊調用,用於獲取host時鐘和釋放host時鐘
二、數據結構
1、mmc_host_class
mmc_host_class代表了mmc_host這個類。其內容如下:
static struct class mmc_host_class = {
.name = "mmc_host", // 添加到sys文件系統之後,會生成/sys/class/mmc_host這個目錄
.dev_release = mmc_host_classdev_release, // 從mmc_host這個class下release掉某個設備之後要做的對應操作
.pm = &mmc_host_pm_ops, // 該class下的host的pm電源管理操作
};
static const struct dev_pm_ops mmc_host_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mmc_host_suspend, mmc_host_resume)
SET_RUNTIME_PM_OPS(mmc_host_runtime_suspend, mmc_host_runtime_resume,
pm_generic_runtime_idle)
};
// 具體函數實現遇到了再補充
2、clk_scaling_attr_grp
一些和時鐘縮放(clk_scaling)相關的屬性組
static struct attribute *clk_scaling_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_up_threshold.attr,
&dev_attr_down_threshold.attr,
&dev_attr_polling_interval.attr,
NULL,
};
static struct attribute_group clk_scaling_attr_grp = {
.name = "clk_scaling",
.attrs = clk_scaling_attrs,
};
對應/sys/class/mmc_host/mmc0/clk_scaling目錄下的屬性
3、dev_attr_grp
和設備相關的屬性組,只定義了perf屬性
static struct attribute *dev_attrs[] = {
#ifdef CONFIG_MMC_PERF_PROFILING
&dev_attr_perf.attr,
#endif
NULL,
};
static struct attribute_group dev_attr_grp = {
.attrs = dev_attrs,
};
對應/sys/class/mmc_host/mmc0/perf屬性
三、介面代碼說明
1、mmc_register_host_class實現
註冊mmc_host class。
int mmc_register_host_class(void)
{
return class_register(&mmc_host_class); // 以mmc_host_class為class創建一個class,關於mmc_host_class在上述數據結構已經說明過了
}
相關節點:/sys/class/mmc_host
2、mmc_alloc_host實現
底層host controller驅動調用,用來分配一個struct mmc_host結構體,將其於mmc_host_class關聯,並且做部分初始化操作。
- 主要工作:
- 分配記憶體空間
- 初始化其class device(對應/sys/class/mmc0節點)
- clock gate、鎖、工作隊列、wakelock、detect工作的初始化
- 初始化detect成員(也就是檢測工作)為mmc_rescan
- 代碼如下:
/**
* mmc_alloc_host - initialise the per-host structure.
* @extra: sizeof private data structure
* @dev: pointer to host device model structure
*
* Initialise the per-host structure.
*/
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
// 參數說明:extra——》mmc_host的私有數據的長度,會和mmc_host結構體一起分配,
// dev——》底層host controller的device結構體,用於作為mmc_host的device的父設備
int err;
struct mmc_host *host;
/* 分配記憶體空間,其中多分配了extra位元組作為私有數據 */
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
if (!host)
return NULL;
/* scanning will be enabled when we're ready */
/* 因為只是分配了一個mmc_host,host還沒有準備好,所以這裡禁用rescan,也就是設置mmc_host->rescan_disable
host->rescan_disable = 1; // 在在mmc_start_host中會去使能
/* 為該mmc_host分配一個唯一的id號,設置到host->index */
idr_preload(GFP_KERNEL);
spin_lock(&mmc_host_lock);
err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);
if (err >= 0)
host->index = err;
spin_unlock(&mmc_host_lock);
idr_preload_end();
if (err < 0)
goto free;
/* 設置mmc_host name */
dev_set_name(&host->class_dev, "mmc%d", host->index); // 以mmc_host的id號構成mmc_host的name,例如mmc0、mmc1
/* 關聯mmc_host class_dev併進行初始化 */
/* class_dev就代表了mmc_host 的device結構體,是其在設備驅動模型中的體現 */
host->parent = dev; // 將mmc_host的parent設置成對應host controller節點轉化出來的device
host->class_dev.parent = dev;
// 將mmc_host的device(class_dev)的parent設置成對應host controller節點轉化出來的device
// 註冊到sysfs之後,會相應生成/sys/bus/platform/devices/7824900.sdhci/mmc_host/mmc0
// 其中7824900.sdhci表示qcom的host controller節點轉化出來的device
host->class_dev.class = &mmc_host_class;
// 將mmc_device(class_dev)的類設置為mmc_host_class
// 註冊到sysfs之後,會相應生成/sys/class/mmc_host/mmc0
device_initialize(&host->class_dev); // 初始化mmc_host->class_dev
/* clock gate、鎖、工作隊列、wakelock、detect工作的初始化 */
mmc_host_clk_init(host);
mutex_init(&host->slot.lock);
host->slot.cd_irq = -EINVAL;
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
host->wlock_name = kasprintf(GFP_KERNEL, "%s_detect", mmc_hostname(host)); // 設置detect_wake_lock的名稱為mmc0_detect,在card檢測的時候會使用
wake_lock_init(&host->detect_wake_lock, WAKE_LOCK_SUSPEND, host->wlock_name); // // 初始化detect_wake_lock
// 可以通過/sys/kernel/debug/wakeup_sources,相應生成了mmc0_detect和mmc1_detect兩個wakelock
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
// !!!!這個很重要!!!!初始化detect工作為mmc_rescan,後續調度host->detect來檢測是否有card插入時,就會調用到mmc_rescan。
#ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify;
#endif
/* 一些size的初始化 */
host->max_segs = 1; // 初始化最大支持段(由host自己根據硬體進行修改),可以通過/sys/block/mmcblk0/queue/max_segments進行修改
host->max_seg_size = PAGE_CACHE_SIZE; // 初始化段大小,(由host自己根據硬體進行修改)
host->max_req_size = PAGE_CACHE_SIZE; // 一次MMC請求的最大位元組數
host->max_blk_size = 512; // 一個塊的最大位元組數
host->max_blk_count = PAGE_CACHE_SIZE / 512; // 一次MMC請求的最大塊數量
return host;
free:
kfree(host);
return NULL;
}
3、mmc_add_host實現
底層host controller驅動調用,註冊mmc_host到設備驅動中,添加到sys類下麵,並設置相應的debug目錄。然後啟動mmc_host。
- 主要工作:
- 使能pm runtime功能
- 將mmc_host的class_dev添加到設備驅動模型中,在sysfs中生成相應的節點
- 初始化mmc_host相關的debug目錄
- 設置mmc_host的class_dev的屬性
- 調用mmc_start_host啟動host(進入mmc core主模塊的部分)
/**
* mmc_add_host - initialise host hardware
* @host: mmc host
*
* Register the host with the driver model. The host must be
* prepared to start servicing requests before this function
* completes.
*/
int mmc_add_host(struct mmc_host *host)
{
int err;
/* 使能mmc host的class_dev的pm runtime功能 */
err = pm_runtime_set_active(&host->class_dev);
if (err)
pr_err("%s: %s: failed setting runtime active: err: %d\n",
mmc_hostname(host), __func__, err);
else if (mmc_use_core_runtime_pm(host))
pm_runtime_enable(&host->class_dev);
/* 通過device_add將mmc_host->class_dev添加到設備驅動模型中,在sys下生成相應節點 */
err = device_add(&host->class_dev);
// 通過mmc_alloc_host中關於mmc_host的class_dev的關聯,可以生成如下兩個節點
// /sys/bus/platform/devices/7824900.sdhci/mmc_host/mmc0
// /sys/class/mmc_host/mmc0
/* 使能mmc host的class_dev的非同步suspend的功能 */
device_enable_async_suspend(&host->class_dev);
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
/* 設置mmc_host的debug節點 */
#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
#endif
// 對應sys節點為/sys/kernel/debug/mmc0
/* 以下設置mmc host的class_dev的屬性 */
mmc_host_clk_sysfs_init(host);
// 對應/sys/class/mmc_host/mmc0/clkgate_delay屬性
host->clk_scaling.up_threshold = 35;
host->clk_scaling.down_threshold = 5;
host->clk_scaling.polling_delay_ms = 100;
err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp);
// 對應/sys/class/mmc_host/mmc0/clk_scaling目錄下的四個屬性,clk_scaling_attr_grp前面已經說明過了
err = sysfs_create_group(&host->class_dev.kobj, &dev_attr_grp);
// 對應/sys/class/mmc_host/mmc0/perf屬性,dev_attr_grp前面已經說明過了
/* 調用mmc_start_host,也就調用到了mmc core主模塊的啟動host部分,在mmc core主模塊的時候說明 */
mmc_start_host(host);
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
register_pm_notifier(&host->pm_notify);
return 0;
}
註意,最後調用了mmc_start_host來啟動host。關於mmc_start_host會在mmc core主模塊的部分裡面說明。
也就是說,關於host的初始化工作,需要在調用mmc_add_host之前就要完成了。
相關節點:
/sys/bus/platform/devices/7824900.sdhci/mmc_host/mmc0
/sys/class/mmc_host/mmc0
/sys/kernel/debug/mmc0
4、mmc_of_parse實現
解析mmc_host的dtsi節點的部分屬性。
mmc_of_parse提供了通用的、解析host controller dtsi節點的屬性的方法,這就要依賴於dtsi的屬性是否符合規範。
但是host controller driver並一定要使用這個,也可以使用自己一套解析的方法。
簡單說明如下:
void mmc_of_parse(struct mmc_host *host)
{
struct device_node *np;
u32 bus_width;
bool explicit_inv_wp, gpio_inv_wp = false;
enum of_gpio_flags flags;
int len, ret, gpio;
if (!host->parent || !host->parent->of_node)
return;
/* 獲取到mmc_host對應的host controller的dts節點 */
np = host->parent->of_node;
// host->parent指向了mmc_host的對應host controller的device,獲取其of_node就獲取到了對應的dtsi節點
/* 以下就是解析屬性,並設置到mmc_host的屬性標識caps和caps2 中 */
/* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */
if (of_property_read_u32(np, "bus-width", &bus_width) < 0) {
dev_dbg(host->parent,
"\"bus-width\" property is missing, assuming 1 bit.\n");
bus_width = 1;
}
switch (bus_width) {
case 8:
host->caps |= MMC_CAP_8_BIT_DATA; // "bus-width"——》MMC_CAP_8_BIT_DATA
/* Hosts capable of 8-bit transfers can also do 4 bits */
case 4:
host->caps |= MMC_CAP_4_BIT_DATA; // "bus-width"——》MMC_CAP_4_BIT_DATA
break;
case 1:
break;
default:
dev_err(host->parent,
"Invalid \"bus-width\" value %ud!\n", bus_width);
}
/* f_max is obtained from the optional "max-frequency" property */
of_property_read_u32(np, "max-frequency", &host->f_max);
//................後面的代碼都類似,直接略過了
}