LPD GPIO controller:
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.
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 |
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>; }; |
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
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.
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 |
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:
/* Export a GPIO pin */ > root@xilinx-zcu102-2017_3:~# echo 338 > /sys/class/gpio/export /* Read the direction and value from the GPIO pin */ > root@xilinx-zcu102-2017_3:~# ls /sys/class/gpio/gpio338/ Output: active_low direction power uevent device edge subsystem value > root@xilinx-zcu102-2017_3:~# cat /sys/class/gpio/gpio338/direction Output: in > root@xilinx-zcu102-2017_3:~# 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_3:~# ls /sys/class/gpio/gpio338 Output: active_low direction power uevent device edge subsystem value > root@xilinx-zcu102-2017_3:~# echo out > /sys/class/gpio/gpio338/direction > root@xilinx-zcu102-2017_3:~# echo 1 > /sys/class/gpio/gpio338/value |
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.
The gpio-keys, gpio-keys-polled and leds-gpio drivers are a powerful alternative to the SysFs interface for accessing GPIO from user space.
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.
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.
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.
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:
The following configs should be present in .config afterwards:
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 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).
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; }; }; |
Full documentation can be found here Documentation/devicetree/bindings/input/gpio-keys.txt.
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:
The following configs should be present in .config afterwards:
At this point, the Linux kernel is ready to be rebuilt.
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.
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.
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> |
This driver is currently in sync with mainline kernel driver.
2023.2
summary
commits
2f674fa - gpio: zynq: fix zynqmp_gpio not an immutable chip warning
f79565a - gpio: synq: remove unused zynq_gpio_irq_reqres/zynq_gpio_irq_relres
2023.1
2022.2
2022.1
2021.2
2021.1
2020.2
2020.1
Summary
commits
9b515a08 - Remove error prints in EPROBE_DEFER
5230a062 - use module_platform_driver to simplify the code
2019.2
summary
commits
9d496bd2 - Add pmc gpio support
2019.1
summary:
commits:
81e13db - Report gpio direction at boot
d2fe433 - properly support runtime PM for GPIO used as interrupts
65b9290 - Disable the irq if it is not a wakeup source
2018.3
52b64a2 - gpio: zynq: Remove call to platform_get_irq
eb816d4 - gpio: zynq: simplifly getting drvdata
5dd4162 - gpio: zynq: Setup chip->base based on alias ID
2018.2
Summary:
Commits:
355168 - gpio: zynq: protect direction in/out with a spinlock
2018.1
Summary:
Commits:
e11de4d - gpio: zynq: Add support for suspend resume
2017.4
2017.3
Summary:
Commits:
9572161 - gpio: gpio-zynq.c: Fix kernel doc warnings
a9e595b - gpio: gpio-zynq: Fix warnings in the driver
554ae6b - gpio: gpio-zynq: shift zynq_gpio_init() to subsys_initcall level
913cf8b - gpio: zynq: Clarify quirk and provide helper function
8bc5037 - gpio: zynq: Provided workaround for GPIO
2017.2
2017.1
Summary:
Commits:
764c694 gpio: zynq: Add support for suspend resume
6a8c796 gpio: zynq: Wakeup gpio controller when it is used as IRQ controller
2016.4
2016.3
Summary:
Commits: