How to control a GPIO in kernel space

1. Purpose[edit source]

This article gives an example of a driver that controls GPIOs from kernel space.
Sample source files are provided as examples: kernel module (driver), device tree and Makefile.
This example is available for STM32MP15_Evaluation_boards or STM32MP15_Discovery_kits

2. Code[edit source]

2.1. Objective[edit source]

Sample gpiolib usage code that makes an LED blink for 20 seconds.

2.2. Device tree[edit source]

dummy_device {
   compatible = "st,dummy";
   status = "okay";
   greenled-gpios = <&gpioa 14 0>;
};

See GPIO_device_tree_configuration for more details of GPIO use in a device tree.

2.3. Kernel module code[edit source]

#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>

struct gpio_desc *red, *green;

static int gpio_init_probe(struct platform_device *pdev)
{
   int i = 0;

   printk("GPIO example init\n");

   /* "greenled" label is matching the device tree declaration. OUT_LOW is the value at init */
   green = devm_gpiod_get(&pdev->dev, "greenled", GPIOD_OUT_LOW);

   /* blink of the green led */	
   while (i < 10)
   {
	ssleep(1);
	gpiod_set_value(green, 1);

	ssleep(1);
	gpiod_set_value(green, 0);
	
	i++;
   }

   return(0);
}

static int gpio_exit_remove(struct platform_device *pdev)
{
   printk("GPIO example exit\n");
   
   return(0);
}

/* this structure does the matching with the device tree */
/* if it does not match the compatible field of DT, nothing happens */
static struct of_device_id dummy_match[] = {
    {.compatible = "st,dummy"},
    {/* end node */}
};

static struct platform_driver dummy_driver = {
    .probe = gpio_init_probe,
    .remove = gpio_exit_remove,
    .driver = {
        .name = "dummy_driver",
                .owner = THIS_MODULE,
                .of_match_table = dummy_match,
    }
};
 
module_platform_driver(dummy_driver);

MODULE_AUTHOR("Bernard Puel");
MODULE_DESCRIPTION("Gpio example");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:dummy_driver");

2.4. Kernel module build[edit source]

See Adding_external_out-of-tree_Linux_kernel_modules for further information on module compilation.


2.5. Kernel module use[edit source]

 scp dummy_driver.ko root@<board ip address>:/lib/modules/
  • Update dependency descriptions for loadable kernel modules, and synchronize the data on disk with memory
 /sbin/depmod -a
 sync
  • Insert the kernel module example into the Linux kernel
 modprobe dummy_driver
[18167.821725] dummy_driver: GPIO example init


3. A simple example which initializes a GPIO with a pull-up configuration[edit source]

3.1. Adding nodes in the Device Tree[edit source]

  • Copy these two following nodes into the board dts file:

workspace/sources/linux-stm32mp/arm/boot/dts/stm32mp157-dk2.dts

/ {
   
       ledpa14  {
             compatible = "st,dummypu";
                        pinctrl-names = "default";
                        pinctrl-0 = <&keyleds_pins_a>;
                        status = "okay";
       };


}

&pinctrl {

        keyleds_pins_a: keyleds_pins_a-0 {
                pins {
                    pinmux = <STM32_PINMUX('A', 14, GPIO)>;
                                  bias-pull-up;
                                  drive-push-pull;
                                  slew-rate = <0>;
                 };
        };
};
  • Create a second dummy driver (dummy_driver_pu.c) since PA14 toggling is already used by "dummy_driver"

dummy_driver_pu.c

#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>


static int gpio_init_probe(struct platform_device *pdev)
{

   printk("GPIO setting pu init\n");


   return(0);
}

static int gpio_exit_remove(struct platform_device *pdev)
{
   printk("GPIO setting pu exit\n");
   
   return(0);
}

/* this structure does the matching with the device tree */
/* if it does not match the compatible field of DT, nothing happens */
static struct of_device_id dummy_match[] = {
    {.compatible = "st,dummypu"},
    {/* end node */}
};

static struct platform_driver dummy_driver_pu = {
    .probe = gpio_init_probe,
    .remove = gpio_exit_remove,
    .driver = {
        .name = "dummy_driver_pu",
                .owner = THIS_MODULE,
                .of_match_table = dummy_match,
    }
};
 
module_platform_driver(dummy_driver_pu);

MODULE_AUTHOR("OK");
MODULE_DESCRIPTION("Gpio pu");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:dummy_driver_pu");

3.2. Interacting on the board[edit source]

  • Read the GPIO pin PA14 default setting configuration
root@stm32mp1:reboot
root@stm32mp1:~# cat /sys/kernel/debug/pinctrl/soc\:pin-controller@50002000/pinconf-pins | grep PA14         
pin 14 (PA14): input - high - floating
  • Describe the use of pins
root@stm32mp1:~# gpioinfo gpiochip0 | grep 14 
line  14:      unnamed       unused   input  active-high
  • Enable the green LED on the board (control of GPIO in userspace, gpio switched to output pushpull)
root@stm32mp1:~# gpioset gpiochip0 14=0
root@stm32mp1:~# cat /sys/kernel/debug/pinctrl/soc\:pin-controller@50002000/pinconf-pins | grep PA14
pin 14 (PA14): output - low - push pull - floating - low speed
root@stm32mp1:~# gpioinfo gpiochip0 | grep 14
line  14:      unnamed       unused  output  active-high
  • Now let's probe the dummy driver to enable the pullup on PA14 :
root@stm32mp1:~# modprobe dummy_driver_pu
[ 1598.403140] GPIO setting pu init  
root@stm32mp1:~# cat /sys/kernel/debug/pinctrl/soc\:pin-controller@50002000/pinconf-pins | grep PA14
pin 14 (PA14): input - high - pull up 
root@stm32mp1:~# gpioinfo gpiochip0 | grep 14
line  14:      unnamed       unused  output  active-high
  • Enable green led again (control of GPIO in userspace, check that the pullup config is still there)
root@stm32mp1:~# gpioset gpiochip0 14=0
root@stm32mp1:~# cat /sys/kernel/debug/pinctrl/soc\:pin-controller@50002000/pinconf-pins | grep PA14                                                        
pin 14 (PA14): output - low - push pull - pull up - low speed