wiki:gpio

Version 29 (modified by Tim Harvey, 3 years ago) ( diff )

clarify the behavior of gpioset

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.

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

Examples:

  • If libgpiod is installed (gpiod package in Ubuntu) the gpioinfo app will show you all gpio controllers and details about the GPIO lines each one offers. Note that using the Linux GPIO character device API and/or libgpiod is the preferred way to access GPIO in userspace:
    root@bionic-newport:~/libgpiod# gpioinfo
    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 
    gpiochip1 - 16 lines:
            line   0:      unnamed       unused  output  active-high 
            line   1:      unnamed       unused   input  active-high 
            line   2:      unnamed    "user_pb"   input   active-low [used]
            line   3:      unnamed       unused   input  active-high 
            line   4:      unnamed       unused   input  active-high 
            line   5:      unnamed       unused   input  active-high 
            line   6:      unnamed       unused   input  active-high 
            line   7:      unnamed       unused   input  active-high 
            line   8:      unnamed       unused   input  active-high 
            line   9:      unnamed       unused   input  active-high 
            line  10:      unnamed       unused   input  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       unused   input  active-high 
            line  15:      unnamed       unused   input  active-high 
    
  • If debugfs is enabled in the kernel and mounted (as it is on Gateworks BSP's) you can see a nice table of sysfsgpio 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
      

See also:

GPIO performance

GPIO performance in terms of latency and frequency at which you can toggle them depends on the GPIO Controller (ie IMX6 SoC ARM GPIO, OcteonTX SoC ARM GPIO, PCA9555 I2C GPIO, FTDI USB UART GPIO) as well as the API used to direct them (ie gpio chardev, sysfsgpio, or direct memory registers in the case of SoC GPIO controllers).

Here is a table showing some max frequencies that GPIO's can be toggled across different platforms:

Controller API Speed Notes
IMX6DL@800Mhz ARM GPIO sysfsgpio 46.3kHz
chardev 140kHz
registers 870kHz
IMX6DL@800MHz FTDI GPIO sysfsgpio 1.0kHz GPIO over USB
chardev 1.33kHz GPIO over USB
IMX6Q@1000Mhz ARM GPIO sysfsgpio 73kHz
chardev 255kHz
registers 805kHz not clear why this was slower than IMX6DL@800MHz
OcteonTX-Dual@800MHz ARM GPIO sysfsgpio 200kHz 4x faster than IMX6DL@800Mhz
chardev 580MHz 4x faster than IMX6DL@800Mhz
OcteonTX-Quad@1500MHz ARM GPIO sysfsgpio 375kHz
chardev 1.08MHz 2.8X faster than
OcteonTX-Quad@1500MHz FTDI GPIO sysfsgpio 5.4kHz GPIO over USB
chardev 6.2kHz GPIO over USB
  • The above tests were changing the output value of a GPIO between 0 and 1 (not evaluating time it took to change between input/output direction)
  • Note that the FT231X and FT232H were evaluated for FTDI gpio and both performed identically (despite the FT231X being a USB full speed device and the FT232H being a USB high speed device)
  • Note that same speed OcteonTX SoC GPIO performs ~4x faster than same speed IMX6
  • Note that GPIO chardev is the recomended kernel API for GPIO as sysfsgpio is now deprecated
  • GPIO performance over I2C (ie PCA9555) was not evaluated

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 installed via the gpiod package:

apt install gpiod

Note that gpioset (and all libgpiod apps) will revert the state of a GPIO line back to its original value when it exits. For this reason if you want the state to persist you need to instruct gpioset to wait for a signal and optionally detach and run in the background.

Examples:

  • Venice; use gpiofind/gpioget/gpioset on the GPIO named 'pci_wdis#'
    # gpiofind pci_wdis#
    gpiochip3 7
    # gpioget gpiochip3 7
    1
    # gpioset --mode=signal --background gpiochip 3 7=0
    
    • Note that once gpioset exits its process the GPIO's it changed will revert back to their prior state (by design) therefor we run it above with --mode=signal telling it to continue to run into it receives a signal and (optinally) --background telling it to detach from the controlling terminal to give our shell back.
  • 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 chardev GPIO API

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 determine the Linux GPIO for a specific offset of a controller you can look for GPIO controller devices that match the bus address of the controller you are interested in. For example if you are trying to find the Linux gpio for the 3rd gpio of an i2c based GPIO controller on i2c0@0x23:
    bus="0-0023"
    for i in $(ls -1d /sys/class/gpio/gpiochip*); do \
         [ "$1" = "$(basename $(readlink $i/device))" ] && base=$(cat $i/base);
    done
    gpio=$((base+3))
    
  • 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
    

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.

Note that early revision Newport boards did not have on-board pull-ups on the off-board DIO's thus they would need to be provided off-board - see newport/DigitalIO for details

References

U-Boot GPIO

The U-Boot bootloader has basic support for GPIO's associated with a driver (such as processor GPIO's):

Examples:

  • Show GPIO's
    u-boot=> gpio status
    Bank GPIO1_:
    GPIO1_0: output: 0 [x] rs485_term.gpio-hog
    GPIO1_1: input: 0 [x] mipi_gpio4.gpio-hog
    GPIO1_6: output: 0 [x] pci_usb_sel.gpio-hog
    GPIO1_7: input: 1 [x] dio0.gpio-hog
    GPIO1_9: input: 1 [x] dio1.gpio-hog
    
    Bank GPIO2_:
    GPIO2_12: input: 1 [x] mmc@30b50000.cd-gpios
    
    Bank GPIO3_:
    GPIO3_0: output: 0 [x] ethernet@30be0000.phy-reset-gpios
    
    Bank GPIO4_:
    GPIO4_0: output: 0 [x] rs485_en.gpio-hog
    GPIO4_1: input: 0 [x] mipi_gpio3.gpio-hog
    GPIO4_2: output: 0 [x] rs485_hd.gpio-hog
    GPIO4_3: input: 0 [x] mipi_gpio2.gpio-hog
    GPIO4_4: input: 0 [x] mipi_gpio1.gpio-hog
    GPIO4_7: output: 1 [x] pci_wdis#.gpio-hog
    
    Bank GPIO5_:
    GPIO5_4: output: 0 [x] led-1.gpios
    GPIO5_5: output: 0 [x] led-0.gpios
    
  • show status of a particular GPIO (using the gpio id reported above)
    u-boot=> gpio status GPIO4_7
    Bank GPIO4_:
    GPIO4_7: output: 1 [x] pci_wdis#.gpio-hog
    u-boot=> gpio status GPIO1_7
    Bank GPIO1_:
    GPIO1_7: input: 1 [x] dio0.gpio-hog
    u-boot=> 
    
    • above you can see that GPIO4_7 (the 'pci_wdis#' gpio on this board) is an output and currently driven high
    • above you can see that GPIO1_7 (the 'dio0' gpio on this board) is an input and its current value is high
    • note that a 0 always represents a voltage low and 1 always represents a voltage high regardless of if the gpio is considered 'active high' or 'active low' (which is typically indicated by the GPIO name as active low gpios typically end with a '#')
  • set a gpio to output low (clr)
    u-boot=> gpio clr GPIO4_7    
    gpio: pin GPIO4_7 (gpio 103) value is 0
    
  • set a gpio to output high (set)
    u-boot=> gpio set GPIO4_7
    gpio: pin GPIO4_7 (gpio 103) value is 1
    
  • toggle gpio:
    u-boot=> gpio toggle GPIO4_7
    gpio: pin GPIO4_7 (gpio 103) value is 0
    u-boot=> gpio toggle GPIO4_7
    gpio: pin GPIO4_7 (gpio 103) value is 1
    

The value specified to the vaious gpio commands can be one of:

  • bank name (ie GPIO4_ above) to represent all GPIO's in that bank
  • pin name (ie GPIO4_7 above) to represent a specific gpio
  • pin number (ie 103) to represent a specific gpio. The pin number notation is not recommended as it depends on GPIO controllers being registered in the same order which may not always be the case. It is always better to refer to the bank and pin name/notation.

Note that on some platforms GPIO's can not be read back unless configured a specific way. This is the case on IMX boards where GPIO cells need to have the 'Software Input On' (SION) bit enabled in order to read it back. If you have such as GPIO you can still drive it high but you will get a wraning such as 'Warning: value of pin is still 0'. Additinally the 'toggle' won't function for these GPIO's as the software can't determine what state they are.

Notable GPIO's on Gateworks boards

Gateworks boards use GPIO control for various things including:

  • General Purpose off-board GPIO/PWM
  • on-board LED's
  • Various enables for add-in cards

GPIO for LED's

While SoC based GPIO's are often used for LED's on various Gateworks boards, there is a Linux gpio-led driver which has a sysfs interface for manipulating the LED's in Linux:

W_DISABLE#

The W_DISABLE# pin (aka WLAN_DIS#) is a pin on miniPCIe cards that if implemented disable radio operation according to the spec. In reality some radios use this as a GPIO that a radio driver can read and certain devices such as the WLE900VX ath10k 802.11 WiFi radio will not even enumerate on the PCIe bus if this is driven low.

Board GPIO pin/cont GPIO offset Linux GPIO Notes
GW700x GPIO4_IO7 GPIO4 7 gpio103 (in bootloader) gpio set 103
GW6xxx GSC P0.0 i2c/0-0023 0 gpio448 This is a GSC gpio (in bootloader) gpio set 24
GW54xx GPIO5_IO17 GPIO5 17 gpio145 ventana U-Boot drives this high by default
GW53xx GPIO7_IO12 12 gpio209 ventana U-Boot drives this high by default
GW52xx GPIO7_IO12 12 gpio209 ventana U-Boot drives this high by default
GW51xx GPIO7_IO12 12 gpio209 ventana U-Boot drives this high by default
GW551x GPIO7_IO12 12 gpio209 ventana U-Boot drives this high by default
GW552x GPIO7_IO12 12 gpio209 ventana U-Boot drives this high by default
GW553x GPIO7_IO12 12 gpio209 ventana U-Boot drives this high by default
GW5910 GPIO1_IO0 0 gpio0 ventana U-Boot drives this high by default
GW5913 GPIO1_IO0 0 gpio0 ventana U-Boot drives this high by default

In most cases shown by the table above the GPIO above will be defaulted to driven high in boot firmware or have a pull-up on the card thus typically you would only need to make a change if you want to drive the signal low to 'assert' W_DISABLE# (to disable the card's RF output).

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.