VAR-SOM-MX6 GPIO
1 GPIO state
The current state of the system's GPIOs can be obtained in user-mode, as shown in the following example:
root@var-som-mx6:~# cat /sys/kernel/debug/gpio GPIOs 0-31, platform/209c000.gpio, 209c000.gpio: gpio-25 (phy-reset ) out lo GPIOs 32-63, platform/20a0000.gpio, 20a0000.gpio: GPIOs 64-95, platform/20a4000.gpio, 20a4000.gpio: gpio-77 (ov5640_mipi_pwdn ) out lo gpio-86 (usb_otg_vbus ) out lo GPIOs 96-127, platform/20a8000.gpio, 20a8000.gpio: gpio-101 (tlv320aic3x reset ) out lo gpio-106 (ov5640_mipi_reset ) out lo gpio-110 (2194000.usdhc cd ) in hi gpio-111 (2194000.usdhc ro ) in hi gpio-120 (spi_imx ) out lo gpio-121 (ads7846_pendown ) in hi GPIOs 128-159, platform/20ac000.gpio, 20ac000.gpio: gpio-141 (PCIe reset ) out lo GPIOs 160-191, platform/20b0000.gpio, 20b0000.gpio: gpio-178 (sysfs ) out lo GPIOs 192-223, platform/20b4000.gpio, 20b4000.gpio: gpio-200 (wlan-en-regulator ) out lo
Each GPIO is defined as in or out and the state is shown as lo or hi.
For example pin 110 is the SD card card-detect.
When an SD card is plugged in, the state will be:
gpio-110 (2194000.usdhc cd ) in lo
When the SD card is removed, the state will be:
gpio-110 (2194000.usdhc cd ) in hi
2 Manipulating GPIO using libgpiod
The Linux GPIO sysfs interface is being deprecated. Moving forward, user space should use the character device /dev/gpiochip*
instead. libgpiod provides bindings and utilities for for manipulating GPIO via user space.
2.1 libgpiod via command line
libgpiod provides command line utilities for GPIO:
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 |
i.MX GPIOs are organized in banks of 32 pins. Each bank corresponds to a character device /dev/gpiochip<bank index>
. The gpiodetect
utility can be used to inspect the available gpiochip character devices:
# gpiodetect gpiochip0 [30200000.gpio] (32 lines) gpiochip1 [30210000.gpio] (32 lines) ...
The gpioinfo
utility can be used to inspect the lines for a given gpiochip:
# gpioinfo gpiochip0 gpiochip0 - 32 lines: line 0: unnamed "spi_imx" output active-high [used] line 1: unnamed unused input active-high line 2: unnamed unused input active-high ...
The gpioset
and gpioget
utilities can be used to manipulate GPIO from the command line.
For example, assuming GPIO4_21 is configured as a GPIO in your device tree:
Set GPIO4_21 high:
gpioset gpiochip3 21=1
Set GPIO4_21 low:
gpioset gpiochip3 21=0
Read GPIO4_21:
gpioget gpiochip3 21
2.2 libgpiod C Application
libgpiod provides bindings for C/C++ applications. C++ examples are available in the libgpiod /tree/bindings/cxx/examples directory.
Below is a simple C application demonstrating how to use the bindings with GPIO4_IO21:
Makefile:
all: main.cpp $(CC) $(CCFLAGS) -Og -lgpiod main.c -g -o hello.bin clean: rm -f hello.bin
main.c
#include <gpiod.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #define CONSUMER "Variscite Demo" int main(int argc, char **argv) { unsigned int i, ret, val; struct gpiod_chip *chip; struct gpiod_line *line; const char * chipname = "gpiochip3"; const unsigned int line_num = 21; chip = gpiod_chip_open_by_name(chipname); if (!chip) { perror("Open chip failed\n"); goto end; } line = gpiod_chip_get_line(chip, line_num); if (!line) { perror("Get line failed\n"); goto close_chip; } ret = gpiod_line_request_output(line, CONSUMER, 0); if (ret < 0) { perror("Request line as output failed\n"); goto release_line; } /* Blink 5 times */ val = 0; for (i = 0; i < 5; i++) { ret = gpiod_line_set_value(line, val); if (ret < 0) { perror("Set line output failed\n"); goto release_line; } printf("Output %u on line #%u\n", val, line_num); sleep(1); val = !val; } release_line: gpiod_line_release(line); close_chip: gpiod_chip_close(chip); end: return 0; }
2.3 libgpiod Python Application
libgpiod provides bindings for python applications:
# pip3 install gpiod
Python examples are available in the libgpiod /tree/bindings/python/examples directory.
3 Manipulating a single GPIO via /sys/class/gpio
3.1 Using a command line or a script
GPIOs in i.MX are grouped in groups of 32 pins.
For example, GPIO1_3 belong to the first group, pin 3. Its absolute number will be 3.
GPIO7_4 will be (7-1)*32+4=196.
Assuming this GPIO is defined in your device tree, the following is an example of how to use it from userspace.
To export the GPIO for userspace use:
# echo 196 > /sys/class/gpio/export
To configure as output:
# echo out > /sys/class/gpio/gpio196/direction
Set GPIO high:
# echo 1 > /sys/class/gpio/gpio196/value
Set GPIO low:
# echo 0 > /sys/class/gpio/gpio196/value
To configure as input:
# echo in > /sys/class/gpio/gpio196/direction
Read the current value:
# cat /sys/class/gpio/gpio196/value
To free the GPIO after you're done using it:
# echo 196 > /sys/class/gpio/unexport
3.2 Manage GPIO from a C application
All of the command line operations above can be translated to C code:
Reserve (export) the GPIO:
#define IMX_GPIO_NR(port, index) ((((port)-1)*32)+((index)&31)) int fd; char buf[MAX_BUF]; int gpio = IMX_GPIO_NR(7, 4); /* Just an example */ fd = open("/sys/class/gpio/export", O_WRONLY); sprintf(buf, "%d", gpio); write(fd, buf, strlen(buf)); close(fd);
Set the GPIO direction:
sprintf(buf, "/sys/class/gpio/gpio%d/direction", gpio); fd = open(buf, O_WRONLY); /* Set out direction */ write(fd, "out", 3); /* Set in direction */ write(fd, "in", 2); close(fd);
In case of out direction set the GPIO value:
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio); fd = open(buf, O_WRONLY); /* Set GPIO high status */ write(fd, "1", 1); /* Set GPIO low status */ write(fd, "0", 1); close(fd);
In case of in direction get the current GPIO value:
char value; sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio); fd = open(buf, O_RDONLY); read(fd, &value, 1); if (value == '0') { /* Current GPIO status low */ } else { /* Current GPIO status high */ } close(fd);
Once finished, free (unexport) the GPIO:
fd = open("/sys/class/gpio/unexport", O_WRONLY); sprintf(buf, "%d", gpio); write(fd, buf, strlen(buf)); close(fd);
Important notes:
- Remember that after the first read operation the file pointer will move to the next position in the file, so to get a correct value for each read operation you simply have to set the file pointer at the beginning of the file before read by using the following command:
lseek(fd, 0, SEEK_SET);
- This is only a short example. If you want to use it in your code remember add error handling to it.
4 Kernel Device Tree GPIO configuration
4.1 Device Tree GPIO files
4.1.1 Pin Func files
In the directory arch/arm/boot/dts/ of the Linux kernel source you will find the pin functions definitions files.
The relevant files are imx6dl-pinfunc.h and imx6q-pinfunc.h, depending on the platform you are using.
For example, if you edit imx6q-pinfunc.h and search for GPIO7_IO04, you will see a group of of definitions with same prefix (pad name), "MX6QDL_PAD_SD3_DAT0".
#define MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x2c0 0x6a8 0x000 0x0 0x0 #define MX6QDL_PAD_SD3_DAT0__UART1_CTS_B 0x2c0 0x6a8 0x000 0x1 0x0 #define MX6QDL_PAD_SD3_DAT0__UART1_RTS_B 0x2c0 0x6a8 0x91c 0x1 0x2 #define MX6QDL_PAD_SD3_DAT0__FLEXCAN2_TX 0x2c0 0x6a8 0x000 0x2 0x0 #define MX6QDL_PAD_SD3_DAT0__GPIO7_IO04 0x2c0 0x6a8 0x000 0x5 0x0
Adding only the one with the GPIO7_IO04 suffix (function) to your dts file will let you use the pin as GPIO.
4.1.2 Variscite dts files
Variscite defines dts file for each platform.
Device Tree Name |
Include dtsi file |
SOM type |
Carrier Board type |
LCD Type |
Evaluation Kit name |
---|---|---|---|---|---|
imx6q-var-som.dts | imx6qdl-var-som.dtsi | VAR-SOM-MX6_V2 (Quad / Dual) | VAR-MX6CustomBoard | Capacitive/Resistive touch | VAR-DVK-MX6_V2-PRO VAR-STK-MX6_V2 |
imx6q-var-som-vsc.dts | imx6qdl-var-som.dtsi | VAR-SOM-MX6_V2 (Quad / Dual) | VAR-SOLOCustomBoard | Capacitive LVDS touch | N/A |
imx6dl-var-som.dts | imx6qdl-var-som.dtsi | VAR-SOM-MX6_V2 (DualLite/ Solo) | VAR-MX6CustomBoard | Capacitive/Resistive touch | N/A |
imx6dl-var-som-solo-vsc.dts | imx6qdl-var-som.dtsi | VAR-SOM-SOLO / VAR-SOM-DUAL | VAR-SOLOCustomBoard | Capacitive LVDS touch | VAR-DVK-SOLO/DUAL VAR-STK-SOLO/DUAL |
imx6dl-var-som-solo.dts | imx6qdl-var-som.dtsi | VAR-SOM-SOLO / VAR-SOM-DUAL | VAR-MX6CustomBoard | Capacitive/Resistive touch | N/A |
imx6q-var-dart.dts | imx6qdl-var-dart.dtsi | VAR-SOM-SOLO / VAR-SOM-DUAL | VAR-DT6CustomBoard | Capacitive LVDS touch | VAR-STK-DT6.VAR-DVK-DT6 |
imx6q-var-som.dts starts with definitions and includindg dtsi files.
#define VAR_SOM_MX6 #include "imx6q.dtsi" #include "imx6qdl-var-som.dtsi"
The imx6q.dtsi define the CPU platform and which pinfunc file will be included. This feature allow the pin name to be agnostic to the CPU type (i.MX6Q vs i.MX6DL)
imx6qdl-var-som.dtsi has the major VAR-SOM-MX6 definitions.
4.2 Define a pin as GPIO in the kernel Device Tree
You need to add the relevant definitions to your device tree, as explained in the Pin Func files section above.
Edit arch/arm/boot/dts/imx6qdl-var-som.dtsi (or imx6qdl-var-dart.dtsi in case of DART-MX6) and add the definition for the GPIO you need in the section below.
pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hog>; imx6qdl-var-som-mx6 { pinctrl_hog: hoggrp { fsl,pins = < /* PMIC INT */ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 /* Wifi Slow Clock */ MX6QDL_PAD_ENET_RXD0__OSC32K_32K_OUT 0x000b0 /* Audio Clock */ MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0 /* Camera Clock */ MX6QDL_PAD_GPIO_3__CCM_CLKO2 0x130b0 MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x0b0b1 MX6QDL_PAD_KEY_COL1__GPIO4_IO08 0x0b0b1 >; };
4.2.1 Device Tree GPIO attribute
If you look at Documentation/devicetree/bindings/pinctrl/fsl,imx6q-pinctrl.txt in the Linux kernel source tree, the number to the right of the pin control spec can be used for additional attributes like pull-ups, pull-downs, keepers, drive strength, etc.
The value 0x80000000 is "don't know value please use the default". Otherwise, the value consists of a bitwise-OR combination of the following values.
CONFIG bits definition |
value |
---|---|
PAD_CTL_HYS | (1 << 16) |
PAD_CTL_PUS_100K_DOWN | (0 << 14) |
PAD_CTL_PUS_47K_UP | (1 << 14) |
PAD_CTL_PUS_100K_UP | (2 << 14) |
PAD_CTL_PUS_22K_UP | (3 << 14) |
PAD_CTL_PUE | (1 << 13) |
PAD_CTL_PKE | (1 << 12) |
PAD_CTL_ODE | (1 << 11) |
PAD_CTL_SPEED_LOW | (1 << 6) |
PAD_CTL_SPEED_MED | (2 << 6) |
PAD_CTL_SPEED_HIGH | (3 << 6) |
PAD_CTL_DSE_DISABLE | (0 << 3) |
PAD_CTL_DSE_240ohm | (1 << 3) |
PAD_CTL_DSE_120ohm | (2 << 3) |
PAD_CTL_DSE_80ohm | (3 << 3) |
PAD_CTL_DSE_60ohm | (4 << 3) |
PAD_CTL_DSE_48ohm | (5 << 3) |
PAD_CTL_DSE_40ohm | (6 << 3) |
PAD_CTL_DSE_34ohm | (7 << 3) |
PAD_CTL_SRE_FAST | (1 << 0) |
PAD_CTL_SRE_SLOW | (0 << 0) |
4.3 Device Tree GPIO default Value
The Bluetooth is a good example to see how to set the default value during boot.
variscite-bluetooth
For example how to reset the Bluetooth:
echo 178 >/sys/class/gpio/export echo "out" > /sys/class/gpio/gpio178/direction echo 0 > /sys/class/gpio/gpio178/value sleep 1 echo 1 > /sys/class/gpio/gpio178/value sleep 1
You can also add it to your default build file system: initscripts