您的位置:首页 > 其它

我的内核学习笔记8:多功能设备mfd驱动

2016-12-05 22:18 375 查看
前段时间分析了Intel的lpc驱动,里面涉及了mfd,但网络上关于mfd的资料少之又少。所以就自己分析一下代码。本文对Linux内核的mfd设备注册过程进行分析,并结合2个实例,加深对其概念。

一、概述

mfd是Multifunction device的简称,即多功能设备,是许多有共性的设备的集合,mfd由核心层(core)以及其下的“子设备”组成。从下文将会看到,mfd只是将设备注册到platform总线——因此,其子设备属于platform设备。它并没有对涉及到的设备或驱动做实质性改变。但是,因为某些设备的共性,所以可以在mfd中提供共同的函数给其下子设备进行调用。如本文提到的ADP5520便是如此设计。

下面就分析mfd设备注册过程,并结合2个实例讲解。但不会涉及很细节的细节,因为这样篇幅太大了,而且还会纠结于语言本身,不利于全局把握。所以最好自行阅读内核源代码。

本文基于linux 3.17.1版本内核分析。

内核配置(make menuconfig)信息如下:Device Drivers --->
Multifunction device drivers --->里面有众多可选的器件,根据实际来选择。

二、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设备添加函数过程。

celss:即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表示不检查资源冲突。

mfd_add_devices函数内部根据设备数量n_devs循环调用mfd_add_device添加设备。该函数完成下面的工作:

1、申请platform_device空间。申请resource空间。

2、调用platform_device_add_data添加platform设备私有数据,亦即platform_data。

3、调用mfd_platform_add_cell将mfd_cell拷贝到platform_device的mfd_cell成员。(使用kmemdup实现)

4、根据参数,设置申请到的resource空间。并调用platform_device_add_resources添加到platform_device的resource成员。这样就能在platform驱动模块中获取到resource资源了。

5、调用platform_device_add添加platform设备。此过程中会调用到对应驱动的probe函数——当然,提前是已经存在对应的驱动。

至此,mfd设备的添加就完成了,最终调用驱动的probe函数。从这个过程中知道,mfd实质上就是封装一个接口,将一些可以归纳到一起的platform设备注册到platform总线上。它就是一个收纳盒子。里面的设备该是怎样处理就怎样处理。

三、mfd实例1:lpc驱动

本节介绍一下LPC驱动中WDT设备添加的过程。——因为前面讲了GPIO设备的添加。
e3800系列的WDT隐藏于ACPI中。后续文章将会进行介绍。这里有个概念即可。
在LPC探测函数lpc_ich_probe对ACPI基地址进行赋值,代码如下:

    priv->abase = ACPIBASE; // ACPI基地址
    priv->actrl_pbase = ACPICTRL_PMCBASE;
其定义是:
#define ACPIBASE		0x40
#define ACPICTRL_PMCBASE	0x44
所有地址都可以在手册对应章节中找到。

初始化WDT在函数lpc_ich_init_wdt中。这个函数获取ACPI基地址。并初始化wdt_ich_res资源结构体数组,数组包含了ICH_RES_IO_TCO和ICH_RES_IO_SMI。TCO就是WDT使用到的部分。主要代码功能描述如下。

1、读取ACPI基地址值,即通过LPC这个PCI设备的配置空间偏移值ACPIBASE。

pci_read_config_dword(dev, priv->abase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;


2、设置resource,即把前面获取到的base_addr地址加上TCO偏移值赋给resource的start成员变量。

res = wdt_io_res(ICH_RES_IO_TCO);
res->start = base_addr + ACPIBASE_TCO_OFF;
res->end = base_addr + ACPIBASE_TCO_END;


3、添加mfd设备。

lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
1, NULL, 0, NULL);


到这样就完成了mfd的添加。

四、mfd实例2:ADP5520驱动

[待写]

 

后记:mfd网上资料太少,本文根据代码分析而成。难免有错误。欢迎一起讨论。

参考资源:
1、baytrail手册:http://www.intel.com/content/www/us/en/embedded/products/bay-trail/atom-e3800-family-datasheet.html
2、ADP5520介绍:https://www.wiki.analog.com/resources/tools-software/linux-drivers/multifunction-device/adp5520
3、内核源码官网:https://www.kernel.org
4、内核源码查询:http://lxr.free-electrons.com/source/?v=3.17

李迟 2016.12.05 周一 夜
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: