【i.MX6ULL】Driver development - Pinctrl subsystem and GPIO subsystem light up the LED
The essence of the previous two articles ( register configuration to light up the LED and device tree version of lighting the LED ) is to control the on and off of the LED through register configuration.
-
Using the direct operation register method is to write the register information related to the LED directly into the LED driver code. This is also a relatively conventional control method. But when the chip registers change, the underlying driver must be rewritten.
-
The method of using the device tree is to write the register information related to the LED into the device tree file. In this way, when the device information is modified, the device information can also be obtained through the interface function of the device tree, which improves the efficiency of the driver code. Reusability.
-
The Pinctrl subsystem and GPIO subsystem introduced in this article do not need to directly operate the registers, because these two subsystems have already implemented the operation of the registers for us. We only need to operate the API functions provided by these two subsystems. Can.
1 Pinctrl subsystem
The Pintrl subsystem, as the name suggests, is a system that manages pins. For example, if you want to light up an LED, that is, to control the high and low levels of the corresponding pins of the LED, you must first multiplex the pins corresponding to the LED as GPIO through the Pintrl subsystem. Function (Is this somewhat similar to the function of the MUX register used in the previous register configuration )?
1.1 iomuxc node in the device tree
How to use the Pintrl subsystem? In fact, it also depends on the device tree. Let's first understand the iomuxc node in the device tree . This node is the node corresponding to the IOMUXC peripheral and is responsible for the reuse of IO functions.
Open the device tree file corresponding to your development board (mine is imx6ull-myboard.dts), then find the iomuxc node, and first take a look at its basic structure:
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 /* SD1 RESET */
>;
};
pinctrl_csi1: csi1grp {
fsl,pins = <
MX6UL_PAD_CSI_MCLK__CSI_MCLK 0x1b088
MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK 0x1b088
MX6UL_PAD_CSI_VSYNC__CSI_VSYNC 0x1b088
MX6UL_PAD_CSI_HSYNC__CSI_HSYNC 0x1b088
MX6UL_PAD_CSI_DATA00__CSI_DATA02 0x1b088
MX6UL_PAD_CSI_DATA01__CSI_DATA03 0x1b088
MX6UL_PAD_CSI_DATA02__CSI_DATA04 0x1b088
MX6UL_PAD_CSI_DATA03__CSI_DATA05 0x1b088
MX6UL_PAD_CSI_DATA04__CSI_DATA06 0x1b088
MX6UL_PAD_CSI_DATA05__CSI_DATA07 0x1b088
MX6UL_PAD_CSI_DATA06__CSI_DATA08 0x1b088
MX6UL_PAD_CSI_DATA07__CSI_DATA09 0x1b088
>;
};
//省略...
Here we take the pinctrl_hog_1 plug-in sub-node as an example for analysis. It is a collection of Pins related to hot-swapping, such as the ID pin of USB OTG. The pinctrl_csi1 sub-node is the PIN used by csi peripherals. This article needs to control the brightness of the LED. If it is off, you need to create a corresponding node, and then put all the Pin configuration information of this custom peripheral into this child node.
1.2 Analysis of the meaning of macro definition
For the byte point pinctrl_hog_1, pay attention to the following:
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
This is the configuration of the Pin. The configuration includes two aspects: one is to set the multiplexing function of the Pin , and the other is to set the electrical characteristics of the Pin .
The previous
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19
macro definition is defined in arch/arm/boot/dts/imx6ul-pinfunc.h (note that
imx6ull.dtsi
will reference
imx6ull-pinfunc.h
, and imx6ull-pinfunc.h will reference
imx6ul-pinfunc.h
)
There are a total of 8 macro definitions starting with MX6UL_PAD_UART1_RTS_B, which represent 8 different functions of this IO.
In addition, the value defined by this macro is divided into 5 segments, and the value of each segment has a specific meaning:
-
0x0090 mux_reg register offset address
-
**0x031C **conf_reg register offset address
-
0x0000 input_reg register offset address (invalid here)
-
0x5 mux_reg register value
-
0x0 input_reg register value (invalid here)
2 GPIO subsystem
The GPIO subsystem, as the name suggests, is a system that manages GPIO functions. Its function is to initialize and configure GPIO (is this function somewhat similar to the PAD register used in the previous register configuration ), and provides an external API interface. After using the GPIO subsystem, you do not need to operate the registers yourself. You can control the GPIO by calling the API functions provided by the GPIO subsystem.
2.1 gpio information in the device tree
Still taking the hot-swappable node as an example:
UART1_RTS_B is multiplexed as GPIO1_IO19, and it is judged whether the SD card is inserted by reading its high and low levels.
So how does the SD card driver know that the CD pin is connected to GPIO1_IO19? You still need to tell the driver from the device tree. Just add an attribute under the SD card node in the device tree to describe the CD pin of the SD card:
The attribute cd-gpios describes which IO is used by the CD pin of the SD card. There are three attribute values:
-
&gpio1 indicates that the IO used by the CD pin belongs to the GPIO1 group
-
19 represents IO No. 19 of GPIO1 group
-
GPIO_ACTIVE_LOW means low level is active
Based on the above information, the SD card driver can use GPIO1_IO19 to detect the CD signal of the SD card.
2.2 gpio subsystem API functions
2.2.1 gpio_request/free
-
gpio_request
Used to apply for a GPIO pin
/**
* gpio: 要申请的gpio标号(使用of_get_named_gpio函数从设备树获取指定GPIO属性信息时返回的标号)
* label: 给gpio设置个名字
* return: 0-申请成功 其他值-申请失败
*/
int gpio_request(unsigned gpio, const char *label)
-
gpio_free
Used to release a GPIO pin
/**
* gpio: 要释放的gpio标号
* return
*/
void gpio_free(unsigned gpio)
2.2.2 gpio_direction_input/output
-
gpio_direction_input
Used to set a certain GPIO as input
/**
* gpio: 要设置为输入的GPIO标号
* return: 0-设置成功 负值-设置失败
*/
int gpio_direction_input(unsigned gpio)
-
gpio_direction_output
This function is used to set a GPIO as output and set the default output value
/**
* gpio: 要设置为输出的GPIO标号
* value: GPIO默认输出值
* return 0-设置成功 负值-设置失败
*/
int gpio_direction_output(unsigned gpio, int value)
2.2.3 gpio_get_value/set_value
-
gpio_get_value
This function is used to get the value of a certain GPIO (0 or 1)
#define gpio_get_value __gpio_get_value
/**
* gpio: 要获取的gpio标号
* return: 非负值-得到的gpio值 负值-获取失败
*/
int __gpio_get_value(unsigned gpio)
-
gpio_set_value
Used to set the value of a certain GPIO
#define gpio_set_value __gpio_set_value
/**
* gpio: 要设置的gpio标号
* value: 要设置的值
* return
*/
void __gpio_set_value(unsigned gpio, int value)
2.3 OF functions related to gpio
2.3.1 of_gpio_named_count
Used to obtain several GPIO information defined in a certain attribute of the device tree.
/**
* np: 设备节点
* propname: 要统计的gpio属性
* return: 正值-统计到的gpio数量 负值-失败
*/
int of_gpio_named_count(struct device_node *np, const char *propname)
2.3.2 of_gpio_count
Count the number of GPIOs with the attribute "gpios"
/**
* np: 设备节点
* return: 正值-统计到的gpio数量 负值-失败
*/
int of_gpio_count(struct device_node *np)
2.3.3 of_get_named_gpio
Get GPIO number
/**
* np: 设备节点
* propname: 包含要获取gpio信息的属性名
* index: gpio索引(一个属性里面可能包含多个gpio)
* return: 正值-获取到的gpio编号 负值-失败
*/
int of_get_named_gpio(struct device_node *np,
const char *propname,
int index)
3 Pinctr version LED driver
The basic situation of the Pinctrl subsystem and GPIO subsystem is introduced above. Let's use them to realize the on and off control of LED.
3.1 Modify device tree file
Modify imx6ull-myboard.dts and create a child node named pinctrl_led under the imx6ull-evk byte point of the iomuxc node. The node content is as follows:
pinctrl_gpioled: ledgrp{
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x10b0
>;
};
-
MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 means reusing this io as GPIO
-
0x10b0 represents the configuration value of the PAD register. The specific meaning is as follows, which was introduced in the previous article (Driver Development 4--Lighting the LED (Register Version)).
/*寄存器SW_PAD_SNVS_TAMPER3设置IO属性
*bit 16:0 HYS关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 R0/6驱动能力
*bit [0]: 0 低转换率
*/
Create an LED node named gpioled under the root node with the following content:
/*pinctrl led*/
gpioled {
compatible = "myboard,gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpioled>;
led-gpios = <&gpio5 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
-
pinctrl-0 sets the pinctrl node corresponding to the PIN used by the LED
-
led-gpio specifies the GPIO used by the LED. Here is IO03 of GPIO5, which is active at low level.
3.2 Check whether the pin usage conflicts
Because the device tree file (imx6ull-myboard.dts) used by my development board is modified from the device tree file (imx6ull-14x14-evk.dts) officially provided by NXP, the configuration of some pins may be different from my own. The development boards are different, so you need to check whether there are any usage conflicts.
The MX6ULL_PAD_SNVS_TA MPER3__GPIO5_IO03 added this time does not conflict with other pins in the file, so there is no need to modify it.
3.3 Modify LED driver file
Make modifications to the driver file of the device tree version in the previous article. The main modifications are as follows.
The header file needs to add one:
#include <linux/of_gpio.h>
The device structure is changed to gpio_led:
/* gpioled设备结构体 */
struct gpioled_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
int led_gpio; /* led使用的GPIO编号*/
};
struct gpioled_dev gpioled; /* led设备 */
The hardware initialization part is the main modification. This time, there is no need to read registers from the device tree, and there is no need to perform I/O memory mapping by yourself. Instead, the API function of the gpio subsystem is used to map the LED's GPIO. To configure:
static int gpioled_hardware_init(void)
{
int ret;
/* 获取设备树中的属性数据 */
/* 1、获取设备节点:gpioled */
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL)
{
printk("gpioled node not find!\r\n");
return -EINVAL;
}
else
{
printk("gpioled node find!\r\n");
}
/* 2、获取gpio属性, 得到LED编号 */
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(gpioled.led_gpio < 0)
{
printk("can't get led-gpio!\r\n");
return -EINVAL;
}
else
{
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
}
/* 3、设置GPIO为输出, 并默认关闭LED */
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret < 0)
{
printk("can't set led-gpio!\r\n");
}
return 0;
}
When switching the LED on and off, you no longer need to directly operate the register. You can also use the API function to operate:
static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
//省略...
if(ledstat == LEDON)
{
gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */
printk("led on!\n");
}
else if(ledstat == LEDOFF)
{
gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */
printk("led off!\n");
}
return 0;
}
4 Experimental tests
4.1 Compiler
-
Compile the device tree file (.dtb). Just like the experiment of lighting up the LED in the device tree in the previous article, first copy the device tree file to the nfs file system location, then start the development board from the network, and check the serial port to see if there is any added device tree. gpioled node:
-
Compile the LED driver file (.ko) and copy it to the rootfs/lib/modules/4.1.15 directory:
-
The LED application program does not need to be modified. You can still use the program used in the previous register version to light up the LED experiment.
4.2 Testing
The test method is the same as before, loading the driver file first, and then calling the application to control the LED on and off:
The effect is the same as the previous effect of lighting up the LED in the register version and lighting up the LED in the device tree version.
5 Summary
This article introduces the use of Pinctrl subsystem and GPIO subsystem to light up the LED. The biggest difference from the previous register version to light up the LED and the device tree version to light up the LED is that there is no need to directly operate the register , but to use API functions. To configure GPIO, the specific operation register is implemented inside the API function, and we do not need to perform cumbersome register operations.
This article is basically the same as the previous article's device tree version of the programming process for lighting up the LED, because both require the use of the device tree . The main difference from the previous article is that there is no need to write register information into the device tree, and then from The device tree obtains the manual configuration register.
end
A mouthful of Linux
Follow and reply [ 1024 ] Massive Linux information will be given away
Collection of wonderful articles
Article recommendation
Click " Read the original text " to view more sharing, welcome to share, collect, like, and watch