1. Introduction

The development of GPIO driver may be the simplest and most common direction in the development of Linux kernel device driver. The keys, LED, buzzer, power control and other modules of the development board may all be implemented using GPIO. The GPIO subsystem of the Linux kernel has been reconfigured several times during the evolution of the kernel. The case study in chapter 2 is based on the familiar GPIO development mode, and chapter 3 introduces the latest GPIO architecture programming mode (based on the 4.18 kernel). The GPIO subsystem of the Linux kernel is closely related to the Pinctrl subsystem, which you can see in this article, and a discussion of the GPIO subsystem will be summarized in a subsequent article. The basic programming patterns for GPIO are discussed below.

2. GPIO programming mode

The GPIO demo introduced in this chapter is based on the Linux kernel 3.10 version, and the development board is OKMX6UL-C2. The function of this demo is very simple. The main function of this demo is to read GPIO configuration information through DTS, and then configure each GPIO port according to the configuration information. The application can then control the specific behavior of the GPIO (IO state reading, IO state output, etc.) through iocTL. Let’s first introduce the driver implementation of Demo.

2.1 Programming Interface

Linux kernel control of GPIO is usually done through the GPIOLIB framework, which must be enabled during kernel configuration ** GPIOLIB**. All of the following interfaces are implemented based on GPIOLIB.

The GPIO programming interface involved in this Demo is as follows:

(linux/asm-generic/gpio.h):
extern int  gpio_request(unsigned gpio, const char *label);
extern void gpio_free(unsigned gpio);

extern int  gpio_direction_input(unsigned gpio);
extern int  gpio_direction_output(unsigned gpio, int value);

extern int  gpio_set_debounce(unsigned gpio, unsigned debounce);

extern int  gpio_get_value_cansleep(unsigned gpio);
extern void gpio_set_value_cansleep(unsigned gpio, int value);

(linux/gpio.h)
static inline int gpio_get_value(unsigned int gpio);
static inline void gpio_set_value(unsigned int gpio, int value);
Copy the code

2.2 DTS configuration

Demo uses the GPIO configuration information in DTS to complete the initialization of GPIO ports. Here is an example of DTS configuration (see the article for the basic SYNTAX of DTS) :

gpios { compatible = "gpio-user"; status = "okay"; /*input*/ gpio0 { label = "in0"; gpios = <&gpio1 1 0>; default-direction = "in"; }; . . /*output*/ gpio17 { label = "out1"; gpios = <&gpio3 3 0>; default-direction = "out"; }; };Copy the code

The above indicates that the system is configured with two GPIO, one as the input GPIO and the other as the output GPIO, where:

  1. Label: Indicates the GPIO port.
  2. The format of the gPIOS property generally depends on the # gPIO-Cells property field of the GPIO Controler. The format of GPIOS for this development board is:

<bank_num, offset, default_value>, bank_num is the bank number of GPIO, offset is the offset of GPIO number in the bank, defalut_value is the default value of GPIO output state; 3. The default-direction attribute indicates whether gPIO is in input or output mode. “in” indicates the input mode, and “out” indicates the output mode.

The DMEO is implemented in the kernel-based MISC device development framework and is registered with the system as platform_driver. The data structure of platform_driver is as follows:

static struct platform_driver gpio_user_driver = {
	.probe = gpio_user_probe,
	.remove = gpio_user_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "gpio-user",
		.of_match_table = of_gpio_user_id_table,
	},
};
Copy the code

Of_gpio_user_id_table defines device compatibility for the driver, which corresponds to the compatible = “gpiO-user “,”gpio-user” fields in Section 2.2. Here is the definition of OF_gpio_user_ID_table:

static const struct of_device_id of_gpio_user_id_table[] = {
	{ .compatible = "gpio-user",},
	{},
};
Copy the code

If the device defined in the DTS matches the driver, then gPIo_user_probe will be executed. Before explaining, we need to analyze the private data structure defined by the driver, which is defined as follows:

struct gpio_user_data{ const char *label; // Bool input; // Unsigned gPIO is in input mode; // gPIO number unsigned DFT; // The default value of gPIO output mode}; static struct gpio_misc{ struct miscdevice misc; // Struct gpio_user_data *data; //gpio configuration data int gpio_count; // Number of gPIO configuration data} *gpio_misc;Copy the code

The implementation of gPIo_user_probe is as follows:

static int gpio_user_probe(struct platform_device *pdev) { int index; struct device_node *node = pdev->dev.of_node, *child; gpio_misc = devm_kzalloc(&pdev->dev,sizeof(*gpio_misc),GFP_KERNEL); ------------------>(1) if(! gpio_misc){ return -ENOMEM; } gpio_misc->gpio_count = of_get_available_child_count(node); ------------------>(2) if(! gpio_misc->gpio_count){ return -ENODEV; } if(gpio_misc->gpio_count > MAX_GPIO_NR){ gpio_misc->gpio_count = MAX_GPIO_NR; } gpio_misc->data = devm_kzalloc(&pdev->dev,sizeof(struct gpio_user_data) * gpio_misc->gpio_count,GFP_KERNEL); if(! gpio_misc->data){ return -ENOMEM; } index = 0; for_each_available_child_of_node(node,child){------------------>(3) const char *input; struct gpio_user_data *data = &gpio_misc->data[index++]; data->label = of_get_property(child,"label",NULL) ? : child->name; input = of_get_property(child,"default-direction",NULL) ? : "in"; if(strcmp(input,"in") == 0) data->input = true; data->gpio = of_get_gpio_flags(child,0,&data->dft); } gpio_user_init_default(); ------------------>(4) gpio_misc->misc.name = "gpio"; ------------------>(5) gpio_misc->misc.minor = MISC_DYNAMIC_MINOR; gpio_misc->misc.fops = &gpio_user_fops; return misc_register(&gpio_misc->misc); }Copy the code

The following is a step-by-step analysis of it:

  • (1) Use devM_kzalloc with memory reclamation function to allocate memory space for gpio_misc.
  • (2) Run of_get_available_child_count to obtain the number of GPIO child nodes of DTS. Determine the validity of this value, and then create the gPIo_misc ->data array based on the number of GPIO child nodes.
  • (3) Use for_each_available_child_of_node to change the GPIO child node in DTS and initialize the gPIo_misc ->data array.
  • (4) Initialize gPIO configuration information. The gPIo_user_init_default function is described in detail below.
  • (5) Initialize misC device information, register gPIO_MISC -> MISC device into the system.

The gpio_user_init_default function is implemented as follows:

static void gpio_user_init_default(void) { int i,ret; struct gpio_user_data *data; data = gpio_misc->data; for(i = 0; i < gpio_misc->gpio_count; i++) { if(! gpio_is_valid(data[i].gpio)) { continue; } ret = gpio_request(data[i].gpio,data[i].label); ------------------>(1) if(ret < 0) { continue; } if(data[i].input) { gpio_direction_input(data[i].gpio); ---------------------->(2) } else { gpio_direction_output(data[i].gpio,data[i].dft); }}}Copy the code

Here is a brief introduction to the implementation steps:

  • (1) Check the validity of GPIo_NUM, and apply to the system for the use of GPIO through gPIo_Request (if the application is successful, the GPIO will be marked as occupied, and the gPIO function attribute is GPIO);

  • (2) Configure the input and output modes of GPIO.

Through the above series of initialization work, the GPIO configuration in DTS is basically complete. Let’s take a look at the IOCTL interface and explain how to control the GPIO’s behavior.

#define GPIO_U_IOCTL_BASE 'x' #define GPIOC_OPS _IOWR(GPIO_U_IOCTL_BASE,0,int) static const struct file_operations gpio_user_fops = { .owner = THIS_MODULE, .open = gpio_user_open, .release = gpio_user_release, .unlocked_ioctl = gpio_user_ioctl, }; static long gpio_user_ioctl(struct file *filp, unsigned int cmd,unsigned long arg) { int no, offset; unsigned long val; unsigned long __user *p = (void __user *)arg; struct gpio_user_data *data; unsigned long get_value; if(! gpio_misc) return -ENODEV; data = gpio_misc->data; if(_IOC_TYPE(cmd) ! = GPIO_U_IOCTL_BASE) return -EINVAL; switch(_IOC_NR(cmd)) { case 0: if(get_user(val,p)) return -EFAULT; no = val & (~(1u << 31)); ------------------>(1) if(data[no].input) { get_value = gpio_get_value(data[no].gpio); ------------------>(2) printk("get_value is %d\n", get_value); offset = data[no].gpio % 32; val = get_value >> offset; printk("val is %d\n",val); put_user(val,p); } else { gpio_set_value(data[no].gpio,val >> 31); ------------------>(3) } break; default: return -ENOTTY; } return 0; }Copy the code

First, an IOCtl operation command is defined with _IOWR :GPIOC_OPS. For details on how to use _IOWR, see the article.

Here is a brief introduction to the implementation steps:

  • (1) Check the validity of the IOCTL control command and obtain the GPIO number;
  • (2) If it is a GPIO in input mode, read the current value of GPIO and return the status to the user program;
  • (3) If it is gPIO in output mode, set the gPIO output value;

The above is the code implementation of the GPIO driver. The complete code can be downloaded here.

2.3 GPIO test program

The GPIO test program uses the device file (/dev/ GPIO) to configure the GPIO output state and read the GPIO input state. The code can be downloaded here.

3. Changes of GPIO subsystem

For changes to the GPIO subsystem, please refer to the following article, to be continued… .