在总线设备驱动模型中,平台设备是写在c文件中。使用设备树时,平台设备事先并不存在,在dts文件中构造节点,节点里面含有资源。dts文件被编译成dtb文件,然后传递给内核。内核会解析dtb文件,得到一个个device_node,每个节点对应一个device_node结构体,每个device_node结构体变成一个platform_device结构体,该结构体中就含有资源,这些资源来源于dts文件。接下来的处理过程和总线设备驱动模型就一样了,如果设备与驱动相匹配,就调用驱动中的probe函数。可以这么认为,设备树是对总线设备驱动模型的一种改进,它仍然属于总线设备驱动模型的一种。
看一下这个设备树文件:
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * SAMSUNG SMDK2440 board device tree source
4 *
5 * Copyright (c) 2018 weidongshan@qq.com
6 * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
7 */
8 //这里定义了一些宏,使用的是c语言的语法
9 #define S3C2410_GPA(_nr) ((0<<16) + (_nr))
10 #define S3C2410_GPB(_nr) ((1<<16) + (_nr))
11 #define S3C2410_GPC(_nr) ((2<<16) + (_nr))
12 #define S3C2410_GPD(_nr) ((3<<16) + (_nr))
13 #define S3C2410_GPE(_nr) ((4<<16) + (_nr))
14 #define S3C2410_GPF(_nr) ((5<<16) + (_nr))
15 #define S3C2410_GPG(_nr) ((6<<16) + (_nr))
16 #define S3C2410_GPH(_nr) ((7<<16) + (_nr))
17 #define S3C2410_GPJ(_nr) ((8<<16) + (_nr))
18 #define S3C2410_GPK(_nr) ((9<<16) + (_nr))
19 #define S3C2410_GPL(_nr) ((10<<16) + (_nr))
20 #define S3C2410_GPM(_nr) ((11<<16) + (_nr))
21
22 /dts-v1/;
23
24 / {
25 model = "SMDK24440";
26 compatible = "samsung,smdk2440";
27
28 #address-cells = <1>;
29 #size-cells = <1>;
30
31 memory@30000000 {
32 device_type = "memory";
33 reg = <0x30000000 0x4000000>;
34 };
35 /*
36 cpus {
37 cpu {
38 compatible = "arm,arm926ej-s";
39 };
40 };
41 */
42 chosen { //设置了内核的命令行参数
43 bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
44 };
45
46
47 led {
48 compatible = "jz2440_led"; //以后就使用compatible在内核里面找到能够支持这个节点的驱动程序,即找到能够支持这个节点的平台drv.
49 reg = <S3C2410_GPF(5) 1>;//reg是register的缩写。在arm系统里面,寄存器和内存是被同样对待的,因为寄存器的访问空间与内存的访问空间没什么差别。
50 };
51 };
单板启动之后,
cd /sys/devices/platform/
ls 下会看到xxxxxxx,假设是50005.led。
cd 50005.led
ls会看到这样的文件
driver_override of_node subsystem modalias power uevent
cd of_node
ls
compatible name reg
cat compatible 就会显示 jz2440_led
cat name 就会显示led
hexdump -C reg
对应着8个字节,
00 05 00 05 00 00 00 01
对应着两个数值,来源于 reg = <S3C2410_GPF(5) 1>
00 05 00 05 对应S3C2410_GPF(5)代表着寄存器的起始地址,对应着这个宏 #define S3C2410_GPF(_nr) ((5<<16) + (_nr)),表示高16位是5,低16位也是5.
00 00 00 01对应着是1,本意就是寄存器的大小。
在总线设备驱动模型中,是通过名字来进行设备与驱动的匹配。那么在利用设备树时,是利用什么进行匹配的呢。
1 static int platform_match(struct device *dev, struct device_driver *drv)
2 {
3 struct platform_device *pdev = to_platform_device(dev);
4 struct platform_driver *pdrv = to_platform_driver(drv);
5
6 /* Attempt an OF style match first */
7 if (of_driver_match_device(dev, drv))//用来判断从设备树中得到的platform_devcie与提供的platform_drv
//是否匹配,
8 return 1;
9
10 /* Then try to match against the id table */
11 if (pdrv->id_table)
12 return platform_match_id(pdrv->id_table, pdev) != NULL;
13
14 /* fall-back to driver name match */
15 return (strcmp(pdev->name, drv->name) == 0);
16 }
1 static inline int of_driver_match_device(struct device *dev, 2 const struct device_driver *drv) 3 {
/*从这里可以看出,平台drv中有个成员变量,of_match_table*/ 4 return of_match_device(drv->of_match_table, dev) != NULL; 5 }
1 struct device_driver { 2 const char *name; 3 struct bus_type *bus; 4 5 struct module *owner; 6 const char *mod_name; /* used for built-in modules */ 7 8 bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ 9 10 const struct of_device_id *of_match_table; //该指针指向一项或多项of_device_id 11 12 int (*probe) (struct device *dev); 13 int (*remove) (struct device *dev); 14 void (*shutdown) (struct device *dev); 15 int (*suspend) (struct device *dev, pm_message_t state); 16 int (*resume) (struct device *dev); 17 const struct attribute_group **groups; 18 19 const struct dev_pm_ops *pm; 20 21 struct driver_private *p; 22 };
1 struct of_device_id
2 {
3 char name[32];
4 char type[32];
5 char compatible[128];//从dts里得到的platform_device里有compatible属性,两者比较,如果一样的话,就是匹配。
6 #ifdef __KERNEL__
7 void *data;
8 #else
9 kernel_ulong_t data;
10 #endif
11 };
1 struct platform_device {
2 const char * name;
3 int id;
4 struct device dev;//对于dts生成的设备platform_device,这里含有Of_node,of_node中含有属性,这含有哪些属性呢,这取决于设备树,比如:
//compatible pin等。这个compatible属性用来寻找支持它的platform_driver.这个compatible是最优先比较。
5 u32 num_resources;
6 struct resource * resource;
7
8 const struct platform_device_id *id_entry;
9
10 /* MFD cell pointer */
11 struct mfd_cell *mfd_cell;
12
13 /* arch specific additions */
14 struct pdev_archdata archdata;
15 };
利用设备树编写的led驱动程序如下:
1 #include <linux/module.h>
2 #include <linux/kernel.h>
3 #include <linux/fs.h>
4 #include <linux/init.h>
5 #include <linux/delay.h>
6 #include <linux/uaccess.h>
7 #include <asm/irq.h>
8 #include <asm/io.h>
9 #include <linux/of.h>
10 #include <linux/of_device.h>
11 #include <linux/of_platform.h>
12 #include <linux/platform_device.h>
13
14 #define S3C2440_GPA(n) (0<<16 | n)
15 #define S3C2440_GPB(n) (1<<16 | n)
16 #define S3C2440_GPC(n) (2<<16 | n)
17 #define S3C2440_GPD(n) (3<<16 | n)
18 #define S3C2440_GPE(n) (4<<16 | n)
19 #define S3C2440_GPF(n) (5<<16 | n)
20 #define S3C2440_GPG(n) (6<<16 | n)
21 #define S3C2440_GPH(n) (7<<16 | n)
22 #define S3C2440_GPI(n) (8<<16 | n)
23 #define S3C2440_GPJ(n) (9<<16 | n)
24
25 static int led_pin;
26 static volatile unsigned int *gpio_con;
27 static volatile unsigned int *gpio_dat;
28
29 /* 123. 分配/设置/注册file_operations
30 * 4. 入口
31 * 5. 出口
32 */
33
34 static int major;
35 static struct class *led_class;
36
37 static unsigned int gpio_base[] = {
38 0x56000000, /* GPACON */
39 0x56000010, /* GPBCON */
40 0x56000020, /* GPCCON */
41 0x56000030, /* GPDCON */
42 0x56000040, /* GPECON */
43 0x56000050, /* GPFCON */
44 0x56000060, /* GPGCON */
45 0x56000070, /* GPHCON */
46 0, /* GPICON */
47 0x560000D0, /* GPJCON */
48 };
49
50 static int led_open (struct inode *node, struct file *filp)
51 {
52 /* 把LED引脚配置为输出引脚 */
53 /* GPF5 - 0x56000050 */
54 int bank = led_pin >> 16;
55 int base = gpio_base[bank];
56
57 int pin = led_pin & 0xffff;
58 gpio_con = ioremap(base, 8);
59 if (gpio_con) {
60 printk("ioremap(0x%x) = 0x%x
", base, gpio_con);
61 }
62 else {
63 return -EINVAL;
64 }
65
66 gpio_dat = gpio_con + 1;
67
68 *gpio_con &= ~(3<<(pin * 2));
69 *gpio_con |= (1<<(pin * 2));
70
71 return 0;
72 }
73
74 static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
75 {
76 /* 根据APP传入的值来设置LED引脚 */
77 unsigned char val;
78 int pin = led_pin & 0xffff;
79
80 copy_from_user(&val, buf, 1);
81
82 if (val)
83 {
84 /* 点灯 */
85 *gpio_dat &= ~(1<<pin);
86 }
87 else
88 {
89 /* 灭灯 */
90 *gpio_dat |= (1<<pin);
91 }
92
93 return 1; /* 已写入1个数据 */
94 }
95
96 static int led_release (struct inode *node, struct file *filp)
97 {
98 printk("iounmap(0x%x)
", gpio_con);
99 iounmap(gpio_con);
100 return 0;
101 }
102
103
104 static struct file_operations myled_oprs = {
105 .owner = THIS_MODULE,
106 .open = led_open,
107 .write = led_write,
108 .release = led_release,
109 };
110
111
112 static int led_probe(struct platform_device *pdev)
113 {
114 struct resource *res;
115
116 /* 根据platform_device的资源进行ioremap */
117 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
118 led_pin = res->start;
119
120 major = register_chrdev(0, "myled", &myled_oprs);
121
122 led_class = class_create(THIS_MODULE, "myled");
123 device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
124
125 return 0;
126 }
127
128 static int led_remove(struct platform_device *pdev)
129 {
130 unregister_chrdev(major, "myled");
131 device_destroy(led_class, MKDEV(major, 0));
132 class_destroy(led_class);
133
134 return 0;
135 }
136
137
138 static const struct of_device_id of_match_leds[] = {
139 { .compatible = "jz2440_led", .data = NULL },
140 { /* sentinel */ }
141 };
142
143
144 struct platform_driver led_drv = {
145 .probe = led_probe,
146 .remove = led_remove,
147 .driver = {
148 .name = "myled",
149 .of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
150 }
151 };
152
153
154 static int myled_init(void)
155 {
156 platform_driver_register(&led_drv);
157 return 0;
158 }
159
160 static void myled_exit(void)
161 {
162 platform_driver_unregister(&led_drv);
163 }
164
165
166
167 module_init(myled_init);
168 module_exit(myled_exit);
169
170
171 MODULE_LICENSE("GPL");
在设备树中形如下面这样的写法太别扭了
reg = <S3C2410_GPF(5) 1>;
S3C2410_GPF(5)这个地方本来定义了引脚,你非要把它当做register。看一下能否将register去掉,能不能使用另外一种方法指定引脚。
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * SAMSUNG SMDK2440 board device tree source
4 *
5 * Copyright (c) 2018 weidongshan@qq.com
6 * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
7 */
8
9 #define S3C2410_GPA(_nr) ((0<<16) + (_nr))
10 #define S3C2410_GPB(_nr) ((1<<16) + (_nr))
11 #define S3C2410_GPC(_nr) ((2<<16) + (_nr))
12 #define S3C2410_GPD(_nr) ((3<<16) + (_nr))
13 #define S3C2410_GPE(_nr) ((4<<16) + (_nr))
14 #define S3C2410_GPF(_nr) ((5<<16) + (_nr))
15 #define S3C2410_GPG(_nr) ((6<<16) + (_nr))
16 #define S3C2410_GPH(_nr) ((7<<16) + (_nr))
17 #define S3C2410_GPJ(_nr) ((8<<16) + (_nr))
18 #define S3C2410_GPK(_nr) ((9<<16) + (_nr))
19 #define S3C2410_GPL(_nr) ((10<<16) + (_nr))
20 #define S3C2410_GPM(_nr) ((11<<16) + (_nr))
21
22 /dts-v1/;
23
24 / {
25 model = "SMDK24440";
26 compatible = "samsung,smdk2440";
27
28 #address-cells = <1>;
29 #size-cells = <1>;
30
31 memory@30000000 {
32 device_type = "memory";
33 reg = <0x30000000 0x4000000>;
34 };
35 /*
36 cpus {
37 cpu {
38 compatible = "arm,arm926ej-s";
39 };
40 };
41 */
42 chosen {
43 bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
44 };
45
46
47 led {
48 compatible = "jz2440_led";
49 pin = <S3C2410_GPF(5)>;
50 };
51 };
1 #include <linux/module.h>
2 #include <linux/kernel.h>
3 #include <linux/fs.h>
4 #include <linux/init.h>
5 #include <linux/delay.h>
6 #include <linux/uaccess.h>
7 #include <asm/irq.h>
8 #include <asm/io.h>
9 #include <linux/of.h>
10 #include <linux/of_device.h>
11 #include <linux/of_platform.h>
12 #include <linux/platform_device.h>
13
14 #define S3C2440_GPA(n) (0<<16 | n)
15 #define S3C2440_GPB(n) (1<<16 | n)
16 #define S3C2440_GPC(n) (2<<16 | n)
17 #define S3C2440_GPD(n) (3<<16 | n)
18 #define S3C2440_GPE(n) (4<<16 | n)
19 #define S3C2440_GPF(n) (5<<16 | n)
20 #define S3C2440_GPG(n) (6<<16 | n)
21 #define S3C2440_GPH(n) (7<<16 | n)
22 #define S3C2440_GPI(n) (8<<16 | n)
23 #define S3C2440_GPJ(n) (9<<16 | n)
24
25 static int led_pin;
26 static volatile unsigned int *gpio_con;
27 static volatile unsigned int *gpio_dat;
28
29 /* 123. 分配/设置/注册file_operations
30 * 4. 入口
31 * 5. 出口
32 */
33
34 static int major;
35 static struct class *led_class;
36
37 static unsigned int gpio_base[] = {
38 0x56000000, /* GPACON */
39 0x56000010, /* GPBCON */
40 0x56000020, /* GPCCON */
41 0x56000030, /* GPDCON */
42 0x56000040, /* GPECON */
43 0x56000050, /* GPFCON */
44 0x56000060, /* GPGCON */
45 0x56000070, /* GPHCON */
46 0, /* GPICON */
47 0x560000D0, /* GPJCON */
48 };
49
50 static int led_open (struct inode *node, struct file *filp)
51 {
52 /* 把LED引脚配置为输出引脚 */
53 /* GPF5 - 0x56000050 */
54 int bank = led_pin >> 16;
55 int base = gpio_base[bank];
56
57 int pin = led_pin & 0xffff;
58 gpio_con = ioremap(base, 8);
59 if (gpio_con) {
60 printk("ioremap(0x%x) = 0x%x
", base, gpio_con);
61 }
62 else {
63 return -EINVAL;
64 }
65
66 gpio_dat = gpio_con + 1;
67
68 *gpio_con &= ~(3<<(pin * 2));
69 *gpio_con |= (1<<(pin * 2));
70
71 return 0;
72 }
73
74 static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
75 {
76 /* 根据APP传入的值来设置LED引脚 */
77 unsigned char val;
78 int pin = led_pin & 0xffff;
79
80 copy_from_user(&val, buf, 1);
81
82 if (val)
83 {
84 /* 点灯 */
85 *gpio_dat &= ~(1<<pin);
86 }
87 else
88 {
89 /* 灭灯 */
90 *gpio_dat |= (1<<pin);
91 }
92
93 return 1; /* 已写入1个数据 */
94 }
95
96 static int led_release (struct inode *node, struct file *filp)
97 {
98 printk("iounmap(0x%x)
", gpio_con);
99 iounmap(gpio_con);
100 return 0;
101 }
102
103
104 static struct file_operations myled_oprs = {
105 .owner = THIS_MODULE,
106 .open = led_open,
107 .write = led_write,
108 .release = led_release,
109 };
110
111
112 static int led_probe(struct platform_device *pdev)
113 {
114 struct resource *res;
115
116 /* 根据platform_device的资源进行ioremap */
117 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
118 if (res) {
119 led_pin = res->start;
120 }
121 else {
122 /* 获得pin属性 */
123 of_property_read_s32(pdev->dev.of_node, "pin", &led_pin);
124 }
125
126 if (!led_pin)
127 {
128 printk("can not get pin for led
");
129 return -EINVAL;
130 }
131
132
133 major = register_chrdev(0, "myled", &myled_oprs);
134
135 led_class = class_create(THIS_MODULE, "myled");
136 device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
137
138 return 0;
139 }
140
141 static int led_remove(struct platform_device *pdev)
142 {
143 unregister_chrdev(major, "myled");
144 device_destroy(led_class, MKDEV(major, 0));
145 class_destroy(led_class);
146
147 return 0;
148 }
149
150
151 static const struct of_device_id of_match_leds[] = {
152 { .compatible = "jz2440_led", .data = NULL },
153 { /* sentinel */ }
154 };
155
156
157 struct platform_driver led_drv = {
158 .probe = led_probe,
159 .remove = led_remove,
160 .driver = {
161 .name = "myled",
162 .of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
163 }
164 };
165
166
167 static int myled_init(void)
168 {
169 platform_driver_register(&led_drv);
170 return 0;
171 }
172
173 static void myled_exit(void)
174 {
175 platform_driver_unregister(&led_drv);
176 }
177
178
179
180 module_init(myled_init);
181 module_exit(myled_exit);
182
183
184 MODULE_LICENSE("GPL");