What exactly do we mean by the word board? While we sometimes use the word to refer to a printed circuit board with a specific configuration, we also use the term to mean something less concrete. This post will describe the flexible bits and hopefully give you a better understanding of how our standard boards can and should be customized to meet your needs. If you’re a customer designing a SOM carrier, it will also provide some tips on how to create and maintain your designs. A follow-up post will give a concrete example of how you can use device trees to re-map our LCD interface.
As Alvin Toffler predicted in The Third Wave, the era of mass customization is upon us, and we hope this set of notes will help you deal with the specifics.
For the impatient
For any given PCB, a number of things can be customized in the boot process, and the following lists the highlights in order of frequency.
|Item||How frequently needed|
|Boot script||Almost always|
|Kernel device tree||Frequently|
|Boot command line||Sometimes|
|U-Boot sources||Infrequently for standard boards, almost always on custom boards|
|Kernel sources||Frequently for custom boards|
Thankfully, the order listed above is also roughly sorted by ease of implementation, since programmers have a way of making the common stuff easy.
The rest of this post will walk through the details of each of these items.
Our current boot script consists of around 150 lines of text, up from around 60 when we first pushed to main-line U-Boot.
Most of this is meaningless for any given application. The bulk of the script is made up of conditionals that try to determine what type of display(s) you have connected and whether or not you have a RAM disk or Device Tree Blob. Once you’ve settled on a configuration, you can simplify all of this to around five lines and two statements (one of them a compound statement using the && operator):
setenv bootargs "stuff copied from /proc/cmdline"; load mmc 0 10800000 /path/to/my/uImage && load mmc 0 13000000 /path/to/my/dtb && load mmc 0 12a00000 /path/to/my/ramdisk && bootm 10800000 12a00000 13000000
After all, the primary purpose of U-Boot is to load and launch the kernel with proper parameters, and all of the variables live in the bootargs variable and the three files listed above (kernel, device tree and RAM disk).
Pay close attention to the first line above. If our standard boot script works for you, the pre-computed kernel command line is available to you in /proc/cmdline, all ready for cutting and pasting:
root@nitrogen6x:~# cat /proc/cmdline enable_wait_mode=off video=mxcfb0:dev=ldb,LDB-XGA,if=RGB666 video=mxcfb1:dev=lcd,CLAA-WVGA,if=RGB666 video=mxcfb2:off video=mxcfb3:off fbmem=10M,10M console=ttymxc1,115200 vmalloc=400M consoleblank=0 vt.global_cursor_default=0 rootwait root=/dev/mmcblk0p2 mxc_hdmi.only_cea=1
When you combine this with our on-line boot script compiler, it’s really pretty simple to create your own boot script.
Our Linux kernel configuration file (nitrogen6x_defconfig) defines the set of device drivers available to the running kernel. We use this to select most of the options we support, including all of the core features of the i.MX6, as well as our touch screen displays and camera modules.
We also include support for some frequently used peripherals like USB sticks, and serial adapters, but our driver set is quite limited.
If you need additional drivers, you’ll need to use make menuconfig to customize the kernel build.
You may also want to remove drivers for parts you’re not using both to save space and time during the boot process, but also to prevent drivers like the Ilitek touch screen from trying to probe needlessly:
ili210x_i2c 2-0041: i2c transfer failed ili210x_i2c 2-0041: Failed to get firmware version, err: -5 ili210x_i2c: probe of 2-0041 failed with error -5
The details of how to do this vary quite a bit between Yocto, Ubuntu, Android and other userspaces, but the process isn’t difficult and is generally worth some effort.
Kernel Device Trees
We’ll be following this post with another that delves deeply into device trees, but some fairly easy changes to the device tree descriptions of our boards can speed up the boot and free up pins for other uses.
In some cases, the changes can be as minor as changing this “okay” to “disabled”“.
Boot command line
We try not to impose restrictions on you, and allow booting to SATA, either SD card, or even USB stick by default. Our default bootcmd variable is what defines this through the use of the bootdevs variable and a couple of nested for loops.
If you know that you’re booting to SATA, you can speed up the boot process by over-riding this without re-compilation like so:
U-Boot > setenv bootcmd 'disk=0; dtype=sata; load 10008000 sata 0 /6x_bootscript && source 10008000' U-Boot > saveenv && boot
Note that the dtype and disk variables are only here in case you try this with our standard boot script. If you have already done away with that, this isn’t needed.
Note that you can also do away with the boot script entirely by simply saving your bootargs and setting bootcmd to load and launch your kernel:
U-Boot > setenv bootargs "copied from /proc/cmdline" U-Boot > setenv bootcmd 'load mmc 0 10800000 /path/to/my/uImage && load mmc 0 13000000 /path/to/my/dtb && load mmc 0 12a00000 /path/to/my/ramdisk && bootm 10800000 12a00000 13000000' U-Boot > saveenv && boot
Our current U-Boot binaries are around 350 KiBytes, and that size can be reduced by tweaking the board’s header file. If you look at nitrogen6x.h, you’ll find a number of macro definitions of the form CONFIG_FEATURE that enable features of U-Boot. Judicious selection of the features you need can result in a smaller image and a faster boot.
The U-Boot variable panel can also be defined at compile time to select from the list of supported displays and bypass probing for the display type. This not only speeds the boot, it also removes a potential source of failure.
Defining other things like the new sdphys variable in the board’s header file can remove the need to configure boards after shipment.
U-Boot source code
Our U-Boot doesn’t currently support using device trees for its’ own use, and pin definitions are defined in the U-Boot source files. If you want to re-purpose these, and especially if you’re designing a SOM carrier board, you may need to update the U-Boot sources directly.
Recent enhancements to U-Boot, especially Masahiro Yamada’s addition of Kconfig offers the prospect of lighter-weight modifications down the road.
When all else fails, you (or we) may need to
hack update the kernel sources.
If you’re using some newly released peripheral, it’s common to need a new device driver to support it.
If the device is supported in newer (or older) kernels, you may need to back-port it from another tree.
Even things as simple as connecting up a new display might involve the addition of a GPIO like our oddly-named DISP0_CONTRAST pin to enable and disable a backlight.
We hope this quick check-list gives you some ideas about how and where your product can benefit from customization.
Remember that our requirements tend to be broad because we don’t know exactly how you’ll use our boards. Once they’re in your hands, the needs tend to be much more specific, and we’re here to help you reach your targets.