一、概述 mfd是Multifunction device的簡稱,即多功能設備,是許多有共性的設備的集合,mfd由核心層(core)以及其下的“子設備”組成。從下文將會看到,mfd只是將設備註冊到platform匯流排——因此,其子設備屬於platform設備。它並沒有對涉及到的設備或驅動做實質性改變 ...
一、概述
mfd是Multifunction device的簡稱,即多功能設備,是許多有共性的設備的集合,mfd由核心層(core)以及其下的“子設備”組成。從下文將會看到,mfd只是將設備註冊到platform匯流排——因此,其子設備屬於platform設備。它並沒有對涉及到的設備或驅動做實質性改變。但是,因為某些設備的共性,所以可以在mfd中提供共同的函數給其下子設備進行調用。
本文提到的hisi_fmc
驅動就是如此:
下麵就分析mfd設備註冊過程,並結合1個實例講解。
內核配置(make menuconfig)信息如下:
在裡面可以選中自己需要的器件;
.config文件中配置CONFIG_MFD_CORE=y
二、mfd設備添加
mfd核心代碼位於drivers/mfd/mfd-core.c文件中。對外提供添加設備和刪除設備的介面:mfd_add_devices、mfd_remove_devices。設備添加函數原型如下:
int mfd_add_devices(struct device *parent, int id,
const struct mfd_cell *cells, int n_devs,
struct resource *mem_base,
int irq_base, struct irq_domain *domain)
- id:即設備ID號。它指示著設備的個數。一般可以設置為-1。即表示系統有且僅有一個這樣的設備。如果有多個foo設備,則需要使用id來區別。
在/sys/bus/platform/devices目錄下會產生foo.0,foo.1等設備。詳情可以看platform設備添加函數過程。
cells:即mfd_cell結構體數組,n_devs為其數組大小,即設備數量。
mem_base:資源resource結構體。如果沒有,可置為NULL。
描述mfd設備單元稱為“cell”,mfd_cell定義如下:
/*
* This struct describes the MFD part ("cell").
* After registration the copy of this structure will become the platform data
* of the resulting platform_device
*/
struct mfd_cell {
const char *name;
int id;
/* refcounting for multiple drivers to use a single cell */
atomic_t *usage_count;
int (*enable)(struct platform_device *dev);
int (*disable)(struct platform_device *dev);
int (*suspend)(struct platform_device *dev);
int (*resume)(struct platform_device *dev);
/* platform data passed to the sub devices drivers */
void *platform_data;
size_t pdata_size;
/*
* Device Tree compatible string
* See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details
*/
const char *of_compatible;
/*
* These resources can be specified relative to the parent device.
* For accessing hardware you should use resources from the platform dev
*/
int num_resources;
const struct resource *resources;
/* don't check for resource conflicts */
bool ignore_resource_conflicts;
/*
* Disable runtime PM callbacks for this subdevice - see
* pm_runtime_no_callbacks().
*/
bool pm_runtime_no_callbacks;
/* A list of regulator supplies that should be mapped to the MFD
* device rather than the child device when requested
*/
const char * const *parent_supplies;
int num_parent_supplies;
};
部分常見的成員介紹如下:
- name:設備平臺。
- platform_data:平臺私有數據指針,數據大小使用pdata_size表示。
- resources:資源結構體,資源數量使用num_resources表示。
- ignore_resource_conflicts:為true表示不檢查資源衝突。
- of_compatible:設備樹匹配compatible的字元串(具體參考
Documentation/devicetree/usage-model.txt Chapter 2.2
)這個根據我的理解,是用於platform device的,只是寫在了mfd設備上;
至此,mfd設備的添加就完成了,最終調用驅動的probe函數。從這個過程中知道,mfd實質上就是封裝一個介面,將一些可以歸納到一起的platform設備註冊到platform匯流排上。它就是一個收納盒子。裡面的設備該是怎樣處理就怎樣處理。
三、mfd實例
下麵介紹hisi_fmc驅動的實例:
static int hisi_fmc_probe(struct platform_device *pdev)
{
struct hisi_fmc *fmc;
struct resource *res;
struct device *dev = &pdev->dev;
int ret;
pr_err("hisi_fmc_probe successfully!\n");
fmc = devm_kzalloc(dev, sizeof(*fmc), GFP_KERNEL);
if (!fmc)
return -ENOMEM;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control");
fmc->regbase = devm_ioremap_resource(dev, res);
if (IS_ERR(fmc->regbase))
return PTR_ERR(fmc->regbase);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory");
fmc->iobase = devm_ioremap_resource(dev, res);
if (IS_ERR(fmc->iobase))
return PTR_ERR(fmc->iobase);
fmc->clk = devm_clk_get(dev, NULL);
if (IS_ERR(fmc->clk))
return PTR_ERR(fmc->clk);
if (of_property_read_u32(dev->of_node, "max-dma-size", &fmc->dma_len)) {
dev_err(dev, "Please set the suitable max-dma-size value !!!\n");
return -ENOMEM;
}
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_warn(dev, "Unable to set dma mask\n");
return ret;
}
fmc->buffer = dmam_alloc_coherent(dev, fmc->dma_len,
&fmc->dma_buffer, GFP_KERNEL);
if (IS_ERR(fmc->buffer))
return PTR_ERR(fmc->buffer);
mutex_init(&fmc->lock);
platform_set_drvdata(pdev, fmc);
ret = mfd_add_devices(dev, 0, hisi_fmc_devs,
ARRAY_SIZE(hisi_fmc_devs), NULL, 0, NULL);
if (ret) {
dev_err(dev, "add mfd devices failed: %d\n", ret);
return ret;
}
return 0;
}
- 讀取fmc的reg_base、io_base;
- 獲取最大的
max-dma-size
- 添加mfd設備
ret = mfd_add_devices(dev, 0, hisi_fmc_devs,
ARRAY_SIZE(hisi_fmc_devs), NULL, 0, NULL);