wiki:gpio

Version 16 (modified by Tim Harvey, 5 years ago) ( diff )

add details about Linux gpio character device

General Purpose I/O

General Purpose I/O or GPIO refers to a digital signal from a processor or peripheral capable of sinking or sourcing current.

There are several details that are important when dealing with GPIOs:

  • voltage they drive to or are pulled up to
  • is there a pull-up or pull-down present
  • are they driving to a active-high or active-low state (totem-pole) or are they driving low and relying on a pull-up to go high (open collector)
  • how much current can they sink
  • how much current can they source
  • what bus is used to access them (which can affect when/where and how-fast you can read or write them)
  • does the gpio have an associated interrupt available (important if you want to use this as a low-overhead input)

Gateworks boards use General Purpose Input / Output (GPIO) pins commonly for things such as:

  • enabling/disabling of devices (ie serial drivers)
  • signal steering (ie steer USB bus between front-panel connector/header and PCI/PCIe slot)
  • device interrupt inputs (ie GSC host interrupt)
  • misc inputs (ie GPS PPS (pulse-per-second) input)
  • LED drivers (these are handled via led-class and not gpio-class)
  • pushbutton inputs (often handled via button-class and not gpio-class)

All off-board GPIO signals use 3.3V TTL voltage levels.

See the following pages for hardware-specific details and capabilities of the off-board digital I/O pins on the various Gateworks product families:

Power-on GPIO states

Usually these signals are used in such a way that the default power-on state requires no configuration of GPIO signals (ie, the power up state matches the desired power-on function).

As customer needs vary, power-on GPIO states may vary. There are a few options for configuring GPIO's that need to be changed from the 'hardware default':

  • bootloader configuration (occurs within milliseconds of board power-up)
  • kernel configuration (occurs within seconds of board power-up)
  • userspace configuration (occurs within 10s of seconds of board power-up)
    • typically this would be done inside of an /etc/rc.local script or other init script. See OpenWrt/init and Yocto/services for more info.

Each of these options requires a different level of software complexity.

Fast vs Slow GPIO

A fast GPIO is one that is directly on the ARM SoC and requires memory mapped register reads and writes to access.

A slow GPIO is one that is provided by a peripheral device that hangs off the SoC on I2C, SPI, USB, or other type of bus that requires complicated bus access mechanisms. These GPIO's are also defined by the linux kernel as GPIO's that 'can sleep' and are not suitable for certain applications that require low latency or deterministic access times in the Linux kernel or in your application usage.

Gateworks products offer a bit of variety:

  • all GPIO's provided by the Gateworks System Controller (including the user pushbutton) are over I2C are slow gpio
  • Ventana boards have up to 4x DIO's to a connector that are ARM based fast GPIO's
  • GPIO's going to the miniPCI and miniPCIe connectors such as WDIS# and PERST# are ARM based fast GPIO's

You can look at the output of /sys/kernel/debug/gpio to see what GPIO's are available and from what controller. The controller name will indicate if its an I2C controller or not.

Linux GPIO

Click here for an in depth introduction to Linux GPIO

chardev GPIO

The GPIO character device ABI (Application Binary Interface) was introduced in Linux 4.8 (which subsequently marked the User-mode sysfsgpio method deprecated). This is a 'descriptor-based' character device exposed as /dev/gpiochipN or /sys/bus/gpiochipN where N is the chip number.

The main feature of this new interface is a discovery mechanism which aids in having to figure out where a particular GPIO was mapped. It also supports open-drain I/O support and the ability to read and set multiple I/O lines at once. The downside to this interface is that it prevents manipulating GPIO with standard command line tools such as echo and cat.

Fortunately the kernel is distributed with three basic user-mode tools written for testing the interface which also show examples of the API. These can be found in the kernel source tools/gpio:

  • lsgpio - example showing how to list the GPIO lines on a system
  • gpio-event-mon - example showing how to monitor GPIO line events from userspace
  • gpio-hammer - example swiss army knife to wiggle GPIO lines

Additionally libgpiod provides both API calls for use in your own programs and the following user-mode apps to manipulate GPIO lines:

  • gpiodetect - list all gpiochips present on the system, their names, labels and number of GPIO lines
  • gpioinfo - list all lines of specified gpiochips, their names, consumers, direction, active state and additional flags
  • gpioget - read values of specified GPIO lines
  • gpioset - set values of specified GPIO lines, potentially keep the lines exported and wait until timeout, user input or signal
  • gpiofind - find the gpiochip name and line offset given the line name
  • gpiomon - wait for events on GPIO lines, specify which events to watch, how many events to process before exiting or if the events should be reported to the console

On Ubuntu libgpiod and its apps can be isntalled via the gpiod package:

apt install gpiod

Examples:

  • Newport
    root@bionic-newport:~# apt install gpiod
    ...
    root@bionic-newport:~# gpiodetect
    gpiochip0 [gpio_thunderx] (48 lines)
    gpiochip1 [pca9555] (16 lines)
    root@bionic-newport:~# gpioinfo gpio_thunderx
    gpiochip0 - 48 lines:
            line   0:      unnamed       unused   input  active-high
            line   1:      unnamed       unused   input  active-high
            line   2:      unnamed       unused   input  active-high
            line   3:      unnamed       unused   input  active-high
            line   4:      unnamed  "interrupt"   input  active-high [used]
            line   5:      unnamed       unused   input  active-high
            line   6:      unnamed       unused   input  active-high
            line   7:      unnamed       unused   input  active-high
            line   8:      unnamed "mmc_supply_3v3" output active-high [used]
            line   9:      unnamed       unused   input  active-high
            line  10:      unnamed       unused  output  active-high
            line  11:      unnamed       unused   input  active-high
            line  12:      unnamed       unused   input  active-high
            line  13:      unnamed       unused   input  active-high
            line  14:      unnamed      "user2"  output  active-high [used]
            line  15:      unnamed       unused  output  active-high
            line  16:      unnamed       unused  output  active-high
            line  17:      unnamed       unused  output  active-high
            line  18:      unnamed       unused  output  active-high
            line  19:      unnamed       unused  output  active-high
            line  20:      unnamed       unused   input  active-high
            line  21:      unnamed       unused   input  active-high
            line  22:      unnamed       unused   input  active-high
            line  23:      unnamed       unused  output  active-high
            line  24:      unnamed       unused   input  active-high
            line  25:      unnamed       unused   input  active-high
            line  26:      unnamed       unused   input  active-high
            line  27:      unnamed       unused   input  active-high
            line  28:      unnamed       unused  output  active-high
            line  29:      unnamed       unused   input  active-high
            line  30:      unnamed   "pps-gpio"   input  active-high [used]
            line  31:      unnamed      "user1"  output  active-high [used]
            line  32:      unnamed       unused   input  active-high
            line  33:      unnamed       unused   input  active-high
            line  34:      unnamed       unused   input  active-high
            line  35:      unnamed       unused   input  active-high
            line  36:      unnamed       unused   input  active-high
            line  37:      unnamed       unused   input  active-high
            line  38:      unnamed       unused   input  active-high
            line  39:      unnamed       unused   input  active-high
            line  40:      unnamed       unused   input  active-high
            line  41:      unnamed       unused   input  active-high
            line  42:      unnamed       unused   input  active-high
            line  43:      unnamed       unused   input  active-high
            line  44:      unnamed       unused   input  active-high
            line  45:      unnamed       unused   input  active-high
            line  46:      unnamed       unused   input  active-high
            line  47:      unnamed       unused   input  active-high
    root@bionic-newport:~# gpioget gpio_thunderx 24 # read gpio24 (DIO0)
    1
    root@bionic-newport:~# gpioset gpio_thunderx 24=0 # drive low gpio24 (DIO0)
    

References:

User-mode GPIO class

The Linux kernel can provide gpio functionality to userspace via a gpio class accessed via sysfs (/sys/class/gpio/) implemented through gpiolib (CONFIG_GPIOLIB, drivers/gpio/gpiolib*). For userspace access this requires sysfs kernel support (CONFIG_SYSFS) and the sysfs filesystem mounted which is standard for most linux systems including the Gateworks BSP's. This framework allows boards to define GPIO configuration (direction, direction-changeable, user-friendly name, state, and userspace configurable).

Note that the 'sysfsgpio' API has been marked deprecated in Linux 4.8 as the new preferred API is the

Note that any gpio that is registered for use by a driver is not available for exporting for userspace control. You can cat /sys/kernel/debug/gpio to see what gpio's are current registered with the kernel.

Here are some example use cases for using gpio-class via sysfs:

  • to see gpio names, config (direction), and current state: (requires debugfs kernel support and fs mounted)
    cat /sys/kernel/debug/gpio
    
  • to export a GPIO that is available to userspace (ie provided by a gpio controller, not in-use by a kernel driver, and not already exported):
    echo 5 > /sys/class/gpio/export # export gpio5 (will create /sys/class/gpio/gpio5 and configure as input)
    echo out > /sys/class/gpio/gpio5/direction # make it an output
    echo 1 > /sys/class/gpio/gpio5/value # set its value to high
    
  • to see the direction of a gpio via sysfs (only allowed for GPIO's configured as bi-directional)
    cat /sys/class/gpio/gpio5/direction  ;# in/out
    
  • to set the direction of a gpio via sysfs (only allowed for GPIO's configured as bi-directional)
    echo out > /sys/class/gpio/gpio5/direction ;# set as output
    echo in > /sys/class/gpio/gpio5/direction ;# set as input
    
  • to see the state of a gpio via sysfs
    cat /sys/class/gpio/gpio5/value  ;# show current state (0=low, any other value than 0=high)
    
  • to set the state of a gpio via sysfs
    echo 0 > /sys/class/gpio/gpio5/value  ;# assert low
    echo 1 > /sys/class/gpio/gpio5/value  ;# assert high
    

GPIO mapping

The various product hardware manuals will call out GPIO devices on the board and provide a table mapping the GPIO to a GPIO host controller, a function, and a connector pinout (if going off-board).

This hardware mapping is defined in the following files depending on board/family:

Product Family specific notes:

  • Newport:
    • CN80XX GPIOs: The CN80XX has one 48bit GPIO controller
      • GPIO[0-48]: gpio-464 to gpio-511
  • Ventana:
    • IMX6 GPIOs: The IMX6 has 7 32bit GPIO controllers for a possible 224 fast ARM based GPIO's
      • GPIO1_IO[0-31]: gpio0-gpio31
      • GPIO2_IO[0-31]: gpio32-gpio63
      • GPIO3_IO[0-31]: gpio64-gpio95
      • GPIO4_IO[0-31]: gpio96-gpio127
      • GPIO5_IO[0-31]: gpio128-gpio159
      • GPIO6_IO[0-31]: gpio160-gpio191
      • GPIO7_IO[0-31]: gpio192-gpio223
    • GSC GPIOs:
      • GSC_GPIO_P0[0-7]: gpio240-gpio247
      • GSC_GPIO_P1[0-7]: gpio247-gpio255
    • additional i2c based GPIO port-expanders present on some boards would begin at gpio116
  • Laguna:
    • cns3xxx GPIOs: The CNS3xxx has 2 32bit GPIO controllers for a possible 64 fast ARM based GPIO's
      • GPIOA[0-31]: gpio0-gpio31
      • GPIOB[0-31]: gpio32-gpio63
    • GSC GPIOs:
      • GSC_GPIO_P0[0-7]: gpio100-gpio107
      • GSC_GPIO_P1[0-7]: gpio108-gpio115
    • additional i2c based GPIO port-expanders present on some boards would begin at gpio116

Note that the Linux gpio mapping is not guaranteed to remain constant across kernel versions. Care should be taken to consult /sys/kernel/debug/gpio and /sys/class/gpio when changing kernel versions.

When referring to Gateworks board user hardware manuals you will need to translate the hardware pin name to a gpio using the information above. Some examples:

  • Newport GPIO8 would be the 8th IO on the CN80XX GPIO controller thus map to gpio-472.
  • Laguna GPIOA8 would be the 9th IO on the GPIOA controller (the first is GPIOA0) thus map to gpio 0+8 = gpio-8
  • Laguna GPIOB1 would be the 2nd IO on the GPIOB controller thus map to gpio 32+2 = gpio-34
  • Ventana GPIO[1]:DIO16 is GPIO block 1 IO 16 thus maps to gpio0 + 16 (or 0*32+16) = gpio-16
  • Ventana GPIO[2]:DIO9 is GPIO block 2 IO 9 thus maps to gpio32 + 9 (or 1*32+9) = gpio-41
  • Equation: Ventana GPIO<x>IO<y> is GPIO block x IO y thus maps to: gpio-((x-1)*32)+y

See also:

Examples

If debugfs is enabled in the kernel and mounted (as it is on Gateworks BSP's) you can see a nice table of GPIO mapping via /sys/kernel/debug/gpio. Some examples:

  • Newport GW6400:
    # cat /sys/kernel/debug/gpio 
    gpiochip1: GPIOs 448-463, parent: i2c/0-0023, pca9555, can sleep:
    
    gpiochip0: GPIOs 464-511, parent: pci/0000:00:06.0, gpio_thunderx:
     gpio-472 (                    |mmc_supply_3v3      ) out hi    
     gpio-478 (                    |user2               ) out lo    
     gpio-495 (                    |user1               ) out lo   
    
  • Ventana GW5400:
    # cat /sys/kernel/debug/gpio 
    GPIOs 0-31, platform/209c000.gpio, 209c000.gpio:
     gpio-26  (pps-gpio            ) in  lo    
     gpio-29  (PCIe reset          ) out lo    
     gpio-30  (phy-reset           ) out hi    
    
    GPIOs 32-63, platform/20a0000.gpio, 20a0000.gpio:
    
    GPIOs 64-95, platform/20a4000.gpio, 20a4000.gpio:
     gpio-86  (usb_otg_vbus        ) out lo    
    
    GPIOs 96-127, platform/20a8000.gpio, 20a8000.gpio:
     gpio-102 (user1               ) out lo    
     gpio-103 (user2               ) out lo    
     gpio-111 (user3               ) out lo    
    
    GPIOs 128-159, platform/20ac000.gpio, 20ac000.gpio:
    
    GPIOs 160-191, platform/20b0000.gpio, 20b0000.gpio:
    
    GPIOs 192-223, platform/20b4000.gpio, 20b4000.gpio:
     gpio-192 (2198000.usdhc cd    ) in  hi    
    
    GPIOs 240-255, i2c/0-0023, pca9555, can sleep:
     gpio-240 (user_pb             ) in  hi 
    
    • Note that this does not show GPIO's that are not used by Linux drivers such as USB HUB reset, Ethernet PHY reset, etc.
  • Laguna GW2388:
    # cat /sys/kernel/debug/gpio 
    GPIOs 0-31, cns3xxx_gpio0:
     gpio-0   (GPS_PPS             ) in  hi
     gpio-3   (USB_FAULT#          ) in  hi
     gpio-6   (USB_VBUS_EN         ) out hi
     gpio-7   (GSM_SEL0            ) out lo
     gpio-8   (GSM_SEL1            ) out lo
     gpio-9   (FP_SER_EN           ) out lo
    
    GPIOs 32-63, cns3xxx_gpio1:
    
    GPIOs 100-115, i2c/0-0023, pca9555, can sleep:
     gpio-100 (USER_PB#            ) in  hi
     gpio-114 (user2               ) out hi
     gpio-115 (user1               ) out hi
    
  • Laguna GW2387:
    # cat /sys/kernel/debug/gpio 
    GPIOs 0-31, cns3xxx_gpio0:
     gpio-0   (GPS_PPS             ) in  hi
     gpio-2   (USB_FAULT#          ) in  hi
     gpio-5   (USB_PCI_SEL         ) out lo
     gpio-6   (USB_VBUS_EN         ) out hi
     gpio-7   (GSM_SEL0            ) out lo
     gpio-8   (GSM_SEL1            ) out lo
     gpio-9   (FP_SER_EN           ) out lo
    
    GPIOs 32-63, cns3xxx_gpio1:
    
    GPIOs 100-115, i2c/0-0023, pca9555, can sleep:
     gpio-100 (USER_PB#            ) in  hi
     gpio-114 (user2               ) out hi
     gpio-115 (user1               ) out hi
    

Detecting GPIO changes efficiently using interrupts

It is extremely inefficient to have an user-mode application that continually checks a GPIO to see if it has changed value as this chews up CPU resources and increases the latency in acting on a GPIO.

Fortunately it is easy to write applications that utilize interrupts as long as the GPIO has an interrupt controller and handler.

chardev GPIO

The GPIO character device API (Application Programming Interface) introduced in Linux 4.8 provides a method of efficiently monitor for interrupt based GPIO changes. This is showing in a couple of readily available examples:

Examples:

sysfsgpio

Note that the sysfsgpio ABI is deprecated as of Linux 4.8 and it is recommended you using the chardev based GPIO ABI instead

A gpio that has an interrupt associated with it (which depends on the gpio controller and how its hooked up to the processor) can be used with a blocking poll system call. This may seem non-intuitive that you use a 'poll' call to block until an interrupt has occurred, but that is exactly what will happen when you use poll on the sysfs 'value' file representing the value of the gpio in question. Note that even if you are not interested in the value files contents (e.g. counting the total number of interrupts caused by a gpio) you must use both an lseek and a read after poll to consume the interrupt. Otherwise the previous interrupt will cause future poll calls to immediately return.

Note that you can only do this with an GPIO that has interrupt support which means when its exported to userspace there will be an 'edge' file in this /sys/class/gpio directory.

Example code:

/*
 * gpio-poll.c - demonstrate catching a GPIO change event without polling
 *               ironically using the poll(2) system call
 *
 * Author: Tim Harvey <tharvey@gateworks.com>
 */

#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifdef __ANDROID__
#include <cutils/android_reboot.h>
#else
#include <sys/reboot.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>


int main(int argc, char **argv)
{
        struct pollfd fdset;
        char path[256];
        char buf[32];
        int gpio;
        int fd, rz, c;

        if (argc < 3) {
                fprintf(stderr, "usage: %s <gpio> <rising|falling|both>\n",
                        argv[0]);
                exit (-1);
        }

        gpio = atoi(argv[1]);

        /* configure gpio trigger:
         *   edge trigger of 'both' (falling and rising) allows catching
         *   changes in both directions vs level triggerd.
         */
        sprintf(path, "/sys/class/gpio/gpio%d/edge", gpio);
        if ((fd = open(path, O_WRONLY)) < 0) {
                perror("open() failed\n");
                fprintf(stderr, "non-exported or non-input gpio: %s\n", path);
                exit (-1);
        }
        write(fd, argv[2], strlen(argv[2]));
        close(fd);

        /* open gpio sysfs value for reading in blocking mode */
        sprintf(path, "/sys/class/gpio/gpio%d/value", gpio);
        if ((fd = open(path, O_RDONLY | O_NONBLOCK)) < 0) {
                perror("open() failed\n");
                fprintf(stderr, "invalid or non-exported gpio: %s\n", path);
                exit (-1);
        }

        printf("monitoring %s for interrupt using poll()\n", path);
        while (1) {
                /* use poll(2) to block for 10s or until interrupt occurs */
                fdset.fd = fd;
                fdset.events = POLLPRI;
                fdset.revents = 0;
                if ((c = poll(&fdset, 1, -1)) < 0) {
                        perror("poll() failed");
                        break;
                }

                /* show gpio value */
                /* Note that both an lseek and read are necessary
                 * in order to clear the interrupt detected by poll
                 */
                usleep (100000); /*100ms delay to debounce*/
                lseek(fd, 0, SEEK_SET);
                rz = read(fd, buf, sizeof(buf));
                buf[rz ? rz - 1 : 0] = 0;
		printf("%d: ret=%d gpio%d=%d rz=%d\n", (int)time(NULL),c, gpio, atoi(buf),
					rz);
        }
        close(fd);

        return 0;
}

Example usage (block until interrupt rising and falling edge interrupt occurs on gpio-100):

root@OpenWrt:/# ./gpio-poll 100 both
monitoring /sys/class/gpio/gpio100/value for interrupt using poll()
gpio100: 1
gpio100: 0
gpio100: 1
gpio100: 0
gpio100: 1

using GPIO's as buttons in Linux

Often users want to use a GPIO as a button. The best way to do this in Linux is to use the gpio-keys (KEYBOARD_GPIO) or gpio-keys-polled (KEYBOARD_GPIO_POLLED) Linux input driver which allows you to assign Linux gpio's to Linux input key events which will be fired when the button is pressed and released.

The gpio-keys driver is for gpio's that are interrupt capable and the gpio-keys-polled driver is for gpio's that are not interrupt capable and thus must be polled periodically.

As an example of how to configure the gpio-keys driver we can look at the GW51xx which has DIO0-DIO4 mapped to ARM gpio's. The imx6qdl-gw51xx.dtsi device-tree can have the following section added:

       /* define linux keyboard gpio-keys driver named 'gpio-button' */
       gpio: gpio-button {
               compatible = "gpio-keys";
               #address-cells = <1>;
               #size-cells = <0>;
               autorepeat;

               /* map GW51xx DIO0 IMX6 GPIO1_IO16 to linux keycode 103 (KEY_UP) */
               button@1 {
                        label = "GPIO Key Up";
                        linux,code = <103>;
                        debounce-interval = <100>;
                        gpios = <&gpio1 16 GPIO_ACTIVE_LOW>;
               };

               /* map GW51xx DIO1 IMX6 GPIO1__IO19 to linux keycode 109 (KEY_DOWN) */
               button@2 {
                        label = "GPIO Key Down";
                        linux,code = <108>;
                        debounce-interval = <100>;
                        gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
               };

               /* map GW51xx DIO2 IMX6 GPIO1__IO17 to linux keycode 106 (KEY_RIGHT) */
               button@3 {
                        label = "GPIO Key Right";
                        linux,code = <106>;
                        debounce-interval = <100>;
                        gpios = <&gpio1 17 GPIO_ACTIVE_LOW>;
               };

               /* map GW51xx DIO3 IMX6 GPIO1__IO18 to linux keycode 105 (KEY_LEFT) */
               button@4 {
                        label = "GPIO Key Left";
                        linux,code = <105>;
                        debounce-interval = <100>;
                        gpios = <&gpio1 18 GPIO_ACTIVE_LOW>;
               };
        };

  • The gpio node needs to be at the top level and its placement is arbitrary however its customary to put it in alphabetical order (thus between the chosen node and the leds node)

BSP Specific notes:

  • OpenWrt replaces the gpio-keys and gpio-keys-polled Linux driver with their out-of-tree driver called gpio-button-hotplug which instead of emiting linux input events emits uevent messages to the button subsystem which tie into the OpenWrt hotplug daemon. See the OpenWrt/gpio page for more details.
  • Android can map Linux input events to Android key events via a KeyLayout file - see here for details.

Double edge trigger

OcteonTX GPIO's support double edge trigger, this will create an IRQ on both the leading and falling edge of a signal.

Here is an example using a GW6304.

Create an input GPIO and "high" GPIO, these are CPU-DIO's:

Connect Push button normally open (push-to-make switch) between leads attached to J13 pin 1 and 2.

#Create double edge input GPIO
echo 488 > /sys/class/gpio/export
echo in > /sys/class/gpio/gpio488/direction
echo both > /sys/class/gpio/gpio488/edge 
#Create "high" GPIO
echo 489 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio489/direction
echo 1 > /sys/class/gpio/gpio489/value 

Watch IRQ events:

watch -n 0.5 cat /proc/interrupts 

Look for "GPIO 24 Edge" IRQ count increase on both edges of signal.

References

Linux LED class for GPIO (and PWM) controlled LEDs

see wiki:linux/led

Other References

Here are some other useful references from our wiki:

Attachments (2)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.