Tool/software:
The objective of this FAQ is for someone with a TI EVM to enable using GPIOs in Linux. This guide will walk through how to setup a GPIO in the Linux Device Tree and how to toggle it in user space. This guide will also cover some common questions and issues someone may run into.
This applies to Linux SDK 10.0+ for AM64x, AM62x, AM62A, AM62P, AM62L.
Datasheets:
Technical Reference Manual:
Various TI EVM Schematics:
Linux SDK Documentation:
This information is discussed in each device's respective datasheet under the "Pin Attributes" section.
Notice from the "Pin Attributes" table that many of these pins are capable to be used many different functions. Before selecting a GPIO to use, make sure that its not conflicting with another pin function to be used.
TI EVMs have RPI headers that are exposed on the board which includes Main Domain GPIOs and some MCU GPIOs. Refer to the schematic of the TI EVM to see what pins are connected to the RPI header.
Refer to the common links to see the EVM product pages where the schematics can be found.
The RPI headers are usually labeled as "User Expansion Connector" and "MCU Header". Here is an example for SK-AM62B-P1:
Refer to the device's datasheet to see which signals can be used as GPIOs. Some pins are labeled for a different function, but still can be used as GPIOs.
Use this information to select a GPIO to use and then it can be enabled in the Linux device tree.
For generating sample device tree code, refer to the Sysconfig tool: https://dev.ti.com/sysconfig/#/start
After launching the tool, just select the device and start (keep Software product empty).
We can use the output of the Sysconfig tool to add to the Linux device tree. Just select which GPIOs to use and download the .dtsi output on the right hand side.
Here is a very simple example for enabling a few GPIOs and MCU GPIOs in the Linux device tree for AM62x:
diff --git a/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi b/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi index b1980b85c..6a34c9eeb 100644 --- a/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi @@ -186,6 +186,20 @@ AM62X_IOPAD(0x1cc, PIN_OUTPUT, 0) /* (E14/E11) UART0_TXD */ >; }; + main_gpio0_pins_default: main-gpio0-default-pins { + pinctrl-single,pins = < + AM62X_IOPAD(0x009c, PIN_INPUT, 7) /* (V25) GPMC0_WAIT1.GPIO0_38 */ + AM62X_IOPAD(0x00ac, PIN_INPUT, 7) /* (L21) GPMC0_CSn1.GPIO0_42 */ + >; + }; + + main_gpio1_pins_default: main-gpio1-default-pins { + pinctrl-single,pins = < + AM62X_IOPAD(0x01b4, PIN_INPUT, 7) /* (A13) SPI0_CS0.GPIO1_15 */ + AM62X_IOPAD(0x01d0, PIN_INPUT, 7) /* (A15) UART0_CTSn.GPIO1_22 */ + >; + }; + main_i2c0_pins_default: main-i2c0-pins-default { pinctrl-single,pins = < AM62X_IOPAD(0x1e0, PIN_INPUT_PULLUP, 0) /* (B16/E12) I2C0_SCL */ @@ -329,6 +343,29 @@ AM62X_IOPAD(0x0078, PIN_OUTPUT, 1) /* (U24) GPMC0_AD15.VOUT0_DATA23 */ }; }; +&mcu_pmx0 { + mcugpio0_pins_default: mcugpio0-default-pins { + pinctrl-single,pins = < + AM62X_MCU_IOPAD(0x003c, PIN_INPUT, 7) /* (E5) MCU_MCAN1_TX.MCU_GPIO0_15 */ + AM62X_MCU_IOPAD(0x0040, PIN_INPUT, 7) /* (D4) MCU_MCAN1_RX.MCU_GPIO0_16 */ + >; + }; +}; + +&main_gpio0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&main_gpio0_pins_default>; + gpio-line-names ="","","","","","","","","","", //GPIO0_0->9 + "","","","","","","","","","", //GPIO0_10->19 + "","","","","","","","","","", //GPIO0_20->29 + "","","","","","","","","TEST_GPIO0_38","", //GPIO0_30->39 + "","","TEST_GPIO0_42","","","","","","",""; //GPIO0_40->49 +}; + +&main_gpio1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&main_gpio1_pins_default>; + gpio-line-names ="","","","","","","","","","", //GPIO1_0->9 + "","","","","","TEST_GPIO1_15","","","","", //GPIO1_10->19 + "","","TEST_GPIO1_22","","","","","","","", //GPIO1_20->29 + "","","","","","","","","","", //GPIO1_30->39 + "","","","","","","","","",""; //GPIO1_40->49 +}; + &wkup_uart0 { /* WKUP UART0 is used by DM firmware */ status = "reserved"; @@ -578,11 +615,13 @@ dpi1_out: endpoint { }; }; -/* mcu_gpio0 and mcu_gpio_intr are reserved for mcu firmware usage */ +/* mcu_gpio0 and mcu_gpio_intr are reserved for mcu firmware usage */ &mcu_gpio0 { - status = "reserved"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&mcugpio0_pins_default>; }; &mcu_gpio_intr { - status = "reserved"; + status = "okay"; };
For the device tree, there were two changes made: setting the Pin Mux under &main_pmx0/&mcu_pmx0 and the reference node (&main_gpio0/&main_gpio1/&mcu_gpio0). Its generally recomended to add names to GPIOs for debugging purposes.
If you are not using AM62x, refer to the pin control bindings to see which one applies to your device: https://git.ti.com/cgit/ti-linux-kernel/ti-linux-kernel/blame/arch/arm64/boot/dts/ti/k3-pinctrl.h?h=ti-linux-6.12.y
Refer back to the common links to the Linux SDK documentation. Follow the instructions to compiling the device tree source into .dtb.
After the device tree is configured, compiled and loaded, the next step is to control the GPIO in userspace after the Linux kernel has booted. In userspace, we can use the GPIO SYSFS (deprecated) or the libgpiod command set. Important to note that you should only use one of these userspace methods, not both at the same time.
In later Linux SDK versions, its recommended to use Libgpiod userspace commands. This tool set and API are not written by TI so please refer to the Github repo: https://github.com/brgl/libgpiod
In the default Linux SDK, this is enabled, but refer to the menuconfig entry if needed:
As seen in the sample device tree, its recommended to add 'gpio-line-names' parameter in the device tree because it makes calling the GPIO in userspace easier. See below for an example of enabling GPIO0_38:
root@am62xx-evm:~# gpioset TEST_GPIO0_38=1 #use ctrl + c to send an interrupt to end the command root@am62xx-evm:~# gpioset -z TEST_GPIO0_38=1 #add the -z flag to daemonize the command
Just be aware when daemonizing the GPIO command, trying to change the GPIO again will return a 'Device or resource busy error' so kill the PID of the existing command.
In case a GPIO doesn't have a name, follow this process for GPIO0_38:
root@am62xx-evm:~# gpiodetect #run gpiodetect to understand how gpio modules are assigned to chips gpiochip0 [600000.gpio] (92 lines) gpiochip1 [601000.gpio] (52 lines) gpiochip2 [1-0022] (24 lines) root@am62xx-evm:~# gpioset --unquoted -c 0 38=1 #use ctrl + c to send an interrupt to end the command, unquoted will not search for gpio line names. root@am62xx-evm:~# gpioset --unquoted -z -c 0 38=1 #add the -z flag to daemonize the command
For the most up to date commands, refer to the man page for the GPIO command or use the --help flag.
For an example of using the libgpiod commands in Linux SDK versions 9.0 and prior, refer to this FAQ: https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1260373/faq-transitioning-the-gpio-userspace-interface-from-sysfs-to-chardev
GPIO SYSFS has been deprecated as noted in the Documentation: https://git.ti.com/cgit/ti-linux-kernel/ti-linux-kernel/tree/Documentation/admin-guide/gpio/sysfs.rst?h=ti-linux-6.6.y
In the case that it needs to be re-enabled, refer to Kernel config below:
Once the kernel config is updated, refer to the Linux documentation for compiling a Kernel Image.
For this example, GPIO0_38 will be enabled. GPIO0's memory mapped address is 0x600000.
root@am62xx-evm:~# cd /sys/class/gpio/ root@am62xx-evm:/sys/class/gpio# ls -l total 0 --w------- 1 root root 4096 Feb 24 04:51 export lrwxrwxrwx 1 root root 0 Feb 24 04:51 gpiochip287 -> ../../devices/platform/bus@f0000/20010000.i2c/i2c-1/1-0022/gpio/gpiochip287 lrwxrwxrwx 1 root root 0 Feb 24 04:51 gpiochip311 -> ../../devices/platform/bus@f0000/601000.gpio/gpio/gpiochip311 lrwxrwxrwx 1 root root 0 Feb 24 04:51 gpiochip399 -> ../../devices/platform/bus@f0000/600000.gpio/gpio/gpiochip399 #Use for GPIO0 lrwxrwxrwx 1 root root 0 Feb 24 04:51 gpiochip486 -> ../../devices/platform/bus@f0000/bus@f0000:bus@4000000/4201000.gpio/gpio/gpiochip486 lrwxrwxrwx 1 root root 0 Feb 24 04:51 gpiochip510 -> ../../devices/platform/bus@f0000/3b000000.memory-controller/gpio/gpiochip510 --w------- 1 root root 4096 Feb 24 04:51 unexport root@am62xx-evm:/sys/class/gpio# echo 437 > export #GPIO0=399 + pin 38 = 437 root@am62xx-evm:/sys/class/gpio# cd gpio437 root@am62xx-evm:/sys/class/gpio/gpio437# echo out > direction root@am62xx-evm:/sys/class/gpio/gpio437# echo 1 > value
First lets start with checking the setup from the Linux SDK:
Next check while Linux is booting:
After Linux has booted, check the following:
This next section is to discuss some commonly asked questions.
Each SoC is partitioned into different device domains where each device domain is tied to SoC's core and is integrated with a certain list of peripherals. For more information about device domains, refer to each device's technical reference manual (TRM).
Main Domain will have two GPIO modules name GPIO0 and GPIO1 & MCU Domain will have one GPIO module name MCU_GPIO0. Each of these GPIOs in different device domains function the same. They differ in the number of GPIOs each modules may have.
To determine the number of consecutive GPIOs each peripheral has, refer to the device tree GPIO's 'ti,ngpio' parameter. This can be found in '<Linux SDK Install Path>/board-support/ti-linux-kernel-<version>/arch/arm64/boot/dts/ti/k3-<device>-<main/mcu>.dtsi'
For example, see the following for AM62x:
Notice for AM62x that the 'ti,ngpio' parameter value: Main Domain's GPIO0 = 92, Main Domain's GPIO1 = 52, and MCU Domain's MCU_GPIO0 =24.
For more information about 'ti,ngpio', refer to the Linux device tree documentation: https://git.ti.com/cgit/ti-linux-kernel/ti-linux-kernel/tree/Documentation/devicetree/bindings/gpio/gpio-davinci.yaml?h=ti-linux-6.6.y#n53
Note for AM62L, the GPIO modules are GPIO0 and GPIO2.
Generally speaking, different peripherals can be accessed from different cores/device domains including GPIOs.
Yes, the A53 cores running Linux will be able to access MCU GPIO. Refer to this E2E FAQ for an example: https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1398198/faq-am62x-how-to-allocate-use-gpios-from-different-device-domains
Important to note, when a MCU peripheral is claimed by Linux, the MCU application will not be able to access the same GPIO. Refer to the Multicore Academy for more information:
The first field is the PADCONFIG register address offset which is the last 3 hex bits defined in the device's TRM and datasheet. This tells Linux which pin these settings apply to.
The second field controls the input/output buffer which will be discussed in the next section. This does not define if a GPIO is input/output direction.
The third field is the Mux Mode of the pad. Typically, GPIOs use Mux Mode 7 but refer to the device's datasheet for complete information.
At a hardware level, for each pad on the SoC, there is a PADCONFIG register to control the settings of the Pad. See Pad Configuration Registers Section in the device's TRM for more information.
Each Pad as a Transceiver (Bit21) and Receiver (Bit18) buffer like the diagram below. By using the independent enable signals on each buffer, we can allow the pad to receive and/or transmit. These buffers allow the capability to receive and transmit data. This is true for most pads on the SoC, but refer to the datasheet for complete information.
So how does Linux configure the hardware? Refer to k3-pinctrl.h which defines the pin control bindings: https://git.ti.com/cgit/ti-linux-kernel/ti-linux-kernel/tree/arch/arm64/boot/dts/ti/k3-pinctrl.h?h=ti-linux-6.6.y
The definitions are the exact same as the PADCONFIG registers bit fields seen in the TRM Section named 'Pad Configuration Registers'. Linux will use PIN_INPUT/PIN_OUTPUT and any other parameters to configure the pin when Linux is booting.
#define PULLUDEN_SHIFT (16) #define PULLTYPESEL_SHIFT (17) #define RXACTIVE_SHIFT (18) #define DEBOUNCE_SHIFT (11) #define WKUP_EN_SHIFT (29) #define PULL_DISABLE (1 << PULLUDEN_SHIFT) #define PULL_ENABLE (0 << PULLUDEN_SHIFT) #define PULL_UP (1 << PULLTYPESEL_SHIFT | PULL_ENABLE) #define PULL_DOWN (0 << PULLTYPESEL_SHIFT | PULL_ENABLE) #define INPUT_EN (1 << RXACTIVE_SHIFT) #define INPUT_DISABLE (0 << RXACTIVE_SHIFT) /* Only these macros are expected be used directly in device tree files */ #define PIN_OUTPUT (INPUT_DISABLE | PULL_DISABLE) #define PIN_OUTPUT_PULLUP (INPUT_DISABLE | PULL_UP) #define PIN_OUTPUT_PULLDOWN (INPUT_DISABLE | PULL_DOWN) #define PIN_INPUT (INPUT_EN | PULL_DISABLE) #define PIN_INPUT_PULLUP (INPUT_EN | PULL_UP) #define PIN_INPUT_PULLDOWN (INPUT_EN | PULL_DOWN)
Notice the following:
This means in the device tree, PIN_INPUT means both the input buffer and the output buffer is enabled. PIN_OUTPUT means only the output buffer is enabled and input buffer is disabled. Remember, enabling the buffers allow for the functionality to receive and transmit on the pad.
This also means that configuring the PIN_INPUT/PIN_OUTPUT does not impact the GPIO state.
GPIO SYSFS has been deprecated so its possible that it was removed from the kernel config. It can be re-enabled in the kernel config as seen in the GPIO Userspace section.
Libgpiod is a tool set and API that is not written by TI. For new SDK versions, the libgpiod version imported might be different that previous SDKs. For the most up to date commands, refer to the man page for the GPIO command or use the --help flag.
When Linux is booting, it dynamically assigns gpiochips to GPIO modules defined in the device tree. With different Linux SDK version/Linux Kernel versions, there could be new dependencies that cause a deferred probing during the booting process. This will change how the gpiochips are enumerated. Its recommended to assign names to GPIOs for the application to search for the GPIO's name rather than relying on the gpiochip number.
The gpio-hog property can be used to do this. See below for some examples:
First, there is very limited support for custom drivers and applications because its beyond the scope of the E2E forums. We can only provide support for code provided in the Linux SDK because its been tested and validated by TI.
Make sure that the GPIO can be used correctly in userspace before porting over to a custom application.
For some basic examples, refer to the Libgpiod github page: https://github.com/brgl/libgpiod/tree/master/examples
For using the legacy interface, refer to its documentation: https://git.ti.com/cgit/ti-linux-kernel/ti-linux-kernel/tree/Documentation/driver-api/gpio/legacy.rst?h=ti-linux-6.6.y