User:WillWare/Leopardboard DM368 hacking

Getting started
I'm going to try to bring up Linux on the Leopardboard 368. There's a Getting started guide and Beginner's guide. Looking at the second picture in "Basic hardware" on the Beginner's guide, I see that I need to pick up a female DB-9 connector and a 3/32" stereo phone plug (Radio Shack 274-0244) to build the serial cable. I'll be plugging the DB-9 into a Keyspan High Speed USB serial adapter because modern computers don't have serial ports. Some of my earlier work on Angstrom and Beagleboard is likely to be useful. I notice the Narcissus server which builds Angstrom distributions offers a "DM355-leopard" hardware setting, so let's get an Angstrom build from Narcissus: random-ff053c61-image-dm355-leopard.tar.gz. It says "Additional Packages: initscripts, sysvinit, sysvinit-pidof".

It looks like that's a rootfs. I'm going to need a uImage, and there are a few choices in the /boot directory. That's good, but I think I need a boot.bin.

I think I need to build my own kernel. Cloning from git://gitorious.org/linux-davinci/linux-davinci.git, I've set up a Github repo with my own changes. I'm going to try to follow http://processors.wiki.ti.com/index.php/Linux_Toolchain#Build_kernel. Having run my toolchain build script, I'm ready to try to build the kernel and bootloader.

$ sudo apt-get install uboot-mkimage $ export PATH=$PATH:/opt/gnu-arm-linux/bin $ make distclean $ make ARCH=arm davinci_all_defconfig $ make ARCH=arm CROSS_COMPILE=arm-linux-eabi- uImage $ make ARCH=arm CROSS_COMPILE=arm-linux-eabi- modules

It turns out the bootloader is a separate thing. I used this as an opportunity to learn more about git submodules.

$ git submodule add git://www.denx.de/git/u-boot.git u-boot $ git add -f .gitmodules $ git commit -a

Putting stuff on the SD card
There is information about booting from the SD card. Other stuff:


 * http://processors.wiki.ti.com/index.php/SD_card_boot_and_flashing_tool_for_DM355_and_DM365
 * https://www.leopardimaging.com/uploads/sd_boot_readme.txt
 * https://www.leopardimaging.com/uploads/dm3xx_sd_boot-6_leopard.tgz

I've set up a dm3xx_sd_boot repository on Github, and made the linux-davinci repo a git submodule of it. It can be built and used by doing this from the root of the repo. Here's how to set up the SD card.

$ cd dm3xx_sd_boot/sdcard_flash/ $ CROSSCOMPILE=arm-linux-eabi- make $ cd .. $ CROSSCOMPILE=arm-linux-eabi- make $ sudo ./dm3xx_sd_boot format /dev/sdb

There is one SD card partition with 43 MB of space. My procedure for populating the SD card is a shell script which first makes tweaks to the ramfs file, adding the stuff I want to play with. To do a full build, I do "make clean kclean kernel sdcard", where "make sdcard" just invokes the shell script.

Intermezzo: possibly useful web pages

 * http://tw.myblog.yahoo.com/stevegigijoe/article?mid=366 -- this looks helpful
 * http://wiki.linpert.de/index.php?title=LeopardBoard/SD_CARD
 * http://processors.wiki.ti.com/index.php/SD_card_boot_and_flashing_tool_for_DM355_and_DM365#Usage
 * http://processors.wiki.ti.com/index.php/DM355_SD_card_boot_and_flash_utility
 * http://processors.wiki.ti.com/index.php/Initrd <== this was helpful

My userspace C program doesn't work, and I don't know why
My simple little C program is giving me "Illegal instruction" and I don't know why. Very annoying. I can run it in GDB functioning as an ARM simulator:

$ arm-linux-eabi-gdb hello GNU gdb 6.8 ... tedious boilerplate ... (gdb) target sim Connected to the simulator. (gdb) load Loading section .init, size 0x18 vma 0x8000 Loading section .text, size 0x88e8 vma 0x8018 ... more sections loading, blah blah blah ... (gdb) run Starting program: /home/wware/dm3xx_sd_boot/my_code/hello Hello, 3 plus 4 equals 7 Program exited normally. (gdb) quit

Maybe I need to use a kernel that was built with my arm-linux-eabi toolchain? That got me to a segfault, which is an improvement over an illegal instruction. I've verified the executable is really static, that's not it.


 * Linux startup process
 * initrd (also talks about initramfs)

I know I can build a kernel, and I know I can make a shell script work. Maybe any C code I write could be written as a kernel module which is then invoked via a shell script. Let's give that a try.

So that worked splendidly. Regarding the segfault, I found some possible help. The segfault is really only an issue when trying to run C code in user space. Maybe I can just put shell scripts (1, 2) in user space and keep all my C code in the kernel. The shell on the LeopardBoard is ash, which is very close to bash, and as far as I am aware, Turing-complete.
 * http://www.rt-embedded.com/blog/archives/resolving-crashes-and-segmentation-faults/
 * http://embeddedlinuz.wordpress.com/2011/12/19/debugging-segmentation-fault-using-gdb/

I'm still scratching my head over GPIOs and LEDs. But I hope I'll get that figured out in due course.

Shell scripts and kernel modules
I've got a working kernel module demonstrating that I can get C code to work in kernel space. The shell on the LeopardBoard is ash as noted above, and it has while loops, tests, and functions. It doesn't seem to have data structures like arrays, lists (in the Python sense) or associative arrays.

f { i=1 while [ $i -le $1 ] do    echo $i i=$(($i+1)) done } f 12
 * 1) !/bin/ash

Bash has lists, and Bash version 4 has real associative arrays, but ash has neither. I think that's probably OK. We can spell out the things we need explicitly.

For the immediate-term project, my plan is to write the innermost piece in C as a kernel module (since it needs high-bandwidth access to image memory anyway), and write the outer loops as shell scripts. I think that should work fine.

I am still stuck with GPIOs and LEDs. Those may yet be solved by more research and experimentation, or I may need to email some of the developers who've worked on the appropriate parts of the kernel.

Still banging on GPIOs and LEDs
There is a swarm of confusion around all this. The DM368 has something called a pinmux which controls whether pins are assigned as GPIOs or as dedicated IOs in service of on-chip peripherals. This is discussed in the DM36x User's Guide. Note that on page 122, bit 6 of PINMUX2 controls GPIO[64:57], which includes GPIO57 and GPIO58 which are the two LEDs on the Leopard DM368 board.

The file gpio.txt in the Linux kernel documentation describes a function called gpio_request which is used in dm355_leopard_init and its friends to set up some GPIOs.


 * drivers/gpio/gpio-davinci.c
 * arch/arm/mach-davinci/include/mach/gpio-davinci.h
 * Board support packages
 * arch/arm/mach-davinci/board-dm365-evm.c
 * arch/arm/mach-davinci/board-dm355-evm.c
 * arch/arm/mach-davinci/board-dm355-leopard.c

I used pr_info calls in dm365_evm_init, dm355_evm_init, and dm355_leopard_init and discovered that my board is booting with dm365_evm_init for some reason when it should be booting with dm355_leopard_init. I think that's why I am having so much trouble with GPIOs.

A single kernel can support many platforms each represented by a struct machine_desc. There is a procedure for looking at the available peripheral devices and deciding which platform (or board support) is correct. So I need to find out why I'm ending up with the wrong one. Here's the relevant call tree where the wrong machine_desc is selected. There are no explicit device tree files (*.dts) for the Davinci boards. That's a little curious.
 * start_kernel in init/main.c calls
 * setup_arch in arch/arm/kernel/setup.c which calls
 * setup_machine_fdt in arch/arm/kernel/devtree.c which chooses the wrong machine_desc in lines 88-97 using
 * of_get_flat_dt_root and of_flat_dt_match, both in drivers/of/fdt.c, which are doing string compares in tree structure called device trees

I tried switching off the DM365_EVM and DM355_EVM configurations in arch/arm/configs/davinci_all_defconfig and got a linker error. I'm trying now to chase down now what that's about. arm-linux-eabi-ld: no machine record defined arm-linux-eabi-ld: no machine record defined What this means is that arch/arm/mach-davinci/board-dm355-leopard.c has failed to provide a machine_desc. The linker is complaining that the "__arch_info" segment is empty. Looking at arch/arm/kernel/vmlinux.lds.S, there should be stuff in ".arch.info.init", described as the "machine type table", and populated using the MACHINE_START and MACHINE_END macros, which do appear in board-dm355-leopard.c. So wtf is going on?

It turns out that I had switched off CONFIG_ARCH_DAVINCI_DM355 in davinci_all_defconfig, hoping that CONFIG_ARCH_DAVINCI_DM365 would do the job, but CONFIG_MACH_DM355_LEOPARD is fickle and won't work with the DM365. OK, I fixed that, but now my kernel won't boot. Time to enumerate several possible configurations of davinci_all_defconfig and see if any offer better results.

I'm hopeful about the following plan. I've edited arch/arm/mach-davinci/Makefile to make sure that board-dm355-leopard.o will be the first platform in the obj-y target. In arch/arm/kernel/devtree.c, I've replaced this loop: for_each_machine_desc(mdesc) { score = of_flat_dt_match(dt_root, mdesc->dt_compat); if (score > 0 && score < mdesc_score) { mdesc_best = mdesc; mdesc_score = score; }       } with this one: for_each_machine_desc(mdesc) { mdesc_best = mdesc; mdesc_score = 11; break; } My plan is that instead of comparing scores to match the board's device tree with different possible platforms, it will just take the first platform, which I've hopefully guaranteed will be the Leopard platform. So we'll see if that solves the problem.

Barking up the wrong tree! The choice of platform is being made in arch/arm/kernel/setup.c near line 843. So I'm going to change the loop from: for_each_machine_desc(p) if (nr == p->nr) { printk("Machine: %s\n", p->name); mdesc = p;                       break; } to: for_each_machine_desc(p) if (1) { printk("Machine: %s\n", p->name); mdesc = p;                       break; } and we'll see if we do any better. Note that the "nr" in the first loop comes from the machine_arch_type variable, which gets set (afaik) during decompress_kernel (see the last arg, "arch_id", at line 134 of arch/arm/boot/compressed/misc.c) but it's extracted from somewhere magical by assembly language that I don't really understand. So instead of "if (1)" I could simply force the value of nr to either 2138 (DM355 Leopard) or 3449 (DM368 Leopard).

Well that didn't work, and a few other things didn't work. But I learned a few things on the way. One is that the Leopard DM368 board will not run DM355 code. The only board support I have for Leopard is arch/arm/mach-davinci/board-dm355-leopard.c, which is why I'm never seeing any of my printks. I need to create a arch/arm/mach-davinci/board-dm365-leopard.c or arch/arm/mach-davinci/board-dm368-leopard.c file by somehow merging board-dm355-leopard.c with board-dm365-evm.c. I just compared the two files with Meld and it looks like this will be a lot easier than I feared.

Finally lit up the LEDs

 * board-dm365-evm.c, which I've started to tweak for board support
 * quux.c, my kernel driver
 * r, a shell script to blink LEDs, because I'm a lazy typist and this shell lacks readline

None of my numerous attempts to give the Leopard its own board support were successful. Finally I decided to ride the horse in the direction it's going. Instead of trying to create a new board-*.c file, I modified the existing board-dm365-evm.c file that already works. I hope some day I can come back and do this correctly, but I have near-term goals that don't allow that much patience. My modifications were mostly to comment out all the CPLD-related code, since AFAIK there is no CPLD on the LeopardBoard.

In my kernel driver, I put some calls to gpio_request to claim the GPIO pins for the two LEDs. These get blinked when you read from the device (/dev/quux).