GPIO Linux Driver for Zynq and Zynq Ultrascale+ MPSoC

Introduction

The purpose of this page is to introduce two methods for interacting with GPIO from user space: the SysFs interface and the Linux kernel drivers (gpio-keys, leds-gpio).
The GPIO subsystem is documented in the kernel documentation in Documentation/gpio/.

Note: The SysFs driver has been tested and is working. It has been accepted into the mainline kernel and the old char mode GPIO driver that didn't work with arch/powerpc has been removed from the tree.

HW IP Features

ZynqMP

  • 78 GPIO signals for device pins
    • Routed through the MIO multiplexer.
    • Outputs are 3-state capable.
  • 288 GPIO signals between the PS and PL through the EMIO interface
    • 96 inputs.
    • 192 outputs (96 true outputs and 96 output enables).
  • The function of each GPIO can be dynamically programmed on an individual or group basis.
  • Enable, bit or bank data write, output enable and direction controls.
  • Programmable interrupts on individual GPIO basis
    • Status read of raw and masked interrupt.
    • Selectable sensitivity: Level-sensitive (High or Low) or edge-sensitive (positive,negative, or both).

Zynq

  • 54 GPIO signals for device pins
    • Routed through the MIO multiplexer.
    • Outputs are 3-state capable.
  • 192 GPIO signals between the PS and PL via the EMIO interface
    • 64 Inputs
    • 128 Outputs(64 true outputs and 64 output enables).
  • The function of each GPIO can be dynamically programmed on an individual or group basis.
  • Enable, bit or bank data write, output enable and direction controls.
  • Programmable interrupts on individual GPIO basis
    • Status read of raw and masked interrupt.
    • Selectable sensitivity: Level-sensitive (High or Low) or edge-sensitive (positive,negative, or both).

Known Issues and Limitations

Gpio driver have a dependency on pin-controller driver. Because of this dependency the GPIO driver probe was deferred from the 2017.1 release.
If any drivers have a dependency on GPIO driver that driver should have defer the probe.

Kernel Configuration Options for Driver

To enable GPIO in the kernel, the following configuration options need to be enabled:
CONFIG_GPIO_SYSFS=y
CONFIG_SYSFS=y
CONFIG_GPIO_ZYNQ=y

Devicetree

 gpio@e000a000 {
                #gpio-cells = <2>;
                #interrupt-cells = <2>;
                compatible = "xlnx,zynq-gpio-1.0";
                clocks = <&clkc 42>;
                gpio-controller;
                interrupt-controller;
                interrupt-parent = <&intc>;
                interrupts = <0 20 4>;
                interrupt-controller;
                #interrupt-cells = <2>;
                reg = <0xe000a000 0x1000>;
        };
 

Test procedure

SysFs Interface

The SysFs interface is a very simple way to access the GPIO from user space and has the advantage of requiring very little setup. This option is a good choice for manually checking the status of an input or writing values to outputs; this method should not be used where interrupts are required.
The SysFs interface is documented in the kernel documentation in Documentation/gpio/sysfs.txt

Using GPIO with SysFs

The GPIO driver fits in the Linux GPIO framework. It does provide access to the GPIO by user space through the sysfs filesystem. For details please refer to - the above-mentioned - documentation. The following text is aimed to augment, not replace the existing documentation.

For the examples below, there are some important points with sysfs.
  1. The GPIO controllers are visible in /sys/class/gpio. Each controller controls a number of GPIO signals.
  2. The GPIO signals must be exported into the sysfs before they can be manipulated. The number of the GPIO signal must be written to the GPIO export file to cause this to happen.
  3. After exporting the GPIO signal a new directory based on the GPIO signal number will appear in /sys/class/gpio. Under that directory are direction and value files that can be read and written.

Mounting Sysfs

The root file system being used may not have sysfs mounted and it cannot be used if it's not mounted. The directory /sys is also needed to mount the sysfs on. The root file system must be writable to do the following steps.
> mkdir /sys
> mount -t sysfs sysfs /sys

Using the GPIO driver from User Space

Figuring out the exact GPIO was not totally obvious when there were multiple GPIOs in the system. One way to do is to go into the gpiochips in /sys/class/gpio and view the label as it should reflect the address of the GPIO in the system. The name of the chip appears to be the 1st GPIO of the controller.
> root@xilinx-zcu102-2017_2:~# cd /sys/class/gpio/
  /* For ZynqMP */
> root@xilinx-zcu102-2017_2:/sys/class/gpio# ls
  export    gpiochip306    gpiochip322    gpiochip338    unexport
> root@xilinx-zcu102-2017_2:/sys/class/gpio# cat gpiochip338/label
  zynqmp_gpio
  The above line indicates that gpio 0th pin starts from 338 and ends at 511 (GPIO has total 174 pins for ZynqMP)
  /* For Zynq */
> root@xilinx-zc702-2017_2:~# cd /sys/class/gpio/
> root@xilinx-zc702-2017_2:~# ls
  export    gpiochip906    unexport
> root@xilinx-zc702-2017_2:/sys/class/gpio# cat gpiochip906/label
  zynq_gpio
  The above line indicates that gpio 0th pin starts from 906 and ends at 1023 (GPIO has total 118 pins for Zynq)

It may also be calculated ahead of time based on compile-time options for Linux. The basic formula (for Zynq) is base_gpio=ARCH_NR_GPIOS - ZYNQ_GPIO_NR_GPIOS. Then, allocated_gpios=ARCH_NR_GPIOS - base_gpio. Next, other_gpio=allocated_gpios - ZYNQ_GPIO_NR_GPIOS. Finally, gpio_offset=base_gpio + other_gpio. So, to calculate a specific GPIO number, it is base_gpio + other_gpios. This method may not be reliable permanently if ARCH_NR_GPIOS or ZYNQ_GPIO_NR_GPIOS changes without warning in the Linux kernel source code. See the Linux Kernel Mailing List for more details on the challenges with this method: https://lkml.org/lkml/2014/7/7/390

The following commands from the console setup:
  1. Export a GPIO pin
  2. Read the direction and value from the GPIO pin.
  3. Set the direction to an output and write a value 1 to GPIO pin
/* Export a GPIO pin */
> root@xilinx-zcu102-2017_2:~# echo 338 > /sys/class/gpio/export
 
/* Read the direction and value from the GPIO pin */
> root@xilinx-zcu102-2017_2:~# ls /sys/class/gpio/gpio338/
  Output:
  active_low  direction  power  uevent  device  edge  subsystem  value
> root@xilinx-zcu102-2017_2:~# cat /sys/class/gpio/gpio338/direction
  Output:
  in
> root@xilinx-zcu102-2017_2:~# cat /sys/class/gpio/gpio338/value
  Output:
  0
 
/* Set the direction to an output and write a value 1 to GPIO pin */
> root@xilinx-zcu102-217_2:~# ls /sys/class/gpio/gpio338
  Output:
  active_low  direction  power  uevent  device  edge  subsystem  value
> root@xilinx-zcu102-2017_2:~# echo out > /sys/class/gpio/gpio338/direction
> root@xilinx-zcu102-2017_2:~# echo 1 > /sys/class/gpio/gpio338/value
 

Using the GPIO Driver from a User Space Application

The performance of this is pretty good. Using nfs root, running open source linux on the ML507 reference system, the GPIO can be toggled about every 4 usec.
This page, GPIO User Space Application, shows the application and provides it for download.


Linux Kernel Drivers

The gpio-keys, gpio-keys-polled and leds-gpio drivers are a powerful alternative to the SysFs interface for accessing GPIO from user space.
  • gpio-keys is used when GPIO line can generate interrupts in response to a key press.
  • gpio-keys-polled is used when GPIO line cannot generate interrupts, so it needs to be periodically polled by a timer.
  • leds-gpio will handle LEDs connected to GPIO lines, giving the LED sysfs interface.
This section covers process of modifying the device tree (DTS) and kernel configuration and includes an example application to demonstrate the function of these drivers.
These drivers are suitable for more complex applications involving generating patterns or handling interrupts.

Additional Setup Required

Required Information from the Device Tree

Use your preferred editor to open the device tree DTS file and locate the GPIO controller under the AXI interconnect; it should look something like this:
ps7_gpio_0: ps7-gpio@e000a000 {
    #gpio-cells = <2>;
    clocks = <&clkc 42>;
    compatible = "xlnx,zynq-gpio-1.0";
    emio-gpio-width = <64>;
    gpio-controller ;
    gpio-mask-high = <0x0>;
    gpio-mask-low = <0x5600>;
    interrupt-parent = <&ps7_scugic_0>;
    interrupts = <0 20 4>;
    reg = <0xe000a000 0x1000>;
};
Note the name of your GPIO controller, since you will need it to create the new nodes; in this example it is ps7_gpio_0.

LEDS-GPIO Driver

Add LEDs to the Device Tree

The leds-gpio driver enables many LED control schemes (via the "default-trigger" option) and allows them to be modified from user space; these drivers are suitable for any output-only GPIO application.
Under the AXI interconnect, create a node named “leds-gpio”, like in the example below:
gpio-leds {
    compatible = "gpio-leds";
    led-ds23 {
        label = "led-ds23";
        gpios = <&ps7_gpio_0 10 0>;
        default-state = "on";
        linux,default-trigger = "heartbeat";
    };
};
Multiple LEDs can be nested at the same level as led-ds23 within gpio-leds. In this example, the LED is named led-ds23 to match the label ("DS23") on the ZC702 board, but this does not have any technical impact.
The string <&ps7_gpio_0 10 0> references the GPIO controller and states that this device is on the GPIO pin/bit of 10; the 0 states that the device is active high.
The default-trigger property dictates that the control scheme for the LED is “heartbeat” mode; in this mode the LED will double-pulse with a frequency relative to the CPU load.

Kernel Configuration

In order to use these drivers the kernel must be configured correctly. Configure the kernel as described in Build Kernel, and then make sure the following options are enabled in menuconfig:
  • Device Drivers
    • GPIO Support
      • Xilinx GPIO support
      • Xilinx Zynq GPIO support
    • LED Support
      • LED Class Support
      • LED Support for GPIO connected LEDs
      • LED Trigger support
        • all desired triggers
The following configs should be present in .config afterwards:
  • CONFIG_NEW_LEDS
  • CONFIG_LEDS_CLASS
  • CONFIG_LEDS_TRIGGERS (all)
  • CONFIG_LEDS_GPIO

Using the LEDs

The LED can then be accessed from /sys/class/leds/led-ds23. Depending on the trigger type selected, different files will be visible; the trigger type can be changed by writing to /sys/class/leds/led-ds23/trigger.
A full list of triggers can be found at Documentation/devicetree/bindings/leds/common.txt.

GPIO-Keys Driver

GPIO Keys are more complex than LEDs. These drivers offer proper interrupt handling and work well with multi-key setups by mapping each key to a linux code; the entire gpio-keys node will be read as a single device with multiple key codes (like a keyboard).

Add GPIO-Keys to the Device Tree

Under the AXI interconnect, create a node named “gpio-keys”, like in the example below:
gpio-keys {
    compatible = "gpio-keys";
    #address-cells = <1>;
    #size-cells = <0>;
    autorepeat;
    sw14 {
        label = "sw14";
        gpios = <&ps7_gpio_0 12 0>;
        linux,code = <108>; /* down */
        gpio-key,wakeup;
        autorepeat;
    };
    sw13 {
        label = "sw13";
        gpios = <&ps7_gpio_0 14 0>;
        linux,code = <103>; /* up */
        gpio-key,wakeup;
        autorepeat;
    };
};
  • The string <&ps7_gpio_0 12 0> references the GPIO controller and states that sw14 device is on pin 12, while sw13 is on pin 14; the 0 states that the device is active high.
  • The linux,code property determines which key will show up in the event.
  • The autorepeat property allows holding the key to continuously generate events.
  • Using gpio-key,wakeup will enable the GPIO to wake the system from suspend.
Full documentation can be found here Documentation/devicetree/bindings/input/gpio-keys.txt.

Kernel Configuration

In order to use these drivers the kernel must be configured correctly. Configure the kernel as described in Build Kernel, and then make sure the following options are enabled in menuconfig:
  • Device Drivers
    • GPIO Support
      • Xilinx GPIO support
      • Xilinx Zynq GPIO support
    • Input device support
      • Keyboards
        • GPIO Buttons
        • Polled GPIO buttons

The following configs should be present in .config afterwards:
  • CONFIG_KEYBOARD_GPIO and CONFIG_KEYBOARD_GPIO_POLLED respectively

At this point, the Linux kernel is ready to be rebuilt.

Using the GPIO-Keys

The key events generated by the GPIO-keys can be read by opening the file /dev/input/event0. For a quick test, use cat /dev/input/event0 | hexdump and then press a key and the input event will print to the console; this data is of the input_event structure and is documented here Documentation/input/input.txt.

GPIO-Keys-Polled

For GPIO that do not use interrupts, the gpio-keys-polled drivers can be used and will still benefit from having linux key codes associated with them. Like in the previous example, create a new node under the AXI interconnect, but this time name it “gpio-keys-polled”. The structure for gpio-keys-polled is almost identical to that of gpio-keys – the polled keys do not have any interrupt capabilities, so gpio-key,wakeup is not relevant; the gpio-keys-polled driver also includes a poll-interval property, in milli-seconds.
Full documentation of this driver can be found here Documentation/devicetree/bindings/input/gpio-keys-polled.txt.

Demo Application

This application showcases the use of the gpio-keys and leds-gpio drivers by passing events into user space and then modifying the LEDs from there:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <pthread.h>
#include <linux/input.h>
 
#define LED_BRIGHTNESS    "/sys/class/leds/led-ds23/brightness"
#define    LED_TRIGGER    "/sys/class/leds/led-ds23/trigger"
#define    INPUT_EVENT    "/dev/input/event0"
#define    LED_MAX_SPEED    10
#define    PERIOD_COEFF    16000
 
unsigned int led_speed;
pthread_mutex_t lock;
 
/* Blink LED */
static void *LEDMod(void *dummy)
{
    unsigned int led_period;
    int tmp;
    tmp = open(LED_BRIGHTNESS, O_WRONLY);
    if (tmp < 0)
        exit(1);
    while (1) {
        pthread_mutex_lock(&lock);
        led_period = (LED_MAX_SPEED - led_speed) * PERIOD_COEFF;
        pthread_mutex_unlock(&lock);
 
        write(tmp, "1", 2);
        usleep(led_period);
        write(tmp, "0", 2);
        usleep(led_period);
    }
}
 
int main()
{
    pthread_t pth;
    struct input_event ev;
    int tmp;
    int key_code;
    int size = sizeof(ev);
 
    /* Configure LED */
    led_speed = 5;
    tmp = open(LED_TRIGGER, O_WRONLY);
    if (tmp < 0)
        return 1;
    if (write(tmp, "default-on", 10) != 10) {
        printf("Error writing trigger");
        return 1;
    }
    close(tmp);
    printf("Configured LED for use\n");
 
    /* Create thread */
    pthread_mutex_init(&lock, NULL);
    pthread_create(&pth, NULL, LEDMod, "Blinking LED...");
 
    /* Read event0 */
    tmp = open(INPUT_EVENT, O_RDONLY);
    if (tmp < 0) {
        printf("\nOpen " INPUT_EVENT " failed!\n");
        return 1;
    }
    /* Read and parse event, update global variable */
    while (1) {
        if (read(tmp, &ev, size) < size) {
            printf("\nReading from " INPUT_EVENT " failed!\n");
            return 1;
        }
 
        if (ev.value == 1 && ev.type == 1) {    /* Down press only */
            key_code = ev.code;
            if (key_code == KEY_DOWN) {    /* lower speed */
                /* Protect from concurrent read/write */
                pthread_mutex_lock(&lock);
                if (led_speed > 0)
                    led_speed -= 1;
                pthread_mutex_unlock(&lock);
            } else if (key_code == KEY_UP) {    /* raise speed */
                pthread_mutex_lock(&lock);
                if (led_speed < 9)
                    led_speed += 1;
                pthread_mutex_unlock(&lock);
            }
            printf("Speed: %i\n", led_speed);
            usleep(1000);
        }
    }
}
Run this application with leds-gpio and either gpio-keys or gpio-keys-polled. The demo will allow you to use push buttons to dictate the frequency of LED blinking using the kernel drivers.

Expected Output

zynq> cat /sys/class/gpio/
export        gpiochip906/  unexport
zynq> cat /sys/class/gpio/gpiochip906/
base        label       power/      uevent
device/     ngpio       subsystem/
zynq> cat /sys/class/gpio/gpiochip906/label
zynq_gpio
zynq> echo 906 > /sys/class/gpio/export
zynq> echo out > /sys/class/gpio/gpio
gpio906/      gpiochip906/
zynq> echo out > /sys/class/gpio/gpio906/direction
zynq> echo 1 > /sys/class/gpio/gpio906/value
zynq>

Changelog

  • 2016.3
    • Summary
      • gpio:Added zynq specific check for special pins on bank zero.
    • Commits
      • e3296f1 gpio:Added zynq specific check for special pins on bank zero.

  • 2016.4
    • None

  • 2017.1
    • Summary
      • gpio: zynq: Add support for suspend resume
      • gpio: zynq: Wakeup gpio controller when it is used as IRQ controller
    • Commits
      • 764c694 gpio: zynq: Add support for suspend resume
      • 6a8c796 gpio: zynq: Wakeup gpio controller when it is used as IRQ controller
  • 2017.2
    • None

Related Links