Zhengdian Atom embedded linux driver development – platform driver under the device tree

In the previous note, I explained in detail the driver separation and layering under Linux, as well as the driver framework such as buses, devices and drivers. Based on the driver framework of buses, devices and drivers, the Linux kernel proposes the platform virtual bus, and correspondingly there are platform devices and platform drivers. The previous note explained the traditional platform device and driver writing method that does not use the device tree. The latest Linux kernel already supports device tree, so how to write platform driver under device tree is particularly important. In this chapter, we will learn How to write platform driver under device tree.

Introduction to platform driver under device tree

The platform driver framework is divided into bus, device and driver. The bus does not need to be managed by driver programmers. This is provided by the Linux kernel. When writing the driver, you only need to focus on the specific implementation of the device and driver. Can. Under the Linux kernel without a device tree, platform_device and platform_driver need to be written and registered separately, representing the device and driver respectively. When using the device tree, the device description is placed in the device tree, so platform_device does not need to be written, only platform_driver needs to be implemented.

Modify pinctrl-stm32.c

I have learned pinctrl in previous notes, but I have never used pinctrl in subsequent experiments. Logically speaking, when using a certain pin, you need to configure its electrical properties first, such as multiplexing, input or input, default pull-down, etc.! However, in the previous experiments, the electrical properties of the pins were not configured, that is, the pinctrl configuration of the pins. This is because in the Linux system provided by ST for STM32MP1, the electrical properties of its pinctrl configuration can only be referenced under the platform.

For example, when Zhengdian Atom is developing the I.MX6ULL chip, the Linux system will automatically parse the pinctrl configuration under the device tree during startup and operation, and then initialize the electrical properties of the pins, without the need for a platform driver framework. So when pinctrl is effective, different chip manufacturers have different processing methods. Everything is subject to the actual chip used!

For STM32MP1, you need to modify the pinctrl-stm32.c file when using pinctrl, otherwise when a certain pin is used as a GPIO, it will prompt that this pin cannot be applied for, as shown below Shown:
IO application failed
As can be seen from the picture above, it is prompted that the IO of PI0 has been applied for by other peripherals and cannot be applied for again. Open the pinctrl-stm32.c file and find the following code:
Follow them 2_pmx_ops structure
The strict member variable on line 7 defaults to true and needs to be changed to false. After the modification is completed, use the following command to recompile the Linux kernel:

make uImage LOADADDR=0XC2000040 -j16 //Compile kernel

After compilation is completed, just use uImage to start it.

Create the pinctrl node of the device

As mentioned above, under the platform driver framework, pinctrl must be used to configure the pin multiplexing function. Take LED0 that needs to be used in the experiment of this chapter as an example, and write the pinctrl configuration of the LED0 pin. Open the stm32mp15-pinctrl.dtsi file. All pin pinctrl configurations of STM32MP1 are completed in this file. Add the following content under the pinctrl node:

Sample code 35.1.2.1 GPIO pinctrl configuration
1 led_pins_a: gpioled-0 {<!-- -->
2 pins {<!-- -->
3 pinmux = <STM32_PINMUX('I', 0, GPIO)>;
4 drive-push-pull;
5 bias-pull-up;
6 output-high;
7 slew-rate = <0>;
8     }; 
9 };

The led_pins_a node in the sample code 35.1.2.1 is the pinctrl configuration of the LED, which reuses the PI0 port as a GPIO function and sets the electrical characteristics of PI0. In previous notes, I have learned how to configure the electrical properties of STM32MP1. Here is a brief introduction to the configuration of LED0:

Line 3 sets PI0 to be reused as GPIO function.

Line 4 sets PI0 as push-pull output.

Line 5, sets the PI0 internal pull-up.

Line 6 sets PI0 to output high level by default.

Line 7 sets the speed of PI0 to 0, which is the slowest.

Create device nodes in the device tree

Next, create a device node in the device tree to describe the device information. The key point is to set the value of the compatible attribute, because the platform bus needs to match the driver through the compatible attribute value of the device node! This is important to remember. Modify the previously created gpioled node, as shown below:

Sample code 35.1.3.1 gpioled device node
1 gpioled {<!-- -->
2 compatible = "alientek,led";
3 pinctrl-names = "default";
4 status = "okay";
5 pinctrl-0 = < & amp;led_pins_a>;
6 led-gpio = < & amp;gpioi 0 GPIO_ACTIVE_LOW>;
7};

The compatible attribute value in line 2 is “alientek,led”, so when writing the platform driver later, there must be “alientek,led” in the of_match_table attribute table.

In line 5, the pinctrl-0 attribute sets the pinctrl node corresponding to the LED’s PIN, which is led_pins_a written in sample code 35.1.1.

Pay attention to compatibility attributes when writing platform drivers

As explained in detail in the previous chapter, when using the device tree, the platform driver will save the compatibility value through of_match_table, which indicates which devices the driver is compatible with. Therefore, of_match_table will be particularly important. For example, the platform_driver in the platform driver of this routine can be set as follows:

Sample code 35.1.4.1 of_match_table matching table settings
1 static const struct of_device_id led_of_match[] = {<!-- -->
2 {<!-- --> .compatible = "alientek,led" }, /* compatible attribute */
3 {<!-- --> /* Sentinel */ }
4};
5
6 MODULE_DEVICE_TABLE(of, led_of_match);
7
8 static struct platform_driver led_platform_driver = {<!-- -->
9 .driver = {<!-- -->
10.name = "stm32mp1-led",
11.of_match_table = led_of_match,
12},
13.probe = led_probe,
14.remove = led_remove,
15};

Lines 1-4, the of_device_id table, which is the driver compatibility table, is an array, and each array element is of of_device_id type. Each array element is a compatibility attribute, indicating a compatible device. One driver can match multiple devices. Only one device is matched here, which is the gpioled device created in sample code 35.1.2. The compatible value in line 2 is “alientek,led”. The compatible attribute in the driver matches the compatible attribute in the device, so the corresponding probe function in the driver will be executed. Note that line 3 is an empty element. When writing of_device_id, the last element must be empty!

Line 6, declare the led_of_match device matching table through MODULE_DEVICE_TABLE.

Line 11, set the of_match_table matching table in platform_driver to the leds_of_match created above. At this point, the matching table of the platform driver is set.

The last step is to write the driver.The platform driver based on device tree is basically the same as the platform driver without device tree in the previous chapter. After the driver and device are successfully matched, the electrical characteristics of the PIN are set according to the pinctrl attribute in the device tree. Then execute the probe function. You need to execute the character device driver in the probe function. When the driver module is unregistered, the remove function will be executed. They are all similar.

Check pin multiplexing configuration

Check pin pinctrl configuration

One pin of STM32MP1 can be multiplexed for multiple functions. For example, PI0 can be used as GPIO, TIM5_CH4, SPI2_NSS, DCMI_D13, LCD_G5, etc. When developing STM32 microcontroller, one IO can be used by multiple peripherals. For example, PI0 can be used as TIM5_CH4 and LCD_G5 at the same time, but it can only be used for one function at the same time. Under embedded Linux, the hardware must be designed strictly according to one pin corresponding to one function. For example, if PI0 is now used as a GPIO to drive LED lights, then PI0 cannot be used as other functions.

The PI0 is connected to LED0 on the Zhengdian Atom STM32MP1 development board, that is, the pinctrl configuration corresponding to using it as an ordinary GPIO is the sample code 35.1.2.1. However, stm32mp15-pinctrl.dtsi was written by ST based on its official EVK development board, so PI0 may be officially used by ST for other functions. Find the following code in stm32mp15-pinctrl.dtsi:
ltdc_pins_anode
As can be seen from the picture above, ST officially reuses PI0 as LCD_G5 by default. As mentioned earlier, one IO can only be reused as one function, so you need to change “” is shielded because PI0 is now used as GPIO. Similarly, continue to search in the stm32mp15-pinctrl.dtsi file, and you will find that PI0 is also reused as LCD_G5 as shown in the figure below:
ltdc_pins_sleep_anode
The ltdc_pins_sleep_a node in the above figure also multiplexes PI0 as LCD_G5, and this line of code is also blocked. Make sure that in the device tree you are using, one pin is only reused for one function!

Check GPIO usage

The previous section just checked whether the PI0 pin has been reused as multiple devices. In this section, PI0 is reused as GPIO. Because it is modified on the device tree provided by ST officially, it is also necessary to check whether ST officially assigns this GPIO to other devices when PI0 is used as GPIO.

So when using a pin as a GPIO, be sure to check whether there are other devices in the current device tree that also use this GPIO, and ensure that only one device tree in the device tree is using this GPIO.

Hardware schematic analysis

It’s LED, so I won’t analyze it here.

Experimental program writing

You need to add a device node to the device tree, and then you only need to write the platform driver.

Modify device tree files

First modify the device tree file and add the required device information. This chapter uses LED0. You need to create the pinctrl node of the LED0 pin. This directly uses the led_pins_a node in the sample code 35.1.2.1. In addition, a LED0 device node must be created. This directly uses the gpioled device node in sample code 35.1.3.1.

Platform driver writing

The driver here is very similar to the previous gpio driver LED.

In the device structure, the labels of the device nodes and GPIOs are required.

In led_switch, it can be directly controlled through gpio_set_value.

In led_gpio_init, you need to get the gpio in the device tree through of_get_named_gpio, then apply to use GPIO through gpio_request, and set the GPIO output and initial high level through gpio_directon_output.

In led_write, as before, it is the copy_to_user read command, and then led_switch controls the status of the LED.

Here, the most important thing is the self-implemented probe function led_probe, in which the LED is first initialized through led_gpio_init; then the character device is initialized, alloc_chrdev_region sets the device number; cdev is initialized through cdev_init, and then cdev_add adds cdev; then class_create creates the class, device_create Create device.

led_remove means first gpio_set_value to turn off the LED when uninstalling the driver, and then gpio_free to unregister the GPIO; then there are the regular operations of the character device, cdev_del, then unregister_chrdev_region, then device_destroy and class_destroy.

Then create a match list, the led_of_match[] array of of_device_id structure type, in which the .compatible attribute must be set, and must match the name of the node; after completing the array, call MODULE_DEVICE_TABLE to pass in the custom led_of_match array.

Only the platform_driver driver structure needs to be defined, in which .driver must be set (.name must match .compatible, .of_match_table needs the array just set), and then add .probe and .remove.

Finally, we need to write the driver loading and unloading function, which is to return platform_driver_register in leddriver_init; call platform_driver_unregister in leddriver_exit.

Write a test APP

This can be done by directly using the APP from the previous platform experiment.

Run the test

Compile driver

Makefile changes obj-m to leddriver.o, and then “make” it.

Compile and test APP

Just bring over the platform’s APP that didn’t have a device tree before.

Run the test

Copy leddriver.ko compiled in the previous section to the rootfs/lib/modules/5.4.31 directory, restart the development board, and enter
Go to the directory lib/modules/5.4.31 and enter the following command to load the leddriver.ko driver module:

depmod //You need to run this command when loading the driver for the first time
modprobe leddriver.ko //Load driver module

After the driver module is loaded, go to the /sys/bus/platform/drivers/ directory to check whether the driver exists. Set the name field in leddriver.c to “stm32mp1-led”, so it will be in the /sys/bus/platform/drivers/ directory. There is a file named “stm32mp1-led” under it. In the same way, the LED device file also exists in the /sys/bus/platform/devices/ directory, which is the gpioled node in the device tree.

Both the driver and the module exist. When the driver and device are successfully matched, a line of statements as shown below will be output:
Driver and device matching
After the driver and device are successfully matched, you can test the LED light driver. Enter the following command to control the LED:

./ledApp /dev/dtsplatled 1 //Turn on the LED light
./ledApp /dev/dtsplatled 0 //Turn off the LED light

If you want to uninstall the driver, enter the following command:

rmmod leddriver.ko

Summary

This chapter is very similar to the previous platform without device tree, that is, you need to modify the device tree file to add your own nodes.

You need to first modify the pinctrl-stm32.c file, change the .strict attribute of stm32_pmx_ops in it to false, and then recompile the Linux kernel.

Then in stm32mp15-pinctrl.dtsi, add the pinctrl settings of the relevant pins, that is, the settings of the electrical attributes; then add the relevant node information in the device tree file stm32mp157d-atk.dts, and associate it with the pinctrl. When modifying, you need to check whether the pin has been set in the dtsi file of pinctrl. If so, comment it out to prevent conflicts.

When writing the driver, we combine the platform and previous gpio experiments to write. On the basis of gpio, we add the matching list and the platform driver structure. The probe function completes the registration of the character device, and the remove function completes the driver unregistration. ;Then just change the loading and unloading of the driver to the platform.