platform
作用:实现硬件的操作方法和设备信息的分离,便于SOC控制器驱动的升级
<结构体>
platform_driver {
struct platform_driver {
int (*probe)(struct platform_device *); //实现初级驱动中加载函数中的代码
(1.实例化全局对象-----kzalloc)
(2.申请设备号 --- register_chrdev)
(3.自动创建设备节点 ---- class_create,device_create)
(4.拿到pdev中的资源)
(5.初始化硬件------ ioremap)
(6.实现file_opretions)
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中的名称匹配
}
platform_bus_type {
const char *name;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
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 dev_pm_ops *pm;
struct subsys_private *p;
int (*match)(struct device *dev, struct device_driver *drv); // 匹配方法-------------↓
} ↓
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);
}
platform_device {
const char * name; //名字,用于与pdrv匹配
int id; //表示不同寄存器组的编号,一般可以填:-1
struct device dev; //父类 ——————> void *platform_data; // 指向自定义数据的指针
const struct platform_device_id *id_entry;/* MFD cell pointer */
struct mfd_cell *mfd_cell;/* arch specific additions */
struct pdev_archdata archdata;
u32 num_resources; //资源的个数
struct resource * resource; //资源的详细信息----描述中断或者内存地址————↓
} ↓
struct resource {
resource_size_t start; //如果资源为地址,则为起始地址,如果是中断,则表示中断号
resource_size_t end; //如果资源为地址,则为最后一个字节的地址,如果是中断,则表示中断号
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
<函数>
注册:
int platform_driver_register(struct platform_driver *);
void platform_driver_unregister(struct platform_driver *);
代码示例:https://www.cnblogs.com/panda-w/p/10991957.html
<笔记>
1. 总线匹配成功自动调用pdrv中的probe方法;
2. pdrv可以一对多,只要名字相同均可匹配
3. 什么时候用平台总线:------将操作和数据(资源,自定义数据)分离
1,只要有设备地址和中断都可以用
2,如果驱动需要咋多个平台总升级使用
4. 查看平台总线:ls /sys/bus/platform/devices/ (内核启动自动调用,开机批量注册pdev)
5. 因为probe 和 remove都在pdrv实现,卸载驱动时,要先卸载pdev,然后再卸载pdrv
6. gpio口的寄存器初始化操作方式:
看原理图 ----> 看芯片手册 ------> 寄存器地址 --------> ioremap(地址映射)
*gpc0_conf &= ~(0xff<<12);
*gpco_conf |= 0x11<<12;
1, 直接地址操作
volatile unsigned long *gpc0conf;
volatile unsigned long *gpc0data;
gpc0conf = ioremap(地址, 长度);
gpc0conf = gpc0data + 1;
2, 内核提供的库函数操作
__raw_readl(地址) __raw_writel(value, 地址);
unsigned long value = __raw_readl(led_reg_base);
value &= ~(0xff<<12);
value |= (0x11<<12); //配置成输出功能
__raw_writel(value, led_reg_base);
7. 内核提供的接口: readl/writel ------ __raw_readl __raw_writel
static unsigned int __raw_readl(unsigned int ptr) //参数: ptr ----- 地址:address
{
return *((volatile unsigned int *)ptr);
}
static void __raw_writel(unsigned int value, unsigned int ptr) //参数: value--写入的数据,ptr---地址:address
{
*((volatile unsigned int *)ptr) = value;
}
8. 编程思想:
第一步:设计自定义数据类型:
struct led_platdata{ //设计一个平台总线的自定义数据类型
char *name;
int shift; //移位数
int conf_reg_data; //配置寄存器的值
int conf_reg_clear; //配置寄存器的值清空
int data_reg; //数据寄存器的值
};
第二步:在pdev中初始化自定义数据
struct led_platdata led_pdata = {
.name = "gpc0_3/4",
.shift = 3,
.conf_reg_data = 0x11,
.conf_reg_clear = 0xff,
.data_reg = 0x3,
};
第三步:
struct platform_device led_pdev = {
.name = "s5pv210_led",
.id = -1,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
.dev = {
.platform_data = &led_pdata, //将自定义数据赋给pdev的父类中的:platform_data
.release = led_dev_release,
},
9. 平台总线默认内核就自动帮我们创建,使用平台总线编写驱动,将一个驱动分成两个模块:pdev,pdrv
10. 杂项设备的注册: 默认的主设备号是10
struct miscdevice {
int minor; //次设备--自由定义--0-255之间
const char *name; //设备节点的名字
const struct file_operations *fops; //文件操作接口
struct list_head list; //链表
};