One of the most commonly used mechanisms of swapping between different established boot processes or configurations is by using a DIP Switch. This small manual electric switch contains n different setting values which would lead to 2^n different status: A 2 bit DS, would let swapping between 4 different states; a 3 bit DS, 8, and so on.
The idea behind a dip switch circuit is very simple and can vary depending on the default state you want the circuit to have. For instance, if the DS will be closed by default, leaving a path to GND, the pin connected to it will read a 0, while, if the DS is opened, a path to VCC will open and this will change to a 1.
Warning: Remember using pull-up/pull-down resistors in your circuit to avoid creating a short between VCC and GND in any case.
In our example, we will be using a 2-bit dip switch, in order to have up to 4 different boot processes, with our Colibri iMX6ULL and our Iris Carrier Board. This inexpensive combination makes it suitable for DIY projects, projects where not much computational power is required, or no need for a dedicated GPU/OpenGL processor.
This article is a reference only, Toradex does not support nor regularly updates the instructions provided. It may not be suitable for the entire family of Toradex SoMs and it is not possible to guarantee that it will always work.
In order to change the boot process, we will be making some changes to the bootloader logic of our boards. The bootloader is the first piece of code executed, and it is usually located in a hardcoded address defined by the SoC vendor and the fusing of the chip. Toradex's modules use U-boot as Linux bootloader and are where we will be applying our changes to this example.
Before jumping directly to the software changes, make sure you have a cross-compilation environment ready. If you don't, make sure to check the Toradex Quickstart Guide.
Attention: the SDK from the Quickstart Guide is provided as-is for the sole purpose of following the steps on that guide. You are highly encouraged to use Yocto to build and integrate your changes to the Toradex BSP or, if required, build your own SDK using Yocto. See OpenEmbedded (core) for more information.
Once you have downloaded and setup the environment with your toolchain and your environment variables (ARCH, CROSS_COMPILE and PATH should suffice), install Git (if you don't have it already) and clone Toradex U-boot repository:
While the article from the link above provides always up-to-date instructions for how to obtain and build U-Boot for all Toradex modules, the example in this article provides instructions for the version of U-Boot available at time of writing, that is January 2019.
cdmkdir DIPexamplegit clone git://git.toradex.com/u-boot-toradex.gitcd u-boot-toradex/git checkout 2016.11-toradex
Note: If you have problems with the download, check with "git clone https://..." or "git clone http://... instead of git://. It may be capped by your firewall rules"
With this, you will have Toradex U-boot repository with the Colibri iMX6ULL (and other) board specific files in it.
In order to quickly check if everything is in order, let's try a compilation with the default settings of U-boot. Additionally, in the U-boot compilation, the device tree is also compiled, which is what keeps the hardware information and is used by U-boot to know the detailed system information.
$ sudo apt-get install device-tree-compiler
Note: This applies for Ubuntu. For other distros, use the advised package manager.
u-boot-toradex
$ make colibri-imx6ull_defconfig
$ make -j4
Note: You can ignore the DTB format warnings that may appear.
If you are having an error at this point, is likely that you haven't set the environment variables. Make sure that the toolchain is correctly pointed and the architecture selected:
$ echo $ARCH arm $ echo $CROSS_COMPILE arm-linux-gnueabihf- $ echo $PATH /home/alvaro/gcc-linaro-7.3.1-2018.05-x86_64_arm-linux-gnueabihf/bin/:/home/alvaro/platform-tools:/home/alvaro/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
Note: Your PATH and/or toolchain version may differ.
In this example, we will be changing the board files for the Colibri iMX6ULL:
u-boot-toradex
board/toradex/colibri-imx6ull/colibri_imx6ull.c
Note: You can expect similar paths for other modules.
This file contains the board-specific boot-process logic, and where we will be adding our changes.
In order to use this file correctly, we should take into account 3 additional c:
In this case, we will be using 2 of the default GPIO pins found in the Colibri iMX6ULL and the Iris Carrier board. However, take into account that if required, you can use many of the other pins available from unused functions.
In order to better choose the pins, we recommend going through the Iris Carrier Board datasheet and Colibri iMX6ULL datasheet: Colibri iMX6ULL Datasheet Iris Carrier Board Datasheet
For this example, we will be using the following pins from the X16 Extension Connector of the Iris:
The pinmux with the pin definitions and its different functions available is covered in the following file:
u-boot-toradex
arch/arm/include/asm/arch-mx6/mx6ull_pins.h
While the pinmux definitions and bit fields are covered in:
u-boot-toradex
arch/arm/include/asm/imx-common/iomux-v3.h
Note: Pin refers to the physical pin of the SoC/SoM, while pinmux stands for Pin Multiplexing, and its a convenient way of supporting more functions than available pins. This documentation is nothing more than showing how to use the pinmux in order to change the default function to a GPIO (if required) and then read the value of the selected GPIO during the bootloader phase.
You may find the field information in Toradex Device Tree customization page and in the Colibri iMX6ULL Datasheet: https://developer.toradex.com/device-tree-customization#pinmux-imx6
Note: The detailed meaning of all these fields can be found in the i.MX6 ULL Reference Guide from NXP.
Going back to our pins, according to the Colibri iMX6ULL datasheet, we obtain the following information:
Note: The IOMUX_PAD macro function is just a convenient way to convert the different field values into the corresponding bit fields.
With the above information clear, we can go back to our board file and add our changes.
Every function in the board file defines the routine at a certain point of the booting process (board_eth_init, board_mmc_init...). In this case, we decided to add our logic after other more critical options, so we choose the board_late_init function, but it is completely up to the user where to add their logic.
In order to change the GPIO values of a GPIO pin, we will have to go through 3 steps:
#define DIP_SW0 IMX_GPIO_NR(4, 26) #define DIP_SW1 IMX_GPIO_NR(4, 14)
static iomux_v3_cfg_t const dip_switch_pads[] = { MX6_PAD_CSI_DATA05__GPIO4_IO26 | MUX_PAD_CTRL(NO_PAD_CTRL), MX6_PAD_NAND_CE1_B__GPIO4_IO14 | MUX_PAD_CTRL(NO_PAD_CTRL), };
imx_iomux_v3_setup_multiple_pads(dip_switch_pads, ARRAY_SIZE(dip_switch_pads)); gpio_request(DIP_SW0, "DIP_SW0"); gpio_request(DIP_SW1, "DIP_SW1"); int dip0; int dip1; dip0 = gpio_get_value(DIP_SW0); dip1 = gpio_get_value(DIP_SW1);
At this point, you can add pretty much any logic you want depending on the values read in the DIP Switch.
One quick test you can do is simply adding a printf with both values in the U-boot output:
printf("DIP Switch: %x%x\n", dip0, dip1);
Another desired change would be to add or change the environment variables found in U-boot. Some examples would be:
Note: Toradex modules feature 2 different flash technologies: NAND-RAW and eMMC. Colibri iMX6ULL modules uses NAND-RAW.
All these can be achieved with the setenv(name, value), and many of these above examples can be actually found in the Toradex Developer site. In this example, we are going to use the DIP_SW0 to change between framebuffer configurations (in case we have 2 different LCD options).
In order to flash the new U-boot, the easiest way is to use Toradex Easy Installer (TEZI).
We will be using an already formatted Colibri iMX6ULL TEZI image, where we will simply overwrite the U-boot binary before flashing.
Removable Device
$ tar xvf <Path of your tar>/colibri-imx6ull_lxde-image-tezi_2.8...tar --no-sameowner
Removable Device/Colibri-iMX6ULL...
$ sudo cp <uboot-toradex folder>/u-boot-nand.imx u-boot-nand.imx
$ sudo sync
Note: In modules with an eMMC flash, you can use the UMS function of U-boot to easily modify the contents of the flash memory without having to use TEZI. You can check more information about this function in "How to Clone Embedded Linux on eMMC Based Toradex Modules" documentation.
#define DIP_SW0 IMX_GPIO_NR(4, 26) #define DIP_SW1 IMX_GPIO_NR(4, 14) static iomux_v3_cfg_t const dip_switch_pads[] = { MX6_PAD_CSI_DATA05__GPIO4_IO26 | MUX_PAD_CTRL(NO_PAD_CTRL), MX6_PAD_NAND_CE1_B__GPIO4_IO14 | MUX_PAD_CTRL(NO_PAD_CTRL), }; int board_late_init(void) { ... // DIP Switch routine imx_iomux_v3_setup_multiple_pads(dip_switch_pads, ARRAY_SIZE(dip_switch_pads)); gpio_request(DIP_SW0, "DIP_SW0"); gpio_request(DIP_SW1, "DIP_SW1"); int dip0; int dip1; dip0 = gpio_get_value(DIP_SW0); dip1 = gpio_get_value(DIP_SW1); printf("DIP Switch: %x%x\n", dip0, dip1); // EDT 5.7" TFT VGA if (dip0 == 0) { setenv("vidargs", "video='mxsfb:640x480M-16@60'"); } // EDT 7.0" TFT WVGA else if (dip0 == 1) { setenv("vidargs", "video='mxsfb:800x480M-16@60'"); } return 0; }