Select the version of your OS from the tabs below. If you don't know the version you are using, run the command cat /etc/os-release
or cat /etc/issue
on the board.
Remember that you can always refer to the Torizon Documentation, there you can find a lot of relevant articles that might help you in the application development.
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
Toradex SoM with TorizonCore installed. For instructions about how to install TorizonCore in your board, refer to Toradex Quickstart Guide
Basic understanding of Docker and docker run
command options
Basic understanding of a Device Tree and Device Tree Overlays
Pinout Designer Tool installed on the host machine (recommended)
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.
Note: 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"; };
Note: To simplify the comprehension, in this article we divided this example dts file into the following three sections: A, B, C and D.
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:
SoM | Header file |
---|---|
Apalis iMX8 | device-trees/include/dt-bindings/pinctrl/pads-imx8qm.h |
Apalis/Colibri iMX8X | device-trees/include/dt-bindings/pinctrl/pads-imx8qxp.h |
Apalis iMX6 Quad | device-trees/dts-arm32/imx6q-pinfunc.h |
Apalis/Colibri iMX6 Dual/DualLite | device-trees/dts-arm32/imx6dl-pinfunc.h |
Colibri iMX7 1GB | device-trees/dts-arm32/imx7d-pinfunc.h |
Verdin iMX8M Plus | device-trees/dts-arm64/imx8mp-pinfunc.h |
Verdin iMX8M Mini | device-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:
MX6QDL_PAD_SD2_DAT1
SD2_DATA1
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.
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.
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:
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.
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.
Warning: 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.
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:
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.
Note: The device tree that is loaded is set at boot time via u-boot variable fdtfile. Note: 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/
.
To build and enable the overlay defined by the .dts file, refer to:
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:
device-trees/dts-arm32/
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.
Attention: Failure to resolve all pin conflicts can cause the kernel not to boot at all.
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.
Note: 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).
Note: 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.
To improve design flexibility, the NXP's i.MX SoCs family provides pin muxing capability. This feature allows developers to select among multiple functions for the device's IO pins. These pins have a default (ALT0) function and may have other functionalities (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 writing a dts file, building the overlay, and enabling it directly in the module. Make sure to read the related documentation to get a better idea.
In this article, we will explore how to create a dts file to change the pin muxing configuration.
This article complies to the Typographic Conventions for Torizon Documentation
Toradex SoM with TorizonCore installed. For instructions about how to install TorizonCore in your board, refer to Toradex Quickstart Guide
Basic understanding of Docker and docker run
command options
Basic understanding of a Device Tree and Device Tree Overlays
Pinout Designer Tool installed on the host machine (recommended)
Before we start modifying the software we need to plan the function for each module's pin we plan to use.
As explained earlier, Pinout Designer tool is a handy tool to understand the default pins function (ALT0) allocation before modifying it.
Note: 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 to set 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 on 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/;
// A) Header file with pin definitions
#include <soc-pinfunc.h>
/ {
compatible = "toradex";
fragment@0 {
target = <&iomuxc>;
__overlay__ {
pinctrl-names = "default";
// B) GPIO pinctrl node
pinctrl-0 = <&pinctrl_my_gpios>;
pinctrl_my_pins: my_muxgrp {
fsl,pins = <
PIN_NAME BIT_CONFIG // C) PINS
>;
};
};
};
};
Note: To simplify the comprehension, in this article we divided this example dts file into the following three sections: A, B, and C.
Each SoC has it's own pin definitions file. These files are present on the Developer Tools Container, which is the tool to build the Device Tree Overlays. These are the definition files for each SoC:
SoM | Header file |
---|---|
Apalis iMX8 | </device-tree-overlays/include/dt-bindings/pinctrl/pads-imx8qm.h> |
Apalis/Colibri iMX8X | </device-tree-overlays/include/dt-bindings/pinctrl/pads-imx8qxp.h> |
Apalis/Colibri iMX6 | </device-tree-overlays/dts-arm32/imx6q-pinfunc.h> |
Colibri iMX7 1GB | </device-tree-overlays/dts-arm32/imx7d-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:
MX6QDL_PAD_SD2_DAT1
SD2_DATA1
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 accordingly with your preference.
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, 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.
Suppose we want to change the pin 11 and 13 of the MXM3 connector of the Apalis iMX8 for GPIO function.
By looking to the SoC Functions List chapter on the datasheet, we see the following table:
The corresponding SoC balls' names are: FLEXCAN2_RX
and FLEXCAN2_TX
.
First, make sure to run the Torizon Developer Tools container.
Once inside the container, we can 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 is the fsl-imx8qm-apalis.dtsi
:
## cat /device-tree-overlays/dts-arm64/fsl-imx8qm-apalis.dtsi | grep FLEXCAN2_RX
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.
Note: The device tree that is loaded is set at boot time via u-boot variable fdt_file. Note: Protip: You can use the command grep to check all the files at the same time.
In our case, we didn't find any peripheral using the FLEXCAN2_RX
and FLEXCAN2_TX
pin.
The next step is to find the pin functionality definition macro. Looking to the pin definition file inside the Developer Tools container, we can see the FLEXCAN2_RX
pin:
## cat /device-tree-overlays/include/dt-bindings/pinctrl/pads-imx8qm.h | grep FLEXCAN2_RX
#define SC_P_FLEXCAN2_RX 150 /* DMA.FLEXCAN2.RX, LSIO.GPIO4.IO01 */
#define SC_P_FLEXCAN2_RX_DMA_FLEXCAN2_RX SC_P_FLEXCAN2_RX 0
#define SC_P_FLEXCAN2_RX_LSIO_GPIO4_IO01 SC_P_FLEXCAN2_RX 3
The corresponding pin definition macro for the GPIO for FLEXCAN2_RX
and FLEXCAN2_TX
functionalities are, respectively:
SC_P_FLEXCAN2_RX_LSIO_GPIO4_IO01
SC_P_FLEXCAN2_TX_LSIO_GPIO4_IO02
In this case, the bits configuration values were selected according to the registers SW_PAD_CTL
of the SoC, as described in the previous sections of this article. In our case, we will set it with the value of 0x41
.
With these values, we go forward and write dts file:
/dts-v1/;
/plugin/;
#include </device-tree-overlays/include/dt-bindings/pinctrl/pads-imx8qm.h>
/ {
compatible = "toradex";
fragment@0 {
target = <&iomuxc>;
__overlay__ {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_my_gpios>;
pinctrl_my_gpio_example: my_example_grp {
fsl,pins = <
SC_P_FLEXCAN2_RX_LSIO_GPIO4_IO01 0x41
SC_P_FLEXCAN2_TX_LSIO_GPIO4_IO02 0x41
>;
};
};
};
};
Note: You can write this file in your development PC and then transfer it to the Torizon Developer Tools container OR directly write it in the module with vim (also available inside the container).
To build and enable the overlay defined by the dts file, refer to:
It's a common situation when developers are defining a function for a pin, that this unit to be 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 describes the device tree for the board is available inside the Developer Tools Container in the following path:
/device-tree-overlays/dts-arm32/
./device-tree-overlays/dts-arm64/
In TorizonCore, the Developer Tools Container also have the command dtconf status
to verify which is the base device tree for your board.
See in the next chapter 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 select another pin.
Attention: Failure to resolve all pin conflicts can cause the kernel not to boot at all.
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.
Note: Check your carrier board datasheet what ports 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). Note: You can delete the overlays.txt file if you don't have any DTBO working. TorizonCore will recreate this file when activating a new DTBO.