什么是platform(平台)总线
相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。那为什么需要platform总线呢?其实是Linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。因为对于usb设备、i2c设备、pci设备、spi设备等等,他们与cpu的通信都是直接挂在相应的总线下面与我们的cpu进行数据交互的,但是在我们的嵌入式系统当中,并不是所有的设备都能够归属于这些常见的总线,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。所以Linux驱动模型为了保持完整性,将这些设备挂在一条虚拟的总线上(platform总线),而不至于使得有些设备挂在总线上,另一些设备没有挂在总线上。
两个结构体platform_device和platform_driver
对于任何一种Linux设备驱动模型下的总线都由两个部分组成:描述设备相关的结构体和描述驱动相关的结构体在platform总线下就是platform_device和platform_driver,下面是对两个结构体的各个元素进行分析:
platform_device结构体:(include\linux\platform_device.h)
struct platform_device {
const char * name; /* 平台设备名字 */
int id; /* 一般设置为-1,表示系统自动分配 */
bool id_auto;
struct device dev; /* 每一个设备都有一个 device 结构体 */
u32 num_resources; /* 资源数量 */
struct resource* resource; /* 资源 */
/* platform_device_id,里面有个名字列表 */
const struct platform_device_id *id_entry;
…
};
其中有个重要的结构体成员,那就是资源(resource)
struct resource {
resource_size_t start; /* 资源的起始值 */
resource_size_t end; /* 资源的结束值 */
const char *name;
/* 资源的类型,如:
* IORESOURCE_IO(IO 资源)、 IORESOURCE_MEM(内存资源)、
* IORESOURCE_IRQ(中断资源)、 IORESOURCE_DMA(DMA 资源)
*/
unsigned long flags;
struct resource *parent, *sibling, *child;
};
platform_driver结构体:(include\linux\platform_device.h)
struct platform_driver {
int (*probe)(struct platform_device *); // 这个probe函数其实和 device_driver中的是一样的功能,但是一般是使用device_driver中的那个
int (*remove)(struct platform_device *); // 卸载平台设备驱动的时候会调用这个函数,但是device_driver下面也有,具体调用的是谁这个就得分析了
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; // 该设备驱动支持的设备的列表 他是通过这个指针去指向 platform_device_id 类型的数组
};
平台总线如何匹配设备和驱动
int platform_driver_register(struct platform_driver *); // 用来注册我们的设备驱动
void platform_driver_unregister(struct platform_driver *); // 用来卸载我们的设备驱动
int platform_device_register(struct platform_device *); // 用来注册我们的设备
void platform_device_unregister(struct platform_device *); // 用来卸载我们的设备
平台总线驱动设备驱动实例
平台设备驱动实例(platform_device)
流程:
- 定义platform_device实例
- 根据实际情况定义platform_device里的resource实例或这dev->platform_data实例
- 在模块入口函数platform_device_register注册一个平台设备
- 在模块出口函数platform_device_unregister注销一个平台设备
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#define GPH2CON 0xE0200C40
static struct resource key_resource[] = {
[0] = {
.start = GPH2CON,
.end = GPH2CON + 8,
.flags = IORESOURCE_MEM
},
[1] = {
.start = IRQ_EINT(16),
.end = IRQ_EINT(17),
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device key_dev = {
.name = "my_key",
.id = 0,
.num_resources = ARRAY_SIZE(key_resource),
.resource = key_resource,
};
static int __init key_dev_init()
{
platform_device_register(&key_dev);
printk("platform device register success!\n");
return 0;
}
static void __exit key_dev_exit()
{
platform_device_unregister(&key_dev);
}
module_init(key_dev_init);
module_exit(key_dev_exit);
MODULE_LICENSE("GPL");
平台驱动实例(platform_driver)
流程:
- 定义platform_driver实例
- 根据实际情况定义实现platform_driver里的probe成员函数,里面注册什么由你决定
- 根据实际情况定义实现platform_driver里的remove成员函数,里面注销什么由你决定
- 在模块入口函数platform_driver_register注册一个平台驱动
- 在模块出口函数platform_driver_unregister注销一个平台驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#define GPH2CON 0xE0200C40
#define GPH2DAT 0xE0200C44
volatile int ev_press = 0;
unsigned int key_num = 0;
struct work_struct *work1 = NULL;
struct timer_list key_timer;
unsigned int * key_base = NULL;
DECLARE_WAIT_QUEUE_HEAD(key_queue);
struct resource *res = NULL;
struct resource *res_irq = NULL;
static int key_open (struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t key_read (struct file * file, char __user * buf, size_t size, loff_t * ppos)
{
wait_event(key_queue, ev_press);
copy_to_user(buf, &key_num, 4);
key_num = 0;
ev_press = 0;
return 0;
}
static const struct file_operations key_fops =
{
.open = key_open,
.read = key_read,
};
static struct miscdevice key_misc =
{
.minor = 200,
.name = "210key",
.fops = &key_fops,
};
void key_hw_init()
{
unsigned short data = 0;
data = readl(key_base);
data |= 0xf;
writel(data, key_base);
}
static irqreturn_t key_irq(int irq, void *dev_id)
{
/* 检查是否产生中断(共享中断才用)*/
/* 清除中断标志*/
/* 中断下半部: 将产生中断的中断号传递到工作函数,提交工作(到内核默认的工作队列)*/
schedule_work(work1);
return IRQ_HANDLED;
}
void work1_fun()
{
mod_timer(&key_timer, jiffies + HZ/5);
}
static void key_timer_function (unsigned long data)
{
unsigned int pinval = 0;
ev_press = 1;
pinval = readl(key_base + 1) & 0x01;
if(0 == pinval)
{
key_num = 1;
}
pinval = readl(key_base + 1) & 0x02;
if(0 == pinval)
{
key_num = 2;
}
wake_up(&key_queue);
}
static int key_probe(struct platform_device *pdev)
{
int ret = 0;
int size = 0;
/* 注册混杂设备驱动*/
ret = misc_register(&key_misc);
printk("register success!\n");
if(ret != 0)
printk("register fail!\n");
/* 硬件初始化*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
size = (res->end - res->start) + 1;
key_base = ioremap(res->start, size);
key_hw_init();
/* 注册中断*/
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
request_irq(res_irq->start, key_irq, IRQF_TRIGGER_FALLING, "S1", (void *)0);
request_irq(res_irq->end, key_irq, IRQF_TRIGGER_FALLING, "S2", (void *)1);
/* 初始化工作*/
work1 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
INIT_WORK(work1, work1_fun);
/* 初始化定时器*/
init_timer(&key_timer);
key_timer.function = key_timer_function;
/* 注册定时器*/
add_timer(&key_timer);
return 0;
}
static int key_remove(struct platform_device *pdev)
{
kfree(work1);
/* 注销中断*/
free_irq(IRQ_EINT(16), (void *)0);
free_irq(IRQ_EINT(17), (void *)1);
/* 注销混杂设备*/
misc_deregister(&key_misc);
return 0;
}
static struct platform_driver key_driver = {
.probe = key_probe,
.remove = key_remove,
.driver = {
.name = "my_key",
},
};
static int __init key_drv_init()
{
return platform_driver_register(&key_driver);
}
static void __exit key_drv_exit()
{
platform_driver_unregister(&key_driver);
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");