当前位置: 首页 > Linux内核研究, 嵌入式Linux > 正文

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

前段时间分析了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 周一 夜

近来经济拮据,如本文对阁下有帮助,可慷慨解囊赞助笔者以输出更多好文章。
[email protected] 或 微信fly_camel_fly 均可。感谢!
14373903313201                                  14373903313202

本文固定链接: http://www.latelee.org/embedded-linux/kernel-note-8-mfd-driver.html

如无特别说明,迟思堂工作室文章均为原创,转载请注明: 我的内核学习笔记8:多功能设备mfd驱动 | 迟思堂工作室

目前暂无评论

发表评论

*

快捷键:Ctrl+Enter