Linux驱动模型 - 平台设备(unfinished)

来源:互联网 发布:js touchend 坐标 编辑:程序博客网 时间:2024/05/29 08:13

Platform Devices and Drivers

~~~~~~~~~~~~~~~~~~~~~~~~~~~~

新的驱动模型中有关Platform总线的接口可以参考<linux/platform_device.h>: platform_device, 和 platform_driver. 

这个虚拟总线用于连接那些简单总线上的设备。简单总线可以认为是以前SOC中集成外设的总线,或一些PC上使用的过时的连接器;做为对比,那些庞大的、正式定义的总线就不属于platform总线,比如PCI或USB.

译注:SOC中往往集成了一些外设控制器,比如中断/串口/LCD控制器等等。这些集成的控制器通过SOC内部总线和CPU连接,而不同的SOC其内部总想也不尽相同。为了统一管理这些集成的控制器,Linux使用Platform总线这个概念。

 

Platform devices

~~~~~~~~~~~~~~~~

Platform设备在系统中往往以具有自主权的实体的形式呈现. 比如以前的基于端口的设备和主机连接外设总线的桥设备,或是SOC中集成的那些控制器. 它们的共同特点是可以被CPU总线直接寻址. 一种少见的情形是platform设备被连接到其它总线的某一区域,但即使这样,该设备的寄存器仍旧是可以被直接寻址的.

 

需要为Platform设备指定名称,用于和设备驱动绑定, 还要指定该设备所用的资源,如地址和IRQx.

struct platform_device {

         const char                 *name;

         u32                               id;

         struct device             dev;

         u32                               num_resources;

         structresource        *resource;

};

 

 

Platform drivers

~~~~~~~~~~~~~~~~

Platform设备驱动遵循标准驱动模型的约定,也就是说,探测/枚举设备的工作不在驱动中进行,而驱动要提供probe() 和 remove() 方法.  Platform设备驱动还必须通过标准驱动模型的约定支持电源管理和设备关闭通知.

struct platform_driver {

         int(*probe)(struct platform_device *);

         int(*remove)(struct platform_device *);

         void(*shutdown)(struct platform_device *);

         int(*suspend)(struct platform_device *, pm_message_t state);

         int(*suspend_late)(struct platform_device *, pm_message_t state);

         int(*resume_early)(struct platform_device *);

         int(*resume)(struct platform_device *);

         structdevice_driver driver;

};

一般说来,probe()的工作是确认硬件设备是否真的存在; 有时系统的启动代码无法做此确认.  Probe过程中可以使用设备的资源,包括时钟、设备的platform_data.

 

平台设备驱动以以下方式注册自己:

         int platform_driver_register(struct platform_driver *drv);

 

或者,如果确认设备不支持热插拔,则可以将probe移入module_init以减少驱动运行时的内存开销:

 

         int platform_driver_probe(struct platform_driver *drv,

                              int (*probe)(struct platform_device *))

 

 

Device Enumeration

~~~~~~~~~~~~~~~~~~

做为惯例,板级的代码负责注册platform设备:

         int platform_device_register(struct platform_device *pdev);

         int platform_add_devices(struct platform_device **pdevs, int ndev);

 

一般的规则是只注册那些确实存在的设备, 但某些特定情况下可能会注册额外的设备. 比如,内核可能会被配置为支持一块外置的网络适配器,但不一定所有的板子上都有这块适配器。或者类似地,内核要支持一个集成的控制器,但某些板子可能不与任何外设连接.

 

某些情况下,启动代码会导出板子上设备的列表. 如果没有这个列表,通常唯一的做法就是为特定的板子配置特定的kernel. 在嵌入式系统和客制化系统之中,这种做法相当常见.

 

很常见的情形是为platform设备所分配的内存和中断并不足以让设备驱动工作. 板级代码往往利用设备的platform_data域来提供额外的信息.

 

在嵌入式系统中,平台设备往往需要一个或多个时钟,这些时钟在不需要设备工作时是关闭的(省电). 系统代码用如下方式为设备分配时钟

clk_get(&pdev->dev,clock_name)

 

 

Legacy Drivers:  Device Probing

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

有些驱动尚未完全转移到新的驱动模型,因为它们做了不属于驱动该做的工作: 驱动注册了platform设备,而不是将这项工作留给系统的其它部分(译注:如板级代码)去做. 这样的驱动既不支持热插拔,也不支持冷插拔, 因为冷/热插拔的机制要求由驱动以外的系统组件来创建设备.

译注:热插拔在内核启动之后发生,而冷插拔发生在内核启动的过程中。

这么做的唯一好处是适应早先的系统设计,比如最早的IBM PC架构,其硬件配置的过程依赖于容易出错的probe-the-hardware机制。 

得益于对动态配置的总线级的支持(PCI,USB),或由引导程序提供的设备列表,较新的系统在很大程度上抛弃了这个机制。对于什么应该放在哪里这样的问题的来说,有太多选择是互相冲突的,而且,即便OS做出的合理猜测也有可能根本就是错的,并引起很大的麻烦。

这种类型的驱动最好不要使用。如果你正在移植这样的驱动,请务必尝试将设备枚举的动作转移到驱动以外的更合适的地方。

 

This style of driver is discouraged.  If you're updating such a driver,

please try to move the device enumeration to a more appropriate location,

outside the driver.  This will usually be cleanup, since such drivers

tend to already have "normal" modes, such as ones using device nodes that

were created by PNP or by platform device setup.

 

None the less, there are some APIs to support such legacy drivers.  Avoid

using these calls except with such hot plug-deficient drivers.

 

         struct platform_device *platform_device_alloc(

                            const char *name, int id);

 

You can use platform_device_alloc() to dynamically allocate a device, which

you will then initialize with resources and platform_device_register().

A better solution is usually:

 

         struct platform_device *platform_device_register_simple(

                            const char *name, int id,

                            struct resource *res, unsigned int nres);

 

You can use platform_device_register_simple() as a one-step call to allocate

and register a device.

 

 

设备命名,驱动绑定

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

platform_device.dev.bus_id 是命名设备的经典例子。它由两部分组成:

is the canonical name for the devices.

    *platform_device.name ... 还用作匹配驱动。

    *platform_device.id ... 设备实例号,  "-1"代表没有实例。

以上两部分串联起来,name/id组合"serial"/0 代表 bus_id "serial.0", and

"serial/3" 代表 bus_id"serial.3"; 两者都使用命名为"serial"的平台设备驱动.

而 "my_rtc"/-1 则代表 bus_id "my_rtc" (没有实例号)并使用名为"my_rtc"的平台设备驱动。

 

驱动绑定的动作由Linux驱动模型的核心代码自动执行,Driver binding is performed automatically by the driver core, 找到匹配的设备/驱动后调用驱动的probe()接口。如果probe()函数顺利执行完成,那么设备和驱动就成功匹配上了。有三种不同的方法进行匹配:


    -每当注册一个设备,会遍历该总线上的每个驱动以找到匹配的那个。平台设备应该在系统启动时尽早注册。

 

    -当使用platform_driver_register()接口注册驱动时, 会遍历该总线上所有未匹配的设备以找到匹配的那个设备。驱动要么是在启动阶段的晚期,或是在加载模块时被注册。

 

    -使用 platform_driver_probe() 接口注册驱动和使用platform_driver_register()类似, 除了当以后有设备被注册时,该驱动不会被检测(也还好,因为这个接口只是为那些不支持热插拔的设备准备的)

 

Early Platform Devices and Drivers

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The early platform interfaces provide platform data to platform device

drivers early on during the system boot.The code is built on top of the

early_param() command line parsing and can be executed very early on.

 

Example: "early printk" class early serial console in 6 steps

 

1. Registering early platform device data

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The architecture code registers platform device data using the function

early_platform_add_devices(). In the case of early serial console this

should be hardware configuration for the serial port. Devices registered

at this point will later on be matched against early platform drivers.

 

2. Parsing kernel command line

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The architecture code calls parse_early_param() to parse the kernel

command line. This will execute all matching early_param() callbacks.

User specified early platform devices will be registered at this point.

For the early serial console case the usercan specify port on the

kernel command line as "earlyprintk=serial.0"where "earlyprintk" is

the class string, "serial" is the name of the platform driver and

0 is the platform device id. If the id is-1 then the dot and the

id can be omitted.

 

3. Installing early platform drivers belonging to a certain class

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The architecture code may optionally force registration of all early

platform drivers belonging to a certain class using the function

early_platform_driver_register_all(). User specified devices from

step 2 have priority over these. This stepis omitted by the serial

driver example since the early serial driver code should be disabled

unless the user has specified port on the kernel command line.

 

4. Early platform driver registration

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Compiled-in platform drivers making use of early_platform_init() are

automatically registered during step 2 or3. The serial driver example

should use early_platform_init("earlyprintk", &platform_driver).

 

5. Probing of early platform drivers belonging to a certain class

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The architecture code calls early_platform_driver_probe() to match

registered early platform devices associated with a certain class with

registered early platform drivers. Matched devices will get probed().

This step can be executed at any point during the early boot. As soon

as possible may be good for the serial portcase.

 

6. Inside the early platform driver probe()

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The driver code needs to take special care during early boot, especially

when it comes to memory allocation and interrupt registration. The code

in the probe() function can use is_early_platform_device() to check if

it is called at early platform device or at the regular platform device

time. The early serial driver performs register_console() at this point.

 

For further information, see<linux/platform_device.h>.

0 0