linux驱动学习笔记---平台总线框架(九)

平台总线的需求由来

 平台总线的模型

 一.初始化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");

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值