[punctual atom MP157 serial] Chapter 24 LED driving experiment under the device tree - extracted from [punctual atom] STM32MP1 embedded Linux Driver Development Guide v1 seven

1) Experimental platform: punctual atom STM32MP157 development board
2) Purchase link: https://item.taobao.com/item.htm?&id=629270721801
3) Full set of experimental source code + manual + video download address: http://www.openedv.com/thread-318813-1-1.html
4) Official station B of punctual atom: https://space.bilibili.com/394620890
5) Punctual atom STM32MP157 technical exchange group: 691905614

Chapter 24 LED driving experiment under equipment tree

In the previous chapter, we explained the device tree syntax and the OF functions commonly used in driver development in detail. In this chapter, we began the first Linux driver experiment based on device tree. This chapter is completed on the basis OF the experiment in Chapter 22, only changing its driver development to the form OF device tree.

24.1 device Tree LED driving principle
In Chapter 22 new character device driver experiment, we directly in the driver file newchrled C, and then use io_remap function performs memory mapping to obtain the corresponding virtual address. Finally, operate the virtual address corresponding to the register to complete the initialization OF GPIO. This chapter is completed on the basis OF the experiment in Chapter 42. In this chapter, we use the device tree to pass the relevant register physical address to the Linux kernel. The linux driver file uses the OF function explained in the previous chapter to obtain the required attribute value from the device tree, and then uses the obtained attribute value to initialize the relevant IO. The experiment in this chapter is relatively simple. The key contents OF this chapter are as follows:
① , at stm32mp157d ATK Create the corresponding device node in the DTS file.
② Write the driver (based on the experiment in Chapter 22) to obtain the relevant attribute values in the device tree.
③ . use the obtained relevant attribute values to initialize the GPIO used by the LED.
24.2 hardware schematic diagram analysis
Refer to section 21.2 for the hardware principle of this experiment.
24.3 preparation of experimental procedures
24.3.1 modify the equipment tree file
Create a child node named "stm32mp1_led" under the root node "/", and open stm32mp157d ATK DTS file, enter the following content at the end of the root node "/":

Example code 44.3.1.1 stm32mp1_led node
1    stm32mp1_led {
2        compatible = "atkstm32mp1-led";
3        status = "okay";
4        reg = <0X50000A28 0X04    	/* RCC_MP_AHB4ENSETR    	*/
5                0X5000A000 0X04    	/* GPIOI_MODER          	*/
6                0X5000A004 0X04    	/* GPIOI_OTYPER         	*/
7                0X5000A008 0X04    	/* GPIOI_OSPEEDR        	*/
8                0X5000A00C 0X04    	/* GPIOI_PUPDR          	*/
9                0X5000A018 0X04 >; 	/* GPIOI_BSRR           	*/
10   };
Line 2, attributes compatible set up stm32mp1_led Node compatibility is“ atkstm32mp1-led". 
Line 3, attributes status Set the status to“ okay". 
fourth~9 that 's ok, reg Attribute, very important! reg Property sets the physical address of the register to be used in the driver, such as "0" in line 4 X50000A28 0X04"express STM32MP1 of RCC_MP_AHB4ENSETR Register, where the register address is 0 X50000A28,The length is 4 bytes.

After modifying the device tree, enter the following command to recompile stm32mp157d ATK dts:
make dtbs
After compiling, stm32mp157d-atk DTB, using the new stm32mp157d ATK DTB starts the Linux kernel. After Linux starts successfully, enter the / proc / device tree / directory to check whether there is a "stm32mp1_led" node. The results are shown in figure 24.3.1.1:

Figure 24.3.1.1 stm32mp1_led node
If there is no "stm32mp1_led" node, please focus on the following two points:
① Check whether the modification of the device tree is successful, that is, stm32mp1_ Whether the LED node is a child of the root node "/".
② Check whether the Linux kernel started with the new device tree.
You can enter stm32mpl in figure 24.3.1.1_ In the LED directory, check the attribute files. The results are shown in figure 24.3.1.2:

Figure 24.3.1.2 stm32mp1_led node file
You can use the cat command to check whether the attribute values such as compatible and status are consistent with our settings.
24.3.2 LED driver programming
After the device tree is ready, you can write the driver. The experiment in this chapter is in the experiment driver file newchrled in Chapter 22 C. Create a new folder named "4_dtsled" and then_ Create a vscode project in the dtsled folder, and the workspace is named "dtsled". After the project is created, create a new dtsled C file, in dtsled C enter the following contents:

Example code 24.3.2.1 dtsled.c Document content
1   #include <linux/types.h>
2   #include <linux/kernel.h>
3   #include <linux/delay.h>
4   #include <linux/ide.h>
5   #include <linux/init.h>
6   #include <linux/module.h>
7   #include <linux/errno.h>
8   #include <linux/gpio.h>
9   #include <linux/cdev.h>
10  #include <linux/device.h>
11  #include <linux/of.h>
12  #include <linux/of_address.h>
13  #include <asm/mach/map.h>
14  #include <asm/uaccess.h>
15  #include <asm/io.h>
16 
17  /***************************************************************
18  Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
19  File name 		:  dtsled.c
20  Author 	:  Punctual atomic Linux team
21  edition 	:  V1.0
22  Description 	:  LED driver file.
23  other 	:  nothing
24  Forum 	:  www.openedv.com
25  journal 	:  First edition v1.0 0 2020 / 12 / 19 punctual atomic Linux team creation
26  ***************************************************************/
27  #define DTSLED_CNT        	 one 	/*  Number of equipment numbers 	*/
28  #define DTSLED_NAME      	 "dtsled"   	/*  name 		*/
29  #define LEDOFF            	 0 / * turn off the light 	 		*/
30  #define LEDON             	 1 / * turn on the light 			*/
31 
32  /* Mapped register virtual address pointer */
33  static void __iomem *MPU_AHB4_PERIPH_RCC_PI;
34  static void __iomem *GPIOI_MODER_PI;
35  static void __iomem *GPIOI_OTYPER_PI;
36  static void __iomem *GPIOI_OSPEEDR_PI;
37  static void __iomem *GPIOI_PUPDR_PI;
38  static void __iomem *GPIOI_BSRR_PI;
39 
40  /* dtsled Equipment structure */
41  struct dtsled_dev{
42      dev_t devid;            	/* Equipment number     	*/
43      struct cdev cdev;       	/* cdev     	*/
44      struct class *class;   	/* class      		*/
45      struct device *device;  	/* equipment    		*/
46      int major;              	/* Main equipment No   	*/
47      int minor;              	/* Secondary equipment No   	*/
48      struct device_node  *nd; /* Device node 	*/
49  };
50 
51  struct dtsled_dev dtsled;   	/* led equipment 	*/
52 
53  /*
54   * @description  	: LED On / off
55   * @param - sta  	: LEDON(0) Turn on the LED and LEDOFF(1) turns off the LED
56   * @return       	: nothing
57   */
58  void led_switch(u8 sta)
59  {
60      u32 val = 0;
61      if(sta == LEDON) {
62          val = readl(GPIOI_BSRR_PI);
63          val |= (1 << 16);   
64          writel(val, GPIOI_BSRR_PI);
65      }else if(sta == LEDOFF) {
66          val = readl(GPIOI_BSRR_PI);
67          val|= (1 << 0); 
68          writel(val, GPIOI_BSRR_PI);
69      }   
70  }
71 
72  /*
73   * @description 	: Unmap
74   * @return        	: nothing
75   */
76  void led_unmap(void)
77  {
78    	/* Unmap */
79      iounmap(MPU_AHB4_PERIPH_RCC_PI);
80      iounmap(GPIOI_MODER_PI);
81      iounmap(GPIOI_OTYPER_PI);
82      iounmap(GPIOI_OSPEEDR_PI);
83      iounmap(GPIOI_PUPDR_PI);
84      iounmap(GPIOI_BSRR_PI);
85  }
86 
87  /*
88   * @description  	: open device
89   * @param – inode	: inode passed to driver
90   * @param - filp 	: The device file has a file structure called private_ Member variable of data
91   *                    Generally, private is used when open ing_ Data points to the device structure.
92   * @return        	: 0 success; Other failures
93   */
94  static int led_open(struct inode *inode, struct file *filp)
95  {
96      filp->private_data = &dtsled; /* Set private data */
97      return 0;
98  }
99 
100 /*
101  * @description  	: Read data from device 
102  * @param - filp 	: Device file to open (file descriptor)
103  * @param - buf  	: Data buffer returned to user space
104  * @param - cnt  	: Length of data to read
105  * @param - offt 	: Offset relative to the first address of the file
106  * @return        	: The number of bytes read. If it is negative, it indicates that the read failed
107  */
108 static ssize_t led_read(struct file *filp, char __user *buf, 
size_t cnt, loff_t *offt)
109 {
110     return 0;
111 }
112
113 /*
114  * @description 	: Write data to device 
115  * @param - filp 	: A device file that represents an open file descriptor
116  * @param - buf  	: Data to be written to the device
117  * @param - cnt  	: Length of data to write
118  * @param - offt 	: Offset relative to the first address of the file
119  * @return        	: The number of bytes written. If it is negative, it indicates that the write failed
120  */
121 static ssize_t led_write(struct file *filp, const char __user *buf,
                  size_t cnt, loff_t *offt)
122 {
123     int retvalue;
124     unsigned char databuf[1];
125     unsigned char ledstat;
126
127     retvalue = copy_from_user(databuf, buf, cnt);
128     if(retvalue < 0) {
129         printk("kernel write failed!\r\n");
130         return -EFAULT;
131     }
132
133     ledstat = databuf[0];       /* Get status value 		*/
134
135     if(ledstat == LEDON) {  
136         led_switch(LEDON);      	/* Turn on the LED 		*/
137     } else if(ledstat == LEDOFF) {
138         led_switch(LEDOFF); 		/* Turn off the LED 		*/
139     }
140     return 0;
141 }
142
143 /*
144  * @description  	: Turn off / release the device
145  * @param – filp	: Device file to close (file descriptor)
146  * @return       	: 0 success; Other failures
147  */
148 static int led_release(struct inode *inode, struct file *filp)
149 {
150     return 0;
151 }
152
153 /* Device operation function */
154 static struct file_operations dtsled_fops = {
155     .owner = THIS_MODULE,
156     .open = led_open,
157     .read = led_read,
158     .write = led_write,
159     .release =  led_release,
160 };
161
162 /*
163  * @description 	: Drive exit function
164  * @param       	: nothing
165  * @return      	: nothing
166  */
167 static int __init led_init(void)
168 {
169     u32 val = 0;
170     int ret;
171     u32 regdata[12];
172     const char *str;
173     struct property *proper;
174
175     /* Get the attribute data in the device tree 			*/
176     /* 1,Get device node: stm32mp1_led 	*/
177     dtsled.nd = of_find_node_by_path("/stm32mp1_led");
178     if(dtsled.nd == NULL) {
179         printk("stm32mp1_led node nost find!\r\n");
180         return -EINVAL;
181     } else {
182         printk("stm32mp1_lcd node find!\r\n");
183     }
184
185     /* 2,Get the content of the compatible property */
186     proper = of_find_property(dtsled.nd, "compatible", NULL);
187     if(proper == NULL) {
188         printk("compatible property find failed\r\n");
189     } else {
190         printk("compatible = %s\r\n", (char*)proper->value);
191     }
192
193     /* 3,Get the content of the status attribute */
194     ret = of_property_read_string(dtsled.nd, "status", &str);
195     if(ret < 0){
196         printk("status read failed!\r\n");
197     } else {
198         printk("status = %s\r\n",str);
199     }
200
201     /* 4,Get reg property content */
202     ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 12);
203     if(ret < 0) {
204         printk("reg property read failed!\r\n");
205     } else {
206         u8 i = 0;
207         printk("reg data:\r\n");
208         for(i = 0; i < 12; i++)
209             printk("%#X ", regdata[i]);
210         printk("\r\n");
211     }
212
213     /* Initialize LED 			*/
214     /* 1,Register address mapping 	*/
215     MPU_AHB4_PERIPH_RCC_PI = of_iomap(dtsled.nd, 0);
216     GPIOI_MODER_PI = of_iomap(dtsled.nd, 1);
217     GPIOI_OTYPER_PI = of_iomap(dtsled.nd, 2);
218     GPIOI_OSPEEDR_PI = of_iomap(dtsled.nd, 3);
219     GPIOI_PUPDR_PI = of_iomap(dtsled.nd, 4);
220     GPIOI_BSRR_PI = of_iomap(dtsled.nd, 5);
221
222     /* 2,Enable PI clock */
223     val = readl(MPU_AHB4_PERIPH_RCC_PI);
224     val &= ~(0X1 << 8); 	/* Clear previous settings 	*/
225     val |= (0X1 << 8);  	/* Set new value 			*/
226     writel(val, MPU_AHB4_PERIPH_RCC_PI);
227
228     /* 3,Set the general output mode of PI0.*/
229     val = readl(GPIOI_MODER_PI);
230     val &= ~(0X3 << 0); 		/* bit0:1 Clear 		*/
231     val |= (0X1 << 0);  		/* bit0:1 Setting 01 	*/
232     writel(val, GPIOI_MODER_PI);
233
234     /* 3,Set PI0 to push-pull mode.*/
235     val = readl(GPIOI_OTYPER_PI);
236     val &= ~(0X1 << 0); 		/* bit0 Reset to pull-up*/
237     writel(val, GPIOI_OTYPER_PI);
238
239     /* 4,Set PI0 to high speed.*/
240     val = readl(GPIOI_OSPEEDR_PI);
241     val &= ~(0X3 << 0); 		/* bit0:1 Clear 		*/
242     val |= (0x2 << 0); 		/* bit0:1 Set to 10		*/
243     writel(val, GPIOI_OSPEEDR_PI);
244
245     /* 5,Set PI0 as pull-up.*/
246     val = readl(GPIOI_PUPDR_PI);
247     val &= ~(0X3 << 0); 		/* bit0:1 Clear			*/
248     val |= (0x1 << 0); 		/* bit0:1 Set to 01		*/
249     writel(val,GPIOI_PUPDR_PI);
250
251     /* 6,Default off LED */
252     val = readl(GPIOI_BSRR_PI);
253     val |= (0x1 << 0);
254     writel(val, GPIOI_BSRR_PI);
255
256     /* Register character device driver */
257     /* 1,Create device number */
258     if (dtsled.major) {     	/*  Defines the equipment number 		*/
259         dtsled.devid = MKDEV(dtsled.major, 0);
260         ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
261         if(ret < 0) {
262             pr_err("cannot register %s char driver [ret=%d]\n",DTSLED_NAME, DTSLED_CNT);
263             goto fail_map;
264         }
265     } else {                   	/* No device number defined 		*/
266         ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, 
			DTSLED_NAME);   /* Application equipment No */
267         if(ret < 0) {
268             pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", 
DTSLED_NAME, ret);
269             goto fail_map;
270         }
271         dtsled.major = MAJOR(dtsled.devid); /* Get the master device number of the assigned number */
272         dtsled.minor = MINOR(dtsled.devid); /* Get the secondary equipment number of the assigned number */
273
274     }
275     printk("dtsled major=%d,minor=%d\r\n",dtsled.major, 
dtsled.minor);  
276     
277     /* 2,Initialize cdev */
278     dtsled.cdev.owner = THIS_MODULE;
279     cdev_init(&dtsled.cdev, &dtsled_fops);
280     
281     /* 3,Add a cdev */
282     ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
283     if(ret < 0)
284         goto del_unregister;
285
286     /* 4,Create class 		*/
287     dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
288     if (IS_ERR(dtsled.class)) {
289         goto del_cdev;
290     }
291
292     /* 5,Create device 		*/
293     dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, 
NULL, DTSLED_NAME);
294     if (IS_ERR(dtsled.device)) {
295         goto destroy_class;
296     }
297
298     return 0;
299
300 destroy_class:
301     class_destroy(dtsled.class);
302 del_cdev:
303     cdev_del(&dtsled.cdev);
304 del_unregister:
305     unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
306 fail_map:
307     led_unmap();
308     return -EIO;
309 }
310
311 /*
312  * @description 	: Drive exit function
313  * @param       	: nothing
314  * @return      	: nothing
315  */
316 static void __exit led_exit(void)
317 {
318     /* Unmap 		*/
319     led_unmap();
320
321     /* Unregister character device driver */
322     cdev_del(&dtsled.cdev);	/*  Delete cdev */
323     unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /*cancellation*/
324
325     device_destroy(dtsled.class, dtsled.devid);
326     class_destroy(dtsled.class);
327 }
328
329 module_init(led_init);
330 module_exit(led_exit);
331 MODULE_LICENSE("GPL");
332 MODULE_AUTHOR("ALIENTEK");
333 MODULE_INFO(intree, "Y");

dtsled.c contents in the document and newchrled in Chapter 22 The contents in the C file are basically the same, except dtsled C contains the code for processing the device tree. Let's focus on this part of the code.
Line 48, dtsled in equipment structure_ The member variable nd is added to dev, and Nd is device_node structure type pointer, indicating the device node. If we want to read the attribute value of a node in the device tree, we must first get the node, and generally add device to the device structure_ Node pointer variable to store this node.
Lines 177-183, pass OF_ find_ node_ by_ The path function gets stm32mp1_led node, and other subsequent OF functions need to use device_node.
Lines 186-191, pass of_ find_ The property function gets stm32mp1_ For the compatible attribute of LED node, the return value is the pointer variable of property structure type, and the member variable value of property represents the attribute value.
Lines 194-199, pass of_ property_ read_ The string function gets stm32mp1_ The status attribute value of the LED node.
Lines 202-211, pass of_ property_ read_ u32_ The array function gets stm32mp1_ The reg attribute of the LED node has all values, and the obtained values are stored in the regdata array. Line 209 outputs the obtained reg attribute values to the terminal in turn.
Lines 215-220, use of_iomap function reads reg attribute and memory mapping at one time, OF_ The iomap function is the OF function recommended for the device tree.
24.3.3 write test APP
This chapter directly uses the test APP in Chapter 22 and the ledapp in the previous chapter C file can be copied to the experimental project of this chapter.
24.4.1 compiling driver and testing APP
1. Compile driver
Write the Makefile file. The Makefile file of the experiment in this chapter is basically the same as the experiment in Chapter 20, except that the value of obj-m variable is changed to dtsled o. The contents of Makefile are as follows:
Example code 24.4.1.1 Makefile file

1  KERNELDIR := /home/zuozhongkai/linux/my_linux/linux-5.4.31
...... 
4  obj-m := dtsled.o
......
11 clean:
12  $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

In line 4, set the value of obj-m variable to dtsled o.
Input the following command to compile the driver module file:
make -j32
After successful compilation, a driver module file named "dtsled.ko" will be generated.
2. Compile test APP
Enter the following command to compile the test ledapp C this test procedure:
arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp
After the compilation is successful, the ledApp application will be generated.
24.4.2 operation test
Dtsled compiled from the previous section Copy Ko and ledApp files to rootfs/lib/modules/5.4.31 directory, restart the development board, enter the directory lib/modules/5.4.31, and enter the following command to load dtsled Ko drive module:
depmod / / this command needs to be run when the driver is loaded for the first time
modprobe dtsled / / load driver
After the driver is loaded successfully, some information will be output in the terminal, as shown in figure 24.4.2.1:

Figure 24.4.2.1 information output after successful driver loading
As can be seen from figure 24.4.2.1, stm32mp1_ The node LED is found, and the value of the compatible attribute is "atkstm32mp1 led", the value of the status attribute is "okay", and the value of the reg attribute is "0X50000A28 0X4 0X5000A000 0X4 0X5000A004 0X4 0X5000A008 0X4 0X5000A00C 0X4 0X5000A018 0X4", which are consistent with the device tree we set.
After the driver is loaded successfully, you can use ledApp software to test whether the driver works normally. Enter the following command to turn on the LED:
. / ledApp /dev/dtsled 1 / / turn on the LED
After entering the above command, observe whether the red LED light on the development board is on. If it is on, it indicates that the drive works normally. Turn off the LED after entering the following command:
. / ledApp /dev/dtsled 0 / / turn off the LED
After entering the above command, observe whether the red LED light on the development board is off. If you want to unload the driver, you can enter the following command:
rmmod dtsled.ko

Keywords: Linux stm32

Added by michibk on Sat, 12 Feb 2022 07:12:32 +0200