GPIO (Linux)
Introductionβ
This article will guide you on how to use libgpiod's command line tools and C API to control GPIO pins on Toradex's System on Modules (SoMs). It also provides guidance on working with GPIO-related drivers and explains how to access GPIO within U-Boot.
Keep in mind that Torizon OS is preferred for using libgpiod due to its ease of use and smoother application development experience. For controlling GPIO pins using Torizon, refer to How to Use GPIO on Torizon OS.
GPIO - Libgpiodβ
libgpiod provides a set of command-line tools and a userspace C API that allow access to the GPIO chips and lines via the /dev/gpiochipN character device interfaces.
It replaces the older sysfs-based GPIO interface (/sys/class/gpio), which is now deprecated, and is the standard method for controlling GPIO pins from userspace.
Command-Line Toolsβ
The following sections present how to use the libgpiod command-line tools.
gpiodetectβ
Search for the GPIO chips and identify how many GPIO lines are available:
# gpiodetect
gpiochip0 [tps65219-gpio] (3 lines)
gpiochip1 [1-0021] (16 lines)
gpiochip2 [4201000.gpio] (24 lines)
gpiochip3 [600000.gpio] (92 lines)
gpiochip4 [601000.gpio] (52 lines)
gpioinfoβ
Read and display the information contained in the GPIO chip lines. All usable pins exposed on the edge connector - either SODIMM or MXM3, depending on the SoM family - are labeled as SODIMM_x or MXM3_x.
Pins labelled as unnamed are not available for use on the SODIMM/MXM3 connector.
See the example below:
# gpioinfo
gpiochip0 - 32 lines:
line 0: "SODIMM_216" unused input active-high
line 1: "SODIMM_19" unused input active-high
line 2: unnamed unused input active-high
line 3: unnamed "interrupt" input active-high [used]
...
To see only available pins, you can filter the command output with grep:
# gpioinfo | grep -e "SODIMM" -e "MXM3"
This command is useful to check which rows are being used, with their respective use descriptions, on a GPIO chip.
gpiosetβ
Write the value of an output GPIO pin. Requires the pin's gpiochip and line. The commands below drive the pin high and low, respectively.
# gpioset -c /dev/gpiochip1 2=0
# gpioset -c /dev/gpiochip1 2=1
You can also pass only the GPIO chip index as an argument:
# gpioset -c 1 2=0
# gpioset -c 1 2=1
gpiogetβ
Read the value of a GPIO pin. Requires the pin's gpiochip and line. The examples below show the console output for a pin in high and low states, respectively.
# gpioget -c 1 2
"2"=active
# gpioget -c 1 2
"2"=inactive
gpiomonβ
Monitor events on a GPIO row, which is passed as an argument:
# gpiomon -c 1 2
1770.920262496 falling gpiochip1 2 "SODIMM_208"
1772.226964433 rising gpiochip1 2 "SODIMM_208"
1779.208066935 falling gpiochip1 2 "SODIMM_208"
1779.635657406 rising gpiochip1 2 "SODIMM_208"
This command is useful for polling the lines to expect incoming input events.
How to Replace sysfs Commandsβ
The most important commands are gpioget and gpioset, though you could possibly use gpiomon as well. Check out our example that writes and reads from GPIO pins. The sysfs commands are commented in the script:
C Language APIβ
To get started with the API, we recommend checking the official examples available in the libgpiod repository. Make sure you have a built Yocto SDK to cross-compile applications for the image on your target module.
Follow the steps below to toggle a GPIO pin on and off with libgpiod.
-
Clone the
toggle_line_value.csource code. -
Modify the source code to toggle the desired pin by specifying its chip and line offset. To obtain the chip and offset values, you can use
gpioinfo.toggle_line_value.c@@ -86,8 +86,8 @@
int main(void)
{
/* Example configuration - customize to suit your situation. */
- static const char *const chip_path = "/dev/gpiochip1";
- static const unsigned int line_offset = 2;
- static const char *const chip_path = "/dev/gpiochip0";
- static const unsigned int line_offset = 5;
enum gpiod_line_value value = GPIOD_LINE_VALUE_ACTIVE;
struct gpiod_line_request *request; -
Create a Makefile to automate the compilation of the example:
MakefileBIN := gpio-toggle
SRC := gpio-toggle.c
LIBS := -lgpiod
all: $(BIN)
$(BIN): $(SRC)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS) -
Lastly, set up the SDK environment variables, cross-compile the application, and deploy it to your target machine -- in this case, a Verdin AM62 SoM.
$ . /opt/tdx-xwayland/7.4.0/environment-setup-aarch64-tdx-linux
$ make
$ scp gpio-toggle root@verdin-am62-15190476.local:~/ -
Connect to the target device and run the generated binary. You can also connect the selected GPIO pin to an external actuator, such as an LED, to observe the toggling behavior.
# cd ~/
# ./gpio-toggle
.NET Examplesβ
The article .NET Core Development and Debugging on Torizon Using Visual Studio Code explains how to use libgpiod with .NET.
Python Examplesβ
Our samples repository on GitHub provides an example: Python libgpiod example The following articles can help you with enough context for running and extending the sample:
- Visual Studio Code Extension for Torizon
- Python Development and Debugging on Torizon OS Using Visual Studio Code
GPIO Power Management Keysβ
Linux systems use key events to initiate a clean shutdown or suspend-to-memory sequence. On a typical PC, pressing the power button generates a key event which will lead to a shutdown of the system. For an embedded system, a GPIO with a key code assigned can be used to trigger key events. When the key is pressed (GPIO triggered), the system will initiate the sequence.
The systemd service systemd-logind is the user-space program listening to key events (if they are tagged with the string "power-switch", see below). Four key codes are supported:
- KEY_POWER = initiate shutdown
- KEY_POWER2 = initiate shutdown
- KEY_SLEEP = suspend-to-ram, commonly known as "suspend"
- KEY_SUSPEND = suspend-to-disk, commonly known as "hibernate"
There are two steps required:
- Declare a GPIO as an input device and assign a key code using the GPIO keyboard driver
- Use a udev rule to tag the GPIO input device
GPIO Keyboard Driverβ
For device tree enabled kernels, a node as follows can be used in the carrier board device tree file:
gpio-keys {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpiokeys>;
power {
label = "Power-Key";
gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
linux,code = <KEY_POWER>;
debounce-interval = <10>;
};
};
For details on how to customise the device tree refer to the Device Tree Customization article.
Tag the GPIO Input Deviceβ
Use a udev rule to add the power-switch tag to the GPIO key event source. Store the file in a udev rules directory, e.g. /etc/udev/rules.d/power-key.rules
ACTION=="remove", GOTO="power_switch_end"
SUBSYSTEM=="input", KERNEL=="event*", ENV{ID_PATH}=="platform-gpio-keys*", ATTRS{keys}=="*", TAG+="power-switch"
LABEL="power_switch_end"
With that, all keycodes get tagged as power-switch events, and systemd will interpret the relevant key codes. The exact behaviour can be fine-tuned through HandlePowerKey/HandleSuspendKey and HandleHibernateKey in /etc/systemd/logind.conf.
GPIO Power-Offβ
The power supply remains active when halting the system via software (using shutdown or poweroff, for example). To completely interrupt the power supplied to the system, you need to send a shutdown signal via a GPIO pin to the external power source, assuming the source has an internal logic to recognize the signal. The newer Kernel versions provide a specific GPIO power-off driver to achieve this.
Some Toradex carrier boards provide a CTRL_FORCE_OFF_MOCI# or FORCE_OFF input signal. If your board includes one of these, you can connect your configured GPIO to it to send the shutdown signal and power off the board.
Please note that, if you are using a module from the Apalis family, you need to disable the PCIe, as shown in the example below. This is due to some changes made to comply with the PCIe specification.
To send this GPIO signal on system shutdown using the Kernel driver, follow the procedure below:
-
Enable the GPIO Power-Off driver: Verify if the
CONFIG_POWER_RESET_GPIOkernel configuration is set on your module by running the command below. If it is not, either enable the configuration in your Yocto build or build your custom kernel with the configuration enabled.# zcat /proc/config.gz | grep CONFIG_POWER_RESET_GPIO
CONFIG_POWER_RESET_GPIO=y -
Write a Device Tree Overlay to bind your desired GPIO pin to the GPIO Power-Off driver:
poweroff_gpio_overlay.dts#include <dt-bindings/gpio/gpio.h>
...
&{/} {
gpio_poweroff: gpio-poweroff {
compatible = "gpio-poweroff";
gpios = <&gpio3 23 GPIO_ACTIVE_LOW>;
};
}; -
(Optional) If you are using a module from the Apalis family, disable PCIe. Otherwise, you won't be able to turn off the board by connecting the pin to the
FORCE_OFFsignal.&pciea {
status = "disabled";
};
GPIO LEDβ
The GPIO LED driver allows using a GPIO to control a LED. Using the Kernels LED driver framework has the advantage that triggers can be specified, which allow using an LED as a visual activity signal for various system activities.
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_leds>;
led0: user1 {
label = "user1";
gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>; /* SODIMM 103 */
default-state = "off";
linux,default-trigger = "mmc0";
};
};
...
pinctrl_gpio_leds: gpioleds {
fsl,pins = <
VF610_PAD_PTC3__GPIO_48 0x2180
>;
};
You can also get a list of valid triggers (and configure the active trigger) through the sysfs file at /sys/class/leds/user1/trigger.
GPIO PPSβ
The GPIO PPS is a feature present in the Linux Kernel that allows the usage of a GPIO with a given Pulse-Per-Second (PPS) signal.
Starting from BSP 5, it's enabled by default in the kernel config on Toradex Reference Images for Yocto Project and Torizon OS:
CONFIG_PPS_CLIENT_GPIO
CONFIG_PPS_CLIENT_LDISC
Here is one example of a device tree for Colibri iMX7D 1GB. In addition to the example, you must also make sure to check the pin muxing, i.e. make sure that no other driver muxes and claims the pin function GPIO1_IO02, and to mux the pin to gpio as part of the pps node.
Also note that since pps is a new node, it must be created somewhere in the tree under /.
/ {
model = "Toradex Colibri iMX7D 1GB on Colibri Evaluation Board V3 Testing for custom carrier";
compatible = "toradex,colibri_imx7d_emmc-eval", "toradex,colibri_imx7d_emmc", \
"fsl,imx7d";
pps {
compatible = "pps-gpio";
gpios = <&gpio1 2 0>;
assert-falling-edge;
};
};
Please refer to the Linux kernel documentation on device tree bindings for more details: Device-Tree Bindings for a PPS Signal on GPIO
GPIO U-Bootβ
One can also access GPIOs from U-Boot. This allows one to implement functionality such as βPress button X and turn the device ON to upgrade firmware" using U-Boot scripts.
The GPIO driver can be used from within the U-Boot source code. Additionally, the GPIO driver has a corresponding gpio command-line interface that can be used to set and get GPIO values. Note that for the command-line interface to work the corresponding pin must be muxed to its GPIO functionality in the U-Boot code.
The relationship between SOC GPIO names and the numbers used here can be found on GPIO Alphanumeric to GPIO Numeric Assignment article.
Command Line Interfaceβ
# gpio help
gpio - query and control gpio pins
Usage:
gpio <input|set|clear|toggle> <pin>
- input/set/clear/toggle the specified pin
gpio status [-a] [<bank> | <pin>] - show [all/claimed] GPIOs
To set the GPIO:
> gpio set [gpio-number]
To clear the GPIO
> gpio clear [gpio-number]
To toggle the GPIO
> gpio toggle [gpio-number]
To read the state of GPIO:
> gpio input [gpio-number]
Usually, all GPIO pins are muxed as input in U-Boot. You can confirm it, for instance, with the command below:
> gpio status -a
Bank GPIO0_:
GPIO0_0: input: 0 [ ]
GPIO0_1: input: 0 [ ]
GPIO0_2: input: 0 [ ]
...
GPIO7_29: input: 0 [ ]
GPIO7_30: input: 0 [ ]
GPIO7_31: input: 0 [ ]
Example - Colibri iMX8Xβ
This example has been tested on Colibri iMX8QXP + Colibri Evaluation Board V3.2 using the mainline master branch of U-Boot.
To calculate the pin number in U-Boot, use the formula below:
beware that this formula is different from the formula for the Linux user space provided in GPIO Alphanumeric to GPIO Numeric Assignment.
pin = 32 * GPIO_BANK + GPIO_LINE
For example, GPIO3_10:
pin = 32 * 3 + 10 = 106
Change the pinmux configuration in the U-Boot device tree, in the source code. The change below set the GPIO on SODIMM 45 as output:
diff --git a/arch/arm/dts/fsl-imx8qxp-colibri.dts b/arch/arm/dts/fsl-imx8qxp-colibri.dts
index 0c20edf2cf..562effb366 100644
--- a/arch/arm/dts/fsl-imx8qxp-colibri.dts
+++ b/arch/arm/dts/fsl-imx8qxp-colibri.dts
@@ -89,7 +89,7 @@
pinctrl_hog1: hog1grp {
fsl,pins = <
- SC_P_QSPI0A_DATA1_LSIO_GPIO3_IO10 0x00000020 /* 45 */
+ SC_P_QSPI0A_DATA1_LSIO_GPIO3_IO10 0x02000020 /* 45 */
SC_P_ENET0_RGMII_TXD3_LSIO_GPIO5_IO02 0x06000020 /* 65 */
SC_P_CSI_D07_CI_PI_D09 0x00000061
SC_P_QSPI0A_DATA2_LSIO_GPIO3_IO11 0x00000020 /* 69 */
Re-compile and deploy U-Boot, then toggle this GPIO from the U-Boot command-line:
> gpio set 106
gpio: pin 106 (gpio 106) value is 1
> gpio clear 106
gpio: pin 106 (gpio 106) value is 0
From Source Codeβ
To access a GPIO earlier than what would be possible from the U-Boot command line or to include it in an algorithm written in source code using the GPIO API. In most cases this code would live in the board file (e.g. board/toradex/colibri_vf/colibri_vf.c).
The GPIO's need to be configured only after the GPIO driver is loaded, if a GPIO gets configured before the driver has been loaded, the GPIO functions will have no effect. The 'board_init' function is called just after GPIO initialization and hence is an appropriate place to configure custom GPIO's.
E.g. To set GPIO_10 during boot from board file:
diff --git i/board/toradex/colibri_vf/colibri_vf.c w/board/toradex/colibri_vf/colibri_vf.c
index 3272733..ca30af8 100644
--- i/board/toradex/colibri_vf/colibri_vf.c
+++ w/board/toradex/colibri_vf/colibri_vf.c
@@ -478,6 +483,11 @@ int board_init(void)
*/
setbits_le32(&scsc->sosc_ctr, SCSC_SOSC_CTR_SOSC_EN);
+#ifdef CONFIG_VYBRID_GPIO
+ gpio_request(10, "SODIMM_PIN_23");
+ gpio_direction_output(10, 1);
+#endif