在總線設備驅動模型中,平台設備是寫在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\n", 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)\n", 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\n", 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)\n", 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\n"); 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");