Skip to main content
Version: 5.0

Pin Multiplexing - Changing Pin Functionalities in the Linux Device Tree

Introduction​

To improve design flexibility, the NXP's i.MX SoC family provides pin muxing capability. This feature allows developers to select, for the device's IO pins, one among multiple functions. These pins have a default function and may have other functionalities (ALT0, ALT1, ALT2, ALT3, etc.).

Toradex provides the Pinout Designer tool. Among other features, with this software you can verify in a simple and easy-to-use graphical interface the default function for each module's pins. You can also check which pins are available for a specific function so that you can determine the best allocation of pins for your design.

In Linux, to modify the default status of the pin muxing configuration, it is necessary to alter the Device Tree. However, in Torizon you can use the Device Tree Overlays article approach in order to change this without the need of external cross-compilation. This process comprises of writing a device tree overlay (.dts file) on the host system, building the overlay and applying it using TorizonCore Builder Tool directly to the module connected to the host system via ssh. Make sure to read the related documentation to get a better idea.

In this article, we will explore how to create a DTS i.e. device tree source file to change the pin muxing configuration.

This article complies to the Typographic Conventions for Torizon Documentation

Prerequisites​

Creating the DTS​

Introduction​

Before we start modifying the software we need to plan the function for each module pin we plan to use.

As explained earlier, Pinout Designer tool is a handy tool to understand the default pins function allocation before modifying it.

info

Tip: On the Pinout Designer tool, you can select View->Pin Filter->Configure Pin Filter to visualize only the functions of a specific pin.

And of course, another important source of information about the pin function multiplexing for a specific SoM is the Toradex module's datasheet.

The .dts file that sets the pin muxing will target the IOMUX Controller (IOMUXC) for i.MX. You can find the documentation and device tree binding for the IOMUX controller in the documentation files in the Linux Kernel source-code. Please, be sure to check the documentation for the corresponding kernel version of your SoM.

Here we show the base template for an overlay to change the default pin muxing:

/dts-v1/;
/plugin/; // Indicates that the file is a Device Tree Overlay

// A) Header file with pin definitions
#include <soc-pinfunc.h>

/ {
compatible = "toradex,apalis-imx8";
};

&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl-originally-in-device-tree>, <&pinctrl_my_pins>; // D) OPTION #1: Pin group available in userspace i.e. as GPIO

name-of-som* {
// B) Pin Control node
pinctrl_my_pins: my_muxgrp {
fsl,pins = <
PIN_NAME BIT_CONFIG // C) PINS
>;
};
};
};

&genericPeripheral {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_my_pins>; // D) OPTION #2: Pin group will be used in genericPeripheral
status = "okay";
};
info

To simplify the comprehension, in this article we divided this example dts file into the following four sections: A, B, C and D.

A) Pin Definitions Header File​

Each SoC has its own pin definitions file. These files can be downloaded by using dt checkout subcommand of TorizonCore Builder Tool, which is the tool that is able to build and apply Device Tree Overlays. These are the definition files for each SoC:

SoMHeader file
Apalis iMX8device-trees/include/dt-bindings/pinctrl/pads-imx8qm.h
Apalis/Colibri iMX8Xdevice-trees/include/dt-bindings/pinctrl/pads-imx8qxp.h
Apalis iMX6 Quaddevice-trees/dts-arm32/imx6q-pinfunc.h
Apalis/Colibri iMX6 Dual/DualLitedevice-trees/dts-arm32/imx6dl-pinfunc.h
Colibri iMX7 1GBdevice-trees/dts-arm32/imx7d-pinfunc.h
Verdin iMX8M Plusdevice-trees/dts-arm64/imx8mp-pinfunc.h
Verdin iMX8M Minidevice-trees/dts-arm64/imx8mm-pinfunc.h

This file lists each pin with all of its possible muxing options. It contains definitions in a pattern comprising the ball name prefix and the muxing option suffix.

As an example, MX6QDL_PAD_SD2_DAT1__SD2_DATA1, comprises:

  • The ball name: MX6QDL_PAD_SD2_DAT1
  • The pin mux option: SD2_DATA1

B) Pin Control Node​

Here we have the actual pin control node that will add the chosen pins as GPIOs. In our example, we decided to call the node my_muxgrp and label it as pinctrl_my_pins. You don't need to use the same names though: these are arbitrary names, and you can choose any name according to your preference.

*Depending on the SoM you might have to put the pin control node inside another node named after the SoM, like apalis-imx8qm. Check how pin control nodes are declared inside iomuxc in the unaltered Device Tree and use that as reference.

C) Adding the Pins​

Finally, we come to the pin functions declaration themselves. Here you can add multiple pins.

The PIN_NAME field should be in the format <name prefix>_<muxing option suffix> as explained earlier, and that will directly reference the pin definition .h file, so make sure that it is exactly the same.

The BIT_CONFIG is a hexadecimal value to set the Pad Control register of the SoC. See the SoC Reference Manual and also the Device Tree Customization article for more information.

D) Referencing the Pin Control Node​

A pin control node/group by itself doesn't do much. Other nodes can reference it in order to use its pins, and this is what effectively applies the functions to the pins.

Generally, there are two options when referencing a pin group:

Option #1: iomuxc​

Setting the value of pinctrl-0 in iomuxc with a pin control node indicates that the pins inside it will be available in userspace, making it possible to use them as GPIOs.

<&pinctrl-originally-in-device-tree> represents one or more pin control nodes that were referenced in the unaltered device tree, and it's usually good to keep them in the overlay, as they would be otherwise dereferenced.

Option #2: Peripherals​

Similarly, setting pinctrl-0 in a peripheral node (e.g. lpspi2) with a pin control group makes the pins in it available to the peripheral.

caution

While you can reference the same pin control group on multiple nodes, be sure to only have one of them enabled in the device tree to avoid pin conflicts.

Example: Creating a Device Tree Overlay to Set the GPIO Functionality to a Pin​

Suppose we want to set MXM3 pins 134 and 136 of Apalis iMX8 as GPIO.

By looking to the SoC Functions List chapter in the datasheet, we see the following table:

Pin Multiplexing information on SoM Datasheet

The SoC ball names for pins 134 and 136 areUART0_TX and UART0_RX respectively.

First, run the dt checkout subcommand from TorizonCore Builder Tool to get the device-trees repository.

Then, we have to make sure that these pins are not already in use by some other peripheral in the device tree. In this case, the most important file to take a look at is imx8-apalis-v1.1.dtsi:

$ cat device-trees/dts-arm64/imx8-apalis-v1.1.dtsi | grep UART0_TX

Other files from the device tree may have code activating this pin for different functionality, so it's better to take a look at all the dts and dtsi corresponding to your setup.

info

The device tree that is loaded is set at boot time via u-boot variable fdtfile.

info

Protip: You can use the command grep to check all the files at the same time.

In our case, we found that lpuart0 is using UART0_TX and UART0_RX, as there is a pin control group labeled pinctrl_lpuart0 that has both pins set as UART, and it is referenced in lpuart0:

imx8-apalis-v1.1.dtsi
[...]
pinctrl_lpuart0: lpuart0grp {
fsl,pins = <
IMX8QM_UART0_RX_DMA_UART0_RX 0x06000020
IMX8QM_UART0_TX_DMA_UART0_TX 0x06000020
>;
};
[...]
&lpuart0 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lpuart0>;
};
[...]

The pins are only considered in use if the peripheral referencing them is activated, so disabling lpuart0 should be enough to make them available. Therefore our overlay has to have the following code:

&lpuart0 {
status = "disabled";
};

The next step is to find the pin functionality definition macro. Looking to the pin definition file in device-trees, we can see the UART0_TX pin:

$ cat device-trees/include/dt-bindings/pinctrl/pads-imx8qm.h | grep UART0_TX
#define IMX8QM_UART0_TX 22
#define IMX8QM_M40_I2C0_SDA_M40_UART0_TX IMX8QM_M40_I2C0_SDA 1
#define IMX8QM_M41_I2C0_SDA_M41_UART0_TX IMX8QM_M41_I2C0_SDA 1
#define IMX8QM_UART0_TX_DMA_UART0_TX IMX8QM_UART0_TX 0
#define IMX8QM_UART0_TX_SCU_UART0_TX IMX8QM_UART0_TX 1
#define IMX8QM_UART0_TX_LSIO_GPIO0_IO21 IMX8QM_UART0_TX 3
#define IMX8QM_SCU_GPIO0_01_SCU_UART0_TX IMX8QM_SCU_GPIO0_01 1

The corresponding GPIO pin definition macro for UART0_TX and UART0_RX are, respectively:

  • IMX8QM_UART0_TX_LSIO_GPIO0_IO21
  • IMX8QM_UART0_RX_LSIO_GPIO0_IO20

The bit configuration values were selected according to other GPIO pins set in the device tree. In our case, we will set them to 0x06000021.

With these values, we go forward and write the .dts file:

/dts-v1/;
/plugin/;

#include <dt-bindings/pinctrl/pads-imx8qm.h>

/ {
compatible = "toradex,apalis-imx8";
};

&lpuart0 {
status = "disabled";
};

&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_cam1_gpios>, <&pinctrl_dap1_gpios>,
<&pinctrl_esai0_gpios>, <&pinctrl_fec2_gpios>,
<&pinctrl_gpio3>, <&pinctrl_gpio4>, <&pinctrl_gpio_keys>,
<&pinctrl_gpio_usbh_oc_n>, <&pinctrl_lpuart1ctrl>,
<&pinctrl_lvds0_i2c0_gpio>, <&pinctrl_lvds1_i2c0_gpios>,
<&pinctrl_mipi_dsi_0_1_en>, <&pinctrl_mipi_dsi1_gpios>,
<&pinctrl_mlb_gpios>, <&pinctrl_qspi1a_gpios>,
<&pinctrl_sata1_act>, <&pinctrl_sim0_gpios>,
<&pinctrl_usdhc1_gpios>, <&pinctrl_my_gpios>;

apalis-imx8qm {
pinctrl_my_gpios: my_example_grp {
fsl,pins = <
IMX8QM_UART0_TX_LSIO_GPIO0_IO21 0x06000021
IMX8QM_UART0_RX_LSIO_GPIO0_IO20 0x06000021
>;
};
};
};

Note that pinctrl-0 can have multiple pin control nodes.

Give the file a name like apalis-imx8_extra_gpios_overlay.dts and save it in device-trees/overlays/.

Building and Enabling the Overlay​

To build and enable the overlay defined by the .dts file, refer to:

Important: Pin Settings Conflicts​

A common situation when defining a function for a pin is that it is already in use by other peripherals, causing a pin conflict. If this is the case, the interface that is currently using the pin must be disabled.

To check if the pin is already in use, inspect the base device tree files. The .dts and .dtsi that describe the device tree for the board is available inside the checked out device-trees repository using TorizonCore Builder Tool:

  • For i.MX6 and i.MX7 SoMs (ARM 32-bits): device-trees/dts-arm32/
  • For i.MX8 SoMs (ARM 64-bits): device-trees/dts-arm64/

In TorizonCore Builder, list-devicetrees provides list of available device trees in unpacked Toradex Easy Installer image of Torizon on the host system. For more information on available commands for device tree and device tree overlay handling, refer to Device Tree Overlays on Torizon.

See the previous section on how to find the correct pin name to look for when inspecting the device tree source files.

If you selected a pin that is already in use, you need to disable the current functionality associated with this pin or use another one.

danger

Failure to resolve all pin conflicts can cause the kernel not to boot at all.

Removing a conflicting DTO​

In case you find yourself with a non-working kernel (normally, stuck at the "Loading kernel..." message), you can delete your custom overlays easily directly from your PC. You would need to set up a USB-OTG connection with your PC and run the UMS functionality of U-Boot by simply stopping the autoboot at U-boot, and running the following commands:

> ums 0 mmc 0

If the connection is properly done, you should see a BOOT partition show up in your PC. Simply delete the conflicting .dtbo and the conflicting line in the overlays.txt file.

info

Check your carrier board datasheet which port and jumper configurations work with USB-OTG. In Toradex Carrier Boards, it simply requires using the correct port or a small jumper removal (like Ixora's JP2).

info

You can delete the overlays.txt file if you don't have any DTBO working. If there is need to build and apply an overlay, torizoncore-builder will build and apply overlay directly to the device tree and create devicetree named device tree binary file.

Send Feedback!