platform_bus

来源:互联网 发布:淘宝在国外 编辑:程序博客网 时间:2024/06/10 12:47
静态编译驱动:
1. 首先把led.c 放到内核中放到 driver/char目录中

2. 添加一个编译的选项
   修改driver/char/Kconfig 文件
   config S5PV210_LED
 bool "s5pv210 led device support"
 tristate "s5pv210 led device support"
    help
      s5pv210 led driver
3. 把led.c 编译到内核中去,
   修改driver/char/Makefile 文件
    obj-$(CONFIG_S5PV210_LED) += led.o
4. 配置内核 make menuconfig
   Device Drivers --->
        Character devices --->
               [*] s5pv210 led device support
5. 编译内核
   make zImage -j2


动态编译
config S5PV210_LED
 bool "s5pv210 led device support"
 tristate "s5pv210 led device support"
    help
      s5pv210 led driver
 make zImage modules -j2



platform_bus 的机制
现在Linux中大部分的设备驱动都可以使用这套机制,总线为platform_bus,
设备用platform_device表示,驱动用platform_driver进行注册。
和传统的驱动一样,platform机制也分为三个步骤:
1. 内核启动初始化时的main.c文件中的start_kernel() →rest_init() →kernel_init()→do_basic_setup()
→driver_init()→platform_bus_init()→bus_register(&platform_bus_type),
注册了一条platform总线(虚拟总线,platform_bus)。
2. 添加设备阶段:platform_device
 
依赖头文件<linux/platform_device.h>
struct platform_device {
 const char * name; // 这个设备的名称 ,和驱动的名称一致
 int id; // 这个设备的ID id = -1 这个唯一
                          // 如果设备不是唯一 用0,1,2,3,4
 struct device dev;
 u32 num_resources; //ARRAY_SIZE(resources),
 struct resource * resource;
};

在platform_device中的resource 的结构体
/*
 * Resources are tree-like, allowing
 * nesting etc..
 */
struct resource {
 resource_size_t start;
 resource_size_t end;
 const char *name;
 unsigned long flags;
 struct resource *parent, *sibling, *child;
};

我们关心的字段:
start : 资源的开始
end : 资源的结束
flag : 资源的类型
资源的类型如下:
#define IORESOURCE_MEM 0x00000200 内存资源
#define IORESOURCE_IRQ 0x00000400 中断资源
#define IORESOURCE_DMA 0x00000800 dma的资源


 把platform_device注册到platform_bus上
 int platform_device_register(struct platform_device *pdev); // 注册一个设备到platform_bus上
 int platform_add_devices(struct platform_device **devs, int num)
 // 添加一批的设备到platform_bus上




3. 添加platform_driver注册到platform_bus上
int platform_driver_register(struct platform_driver *drv) // 添加一个驱动到platform_bus
void platform_driver_unregister(struct platform_driver *drv) //卸载函数

结构体定义如下:
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 (*resume)(struct platform_device *);
 struct device_driver driver; // device_driver的结构体
 const struct platform_device_id *id_table;
};

struct device_driver {
 const char *name; // 是和platform_device进行匹配的名称
 struct bus_type *bus;

 struct module *owner;
 const char *mod_name; /* used for built-in modules */

 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */

 const struct of_device_id *of_match_table;

 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);
 const struct attribute_group **groups;

 const struct dev_pm_ops *pm;

 struct driver_private *p;
};



用platform机制实现led的驱动
1. 在内核中添加led 的platform_device
  arch/arm/mach-s5pv210/mach-smdkv210.c

static struct platform_device *smdkv210_devices[] __initdata = {
这个功能是把开发板上的资源用一个结构体指针数组,把所有的platform_device放到
这个数组中进行批量的注册

在进行平台设备初始化时,对启动的platform_device进行批量的注册
static void __init smdkv210_machine_init(void)
   -> platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
        // 批量向内核注册platform_device

smdkv210_machine_init什么时候执行,在平台初始化时执行 ,内核启动后会执行平台的初始化,
会调用smdkv210_machine_init,

实现platform_device如下:
static struct resource s5pv210_led_resource[] ={
    [0] ={
        .start =0xE0200060 ,
        .end =0xE0200074 ,
        .flags = IORESOURCE_MEM,
    },

};
static struct platform_device s5pv210_device_led ={
    .name = "s5pv210-led",
    .id = -1,
    .num_resources = ARRAY_SIZE(s5pv210_led_resource),
    .resource = s5pv210_led_resource,
};

2. 把创建的platform_device 加到平台数组中
static struct platform_device *smdkv210_devices[] __initdata = {

3. 把platform_driver添加到platform_bus上
static struct platform_driver platform_driver_led ={
    .driver.name = "s5pv210-led" , // 这个名称要和platform_device 中的名称一致
    .probe = led_probe,


};
static int platform_driver_init(void)
{
    int ret =0 ;
    platform_driver_register(&platform_driver_led);
    return ret;

}
static void platform_driver_exit(void)
{
    platform_driver_unregister(&platform_driver_led);
}
module_init(platform_driver_init);
module_exit(platform_driver_exit);

设备名称platfrom_device和platform_driver名称一致时,platfrom总线会进行一次匹配,
匹配后会执行驱动中的probe函数


4. probe函数要执行的操作
 对驱动进行注册
 并获取资源

/**
 * platform_get_resource - get a resource for a device
 * @dev: platform device
 * @type: resource type
 * @num: resource index
 */
struct resource *platform_get_resource(struct platform_device *dev,
           unsigned int type, unsigned int num)

    mem_res = platform_get_resource(pdev,IORESOURCE_MEM,0);
    gpc0base = ioremap(mem_res->start,mem_res->end - mem_res->start );




基于platform_bus 的key的实现:
1. 在arch/arm/mach-s5pv210/mach-smdkv210.c 添加platform_device信息

static struct resource s5pv210_key_resource[] ={
    [0] ={
        .start =0xE0200C00 ,
        .end =0xE0200C00 + 4,
        .flags = IORESOURCE_MEM,
    },
    [1] ={
        .start =0xE0200F40 ,
        .end =0xE0200F40 + 4,
        .flags = IORESOURCE_MEM,
    },
    [2] ={
        .start = IRQ_EINT(0) ,
        .end = IRQ_EINT(0) ,
        .flags = IORESOURCE_IRQ,
    },
    [3] ={
        .start = IRQ_EINT(1) ,
        .end = IRQ_EINT(1) ,
        .flags = IORESOURCE_IRQ ,
    },
};
static struct platform_device s5pv210_device_key ={
    .name = "s5pv210-key",
    .id = -1,
    .num_resources = ARRAY_SIZE(s5pv210_key_resource),
    .resource = s5pv210_key_resource,
};



2. 在资源结构体中添加这个成员
    &s5pv210_device_key,

3. 修改驱动,使驱动支持platform机制


驱动的中的时钟机制:
1. 关于时钟的头文件
  <linux/clk.h>
2. 获取时钟函数
struct clk *clk_get(struct device *dev, const char *id);
dev : 时钟的名称 一般不用,为NULL
id : 时钟的匹配名称 ,需要传入相应的字符串
要找匹配的名称是去下面的文件中去找
vi arch/arm/mach-s5pv210/clock.c

static struct clk init_clocks_off[] = {
//这个数组内的时钟都是关闭的,需要手动打开
.name = "timers",
.parent = &clk_pclk_psys.clk,
.enable = s5pv210_clk_ip3_ctrl,
.ctrlbit = (1<<23),


如是实例:
struct clk *myclk = clk_get(null,"timers")

3. 时钟的使能
int clk_enable(struct clk *clk);

实例: clk_enable(myclk);

时钟关闭 :
void clk_disable(struct clk *clk);

实例: clk_enable(myclk);

显示时钟大小:
unsigned long clk_get_rate(struct clk *clk);

实例: printk("clk is %lu",long clk_get_rate(myclk))
0 0
原创粉丝点击