平台总线的需求由来
平台总线的模型
一.初始化device链表与device_driver链表
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
struct dev_pm_info power;
struct dev_power_domain *pwr_domain;
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */
dev_t devt; /* dev_t, creates the sysfs "dev" */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
};
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;
const struct platform_device_id *id_table;
};
二.创建platform_device对象或者platform_driver对象
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
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;
const struct platform_device_id *id_table;
};
三.将pdev或者pdrv对象注册到链表中并进行匹配
实际驱动运行时是先运行一个驱动,进行注册匹配一次,然后第二个驱动运行再注册匹配一次,总线实际就在这两个对象的父类device与device_driver中,其作用就是用于维护两个链表,并且会进行匹配 .
static int __init plat_led_drv_init(void)
{
// 构建pdrv, 并注册到总线中去
return platform_driver_register(&led_pdrv);
}
struct bus_type platform_bus_type = { //内核自带,变量也是内核自动初始化
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, //匹配方法
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
四.匹配到之后就会执行pdrv中的probe方法
这样我们一批配上就可以在probe方法中拿到pdev中的地址资源或者中断资源,这样我们就可以实现地址与中断的隔离,换一个板子之后我们只需要改动pdev中的部分代码即可,实现了代码的复用性.
int led_pdrv_probe(struct platform_device *pdev)
{
int ret;
printk("---------%s------------\n", __FUNCTION__);
// 任务1---与用户交互: a,申请主设备号 b,创建设备节点 c, 实现fops
// 任务2 --- 获取到资源, d,对硬件进行初始化
//如果获取到地址资源,需要映射,操作寄存器
//参数1--从哪个pdev中获取资源
//参数2--资源的类型:IORESOURCE_MEM/IORESOURCE_IRQ
//参数3--同种资源的编号
struct resource *mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(mem_res == NULL)
{
printk("platform_get_resource error\n");
return -ENOMEM;
}
//在地址映射之前,需要独占该地址
//参数1--独占物理地址
//参数2--独占物理地址长度
//参数3--名字--自定义
mem_temp = request_mem_region(mem_res->start, resource_size(mem_res), pdev->name);
if(mem_temp == NULL)
{
printk("request_mem_region error\n");
return -ENXIO;
}
//正式开始映射
reg_base = ioremap(mem_res->start, resource_size(mem_res));
if(mem_res == NULL)
{
printk("platform_get_resource error\n");
ret = -ENOMEM;
goto err_release_res;
}
// 操作寄存器--配置成输出
unsigned long value = __raw_readl(reg_base);
value &= ~(0xff<<12);
value |= (0x11<<12);
__raw_writel(value, reg_base);
//如果需要点灯--操作数据寄存器--一般留给用户来控制
//如果获取到中断在资源,需要中断申请--因为没有真实的中断,所以以下代码只是做演示
struct resource *irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
printk("fake irqno = 0x%x \n", irq_res->start);
int irqno = platform_get_irq(pdev, 0);
printk("fake irqno = 0x%x \n", irqno);
struct resource *fake_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fake_mem");
printk("fake_mem = 0x%x \n", fake_mem->start);
return 0;
err_release_res:
release_resource(mem_temp);
return ret;
}
pdev端驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define GPC0_CONF 0xE0200060
#define GPC0_SIZE 0x8
struct resource led_res[] = {
[0] = {
.start = GPC0_CONF,
.end = GPC0_CONF + GPC0_SIZE -1,
.flags = IORESOURCE_MEM, //类型
},
//以下对于led实际是不存在,演示如何在代码中定义多个资源
[1] = {
.start = 0x666,
.end = 0x666,
.name = "fake_irq",
.flags = IORESOURCE_IRQ, //类型
},
[2] = {
.start = 0x12345678,
.end = 0x12345678 + 8 -1,
.name = "fake_mem",
.flags = IORESOURCE_MEM, //类型
},
};
struct platform_device led_pdev = {
.name = "fs210_led",
.id = -1, // 一般填-1, 用于区分不同组的控制器
.num_resources = ARRAY_SIZE(led_res),
.resource = led_res,
};
static int __init plat_led_dev_init(void)
{
// 构建pdev, 并注册到总线中去
return platform_device_register(&led_pdev);
}
static void __exit plat_led_dev_exit(void)
{
platform_device_unregister(&led_pdev);
}
module_init(plat_led_dev_init);
module_exit(plat_led_dev_exit);
MODULE_LICENSE("GPL");
pdrv端驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/io.h>
static volatile void *reg_base;
static struct resource *mem_temp;
int led_pdrv_probe(struct platform_device *pdev)
{
int ret;
printk("---------%s------------\n", __FUNCTION__);
// 任务1---与用户交互: a,申请主设备号 b,创建设备节点 c, 实现fops
// 任务2 --- 获取到资源, d,对硬件进行初始化
//如果获取到地址资源,需要映射,操作寄存器
//参数1--从哪个pdev中获取资源
//参数2--资源的类型:IORESOURCE_MEM/IORESOURCE_IRQ
//参数3--同种资源的编号
struct resource *mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(mem_res == NULL)
{
printk("platform_get_resource error\n");
return -ENOMEM;
}
//在地址映射之前,需要独占该地址
//参数1--独占物理地址
//参数2--独占物理地址长度
//参数3--名字--自定义
mem_temp = request_mem_region(mem_res->start, resource_size(mem_res), pdev->name);
if(mem_temp == NULL)
{
printk("request_mem_region error\n");
return -ENXIO;
}
//正式开始映射
reg_base = ioremap(mem_res->start, resource_size(mem_res));
if(mem_res == NULL)
{
printk("platform_get_resource error\n");
ret = -ENOMEM;
goto err_release_res;
}
// 操作寄存器--配置成输出
unsigned long value = __raw_readl(reg_base);
value &= ~(0xff<<12);
value |= (0x11<<12);
__raw_writel(value, reg_base);
//如果需要点灯--操作数据寄存器--一般留给用户来控制
//如果获取到中断在资源,需要中断申请--因为没有真实的中断,所以以下代码只是做演示
struct resource *irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
printk("fake irqno = 0x%x \n", irq_res->start);
int irqno = platform_get_irq(pdev, 0);
printk("fake irqno = 0x%x \n", irqno);
struct resource *fake_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fake_mem");
printk("fake_mem = 0x%x \n", fake_mem->start);
return 0;
err_release_res:
release_resource(mem_temp);
return ret;
}
int led_pdrv_remove(struct platform_device *pdev)
{
printk("---------%s------------\n", __FUNCTION__);
iounmap(reg_base);
release_resource(mem_temp);
return 0;
}
const struct platform_device_id led_id_table[] = {
{"s5pv210_led", 0x210}, //第二个成员随便写
{"fs210_led", 0x210},
{"exynos4412_led", 0x4412},
{"s3c2410_led", 0x2410},
};
struct platform_driver led_pdrv = {
.probe = led_pdrv_probe,
.remove = led_pdrv_remove,
.driver = { //一定要初始化
.name = "samsung_led_pdrv", // 自定义,可以用于和pdev进行匹配
},
.id_table = led_id_table, //优先匹配这里面的名字
};
static int __init plat_led_drv_init(void)
{
// 构建pdrv, 并注册到总线中去
return platform_driver_register(&led_pdrv);
}
static void __exit plat_led_drv_exit(void)
{
platform_driver_unregister(&led_pdrv);
}
module_init(plat_led_drv_init);
module_exit(plat_led_drv_exit);
MODULE_LICENSE("GPL");