After designing and building a custom carrier board based on a Variscite i.MX System on Module / Computer on Module, one of the first tasks for your software team will be to create a new device tree file for the Linux kernel. The device tree is responsible for describing the hardware configuration to the kernel, including the function and settings of each pin. Most i.MX SoC pins are multiplexed and offer up to 8 alternate pin functions. Additionally, most pins have configurable settings such as internal pull-ups, pull-downs, etc. This guide walks through how to configure a pin function and its settings for devices in the Linux device tree for Variscite i.MX System on Modules.

Setting up the pinmux for a device

When adding a device node that requires physical control over a pin, such as I2C, PWM, UART, etc. it is necessary to specify the function and settings for each pin. There are two main steps to this:

  1. Add a pin group node to the iomuxc node that defines the pin function and settings
  2. Reference that pin group node in the node of the new device that you are adding

To demonstrate this process, let’s review the pinmux configuration for UART3 on the VAR-SOM-MX8X SoM:

Step 1: Add a pin group node to the iomuxc node

For VAR‑SOM‑MX8X, The UART3 pin group node is defined in imx8qxp-var-som.dtsi:

&iomuxc {

pinctrl_lpuart3: lpuart3grp {

fsl,pins = <

IMX8QXP_SCU_GPIO0_00_ADMA_UART3_RX          0x06000020
IMX8QXP_SCU_GPIO0_01_ADMA_UART3_TX          0x06000020




iomuxc is a device node for the imx8qxp pinctrl driver. It is defined in imx8qxp.dtsi. Documentation for this node can be found in the Linux kernel source tree, at:

pinctrl_lpuart3 is a label used to provide a name for the UART3 pin group. We will use this label in the next step.

“fsl,pins” is a list of pins used on UART3 and their configuration. Each entry consists of a macro for the pin function (pinmux) and an integer for the pad setting (internal pull-ups, pull-downs, etc.).

Pin Function:

Each pin has up to 8 functions that are multiplexed. To simplify selecting the pin function, NXP provides macros for each SOC with the following naming convention:


In this example, the “fsl,pins” has two pin entries: pin SCU_GPIO0_00 is configured for function ADMA_UART3_RX and pin SCU_GPIO0_01 is configured for function ADMA_UART3_TX.

Warning:  To avoid pinmux conflicts, each pin should be set to just one function and each function should be assigned to just one pin.

Each i.MX SoC has a pin config file with pin function macro definitions as well as documentation in the device tree bindings directory. For more information about the values of the pin function macros, please refer to the documentation of the specific SoC.

For i.MX8X and i.MX8QM, the pin header files are located at “include/dt‑bindings/pinctrl/” and as mentioned before, the documentation is located at “Documentation/devicetree/bindings/arm/freescale/”

Header file Documentation
pads-imx8qxp.h fsl,scu.txt
pads-imx8qm.h fsl,scu.txt

For the i.MX8M family, the pinfunc files are located at “arch/arm64/boot/dts/freescale/” and the documentation is located at “Documentation/devicetree/bindings/pinctrl/”

Header file Documentation
imx8mp-pinfunc.h fsl,imx8mp-pinctrl.txt
imx8mq-pinfunc.h fsl,imx8mq-pinctrl.txt
imx8mm-pinfunc.h fsl,imx8mm-pinctrl.txt
imx8mn-pinfunc.h fsl,imx8mn-pinctrl.txt


For the i.MX6/i.MX6UL/i.MX7 families, the pinfunc files are located at “arch/arm/boot/dts/” and the documentation is located at “Documentation/devicetree/bindings/pinctrl/”

Header file Documentation
imx6dl-pinfunc.h fsl,imx6dl-pinctrl.txt
imx6q-pinfunc.h fsl,imx6q-pinctrl.txt
imx6ul-pinfunc.h fsl,imx6ul-pinctrl.txt
imx6ull-pinfunc.h fsl,imx6ul-pinctrl.txt
imx7d-pinfunc.h fsl,imx7d-pinctrl.txt

Pad Setting:

In addition to the pin function macro, each “fsl,pins” entry requires a value for the pad settings. In this case, 0x06000020 is the pad setting value that configures the pin pull-up, drive strength, etc. The configuration options for each pin are described in the i.MX8QXP Reference Manual.

Below is the pad/mux control register for the UART3_RX pin, from the i.MX8QXP Reference Manual Rev. 0, 05/2020, page 957:


                                                 Figure 1: Pad/mux control register for SCU_GPIO0_00



VAR-SOM-MX8X : NXP iMX 8X System on Module (SoM)




Field Function
update lock for mux control
write 1 to allow programming to mux control [29:19]
update lock for pad control
write 1 to allow programming to pad control [14-1:0]
000b – SCU_GPIO0_IO00
001b – SCU_UART0_RX
010b – M40_UART0_RX
011b – ADMA_UART3_RX
100b – LSIO_GPIO2_IO03
output and input configuration
11b – INOUT
lower power configuration
00b – PASS
10b – LATE_ISO
11b – LATCH
Enable WAKEUP change
Allows modification of the WAKEUP_CTRL field without having to read or modify the other bits
wakeup control
000b – OFF
001b – RESAMPLE (The wakeup field is not modified (it retains its old value) but because a write is done, the flag is cleared)
100b – LOW
101b – FALL
110b – RISE
111b – HIGH
18-17 Reserved
Pull Down Pull Up
00b – Bus-Keeper
01b – pull up
10b – pull down
11b – No Pull
4-3 Reserved
000b – Drive select 1mA
001b – Drive select 2mA
010b – Drive select 4mA
011b – Drive select 6mA
100b – Drive select 8mA
101b – Drive select 10mA
110b – Drive select 12mA
111b – High Speed


Considering this register has the value of 0x06000020 (bits 5, 25, 26 enabled) in this example, this is the list of all configurations and their values:

update_mux_mode: value 0

update_pad_ctl: value 0

mux_mode: value 0 (Configured by the pin function macro)

sw_config: value 0b11

lp_config: value 0

enable_wakeup_change: value 0

wakeup_ctrl: value 0

pull: value 0b01: Internal pull up resistor selected.

DSE: value 0b000: Drive Strength of 1mA selected.

To know more about all possible configurations, please check the i.MX8QXP Reference Manual.


Step 2: Reference that pin group node in the device node

After adding the pin group node (lpuart3grp) to iomuxc, the next step is to provide a reference to the device node that will use these pins. In this example, this is lpuart3, which is defined in imx8-ss-dma.dtsi and configured by Variscite in imx8qxp-var-som-symphony.dtsi:

/* Console */
&lpuart3 {

pinctrl-names = “default”;
pinctrl-0 = <&pinctrl_lpuart3>;


During lpuart3 initialization, the Linux device core will call pinctrl_bind_pins prior to calling the probe function in the lpuart3 driver. The pinctrl driver will then write to the i.MX specific registers to configure the default pin state for each entry in pinctrl_lpuart3.

Pin States

Some devices support multiple pin states.
For example, an MMC device:

 &usdhc1 {

pinctrl-names = “default”, “state_100mhz”, “state_200mhz”;
pinctrl-0 = <&pinctrl_usdhc1>;
pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
pinctrl-2 = <&pinctrl_usdhc1_200mhz>;


In this example, usdhc1 has three pinctrl states: default, 100mhz, and 200mhz. The Linux device core will initialize the default state before calling the platform driver probe function. However, the driver also has the option to change pin settings depending on the MMC mode.

For more information on this subject, see the following documentation file in the kernel source tree: “Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt”



In summary, device nodes that need pin configuration require a reference to a pin group node defined in iomuxc. The pin group node defines the pin function and settings for each relevant pin. To learn more about the details for each SoC, the documentation is available in the kernel source tree at “Documentation/devicetree/bindings/pinctrl/”.