本章在以NUC972为平台的Linux3.10.1中添加设备驱动。
platform总线是linux中一种虚拟、抽象出来的总线,它将设备和驱动绑定。platform工作体系都定义在drivers/base/platform.c中,其有两个结构体:platform_device和platform_driver
platform_driver:
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 *);//恢复函数,在开机时被调用
struct device_driver driver;//设备驱动结构
};
struct device_driver {
const char * name;
struct bus_type * bus;
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module * owner;
const char * mod_name; /* used for built-in modules */
struct module_kobject * mkobj;
int (*probe) (struct device * dev);
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, pm_message_t state);
int (*resume) (struct device * dev);
};
platform_device
struct platform_device {
const char * name;//设备名称
u32 id;//取-1
struct device dev;//设备结构
u32 num_resources;// resource结构个数
struct resource * resource;//设备资源
};
了解了上面两个结构体,然后开始修改dev.c文件,这里是基于nuc972平台,在目录linux-3.10.x/arch/arm/mach-nuc970下,这里以KSZ8851芯片为例:
首先这里定义KSZ8851的EBI总线的nCS4基地址(KSZ8851_16MLL_BASE_ADDR),[0] 和[1] 分别是读写数据和地址访问的访问地址,这里(.start = KSZ8851_16MLL_BASE_ADDR+4)为什么加4?因为ksz8851的CMD线接到了EBI总线的ADDR2上,当CMD为高为写命令,CMD为低为写数据,然后对总线写地址时候保证bit2(EBI的ADDR2地址线)为1,那么ADDR2地址线为高,此时写的是命令,bit2为0则ADDR2地址线为为低电平,此时写的是数据,因此bit2对应0x4。
每一个resource都有.start,.end和.flags ,用于表示该资源的起始地址,末地址和类型,struct platform_device中name用于和driver进行匹配,name需要和驱动层中name相同,当匹配后将执行probe函数。
#ifdef CONFIG_KS8851_MLL
#define KSZ8851_16MLL_BASE_ADDR 0x20100000 //512K cs4 adrress--data_access
static struct resource nuc970_ksz8851_resource[] = {
[0] = {
.start = KSZ8851_16MLL_BASE_ADDR, //data_access
.end = KSZ8851_16MLL_BASE_ADDR+3,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = KSZ8851_16MLL_BASE_ADDR+4, //address_access
.end = KSZ8851_16MLL_BASE_ADDR+7,
.flags = IORESOURCE_MEM,
},
[2] = {
.start = IRQ_EXT7_H7, //PH7-INT7
.end = IRQ_EXT7_H7,
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
},
};
struct platform_device nuc970_device_ksz8851 = {
.name = "ks8851_mll",
.id = -1,
.num_resources = ARRAY_SIZE(nuc970_ksz8851_resource),
.resource = nuc970_ksz8851_resource,
};
#endif
最后在static struct platform_device *nuc970_public_dev[] __initdata中注册设备
#ifdef CONFIG_KS8851_MLL
&nuc970_device_ksz8851,
#endif
void __init nuc970_platform_init(struct platform_device **device, int size)函数会调用platform_add_devices(nuc970_public_dev, ARRAY_SIZE(nuc970_public_dev));来进行设备注册