Quantcast
Channel: my tech blog » Linux kernel
Viewing all 69 articles
Browse latest View live

Linux on Microblaze HOWTO (part I)

$
0
0

This is part I of my HOWTO on running Linux on Microblaze. The outline is as follows:

Introduction

This HOWTO goes through the procedures for getting a simple Linux system running on a Xilinx Microblaze processor. The examples are given for an SP605 evaluation board, but almost everything here applies for other FPGAs and boards as well. The Xilinx software version used here is 13.2.

There are quite a few variants on how to get the bitstream and Linux kernel into their right places in the FPGA. The approach taken here is to boot up from the Compact Flash alone by writing a file to it. No bootloader is used in this howto; the SystemACE chip is responsible for loading both the FPGA bitstream and Linux kernel image, and it will do so reading one single (.ace) file. The main advantage of this approach is that there’s no need to set up a boot loader, which is yet another piece of software that can go wrong. The main disadvantage is that a bootloader allows some tweaking of the kernel configuration at boot time, which has to be done by recompiling the kernel otherwise.

The root filesystem is mounted from network (NFS) in this HOWTO.

I’m assuming the following prerequisites:

  • You have the Xilinx tools set up properly, and have managed to compile and run a simple standalone “Hello, World” application with the EDK/SDK (having loaded the code to the FPGA in any way, we’ll get to that)
  • You’ve actually seen the RS-232 console data on a terminal, and feel confident about it (otherwise you may work hard to figure out why everything is stuck, when it’s actually your terminal window’s problem).
  • You’re running on one of the evaluation boards, or know how to set up the processor to work with your own (and have that tested already)
  • Your board has a systemACE flash chip (recent evaluation boards usually do)
  • You have access to a machine running Linux on a computer. Compiling the kernel will require this. The Xilinx tools can be run on whatever’s your cup of tea.
  • You have the ability to read and write files to a Compact Flash. This is most easily done with a simple adapter to a PC computer, which should be available in camera or computer accessories shops. Chances are you have one without necessarily being aware of it.

An outline of the steps

So this is what we’ll do:

  • Set up a Microblaze processor in the Xilinx EDK so it can run Linux.
  • Generate the processor, so an FPGA bitstream is at hand.
  • Export the processor to the Xilinx SDK and compile a dummy C application, so that  necessary metadata files are generated
  • Generate a Device Tree file (.dts) based upon files created by EDK/SDK, and copy it into the Linux kernel sources, so Linux is in sync with the EDK regarding what it’s running on.
  • Configure the kernel and compile it.
  • Create a .ace file from the FPGA bitstream and kernel image just compiled.
  • Set up the Compact Flash card.
  • Boot and hope for good

And of course, certain software tools will need to be downloaded for this. We’ll come to this.

Setting up the processor

If you’re really lazy about this, you can use the minimal processor I’ve generated for the SP605 board. Unzip, double-click system.xmp, and skip to after the bullets below. It will work on that board only, of course.

Otherwise: Start Platform Studio (EDK) and create a new platform, based upon the Wizard’s defaults.

Following a Microblaze on Linux guide, in particular the part regarding minimal hardware requirements, there a need to make sure that the hardware has an MMU with two regions, a timer, an interrupt controller and a UART with an interrupt line. In the platform studio it goes like this:

Starting off with the Wizard’s defaults,

  • Double click “microblaze_0″ on the Ports tab, and set the Linux with MMU preset on the Configuration wizard showing up. This will take care of most settings.
  • Still in the ports view, add an AXI Interrupt Controller (under Clock, Reset and Interrupt in the IP Catalog). Accept default settings. Make a new connection for its irq output, and connect it to the microblaze_0′s interrupt input pin.
  • Pick the RS232_Uart_1 and make a new connection for the interrupt line. Connect that signal to the interrupt controller.
  • Add an AXI Timer/Counter, and accept defaults. Make a new connection for the interrupt, and connect it to the interrupt controller.
  • Connect the interrupts of the Ethernet lite, SPI Flash, IIC SFP, IIC EEPROM, IIC_DVI, and SysACE cores to the interrupt controller as well.

Then generate bitstream, export to SDK, and run the SDK, adopting this hardware platform. The goal of this is to generate a .mss file, which will be needed later. For this to happen, make a new C project (“Hello World” will do just fine) and compile it.

There is no need to “update the bitstream” like with standalone applications: The Linux kernel can take care of itself, without having its entry address hardwired in the FPGA’s block RAM. We’ll use the system.bit, and not the download.bit (even though the latter works too).

Creating a Device Tree file

The purpose of this stage is to generate a .dts file, which is the format expected by the kernel build environment. It informs the kernel about the structure of the processor and its peripherals. The device tree structure is discusses further here.

If you chose to download and use my processor with no changes whatsoever, you can also get my DTS file. Just copy it to arch/microblaze/boot/dts/ in the to-be compiled kernel source tree.

To make your own .dts file, first create a special directory, and make it the working directory of your shell.

The device tree file is generated automatically with the libgen utility with the help of a Tcl script. As of ISE 13.2, this script needs to be loaded separately with git:

bash> git clone git://git.xilinx.com/device-tree.git

This generates a device-tree directory. Another web page explains how to make SDK recognize the script, but I prefer command line for things like this. Another post of mine explains the device tree further.

Copy the system.xml file from the directory to which you exported to SDK (in the “hw” subdirectory), into the current one. Then copy system.mss from the project’s BSP directory. It will have a name like hello_world_bsp_0.

Edit the copy you made of system.mss, so that the BEGIN OS to END part reads

BEGIN OS
 PARAMETER OS_NAME = device-tree
 PARAMETER OS_VER = 0.00.x
 PARAMETER PROC_INSTANCE = microblaze_0
END

and not “standalone” for OS.

And then run libgen as follows (make sure it’s in the PATH. The easiest way is to launch a “Xilinx shell” from the EDK’s project menu):

libgen -hw system.xml -lp device-tree -pe microblaze_0 -log libgen.log system.mss

Which generates a xilinx.dts in microblaze_0/libsrc/device-tree_v0_00_x. Copy this file to arch/microblaze/boot/dts/ in the to-be compiled kernel source tree. If you can’t find the file there, and libgen didn’t complain about some error, you may have forgotten to edit system.mss as mentioned just above.

Now let’s go on to compiling the kernel, in part II.


Linux on Microblaze HOWTO (part II)

$
0
0

This is part II of my HOWTO on running Linux on Microblaze. The outline is as follows:

Kernel compilation in general

Compiling a Linux kernel traditionally consists of the following steps (some of which are elaborated further below):

  • Obtaining a kernel source tree.
  • Configure the kernel. Which all in all means to set up a file named “.config” in the kernel source’s root directory.
  • Compile actual kernel, ending up with an executable image.
  • Compile the post-boot loadable kernel modules.
  • Put everything in its place, set up the bootloader
  • Pray and boot

When compiling for Microblaze, the process is somewhat different:

  • Cross compilation: The compiled binaries run on a processor different from the one doing the compilation.
  • Kernel modules are most likely not used at all. They are a bit pointless when the hardware is known in advance, and also add some complexity in setting up the entire system for boot. Besides, modprobe on a Microblaze can take forever.
  • The hardware configuration is custom made, and the kernel needs to be informed about it (through the Device Tree Structure)

Downloading kernel sources

Note that all kernels compile for all target architectures. If you download a kernel from Xilinx’ repository, it may have the parts relevant to Xilinx slightly more updated. The emphasis is on “may”.
The “vanilla” kernel (maintained by Linus Torvalds) can be downloaded from the main kernel archive or one of its mirrors. Several other flavors float around, including Xilinx own git

git clone git://git.xilinx.com/linux-2.6-xlnx.git

or Petalogix’ git (after all, they do a lot of maintenance on the Xilinx devices):

git clone git://developer.petalogix.com/linux-2.6-microblaze.git

The question is always which kernel is best. The answer is that it’s a bit of a gamble. It’s usually almost exactly the same piece of software, with git version having the latest changes. That means the latest bug fixes, new drivers, but also the latest, undocumented and undiscovered bugs. Vanilla kernels tend to be more conservative, but the only rule is that there are no rules. So in short, toss a coin and pick one.

Personally, I compiled the kernel which happened to be on my hard disk for other purposes.

Cross compilers

The good news is that there’s no need to compile the GNU tools. As a matter of fact, this part turned out to be surprisingly painless. The cross compiler and binutils binaries + initramfs images can be downloaded with

$ git clone git://git.xilinx.com/xldk/microblaze_v1.0_le.git
$ git clone git://git.xilinx.com/xldk/microblaze_v1.0.git

Choose one, depending on whether you prefer little endian or big endian for your processor. I picked little endian, but there’s one initramfs in the big endian bundle which isn’t there for the little endian set (which only has the “minimal” image).

One of the files fetched by git is microblazeel-unknown-linux-gnu.tar.gz (gzipped tarball) for the little endian version and mb_gnu_tools_bin.tar.bz (bzipped tarball) for big endian. I’ll leave the latter, because I didn’t use it.

There’s no need to install anything, and no need to be root (actually, doing this as root is pretty unwise). Just untar the tarball of your choice in any directory. Tar generates several subdirectories, but we’re after the cross compilers. Or more precisely, to make the kernel build system use them. This boils down to this:

export CROSS_COMPILE=/home/myhomedir/untarred-to/microblazeel-unknown-linux-gnu/bin/microblazeel-unknown-linux-gnu-

First of all, note the dash at the end of the statement. The whole string is a prefix for all compilation commands made by the kernel build system. It is often recommended to set the path to where the compilers are, and then set CROSS_COMPILE to a shorter prefix. I don’t see the point in polluting the overall path. The build environment has no problem with the statement above.

It has also crossed my mind to use the mb-gcc and friends, which are part of the SDK. But that may require another paid-for license, in case different people do the FPGA and software (which usually is the case).

And to wrap this up: If I’ll ever need to build a cross compiler from scratch, I would start with looking at Buildroot (and another page about it) or following this guide (I haven’t tried either, though).

Kernel configuration

Setting this up correctly is a tedious process, and even the most seasoned kernel hackers may not get it right on the first go. If it’s your first time, prepare to spend quite a few hours on this. The less experienced you are with Linux in general, the more time will you need to spend to make an educated guess about your need for each feature offered.

You can try to use my configuration file, or at least start off with it. It was made for against a 2.6.38 kernel, and booted well as shown in part III. Copy the file as .config on the kernel source’s root, and start with oldconfig.

The commands involved are basically (all “make” commands issues at the kernel source’s top directory):

  • Clean up everything, including the .config file if present. This is not necessary if you just uncompressed your kernel. It’s actually rarely necessary at all: “make ARCH=microblaze mrproper”. This will delete .config! (I know I just said it).
  • Adopt an existing .config file: “make ARCH=microblaze oldconfig”. This is useful in particular when switching to another kernel version or flavor. Only questions about new features are asked. If you downloaded my configuration file, I would suggest not to turn on options that are offered while running oldconfig, unless they are clearly Xilinx related.
  • Configure the kernel: “make ARCH=microblaze xconfig”, “make ARCH=microblaze gconfig” or “make ARCH=microblaze menuconfig” (pick one). These applications present the kernel options in a fairly user-friendly manner, and eventually save the result to .config. I recommend xconfig, because it’s graphic and has a search feature, which turns out very useful.

When targeting an embedded platform, the strategy is to enable whatever is necessary in the kernel itself, and not count on kernel modules. A second issue is to eliminate anything unnecessary from the kernel. This is not just a matter of the kernel image’s size and speed, but enabling components which have nothing to do there can cause the kernel compilation to fail, and even worse, the kernel to crash at boot. Each architecture maintains a set of #include headers, and some kernel components may assume certain things that these architecture-dependent parts haven’t caught up with. So the rule that is whatever hasn’t been tested, won’t necessarily work. Enabling an esoteric keyboard driver on a Microblaze processor may very well fail the boot, simply because nobody cares.

In particular, you most likely want to follow these:

  • Under Platform Options, set CONFIG_KERNEL_BASE_ADDR to where your DDR RAM begins (0xC0000000 on my processor), the targeted FPGA family as well as the other parameters (USE_*). The USE_* parameters’ correct values can be found in the .dts file. Just copy the values of the processor elements with the same names.
  • Also set
    CONFIG_SERIAL_UARTLITE=y
    CONFIG_SERIAL_UARTLITE_CONSOLE=y
    CONFIG_SERIAL_CORE=y
    CONFIG_SERIAL_CORE_CONSOLE=y
  • Since we’re not going to use any boot loader, the kernel command line needs to be compiled into the kernel itself: Enable CMDLINE_BOOL (default bootloader kernel argument) and set it to something useful. As for the console, set it to console=ttyUL0, or nothing goes to console after the two first lines sent to console from early_printk_console (CONFIG_CMDLINE_FORCE may be necessary as well. It doesn’t hurt in the absence of a boot loader anyhow)
  • Enable CONFIG_MSDOS_FS and CONFIG_VFAT_FS in kernel (not module), so that the SystemACE can be read.
  • Enable CONFIG_XILINX_SYSACE
  • Enable CONFIG_XILINX_EMACLITE and CONFIG_FB_XILINX
  • Disable the FTRACE config option (under kernel hacking, compilation fails) instead of using patch.

And for your own sake, make a copy of the .config file every now and then as you work on it. It’s very easy to delete it by mistake or to mess it up in general.

Setting the Linux boot parameters correctly is very important, because if they’re wrong, kernel recompilation is they only way to fix it in the absence of a boot loader. I’ve chosen to mount the root directory from the network, but note that /dev/sxa is the Compact flash itself (with /dev/sxa1 is the first partition, for example). So it’s fairly simple to add a partition to the flash device, and put a regular root filesystem there. Maybe I’ll do that myself and update this post.

Anyhow, my choice for the Linux boot parameters was

console=ttyUL0 ip=::::::dhcp rootfstype=nfs root=/dev/nfs rw nfsroot=10.11.12.13:/shared/nfsroot,tcp

where “/shared/nfsroot” is the shared NFS directory on the server with IP 10.11.12.13. This command is suitable for getting the root from the network, which is very convenient for development. This setting requires a DHCP server on the LAN. In case you don’t want to configure a DHCP server, use the ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:::off format instead. Documentation/filesystems/nfs/nfsroot.txt in the kernel sources has more about booting from NFS. I’ve also written a post about booting a diskless PC from network, but it’s a bit of an overkill.

In case you’re interested in how the whole configuration thing comes together, let’s take CONFIG_EARLY_PRINTK for example. In arch/microblaze/kernel/Makefile, one of the lines says:

obj-$(CONFIG_EARLY_PRINTK)    += early_printk.o

On the other hand, in the config file it can say

CONFIG_EARLY_PRINTK=y

So when the Makefile is executed, the target early_prink.o is added to either obj-y, obj-m or obj-n. obj-y is the list of objects to be inserted into the kernel, obj-m is the list of modules, and obj- is the junk list. The configuration rules are given in the Kbuild files, next to the Makefiles.

A small Makefile fix

As of 2.6.38, there is a small error in the arch/microblaze/boot/Makefile, which makes the build system always attempt making an U-Boot image, which is not necessary in our case. This may result in an error message (saying “mkimage” wasn’t found), when everything is actually OK. So in the part saying

$(obj)/simpleImage.%: vmlinux FORCE
 $(call if_changed,cp,.unstrip)
 $(call if_changed,objcopy)
 $(call if_changed,uimage)
 $(call if_changed,strip)
 @echo 'Kernel: $@ is ready' ' (#'`cat .version`')'

remove or comment out the line saying “$(call if_changed,uimage)”.

Compiling the kernel

Before starting: You didn’t forget to set CROSS_COMPILE and copy the updated xilinx.dts file to its place… right?

I prefer cleaning up before compiling:

make ARCH=microblaze clean
rm arch/microblaze/boot/simpleImage.*

This is a good time to ask why the image file isn’t cleaned by “make clean”. To be fixed, I suppose.

And then, the compilation is just

make -j 8 ARCH=microblaze simpleImage.xilinx

Note that the “.xilinx” suffix corresponds to the xilinx.dts file in the arch/microblaze/boot/dts/ directory. If another .dts file should be made effective, change the suffix.

The “-j 8″ means that 8 compilation processes run in parallel, which is suitable for a quad processor with hyperthreading. Skip this option or use another number, depending on your computer, your spare time and your need to see the logic of the events.

The basic UNIX rule is that everything went fine unless an error message appeared. A more explicit confirmation is that it said

OBJCOPY arch/microblaze/boot/simpleImage.xilinx

somewhere close to the end, and that the arch/microblaze/boot/simpleImage.xilinx is indeed there, and has a date stamp that makes sense.

If and when you get errors, well, there’s no simple  recipe to solve that. The easiest way is to eliminate the need to compile that certain file by changing the kernel configuration, if the functionality is indeed unnecessary. Otherwise your best friends are Google and your brain, not necessarily in that order.

As for the Device Tree, it was compiled into a .dtb file (the Device Tree binary blob), which can be found in the same directory as the just generated kernel image. The Device Tree Compiler (dtc) comes with the kernel sources, and can be found in scripts/dtc.

And just to wrap this up: If you insist on seeing all the commands issued instead of the otherwise laconic output, there the KBUILD_VERBOSE flag. For example,

make ARCH=microblaze KBUILD_VERBOSE=1 clean

With a compiled kernel image at hand (which already has the Device Tree built-in), all that’s left is to set up the Compact Flash and boot. Go to part III of this HOWTO.

A few other make statements

For completeness:

  • Clean up any compiled binaries: Recommended after a change in .config: “make ARCH=microblaze clean”
  • Generate loadable modules: “make ARCH=microblaze modules”. Not necessary if everything needed is compiled into the kernel.
  • And then gather the modules in a neat directory (making sure you don’t have a /lib/modules directory with the same version number): “make ARCH=microblaze modules_install”. This will write to /lib/modules on the local machine, so if you happen to compile exactly the same kernel version for your own PC and the embedded target, the kernel modules the PC relies on will be overwritten.

Linux on Microblaze HOWTO (part III)

$
0
0

This is part III of my HOWTO on running Linux on Microblaze. The outline is as follows:

Generating the ACE file

The ACE file is what the System ACE chip reads from, and programs the FPGA accordingly. It consists of a sequence of JTAG operations for each necessary task: Configure the FPGA itself, load the software into memory, set the software execution entry point, and kick the software off. All is done with JTAG commands, which the System ACE generates as it scans through its ACE file.

So let’s get down to business.

Create a directory to gather the relevant files, and copy the following into it:

  • The Tcl script for generating ACE file: Found at ISE_DS/EDK/data/xmd/genace.tcl (relative to the path where Xilinx ISE is installed)
  • The bitstream (system.bit) file created by the EDK (explained in part I). Found in the ‘hw’ subdirectory in the export bundle from EDK to SDK. Or just under ‘implementation’ in the processor’s working directory. It’s the same file.
  • The kernel ELF file (simpleImage.xilinx, or the unstripped simpleImage.xilinx.unstrip) created by the kernel build system (explained in part II), found in arch/microblaze/boot/ in the kernel source tree.

Open a command shell (Project > Launch Xilinx Shell if you like), change to this directory and go:

xmd -tcl genace.tcl -hw system.bit -elf simpleImage.xilinx -ace linuxmb.ace -board sp605 -target mdm

which generates a lot of junk files (.svf most notably, which contain JTAG commands in a portable format), and eventually the linuxmb.ace is created (any file name is OK).

In the example above, I assumed that the target is the SP605 board. Looking at the genace.tcl script reveals easily which boards are supported. If it isn’t, it’s not such a big deal. The only reason the board matters is because the System ACE needs to know which device in the JTAG chain to talk with plus some programming parameters. The -board flags to this scrips allows setting the options in a “genace option file” (whatever that means). I would hack the script, though. It looks easier. See here for more information.

Writing to the Compact Flash

First and foremost: If you have a compact flash which boots anything to the FPGA, don’t format it unless you really have to. The System ACE chip (by Xilinx) which reads from the flash directly is a bit picky about the file system format. Preferably use the card which came with the development kit.

And this too: If you just bought a 2 GB flash or so in a general electronics store, odds are that you’ll need to format it.

I explain how to format the flash in another post of mine.

Assuming that the flash is formatted OK, copy the ACE file to the Compact Flash’ root directory. Make sure that

  • there is no other *.ace file in the root directory
  • there is no xilinx.sys in the root directory

It is perfectly OK to have unrelated directories on the flash, so if there are some files on the flash already, I’d suggest creating a directory with just any name (say, “prevroot”) and move everything in the root directory into that one. And then copy the desired ACE file (linuxmb.ace in the example above) into the root directory.

That’s it. The Linux kernel should now boot, but it will complain (the kernel will panic, actually) that it doesn’t have any root filesystem. So…

Setting up the root filesystem

Once the kernel is up, it needs something to mount as a root filesystem, in which it expects to find its init executable and quite a few other files. Xilinx supplies an image of this bundle which were downloaded along with the cross compilers (see part II), in the same directory.

You may recall that I chose to mount root over the network, using NFS. So to create a useful root directory to work with, just change directory to whatever is going to be root (in my case, the one exposed via NFS) and go

zcat /path/to/microblaze_v1.0_le/initramfs_minimal_le.cpio.gz | cpio -i -d -H newc --no-absolute-filenames

This bundle includes a practical set of executables (well, it’s actually a lot of symbolic links to busybox) including vi, watch, dd, grep, gzip, tar, rpm, nc and even httpd (a web server…!). There’s also a rootfs.cpio.gz in the kernel sources when downloaded from Xilinx’ git (linux-2.6-xlnx.git in part II) which I haven’t tried out. But it’s opened in the same way.

You may, of course, compile your own programs, which is discussed in part IV.

There’s no “shutdown” executable, though. There’s “halt” instead.

A test run

Well, plug in the Compact Flash card, turn the power on, and hope to see a green LED blinking, which turns to steady green after a few seconds. When the LED is steady, expect some output on the UART. A typical log for SP605 is given at the end of this post.

At times, the SP605 board’s green LED went on, but nothing runs until SYS_ACE_RESET is pressed (the middle button out of three close to the Compact Flash jack). Looks like a powerup issue.

Is it fast? Is it fast?

This is maybe not such a fair comparison, and still the facts speak for themselves:

On Microblaze @ 75 MHz clock (37 BogoMIPS):

# dd if=/dev/zero of=/dev/null bs=1M count=10k
10240+0 records in
10240+0 records out
10737418240 bytes (10.0GB) copied, 1058.304486 seconds, 9.7MB/s
# dd if=/dev/zero of=/dev/null bs=512 count=100k
102400+0 records in
102400+0 records out
52428800 bytes (50.0MB) copied, 9.531130 seconds, 5.2MB/s

The same thing on my own computer  @ 1.2 GHz (5600 BogoMIPS):

$ dd if=/dev/zero of=/dev/null bs=1M count=10k
10240+0 records in
10240+0 records out
10737418240 bytes (11 GB) copied, 0.941238 s, 11.4 GB/s
$ dd if=/dev/zero of=/dev/null bs=512 count=100k
102400+0 records in
102400+0 records out
52428800 bytes (52 MB) copied, 0.0443318 s, 1.2 GB/s

According to the BogoMIPSes, Microblaze should have been 150 times slower, not 1000 times slower!

A typical boot log

early_printk_console is enabled at 0x40600000
Ramdisk addr 0x00000003, Compiled-in FDT at 0xc03c2348
Initializing cgroup subsys cpuset
Initializing cgroup subsys cpu
Linux version 2.6.38.6 (eli@localhost.localdomain) (gcc version 4.1.2) #19 Fri Aug 5 16:40:02 IDT 2011
setup_cpuinfo: initialising
setup_cpuinfo: Using full CPU PVR support
cache: wt_msr_noirq
setup_memory: max_mapnr: 0x8000
setup_memory: min_low_pfn: 0xc0000
setup_memory: max_low_pfn: 0xc8000
On node 0 totalpages: 32768
free_area_init_node: node 0, pgdat c04f515c, node_mem_map c05ca000
 Normal zone: 256 pages used for memmap
 Normal zone: 0 pages reserved
 Normal zone: 32512 pages, LIFO batch:7
pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
pcpu-alloc: [0] 0
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 32512
Kernel command line: console=ttyUL0 ip=::::::dhcp rootfstype=nfs root=/dev/nfs rw nfsroot=10.11.12.13:/shared/nfsroot,tcp
PID hash table entries: 512 (order: -1, 2048 bytes)
Dentry cache hash table entries: 16384 (order: 4, 65536 bytes)
Inode-cache hash table entries: 8192 (order: 3, 32768 bytes)
allocated 655360 bytes of page_cgroup
please try 'cgroup_disable=memory' option if you don't want memory cgroups
Memory: 123204k/131072k available
SLUB: Genslabs=13, HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS:32
xlnx,xps-intc-1.00.a #0 at 0xc8000000, num_irq=8, edge=0x60
xlnx,xps-timer-1.00.a #0 at 0xc8004000, irq=7
Heartbeat GPIO at 0xc8008000
microblaze_timer_set_mode: shutdown
microblaze_timer_set_mode: periodic
Console: colour dummy device 80x25
Calibrating delay loop... 37.17 BogoMIPS (lpj=185856)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512
Initializing cgroup subsys ns
ns_cgroup deprecated: consider using the 'clone_children' flag without the ns_cgroup.
Initializing cgroup subsys cpuacct
Initializing cgroup subsys memory
Initializing cgroup subsys devices
Initializing cgroup subsys freezer
Initializing cgroup subsys net_cls
devtmpfs: initialized
NET: Registered protocol family 16
PCI: Probing PCI hardware
bio: create slab <bio-0> at 0
XGpio: /axi@0/gpio@40040000: registered
XGpio: /axi@0/gpio@40020000: registered
XGpio: /axi@0/gpio@40000000: registered
vgaarb: loaded
Switching to clocksource microblaze_clocksource
microblaze_timer_set_mode: oneshot
Switched to NOHz mode on CPU #0
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 4096 (order: 3, 32768 bytes)
TCP bind hash table entries: 4096 (order: 2, 16384 bytes)
TCP: Hash tables configured (established 4096 bind 4096)
TCP reno registered
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
PCI: CLS 0 bytes, default 32
Skipping unavailable RESET gpio -2 (reset)
GPIO pin is already allocated
audit: initializing netlink socket (disabled)
type=2000 audit(0.429:1): initialized
VFS: Disk quotas dquot_6.5.2
Dquot-cache hash table entries: 1024 (order 0, 4096 bytes)
squashfs: version 4.0 (2009/01/31) Phillip Lougher
fuse init (API version 7.16)
msgmni has been set to 240
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254)
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
40600000.serial: ttyUL0 at MMIO 0x40600000 (irq = 6) is a uartlite
console [ttyUL0] enabled
brd: module loaded
loop: module loaded
of:xsysace 41800000.sysace: Xilinx SystemACE revision 1.0.12
of:xsysace 41800000.sysace: capacity: 3980592 sectors
 xsa: xsa1
Xilinx SystemACE device driver, major=254
Generic platform RAM MTD, (c) 2004 Simtec Electronics
xilinx_spi 40a00000.spi: at 0x40A00000 mapped to 0xc8080000, irq=0
of:xilinx_emaclite 40e00000.ethernet: Device Tree Probing
Xilinx Emaclite MDIO: probed
of:xilinx_emaclite 40e00000.ethernet: MAC address is now 00:0a:35:49:b2:00
of:xilinx_emaclite 40e00000.ethernet: Xilinx EmacLite at 0x40E00000 mapped to 0xC80A0000, irq=5
device-mapper: uevent: version 1.0.3
device-mapper: ioctl: 4.19.1-ioctl (2011-01-07) initialised: dm-devel@redhat.com
nf_conntrack version 0.5.0 (1925 buckets, 7700 max)
ip_tables: (C) 2000-2006 Netfilter Core Team
TCP cubic registered
Initializing XFRM netlink socket
NET: Registered protocol family 17
Registering the dns_resolver key type
registered taskstats version 1
Sending DHCP requests .
PHY: c0020918:07 - Link is Up - 100/Full
., OK
IP-Config: Got DHCP answer from 10.11.12.13, my address is 10.11.12.155
IP-Config: Complete:
 device=eth0, addr=10.11.12.155, mask=255.255.255.0, gw=10.11.12.13,
 host=10.11.12.155, domain=, nis-domain=(none),
 bootserver=10.11.12.13, rootserver=10.11.12.13VFS: Mounted root (nfs filesystem) on device 0:13.
devtmpfs: mounted
Freeing unused kernel memory: 147k freed
Starting rcS...
++ Mounting filesystem
++ Starting telnet daemon
rcS Complete
/bin/sh: can't access tty; job control turned off
/ # NET: Registered protocol family 10
eth0: no IPv6 routers present

Microblaze + Linux: Sample design of a custom peripheral

$
0
0

Scope

Even though Xilinx supplies a cute wizard for creating peripherals in its EDK (version 13.2 in my case), it’s just enough to work as a demo. For a real-life case there’s no escape from getting down to the system’s guts. As it turns out, things are pretty well organized under EDK’s hood, which makes the attempt to cover it all up with a wizard even more questionable.

This post is a jot-down of the technicalities behind designing a minimal bare-boned peripheral and its Linux driver. With “bare-boned” I mean that it has the absolutely simplest bus interface (AXI4 Lite without even decoding the addresses), and that it’s in pure Verilog. No external IP is used.

This peripheral connects to the SP605′s four LEDs. Any write to its address region updates the LED’s state according to the written value’s four LSBs. Reading back from any of its covered addresses gives the four LSBs back. That’s all.

Sources of information

This post assumes that you’re familiar with running Linux on Microblaze. I have a pretty extensive tutorial on the subject for reference.

These are worth to look at:

  • The official cores’ sources (in VHDL) can be found at ISE_DS\EDK\hw\XilinxProcessorIPLib\pcores (path from where ISE is installed). It’s interesting in particular to look at axi_lite_ipif_v1_00_a.
  • The AMBA AXI protocol specification, downloaded free from ARM’s site.
  • Platform Specification Format Reference Manual (UG642, psf_rm.pdf): Describes the file formats in detail. Necessary when editing the files.
  • EDK Concepts, Tools, and Techniques (UG683, edk_ctt.pdf) : The chapter about Creating Your Own Intellectual Property is somewhat helpful to try out the Wizard.

Understanding the process

It looks like the missing link in Xilinx’ documentation is to explain how the whole machinery works with regard to adopting a custom made peripheral. I’ll try to fill in that gap now.

Generally speaking, the minimal core consists of the following files, which should be in a dedicated directory under the “pcores” directory, which is under the EDK project’s top directory:

  • data/minimal_v2_1_0.mpd: This file is what EDK looks at when an IP is added to a project. It contains all the information used directly by the EDK. The peripheral’s GUI is set up according to this information, and it’s also used when the EDK generates wrappers and connections for it. Its format is well documented, but it looks like it’s easier to just copy snippets from existing core’s MPD files. It’s also possible to generate this file automatically with PsfUtility from the toplevel source file, but it’s not clear if it’s worth the effort to learn yet another tool.
  • data/minimal_v2_1_0.pao: This file supplies EDK with a list of HDL files which need to be synthesized to create the peripheral. It also sets the order of synthesis.
  • hdl/verilog/minimal.v: The Verilog file constituting the peripheral. Of course there may be several files, which need to be mentioned in the PAO file.
  • Note that “black box” modules (presynthesized netlists) are listed in BBD files, which are not necessary in this example. When used, the MPD file is set to reflect this.

The file names above relate to a peripheral called “minimal”. They change according to the project’s setting and version numbers.

All in all, the flow is pretty simple: Only the MPD file is considered by EDK, and only at platform netlist generation are the HDL files synthesized according to the PAO file. The instantiation and connection depend on the settings within the EDK (actually, the MHS file).

It’s easiest to create just any peripheral with the wizard, see what they do, and then modify the files.

Going from Wizard’s output to minimal peripheral

This is a short outline of the stages. The result is given in the next section.

  • Edit the data/*.pao file: Remove all files and insert the single Verilog file, changing the type to verilog.
  • In the data/*.mpd file, change OPTION HDL = VHDL to VERILOG too. Also add, under ##ports, PORT minimal_leds = “”, DIR = O, VEC = [3:0] (so that the I/O port is known. Note the =”" part).
  • Remove data/*.prj file  so no unnecessary files are included (even though this file seems to be ignored).
  • Roughly translate the VHDL file to Verilog. Make it Verilog parameters instead of VHDL generics.
  • Rename/remove the devl directory, since its information is not in sync with the new situation, and Custom IP Wizard can’t do any good at this point.
  • And finally, in EDK, Project > Rescan User Repositories
  • Remove the LED_4bits core from the project, choosing “Delete instance and any connections to internal nets”. This will keep the net names used for connecting to the LEDs, and make them available for connection to the new peripheral. Otherwise, the external net names need to be set, and the system.ucf given at the “project” tab updated to reflect the new nets.
  • Add the minimal core to the project, and connect the just released LEDs_4Bits_TRI_O to its minimal_leds port.
  • Create bitfile

The synthesis of the peripheral’s HDL takes place during the “create netlist” flow (which is, of course, part of generating bitfile). For example, the synthesis of an instance named minimal_0 will appear as follows in the console

INSTANCE:minimal_0 - C:\tryperipheral\system.mhs line 424 - Running XST
synthesis
PMSPEC -- Overriding Xilinx file
<C:/ise13_2/ISE_DS/EDK/spartan6/data/spartan6.acd> with local file
<C:/ise13_2/ISE_DS/ISE/spartan6/data/spartan6.acd>

And if there are errors in the HDL, they will show up at this point.

Sample files

These are the files used for the minimal peripheral. They are a sloppy adoption of the files generated by the Custom IP Wizard, so they’re very likely to contain unnecessary declarations.

First, the Verilog file:

module minimal #(
 parameter C_S_AXI_DATA_WIDTH = 32,
 parameter C_S_AXI_ADDR_WIDTH = 32,
 parameter C_S_AXI_MIN_SIZE = 'h000001FF,
 parameter C_USE_WSTRB = 0,
 parameter C_DPHASE_TIMEOUT = 8,
 parameter C_BASEADDR = 'hFFFFFFFF,
 parameter C_HIGHADDR = 'h00000000,
 parameter C_FAMILY = "spartan6",
 parameter C_NUM_REG = 1,
 parameter C_NUM_MEM = 1,
 parameter C_SLV_AWIDTH = 32,
 parameter C_SLV_DWIDTH = 32
 )
 (
 input S_AXI_ACLK,
 input S_AXI_ARESETN,
 input [(C_S_AXI_ADDR_WIDTH-1):0] S_AXI_AWADDR,
 input S_AXI_AWVALID,
 input  [(C_S_AXI_DATA_WIDTH-1):0] S_AXI_WDATA,
 input  [((C_S_AXI_DATA_WIDTH/8)-1):0] S_AXI_WSTRB,
 input S_AXI_WVALID,
 input S_AXI_BREADY,
 input  [(C_S_AXI_ADDR_WIDTH-1):0] S_AXI_ARADDR,
 input S_AXI_ARVALID,
 input S_AXI_RREADY,
 output S_AXI_ARREADY,
 output  [(C_S_AXI_DATA_WIDTH-1):0] S_AXI_RDATA,
 output  [1:0] S_AXI_RRESP,
 output S_AXI_RVALID,
 output S_AXI_WREADY,
 output [1:0] S_AXI_BRESP,
 output reg S_AXI_BVALID,
 output S_AXI_AWREADY,

 output reg [3:0] minimal_leds
 );

 assign  S_AXI_RDATA = minimal_leds;
 assign  S_AXI_RRESP = 0; // OKAY on AXI4
 assign  S_AXI_ARREADY = 1; // Always ready for read address
 assign  S_AXI_AWREADY = 1; // Always ready for write address
 assign  S_AXI_RVALID = 1; // Read data always valid (ILLEGAL)
 assign  S_AXI_WREADY = 1; // Always ready to write
 assign  S_AXI_BRESP = 0; // OKAY on AXI4

 // This will not work OK if several "bursts" are sent with no BVALIDs
 // inbetween. Not an expected scenario.

 always @(posedge S_AXI_ACLK)
   if (S_AXI_WVALID)
     begin
        S_AXI_BVALID <= 1;
        minimal_leds <= S_AXI_WDATA;
     end
   else if (S_AXI_BREADY && S_AXI_BVALID) // Active BRESP cycle
     S_AXI_BVALID <= 0;

endmodule

Most of the parameters at the top can be removed, I believe. It appears like they are necessary only when creating the MPD file with PsfUtility.

All ports, except minimal_leds are standard AXI4 lite ports. The implementation of the interface isn’t example for anything except a quick and dirty peripheral which responds to bus requests. The only thing it does actively is to update minimal_leds when necessary, and toggle the AXI_BVALID, so that only one burst response is sent for each write cycle (which is always one clock long in AXI4 lite). It’s OK not to decode the address, since it’s the interconnect’s job to make sure each peripheral gets only what it directed to it.

Holding S_AXI_RVALID high all the time violates the AXI4 spec, since it’s required to be asserted only after ARVALID and ARREADY. But the interconnect tolerated this anyhow.

Now to minimal_v2_1_0.mpd:

BEGIN minimal

## Peripheral Options
OPTION IPTYPE = PERIPHERAL
OPTION IMP_NETLIST = TRUE
OPTION HDL = VERILOG
OPTION IP_GROUP = MICROBLAZE:USER
OPTION DESC = MINIMAL
OPTION LONG_DESC = A minimal peripheral to start off with
OPTION ARCH_SUPPORT_MAP = (others=DEVELOPMENT)

## Bus Interfaces
BUS_INTERFACE BUS = S_AXI, BUS_STD = AXI, BUS_TYPE = SLAVE

## Generics for VHDL or Parameters for Verilog
PARAMETER C_S_AXI_DATA_WIDTH = 32, DT = INTEGER, BUS = S_AXI, ASSIGNMENT = CONSTANT
PARAMETER C_S_AXI_ADDR_WIDTH = 32, DT = INTEGER, BUS = S_AXI, ASSIGNMENT = CONSTANT
PARAMETER C_S_AXI_MIN_SIZE = 0x000001ff, DT = std_logic_vector, BUS = S_AXI
PARAMETER C_USE_WSTRB = 0, DT = INTEGER
PARAMETER C_DPHASE_TIMEOUT = 8, DT = INTEGER
PARAMETER C_BASEADDR = 0xffffffff, DT = std_logic_vector, MIN_SIZE = 0x0, PAIR = C_HIGHADDR, ADDRESS = BASE, BUS = S_AXI
PARAMETER C_HIGHADDR = 0x00000000, DT = std_logic_vector, PAIR = C_BASEADDR, ADDRESS = HIGH, BUS = S_AXI
PARAMETER C_FAMILY = virtex6, DT = STRING
PARAMETER C_NUM_REG = 1, DT = INTEGER
PARAMETER C_NUM_MEM = 1, DT = INTEGER
PARAMETER C_SLV_AWIDTH = 32, DT = INTEGER
PARAMETER C_SLV_DWIDTH = 32, DT = INTEGER
PARAMETER C_S_AXI_PROTOCOL = AXI4LITE, TYPE = NON_HDL, ASSIGNMENT = CONSTANT, DT = STRING, BUS = S_AXI

## Ports
PORT S_AXI_ACLK = "", DIR = I, SIGIS = CLK, BUS = S_AXI
PORT S_AXI_ARESETN = ARESETN, DIR = I, SIGIS = RST, BUS = S_AXI
PORT S_AXI_AWADDR = AWADDR, DIR = I, VEC = [(C_S_AXI_ADDR_WIDTH-1):0], ENDIAN = LITTLE, BUS = S_AXI
PORT S_AXI_AWVALID = AWVALID, DIR = I, BUS = S_AXI
PORT S_AXI_WDATA = WDATA, DIR = I, VEC = [(C_S_AXI_DATA_WIDTH-1):0], ENDIAN = LITTLE, BUS = S_AXI
PORT S_AXI_WSTRB = WSTRB, DIR = I, VEC = [((C_S_AXI_DATA_WIDTH/8)-1):0], ENDIAN = LITTLE, BUS = S_AXI
PORT S_AXI_WVALID = WVALID, DIR = I, BUS = S_AXI
PORT S_AXI_BREADY = BREADY, DIR = I, BUS = S_AXI
PORT S_AXI_ARADDR = ARADDR, DIR = I, VEC = [(C_S_AXI_ADDR_WIDTH-1):0], ENDIAN = LITTLE, BUS = S_AXI
PORT S_AXI_ARVALID = ARVALID, DIR = I, BUS = S_AXI
PORT S_AXI_RREADY = RREADY, DIR = I, BUS = S_AXI
PORT S_AXI_ARREADY = ARREADY, DIR = O, BUS = S_AXI
PORT S_AXI_RDATA = RDATA, DIR = O, VEC = [(C_S_AXI_DATA_WIDTH-1):0], ENDIAN = LITTLE, BUS = S_AXI
PORT S_AXI_RRESP = RRESP, DIR = O, VEC = [1:0], BUS = S_AXI
PORT S_AXI_RVALID = RVALID, DIR = O, BUS = S_AXI
PORT S_AXI_WREADY = WREADY, DIR = O, BUS = S_AXI
PORT S_AXI_BRESP = BRESP, DIR = O, VEC = [1:0], BUS = S_AXI
PORT S_AXI_BVALID = BVALID, DIR = O, BUS = S_AXI
PORT S_AXI_AWREADY = AWREADY, DIR = O, BUS = S_AXI
PORT minimal_leds = "", DIR = O, VEC = [3:0]

END

This file is exactly as generated by the Wizard, except for the HDL option in the beginning changed to VERILOG, and the added port minimal_leds at the end. Note its assignment to “”. This file is best created by looking at examples of existing cores.

Now to minimal_v2_1_0.pao:

lib minimal_v1_00_a minimal verilog

which was rewritten to reflect that the peripheral consists of one single Verilog file.

The device tree file

The device tree file needs to be generated as described in one of my posts. The relevant section is given here, since it relates to kernel code presented next:

minimal_0: minimal@7ae00000 {
 compatible = "xlnx,minimal-1.00.a";
 reg = < 0x7ae00000 0x10000 >;
 xlnx,dphase-timeout = <0x8>;
 xlnx,family = "spartan6";
 xlnx,num-mem = <0x1>;
 xlnx,num-reg = <0x1>;
 xlnx,s-axi-min-size = <0x1ff>;
 xlnx,slv-awidth = <0x20>;
 xlnx,slv-dwidth = <0x20>;
 xlnx,use-wstrb = <0x0>;
 }

It’s pretty evident that some of these parameters have no use.

The driver

First, it’s convenient to create a makefile for cross compilation. Even though the correct way is to set the environment variables in the shell, and run the module compilation in the same way the kernel itself is compiled, it’s much more convenient to go just “make” or “make clean” with this makefile. It’s not good for distribution, as the paths to both the kernel tree and cross compiler are hardcoded.

So here’s a dirty, but yet convenient makefile:

export CROSS_COMPILE=/path/to/microblazeel-unknown-linux-gnu/bin/microblazeel-unknown-linux-gnu-
export ARCH=microblaze

ifneq ($(KERNELRELEASE),)
obj-m    := minimal.o

else
KDIR := /path/to/linux-2.6.38.6

default:
 @echo $(TARGET) > module.target
 $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
 @rm -f *.ko *.o modules.order Module.symvers *.mod.? .minimal.* *~
 @rm -rf .tmp_versions module.target

minimal.ko:
 $(MAKE)
endif

And now to the driver itself, minimal.c:

#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <asm/io.h>

/* Match table for of_platform binding */
static struct of_device_id minimal_of_match[] __devinitdata = {
 { .compatible = "xlnx,minimal-1.00.a", },
 {}
};

MODULE_ALIAS("minimal");

static void __iomem *regs;
static struct resource res;

static int __devinit
minimal_of_probe(struct platform_device *op, const struct of_device_id *match)
{

 const int *width;
 int ret;
 int val;

 ret = of_address_to_resource(op->dev.of_node, 0, &res);
 if (ret) {
 printk(KERN_WARNING "minimal: Failed to obtain device tree resource\n");
 return ret;
 }

 printk(KERN_WARNING "minimal: Physical address to resource is %x\n", (unsigned int) res.start);

 if (!request_mem_region(res.start, 32, "mimimal")) {
 printk(KERN_WARNING "minimal: Failed to request I/O memory\n");
 return -EBUSY;
 }

 regs = of_iomap(op->dev.of_node, 0); /* Verify it's non-null! */

 printk(KERN_WARNING "minimal: Access address to registers is %x\n", (unsigned int) regs);

 width = of_get_property(op->dev.of_node, "xlnx,slv-dwidth", NULL);

 printk(KERN_WARNING "minimal: Obtained width=%d\n", be32_to_cpu(*width));

 val = ioread32(regs);
 printk(KERN_WARNING "minimal: Read %d, writing %d\n", val, val+1);

 iowrite32(++val, regs);

 return 0; /* Success */
}

static int __devexit minimal_of_remove(struct platform_device *op)
{
 iounmap(regs);
 release_mem_region(res.start, 32);
 return 0; /* Success */
}

static struct of_platform_driver minimal_of_driver = {
 .probe = minimal_of_probe,
 .remove = __devexit_p(minimal_of_remove),
 .driver = {
 .name = "minimal",
 .owner = THIS_MODULE,
 .of_match_table = minimal_of_match,
 },
};

int __init minimal_init(void)
{
 int ret;
 ret = of_register_platform_driver(&minimal_of_driver);
 return ret;
}

void __exit minimal_exit(void)
{
 of_unregister_platform_driver(&minimal_of_driver);
}

module_init(minimal_init);
module_exit(minimal_exit);

MODULE_AUTHOR("Eli Billauer");
MODULE_DESCRIPTION("Microblaze minimal module");
MODULE_LICENSE("GPL")

It doesn’t do anything special, except for change the state of the LEDs every time it’s loaded. The drivers also reads one of the parameters from the device tree structure. Not fascinating, but keeps the code, well, minimal.

This code should be pretty straightforward to programmers who are familiar with PCI device drivers, with probing and removal working in more or less the same way. I’ve chosen a hardcoded segment of 32 bytes as the requested region. This depends on the peripheral, of course.

A test run

This is the transcript of the session on the UART console, as run on a freshly booted system. LEDs did indeed go on and off as reflected by the numbers.

/ # insmod minimal.ko
minimal: Physical address to resource is 7ae00000
minimal: Access address to registers is c87e0000
minimal: Obtained width=32
minimal: Read 0, writing 1
/ # lsmod
minimal 1978 0 - Live 0xc8056000
ipv6 305961 10 - Live 0xc8763000
/ # cat /proc/iomem
40600000-4060000f : uartlite
40a00000-40a0ffff : xilinx_spi
40e00000-40e0ffff : xilinx_emaclite
7ae00000-7ae0001f : mimimal
/ # rmmod minimal
rmmod: module 'minimal' not found
/ # cat /proc/iomem
40600000-4060000f : uartlite
40a00000-40a0ffff : xilinx_spi
40e00000-40e0ffff : xilinx_emaclite
/ # lsmod
ipv6 305961 10 - Live 0xc8763000
/ # insmod minimal.ko
minimal: Physical address to resource is 7ae00000
minimal: Access address to registers is c8820000
minimal: Obtained width=32
minimal: Read 1, writing 2

Note that rmmod produces an error message, which makes it look as if it failed to remove the module, when in fact all went well.

The physical address was indeed detected correctly (see device tree), and mapped to another kernel virtual address each time.

The FPGA+ARM Armadeus APF51 board: Buildroot notes

$
0
0

Scope

This post was spun off the main post regarding setting up the Armadeus board for Embedded Linux on ARM and Xilinx Spartan-6 FPGA. It covers my own little war story as I set up the Buildroot SDK, so I could have my own cross compiler and Linux kernel to work with.

As kernel.org happened to be down, this was a (forced) opportunity to look a bit deeper into how things work under the hood. These are my notes, reflecting my own experience, and isn’t a substitute for any official documentation.

Setting up the build environment

Downloaded armadeus-4.0.tar.bz2 from the SourceForge page. There are binaries there too (Linux kernel, rootfs and U-boot image). Opened the tarball, changed directory, and followed the instructions and deduced that the apf51_defconfig is valid even though not documented:

$ make apf51_defconfig

that downloaded buildroot-2010.11.tar.bz2 from http://buildroot.uclibc.org/downloads/buildroot-2010.11.tar.bz2 ran some script (a lot of patches applied) and then opened the config menu.

Config menu settings:

  • Under Target Options > Armadeus Device Support > Size of a single RAM chip, change it to 512 MB (to match my board). On second thought, the kernel detects all 512MB anyhow (despite this parameter set at 256 MB) so it looks like there’s no need to change this.
  • Under Build options, set Number of jobs to run simultaneously to the number of cores in the compiling computer (8 for a quadcore with hyperthreading). So it finishes today.

and then simply went “make”. The computer spits a lot of mumbo-jumbo. Loads, really.

In retrospective, I would consider running “make source” to download whatever needs to be downloaded. In my own build, this turned out to be where problems occured.

Note: Always run make from armadeus-4.0 and not from “buildroot”. Doing the latter will work properly for several components, but will fail every now and then with weird errors. It’s very easy to make this mistake, in particular when kickstarting the build after download failures.

After the build finishes, the interesting stuff is in

  • buildroot/output/images — Root, kernel and bootloader images.
  • buildroot/output/build/staging_dir — Things to run on the host computer, such as the cross-compiler (e.g. buildroot/output/build/staging_dir/usr/arm-unknown-linux-uclibcgnueabi/bin/gcc)

Issues resulting from kernel.org being down

The build got stuck at downloading the Linux kernel. kernel.org happened to be down for maintenance, and Armadeus’ FTP site didn’t supply it. So I fetched  linux-2.6.38.1.tar.bz2 from a Linux mirror and put the file in /path/to/armadeus-4.0/buildroot/downloads and ran make again.

And then a git clone for linux-firmware.git failed, since it’s from kerner.org as well. So I went for the safe “trust anyone” strategy, and went for any repository I could find. So instead of the failed

git clone git://git.kernel.org/pub/scm/linux/kernel/git/dwmw2/linux-firmware.git /home/eli/armadeus/build/armadeus-4.0/buildroot/output/build/firmware-af5222c5ded5d944267acfbd001571409bea7eeb

I went

$ git clone https://github.com/mdamt/linux-firmware.git /home/eli/armadeus/build/armadeus-4.0/buildroot/output/build/firmware-af5222c5ded5d944267acfbd001571409bea7eeb

which wasn’t really helpful, since it didn’t satisfy some Make rule. Running “make -d” I found out that the Make rules would be happy with a tarball, so I went

$ tar -czf ../../downloads/firmware-af5222c5ded5d944267acfbd001571409bea7eeb.tar.gz firmware-af5222c5ded5d944267acfbd001571409bea7eeb

and ran “make” again after removing the directory from which I had created the tarball.

Other problems

I also needed to download iproute2-2.6.35.tar.bz2 from Fedora’s repository into armadeus-4.0/buildroot/downloads, as it couldn’t be downloaded from linuxfoundation.org. This was starting to become routine.

And then I got

/usr/bin/ld: cannot find -lc

while building host-module-init-tools-3.12. And as buildroot’s docs guessed correctly, I am using Fedora, so it was all down to a

# yum install glibc-static

and go “make” again.

Just a silly mistake

When attempting to build the kernel I got

drivers/usb/Kconfig:169: can't open file "drivers/armadeus/Kconfig"

which was a direct result of the broken link of the “armadeus” subdirectory in “output/build/linux-2.6.38.1/drivers/”.  But that was just a silly mistake: I ran “make” from “buildroot” and not from the armadeus-4.0 directory, so the Makefile setting up the necessary environment variable was never set.

Doing it all over again

The idea is to rerun everything offline, i.e. without any downloads from the internet. One can’t always rely on that servers will be up and ready… The correct way to do this is to define the download directories before starting off, but I didn’t bother.

My motivation for doing this was that after kickstarting the build so many times, it crossed my mind that I may have messed up something without noticing. So I figured it would be best to rerun it all in one go.

So first of all, move the already built directory to finished-armadeus-4.0. It’s some 3.5 GB, but we’ll need only the download directories.

$ tar -xjf armadeus-4.0.tar.bz2
$ cd armadeus-4.0
$ cp -r ../finished-armadeus-4.0/downloads/ .
$ cp -r ../finished-armadeus-4.0/buildroot/downloads/ buildroot/
$ make apf51_defconfig

Which brings us back to the configuration menu, after which a simple “make” does the work.

It’s also worth to mention, that according to the docs/README file, “make source” will download all necessary sources, which is a good starter.

i.MX51 EIM bus clarified

$
0
0

Banner

These are my notes as I made my way in grasping how the EIM bus works. Unfortunately, the information in the reference manual was far from complete, so except for the list of acronyms, this page consists of things I found out by reverse engineering the bus.

The actual bus cycle outlines and timings are given in section 4.6.7 of the datasheet. There are also timing diagrams in section 63.8 of the reference manual, which include the internal AXI bus as well, which may be a bit confusing.

I worked with an Armadeus APF51 board, which has a 16-bit multiplexed bus connected to the Xilinx Spartan-6 FPGA. My preferences and observations are related to this board.

I wrote some code for the FPGA and processor on the board, for the sake of this reverse engineering, which is available in another post of mine. I’ve also published some oscilloscope shots during the process, which you may look at here.

EMI, EIM and WEIM

Freescale’s nomenclature regarding the external bus is somewhat inconsistent, so here’s a quick clarification.

EMI is the external memory interface, which is the general crossconnect for making AXI masters talk with their slaves, internal or external. EIM is the external interface module, which is embodied as the Wireless External Interface Module (WEIM). Pin names in the datasheet and schematics are used in EIM terms, but the reference manual uses WEIM nomenclature. Section 4.6.7.1 in the datasheet contains a table which connects between the different names used for the same signals. It’s a must to look at.

Parameter acronyms

Section 63 in the reference manual covers the external memory interface. As it uses acronyms pretty extensively, sometimes with forward reference, I ended up making a cheat sheet.

Here’s a list of bus parameter acronyms, along with the values I chose by default for my tests with the bus (which are pretty much my final preferences). The acronyms themselves were taken directly from chapter 63.4.3 in the Reference Manual.

From Chip Select x General Configuration Register 1

  • PSZ = 0, Page Size
  • WP = 0, Write Protect
  • GBC = 1, Gap Between Chip Selects. That is, the gap between asserting one CS pin and then asserting another pin. Not to be confused with CSREC. At least 1 in sync mode.
  • AUS = 1, Address Unshifted.
  • CSREC = 1, CS Recovery, minimal unused clock cycles on the bus between operations on the same CS pin (back to back access). At least 1 in sync mode.
  • SP = 0, Supervisor Protect
  • DSZ = 1, Data Port Size (16 bit on Armadeus)
  • BCS = 0, Bus Clock Start, for fine shifting of the burst clock
  • BCD = 0, Bus Clock Divider (zero means don’t divide). Note that for BCM = 1 (BCLK running continuously), BCD only controls the bus signals’ rate, and not the BCLK signal itself. More about this is the “gotcha” section.
  • WC = 0, Write Continuous
  • BL= 0, Burst Length. When the internal DMA functional unit accesses the EIM bus, BL must be set to cover the longest burst possibly required (typically 32 bytes), or data is corrupted when the SDMA engine induces a burst longer than BL allows.
  • CREP = 1, Configuration Register Enable Polarity
  • CRE = 0, Configuration Register Enable (disabled in my case)
  • RFL/WFL = 1, Read/Write Fix Latency. Whether WAIT should be ignored (it is for RFL= WFL =1)
  • MUM = 1. Multiplexed Mode. If data and address are multiplexed on the same lines. True in this case.
  • SRD/SWD = 1, Synchronous Read/Write Data. Whether bus operations are synchronous. They are, of course.
  • CSEN = 1, CS Enable. If this isn’t set, attempting to write to the relevant region ends up with a bus error (and an oops in the Linux kernel).

From Chip Select x General Configuration Register 2

  • DAP = 0, Data Acknowledge polarity, irrelevant in sync mode
  • DAE = 0, Data Acknowledge Enable, irrelevant in sync mode
  • DAPS = 0, Data Acknowledge Polling Start, irrelevant in sync mode
  • ADH = 0, Address Hold Time

From Chip Select x Read/Write Configuration Register 1 and 2

  • RWSC/WWSC = 1, Read/Write Wait State Control. The number of wait states on a bus transaction, given in BCLK cycles (as opposed to WEIM cycles). Must be at least 1.
  • RADVA/WADVA = 0, Read/Write ADV Assertion. Tells when ADV is asserted in WEIM cycles. Note that while ADV is asserted, the address is present on the multiplexed address/data lines, no matter what, even at the cost of some or all data not appearing on the bus at all.
  • RADVN/WADNV = 0, Read/Write ADV Negation. How many extra WEIM cycles ADV stays asserted. The formula given in the reference manual says that by default, ADV is asserted for a BCLK worth’s of time, starting as required by RADVA/WADVA, and whatever is given by RADVA/WADVA is added to that.
  • RAL/WAL = 0, Read/Write ADV Low. When this bit is set, RADVN/WADVN are ignored, and ADV is asserted during the entire bus operation.
  • RCSA/WCSA = 0, Read/Write CS Assertion. The number of WEIM clocks the CS’s assertion is delayed.
  • RCSN/WCSN = 0, Read/Write CS Negation. Ignored in sync mode.
  • RBEA /WBEA= 0, Read/Write BE Assertion. The number of WEIM clocks to delay BE assertion.
  • RBEN/WBEN=0, Read/Write BE Negation. Ignored in sync mode.
  • RBE = 1, Read BE enable
  • WBED = 0, Write BE disable
  • OEA = 0, (Read) OE Assertion. How many WEIM clock cycles to delay the OE signal assertion. Note that unlike other delay parameters, OEA is relative to the first data clock cycle, so OE will mean “expecting data on data lines on this clock cycle” for OEA=0. It works this way in multiplexed mode, at least.
  • OEN = 0, (Read) OE Negation. Ignored in sync mode.
  • APR = 0, (Read) Asynchronous Page Read. Must be held zero in sync mode.
  • PAT = 0, (Read) Page Access Time. Ignored when APR=0 (and hence ignored in sync mode)
  • RL = 0, Read Latency.
  • WEA = 0, WE Assertion. How many WEIM clock cycles to delay WE assertion
  • WEN = 0, WEN Negation. Ignored in sync mode
  • WBCDD = 0, Write Burst Clock Divisor Decrement

And finally, from the WEIM Configuration Register

  • WDOG_LIMIT = 0, Memory Watchdog. Not really necessary in sync mode
  • WDOG_EN = 0, Memory Watchdog Enable.
  • INTPOL = 1, Interrupt Polarity
  • INTEN = 0, Interrupt Enable
  • BCM = 1, Burst clock mode. When asserted, the BCLK runs continuously, instead of only during bus transactions.
  • GBCD = 0, General Burst Clock Divisor. Used globally for all CS spaces instead of each space’s BCD when BCM=1. See warning below.

Some gotcha notes

  • Clock division with BCD and GBCD is a messy issue, and clock division is best avoided when running the clock continuously (BCM=1). The thing is that while GBCD indeed controls the division of the BCLK signal itself, the bus signals are governed by the clock divided by the individual BCD. So if BCD != GBCD for a specific CS region, the bus signals are completely unrelated to BCLK. But even if BCD and GBCD are equal, there is no guaranteed phase relation between them (as has been observed) because they’re generated by two unrelated clock dividers. So the BCLK signal is useless unless BCD = GBCD = 0.
  • Most delays are given in WEIM clocks, not BCLK clocks. This makes no difference as long as BCLK runs at WEIM rate (BCD=0), but if BCLK is divided, even for the sake of getting clear transitions on an oscilloscope, this needs to be taken into account.
  • For most parameters, delays in assertions make the signal’s assertion duration shorter. It’s not a time shift, as the deassertion doesn’t move.
  • All signals, except OE, are asserted at the same first clock cycle of the bus access, unless delayed by the parameters below. This includes WE and BE, which one could mistakenly expect to be asserted when data is available. OE is indeed asserted when data is due to be supplied, and its assertion timing parameter works relatively to that clock cycle.
  • Delaying and/or extending the ADV signal with RADVA/WADVA  and RADVN/WADVN on a multiplexed bus causes address to be present during the relevant time periods without changing other timings, with precedence to address. So time slots which would otherwise be used for data transmission are overridden with address on the bus, possibly eliminating data presence on the bus completely. This can be compensated with wait states, but note that wait states count in BCLK cycles, while the ADV adjustments count WEIM cycles.
  • The OE signal is somewhat useless as an output-enable when BCLK runs at 95 MHz: If used directly to drive tri-state buffers, the round-trip from its assertion to when data is expected is ridiculously short: The data-to-rising clock setup time is 2 ns, according to the datasheet (section 4.6.7.3, table 53, parameter WE18). OE is asserted on the falling edge of the clock just before the rising edge, for which the data is sampled, with a delay of up to 1.75 ns (same table, WE10). At a clock cycle of 10.5 ns (95 MHz), this half-clock gap between these two events is 5.25 ns, leaving 5.25 – 2 – 1.75 = 1.5 ns for the bus slave to take control of the bus. Not realistic, to say the least. So the bus slave must deduce from WE whether the bus cycle is read or write, and drive the bus according to predefined timing. As for bursts, I’m not on the clear on whether bursts can be stalled in the middle and how OE behaves if that is possible. The timing diagram in section 63.8.7 of the Reference Manual does not imply that OE may get high in the middle of a burst. On the other hand, it shows OE going down together with ADV, which surely isn’t the case as I observed (maybe because I ran on a multiplexed bus?).

Write data cycles

Reminder: This entire post relates to a 16-bit address/data multiplexed EIM bus.

The simplest write data cycle (as defined by parameter settings above) consists of three BCLK cycles. On the first one, the lower 16 bits of the address is present on the bus, and ADV is held low. On the two following clock cycles, ADV is high and the 32-bit word is transferred over the data lines. CS and WE are held low during the entire cycle (three BCLKs). And no, the upper 16 bits of the address are never presented on the bus.

For BCD=0 (BCLK = WEIM clock), the master toggles its signals on the falling edge of BCLK, and samples signals from the slave on its rising edge. This holds true for all bus signals.

Data is sent in little endian order: A 32-bit word is sent with its lower 16-bit part (bits 15:0) in the first clock cycle, and the higher 16 bits (bits 31:16) in the second cycle. The 16-bit words are sent naturally (that is, each DA[15:0] is a consistent 16-bit word).

With AUS=1 (address unshifted) the address’ lower 16 bits appear naturally on the address cycle. For example, writing to offset Ox30 (to the CS2 address range)  sets bits DA[4]=1 and DA[5]=1 (only) in the address clock cycle.

With AUS=0 (address shifted according to port size) the address shown on the bus is shifted by one bit, since there are two bytes in the port size’s width. Hence writing to offset Ox60 sets bits DA[4]=1 and DA[5]=1 (only) in the address clock cycle.

As said above (and verified with scope), WADVA and WADVN don’t just move around the ADV signal’s assertion, but also the times at which the address is given on the bus, possibly overriding time which would otherwise be used to transfer data. It’s the user’s responsibility to make sure (possibly with wait states) that there is enough time for data on the bus.

Wait states, as set by WWSC extend the first data cycle, so the lower 16 bits of data are held on the bus for a longer time. If WWSC=0, only the upper 16 bits are shown (the first data cycle is skipped) but this is an illegal setting anyhow. Again, note that WWSC counts BCLK cycles, as opposed to almost every other timing parameter.

For BCD=0 (only) the data lines’ levels are held with the last written value until the next bus operation. This feature (which AFAIK is not guaranteed by spec) is used by the FPGA bitstream loader: A word is written, and the FPGA’s clock is toggled afterwards to sample the data which is left unchanged (which is maybe why you can’t load the bitstream file from the on-board flash, as indicated in Armadeus’ wiki page). When BCD>0, the data lines go to zero after the cycle is ended.

Read data cycles

Read data cycles are in essence the same as write cycles, only the bus slave is expected to drive the data lines in the same time slots for which the master drove them on a bus write operation. On read cycles, the master samples the data lines on rising BCLK edges, which is symmetric to the slave sampling the same lines on write cycles. The endianess is the same of course.

OE is asserted on the same WEIM cycle for which ADV is deasserted, which is one BCLK cycle after CS’s assertion with the default parameters given above. WE is not asserted in write cycles, of course.

As mentioned in the “gotcha notes” above, the OE line is pretty useless in high bus rates, as it’s asserted on the falling edge of BCLK coming just before the rising edge on which the data is sampled. This gives by far too little time for the slave to respond. So the slave should figure out the correct behavior and timing according to WE and CS.

Using the WAIT signal

In order to use the WAIT signal for adding wait states on the fly, the respective RFL/WFL parameter need to be zeroed. If RFL=0, also set RWSC=2 (instead of the minimal 1), or four useless and unused wait states will be added to each bus cycle, most likely due to an illegal condition in the master’s bus state machine. This is not necessary for writes (i.e. it’s OK to have WFL=0 and WWSC=1).

WAIT is active low. When the master samples WAIT low (on a rising BCLK edge) it considers the following rising BCLK edge as a wait state. It’s or course legal to assert WAIT for consecutive BCLK cycles to achieve long waits. If WAIT is not asserted, the bus runs according to RWSC/WWSC. Each BCLK cycle is considered independently, so when a 32-bit word is transmitted on two BCLK cycles, wait states can be inserted between 16-bit words, resulting in expected behavior. There is no need to consider the fact that these 16-bit words form a 32-bit word when dealing with wait state behavior.

As one would expect, if WAIT is asserted with respect to a bus cycle that wouldn’t occur anyhow (i.e. the last BCLK cycle in a transmission), it’s ignored.

In read cycles, all this boils down to that if the master sampled WAIT low on BCLK rising edge n, no data will be sampled from data lines on rising edge n+1, and the entire bus operation is extended by another BCLK cycle. RWSC must be set to a minimum of 2, since WAIT is ignored on the first bus cycle (on which ADV is asserted) , so the first chance to request a wait state is on the second cycle, which must be a wait state anyhow. If RWSC=1 and RFL=0, the master will insert this wait state anyhow, but misbehave as just mentioned above. Even though counterintuitive, the master may very well sample data from the bus on a BCLK rising edge for which WAIT is asserted. This will make the following bus cycle a wait state, as one can deduce from the mechanism. But it may come intuitively unnatural that an asserted WAIT and valid data are sampled on the same BCLK.

For write cycles, if the master samples WAIT asserted on a rising edge of BCLK, it will behave as usual on the falling BCLK immediately following it, but will not update data lines on the falling BCLK edge afterwards (and hold the internal state machine accordingly). This follows the overall scheme of wait states described above. Unlike read bus operations, this holds true for the ADV cycle as well, so it’s possible to get wait states on the first data transaction by asserting wait on the first BCLK cycle. For WWSC=1, this means in practice to have WAIT asserted while there is no bus activity, because there’s half a clock cycle between the assertion of CS, ADV and other bus signals, and the sampling of WAIT in this case. In order to give the slave time to assert WAIT depending on the bus operation’s nature, WWSC has to be increased to 2 at least.

Bus frequency

The bus EIM bus frequency is derived by default from PLL2 (at least on Armadeus), which is a 665 MHz clock, divided according to the emi_slow_podf field in the CBCDR register (see 7.3.3.6 in the reference manual). On the Armadeus platform, this field is set to 6 by default, so the clock is divided by 7, yielding a bus clock of 95 MHz. To change it, the following code snippet applies:

#define MXC_CCM_CBCDR 0x14
u32 temp_clk;
const emi_slow_podf = 7;

temp_clk = __raw_readl( MX51_IO_ADDRESS(MX51_CCM_BASE_ADDR)+ MXC_CCM_CBCDR );

__raw_writel( (temp_clk & (~0x1c00000)) | (emi_slow_podf << 22),
               MX51_IO_ADDRESS(MX51_CCM_BASE_ADDR)+ MXC_CCM_CBCDR )

The above code reduces the EMI clock to 83.125 MHz (divide by 8).

Note that there’s a little remark in section 4.6.7.3 of the datasheet (Table 53, WEIM Bus Timing Parameters), in footnote 4, saying “The lower 16 bits of the WEIM bus are limited to 90 MHz”. Indeed, running 95 MHz has proven to have rare bus malfunctions (after half a gigabyte of data or so, probably causing some local heating), taking the form of sporadic bus cycle missed, causing injection of bogus data or data being missed.

 

Armadeus APF51 / Freescale i.MX51: A kit for reverse engineering the EIM bus

$
0
0

Banner

What we have here

As one can guess from my notes about the i.MX51′s external bus and the oscilloscope shots I’ve published, I made myself a small dissection kit for watching the bus’ lines activity with a digital oscilloscope.

This is a good time to mention, that the kit was done quickly and dirty, so the code below should not be taken as an example of proper coding for FPGA nor the Linux kernel. Seriously, this is just lab code.

Anyhow, this little kit consists of two parts

  • Verilog code and UCF for programming the FPGA. Except for blinking the LED (at 1 Hz), it also wires all EIM-bus related signals to the FPGA pin headers on the development board, so they can be sampled easily with oscilloscope’s probes. You can download the bitfile directly if your board has the LX9 FPGA, or implement it from the sources below.
  • A kernel module, which performs a single bus operation when it’s loaded. It’s explained further below. If you happen to be running on a 2.6.38.1 Linux kernel on your board (in particular the 2.6.38.1 which comes preloaded on the board), you may try using the precompiled kernel module. Or do it the “right way” and compile the module from the sources below.

The Verilog code below pretty much explains itself. And as the comments in the UCF say, the “debug_pins_outer” pin vector runs from pin #38 downwards continuously, on even pins only, on the outer FPGA pin header. This may sound complicated, but it simply means that out of the two rows of this pin header, only the row reached easily with a probe is used. And since pin #40 (in the corner) isn’t attached to the FPGA, debug_outer_pins[0] is connected to pin #38, debug_outer_pins[1] to #36 and so on.

As for the “debug_pin_inner” it goes more or less the same. Going from pin #3 for debug_inner_pins[0] and up on odd pin numbers, only the inner pin row of the inner pin header is used for easy physical access.

This may look like a weird choice of pin assignments, but this was the only way to get the vectors assigned on the pin headers without any gaps between them, so it’s easy to reach any signal in the vectors just by counting pins on the pin header.

Please make sure that the two “FPGA bank” jumpers are installed on your board, or nothing will appear on the pin headers. These jumpers were installed on the board as I got it, so just check it’s OK.

It’s also worth to note that debug_pins_outer[4] happens to be connected to a pin which is shared with a pushbutton on the board. Since the line is pulled up with a 10 kOhm resistor, this line may have some timing skew.

Simple use

Assuming that both the bitfile and the kernel module are in the currect directory, first load the FPGA if you haven’t done so already:

# load_fpga armaled.bit

A green LED should start blinking as a result of this. Note that according to Armadeus’ wiki page on the FPGA loader, armaled.bit should not be on the on-board flash. Copy it to /tmp first (which is on RAM) or load it from an net drive (e.g. NFS) like I did.

And then, to kick off a bus cycle, load the module and catch it on the oscilloscope:

# insmod eimtest.ko

And then unload the module, so you can load it again for the next try:

# rmmod eimtest

The relevant bus parameters can be set directly when loading the module. For example, to add an extra bus wait state, disable continuous bus clock, run at 1/4 bus rate and use bus address OxABC0, go:

# insmod eimtest.ko WSC=2 BCD=3 BCM=0 addr=0xabc0

A list of kernel module parameters, which in turn changes the bus parameters, is found in the kernel module’s source. Anything declared with “module_param” can be set. The defaults are given in the variable declarations. Setting the address and data is also possible, but be sure not to exceed the address 0xFFFC, or you’ll get a kernel oops. Also note that addresses not aligned to 32-bit words will produce several bus cycles.

The Verilog code

Note that the direct wire connections have a variable delay. This results in some unknown skew (1-2ns, I suppose) between the outputs.

module armaled
 (
 input ext_clk,
 output reg led,
 output irq,

 input [15:0] imx51_da,

 input imx51_cs1,
 input imx51_cs2,
 input imx51_adv,
 input imx51_we,
 input imx51_eb0,
 input imx51_eb1,
 input imx51_oe,
 input imx51_dtack,
 input imx51_wait,
 input imx51_bclk,
 input imx51_clko,

 output [13:0] debug_pins_inner,
 output [12:0] debug_pins_outer
 );

 reg [27:0] counter;

 assign     irq = 0;

 assign     debug_pins_outer[0] = imx51_bclk;
 assign     debug_pins_outer[1] = imx51_clko;
 assign     debug_pins_outer[2] = imx51_oe;
 assign     debug_pins_outer[3] = imx51_cs1;
 assign     debug_pins_outer[4] = imx51_cs2;
 assign     debug_pins_outer[5] = imx51_adv;
 assign     debug_pins_outer[6] = imx51_we;
 assign     debug_pins_outer[7] = imx51_eb0;
 assign     debug_pins_outer[8] = imx51_eb1;
 assign     debug_pins_outer[9] = imx51_dtack;
 assign     debug_pins_outer[10] = imx51_wait;
 assign     debug_pins_outer[12:11] = imx51_da[15:14];

 assign     debug_pins_inner = imx51_da[13:0];

 always @(posedge ext_clk)
 begin
 if (counter >= 47500000)
 begin
 led <= !led;
 counter <= 0;        
 end
 else
 counter <= counter + 1;
 end

endmodule

The UCF file

NET "ext_clk" TNM_NET = "TN_ext_clk";
TIMESPEC "TS_ext_clk" = PERIOD "TN_ext_clk" 10.4 ns HIGH 50 %;

NET "led" LOC="G14" | IOSTANDARD=LVCMOS33;# IO_L41P_GCLK9_IRDY1_M1RASN_1
#NET "button" LOC="G15" | IOSTANDARD=LVCMOS33;# IO_L41N_GCLK8_M1CASN_1
NET "ext_clk" LOC="N8" | IOSTANDARD=LVCMOS33;# = BCLK, IO_L29P_GCLK3_2
NET "irq" LOC="P3" | IOSTANDARD=LVCMOS33;# FPGA_INITB

# Debug pins.

# The "inner" set starts from pin #3, running on odd pins only (effectively
# covering the pins convenient to attach a scope's probe to)

NET "debug_pins_inner[0]" LOC="L2" | IOSTANDARD=LVCMOS33;# IO_L39P_M3LDQS_3
NET "debug_pins_inner[1]" LOC="J2" | IOSTANDARD=LVCMOS33;# IO_L41P_GCLK27_M3DQ4_3
NET "debug_pins_inner[2]" LOC="K4" | IOSTANDARD=LVCMOS33;# IO_L43P_GCLK23_M3RASN_3
NET "debug_pins_inner[3]" LOC="K5" | IOSTANDARD=LVCMOS33;# IO_L45P_M3A3_3
NET "debug_pins_inner[4]" LOC="C2" | IOSTANDARD=LVCMOS33;# IO_L83P_3
NET "debug_pins_inner[5]" LOC="D4" | IOSTANDARD=LVCMOS33;# IO_L53P_M3CKE_3
NET "debug_pins_inner[6]" LOC="K3" | IOSTANDARD=LVCMOS33;# IO_L40P_M3DQ6_3
NET "debug_pins_inner[7]" LOC="H3" | IOSTANDARD=LVCMOS33;# IO_L42P_GCLK25_TRDY2_M3UDM_3
NET "debug_pins_inner[8]" LOC="G2" | IOSTANDARD=LVCMOS33;# IO_L44P_GCLK21_M3A5_3
NET "debug_pins_inner[9]" LOC="F3" | IOSTANDARD=LVCMOS33;# IO_L46P_M3CLK_3
NET "debug_pins_inner[10]" LOC="D3" | IOSTANDARD=LVCMOS33;# IO_L54P_M3RESET_3
NET "debug_pins_inner[11]" LOC="E2" | IOSTANDARD=LVCMOS33;# IO_L52P_M3A8_3
NET "debug_pins_inner[12]" LOC="K13" | IOSTANDARD=LVCMOS33;# IO_L44P_A3_M1DQ6_1
NET "debug_pins_inner[13]" LOC="H13" | IOSTANDARD=LVCMOS33;# IO_L42P_GCLK7_M1UDM_1

# The "outer" set starts from pin #38, running on even pins only (effectively
# covering the pins convenient to attach a scope's probe to). Note that the
# vectors runs from high board pin number to low.

NET "debug_pins_outer[0]" LOC="B15" | IOSTANDARD=LVCMOS33;# IO_L1N_A24_VREF_1
NET "debug_pins_outer[1]" LOC="C15" | IOSTANDARD=LVCMOS33;# IO_L33N_A14_M1A4_1
NET "debug_pins_outer[2]" LOC="D15" | IOSTANDARD=LVCMOS33;# IO_L35N_A10_M1A2_1
NET "debug_pins_outer[3]" LOC="E15" | IOSTANDARD=LVCMOS33;# IO_L37N_A6_M1A1_1
NET "debug_pins_outer[4]" LOC="G15" | IOSTANDARD=LVCMOS33;# IO_L41N_GCLK8_M1CASN_1
NET "debug_pins_outer[5]" LOC="J15" | IOSTANDARD=LVCMOS33;# IO_L43N_GCLK4_M1DQ5_1
NET "debug_pins_outer[6]" LOC="L15" | IOSTANDARD=LVCMOS33;# IO_L45N_A0_M1LDQSN_1
NET "debug_pins_outer[7]" LOC="G12" | IOSTANDARD=LVCMOS33;# IO_L30N_A20_M1A11_1
NET "debug_pins_outer[8]" LOC="F12" | IOSTANDARD=LVCMOS33;# IO_L31N_A18_M1A12_1
NET "debug_pins_outer[9]" LOC="H11" | IOSTANDARD=LVCMOS33;# IO_L32N_A16_M1A9_1
NET "debug_pins_outer[10]" LOC="G13" | IOSTANDARD=LVCMOS33;# IO_L34N_A12_M1BA2_1
NET "debug_pins_outer[11]" LOC="J13" | IOSTANDARD=LVCMOS33;# IO_L36N_A8_M1BA1_1
NET "debug_pins_outer[12]" LOC="K11" | IOSTANDARD=LVCMOS33;# IO_L38N_A4_M1CLKN_1

# i.MX51 related pins

NET "imx51_cs1" LOC="R11" | IOSTANDARD=LVCMOS33;# EIM_CS1
NET "imx51_cs2" LOC="N9" | IOSTANDARD=LVCMOS33;# EIM_CS2
NET "imx51_adv" LOC="R9" | IOSTANDARD=LVCMOS33;# EIM_LBA
NET "imx51_we" LOC="R6" | IOSTANDARD=LVCMOS33;# EIM_RW
NET "imx51_eb0" LOC="P7" | IOSTANDARD=LVCMOS33;
NET "imx51_eb1" LOC="P13" | IOSTANDARD=LVCMOS33;
NET "imx51_oe" LOC="R7" | IOSTANDARD=LVCMOS33;
NET "imx51_dtack" LOC="N4" | IOSTANDARD=LVCMOS33;
NET "imx51_wait" LOC="R4" | IOSTANDARD=LVCMOS33;
NET "imx51_bclk" LOC="N12" | IOSTANDARD=LVCMOS33; # Hardwired to N8
NET "imx51_clko" LOC="N7" | IOSTANDARD=LVCMOS33;

NET "imx51_da[7]" LOC="P11" | IOSTANDARD=LVCMOS33;# EIM_DA7
NET "imx51_da[6]" LOC="M11" | IOSTANDARD=LVCMOS33;# EIM_DA6
NET "imx51_da[5]" LOC="N11" | IOSTANDARD=LVCMOS33;# EIM_DA5
NET "imx51_da[13]" LOC="R10" | IOSTANDARD=LVCMOS33;# EIM_DA13
NET "imx51_da[12]" LOC="L9" | IOSTANDARD=LVCMOS33;# EIM_DA12
NET "imx51_da[11]" LOC="M10" | IOSTANDARD=LVCMOS33;# EIM_DA11
NET "imx51_da[10]" LOC="M8" | IOSTANDARD=LVCMOS33;# EIM_DA10
NET "imx51_da[9]" LOC="K8" | IOSTANDARD=LVCMOS33;# EIM_DA9
NET "imx51_da[8]" LOC="L8" | IOSTANDARD=LVCMOS33;# EIM_DA8
NET "imx51_da[0]" LOC="N6" | IOSTANDARD=LVCMOS33;# EIM_DA0
NET "imx51_da[4]" LOC="P5" | IOSTANDARD=LVCMOS33;# EIM_DA4
NET "imx51_da[3]" LOC="R5" | IOSTANDARD=LVCMOS33;# EIM_DA3
NET "imx51_da[2]" LOC="L6" | IOSTANDARD=LVCMOS33;# EIM_DA2
NET "imx51_da[1]" LOC="L5" | IOSTANDARD=LVCMOS33;# EIM_DA1
NET "imx51_da[15]" LOC="M5" | IOSTANDARD=LVCMOS33;# EIM_DA15
NET "imx51_da[14]" LOC="N5" | IOSTANDARD=LVCMOS33;# EIM_DA14

The kernel module

It currently reads one word from the bus. A write operation is obtained by commenting and uncommenting in the region marked in red.

#include <linux/version.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <asm/io.h>
#include <mach/iomux-mx51.h>
#include <mach/fpga.h>
#include <mach/hardware.h>

MODULE_DESCRIPTION("EIM interface test module");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eli Billauer");

#define EIMTEST ""

static int PSZ = 0;
static int AUS = 1;
static int BCS = 0;
static int BCD = 0;
static int BL = 0;
static int FL = 1; // Cover RFL and WFL alike
static int WC = 0;
static int ADH = 0;
static int WSC = 1;
static int ADVA = 0; // RADVA and WADVA
static int ADVN = 0; // RADVN and WADVN
static int OEA = 0;
static int CSA = 0; // RCSA and WCSA
static int RL = 0;
static int BEA = 0;
static int BE = 1;
static int WEA = 0;
static int INTPOL = 1; // Interrupt polarity
static int INTEN = 0; // Interrupt enable
static int GBCD = 0; // Burst clock divisor
static int BCM = 1; // Burst clock mode (set continuous here)
static int addr = 0x00001234;
static int data = 0xFFFF5555;

module_param(PSZ, int, 0);
module_param(AUS, int, 0);
module_param(BCS, int, 0);
module_param(BCD, int, 0);
module_param(BL, int, 0);
module_param(FL, int, 0);
module_param(WC, int, 0);
module_param(ADH, int, 0);
module_param(WSC, int, 0);
module_param(ADVA, int, 0);
module_param(ADVN, int, 0);
module_param(OEA, int, 0);
module_param(CSA, int, 0);
module_param(RL, int, 0);
module_param(BEA, int, 0);
module_param(BE, int, 0);
module_param(WEA, int, 0);
module_param(INTPOL, int, 0);
module_param(INTEN, int, 0);
module_param(GBCD, int, 0);
module_param(BCM, int, 0);
module_param(data, int, 0);
module_param(addr, int, 0);

static u32 readreg(int offset) {
 return __raw_readl( MX51_IO_ADDRESS(MX51_WEIM_BASE_ADDR) + offset);
}

static void writereg(int offset, u32 val) {
 __raw_writel(val, MX51_IO_ADDRESS(MX51_WEIM_BASE_ADDR) + offset);
}

static u32 bitfield(int shift, int bits, int val) {
 return ((val & ( ( 1 << bits ) - 1 ) ) << shift);
}

static void eimtest_cleanup_module(void) {

}

static int eimtest_init_module(void)
{
 int result = 0;
 void __iomem *cs2_base;
 u32 GCR1, GCR2, RCR1, RCR2, WCR1, WEIMCR;

 iomux_v3_cfg_t iomux_cs2 = MX51_PAD_EIM_CS2__EIM_CS2;

 mxc_iomux_v3_setup_pad(iomux_cs2);

 GCR1 = 0x0111008f |
 bitfield(28, 4, PSZ) |
 bitfield(23, 1, AUS) |
 bitfield(14, 2, BCS) |
 bitfield(12, 2, BCD) |
 bitfield(11, 1, WC) |
 bitfield(8, 3, BL) |
 bitfield(5, 1, FL) |
 bitfield(4, 1, FL);

 GCR2 = bitfield(0, 2, ADH);

 RCR1 =
 bitfield(24, 6, WSC) |
 bitfield(20, 3, ADVA) |
 bitfield(16, 3, ADVN) |
 bitfield(12, 3, OEA) |
 bitfield(4, 3, CSA);

 RCR2 =
 bitfield(8, 2, RL) |
 bitfield(4, 3, BEA) |
 bitfield(3, 1, BE);

 WCR1 =
 bitfield(30, 1, !BE) |
 bitfield(24, 6, WSC) |
 bitfield(21, 3, ADVA) |
 bitfield(18, 3, ADVN) |
 bitfield(15, 3, BEA) |
 bitfield(9, 3, WEA) |
 bitfield(3, 3, CSA);

 WEIMCR =
 bitfield(5, 1, INTPOL) |
 bitfield(4, 1, INTEN) |
 bitfield(1, 2, GBCD) |
 bitfield(0, 1, BCM);

 writereg(0x30, GCR1);
 writereg(0x34, GCR2);
 writereg(0x38, RCR1);
 writereg(0x3c, RCR2);
 writereg(0x40, WCR1);
 writereg(0x90, WEIMCR);

 printk(KERN_WARNING EIMTEST "CS2GCR1=%08x, CS2GCR2=%08x\n",
 readreg(0x30),
 readreg(0x34)
 );
 printk(KERN_WARNING EIMTEST "CS2RCR1=%08x, CS2RCR2=%08x\n",
 readreg(0x38),
 readreg(0x3c)
 );
 printk(KERN_WARNING EIMTEST "CS2WCR1=%08x, CS2WCR2=%08x\n",
 readreg(0x40),
 readreg(0x44)
 );

 printk(KERN_WARNING EIMTEST "WEIM Config register WCR=%08x\n",
 readreg(0x90));

 printk(KERN_WARNING EIMTEST "WEIM IP Access register WIAR=%08x\n",
 readreg(0x94));

 printk(KERN_WARNING EIMTEST "CCM_CBCDR=%08x\n",
 __raw_readl(MX51_IO_ADDRESS(0x73fd4014)));

 cs2_base = ioremap(MX51_CS2_BASE_ADDR, SZ_64K);

 if (!cs2_base) {
 printk(KERN_WARNING EIMTEST "Failed to obtain I/O space\n");
 return -ENODEV;
 }

 // Uncomment as necessary:

 //__raw_writel(data, cs2_base + addr);
 printk(KERN_WARNING EIMTEST "Read data=%08x\n",
 __raw_readl(cs2_base + addr));

 iounmap(cs2_base);

 return result;
}

module_init(eimtest_init_module);
module_exit(eimtest_cleanup_module);

The Makefile

This is a more-or-less standard Makefile for compiling a kernel. Please note that /path/to must be changed (twice) to where your Armadeus buildroot is, because both the crosscompiler and Linux kernel are referenced.

export CROSS_COMPILE=/path/to/armadeus-4.0/buildroot/output/build/staging_dir/usr/bin/arm-unknown-linux-uclibcgnueabi-

ifneq ($(KERNELRELEASE),)
obj-m    := eimtest.o

else
KDIR := /path/to/armadeus-4.0/buildroot/output/build/linux-2.6.38.1
PWD := $(shell pwd)

default:
 $(MAKE) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
 @rm -f *.ko *.o modules.order Module.symvers *.mod.? *~
 @rm -rf .tmp_versions module.target
 @rm -f .eimtest.*
endif

So that’s it. Hope it’s helpful!

When request_irq() fails with -EINVAL

$
0
0

It may help investigating the interrupt descriptors. For a 2.6.38 kernel, putting this in a kernel module load supplies some information (includes, declarations and code mixed below. Organize properly in your own module)

#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/irq.h>

int i;
struct irq_desc *desc;

for_each_irq_desc(i, desc) {
 if (!desc)
   continue;
 printk(KERN_INFO "%d: status=%08x, chip=%08x, handle_irq=%08x\n",
        i, (u32) desc->status, (u32) desc->chip, (u32) desc->handle_irq );
 }

This dumps some meta information about all possible IRQs on the system. Also be sure to look at /proc/interrupts.

Have a look in include/linux/irq.h for the meaning of the flags in desc->status and possibly include/linux/irqdesc.h for the irq_desc structure.

request_irq() may very well fail because the IRQ_NOREQUEST flag was set in status. On ARM architecture, this can be fixed by calling set_irq_flags(irq, IRQF_VALID) assuming that you have a fairly good idea of what you’re doing.

Note that set_irq_chip_and_handler() is usually called before validating an IRQ, so that Linux knows what to do with the interrupt as it happens. Looking at chip and handle_irq in the dump may give a clue about how necessary this is. Searching for the value of handle_irq in /proc/kallsyms (with a simple grep) tells who handles each interrupt.

The “chip” structure is a container for information and methods specific to the interrupt’s owner. In old days, these belonged to peripheral chips, but a “chip” is many times just a group of interrupts having a common way of handling them (setting trigger type, masking etc.).

A final note: It looks like the API is changing vividly in this area, so don’t expect things to be exactly the same on other kernels.


Radeon: reserve failed for wait in /var/log/messages

$
0
0

Looking at my system log (kernel 2.6.35.4), this is a common thing:

Apr 24 12:04:49 kernel: radeon 0000:01:00.0: ffff8802d0aa1a00 reserve failed for wait
Apr 24 12:04:49 kernel: radeon 0000:01:00.0: ffff8802d0aa1a00 reserve failed for wait
Apr 24 12:04:51 kernel: radeon 0000:01:00.0: ffff8802d0aa1a00 reserve failed for wait
Apr 24 12:04:51 kernel: radeon 0000:01:00.0: ffff8802d0aa1a00 reserve failed for wait
Apr 24 12:04:57 kernel: radeon 0000:01:00.0: ffff8802d0aa1a00 reserve failed for wait
Apr 24 12:05:08 kernel: radeon 0000:01:00.0: ffff8802d0aa1a00 reserve failed for wait
Apr 24 12:05:14 kernel: radeon 0000:01:00.0: ffff8802d0aa1a00 reserve failed for wait

This doesn’t sound too good, but it looks like it’s harmless, according to this and this. It also looks like the decision was to remove the error message, rather than to fix something else. I suppose this issue is old news pretty soon.

It has been suggested that this error message appears only when compiz (that cool OpenGL toy making all kinds of effects on the desktop).

So just let it be.

Interrupt definitions in DTS (device tree) files for Xilinx Zynq-7000 / ARM

$
0
0

Having some trouble to figure out what I should write in my own hand-written DTS entry for my logic, I ended up reading the sources of the Linux kernel (version 3.3, which is the currently used for Zynq). The purpose is to hook up a device defined in the PL of a Zynq-7000 (FPGA-style logic fabric) for my Zedboard, but at this time, the automatic generation tool for DTS I’ve written about ignores PL modules.

There’s also a more general post about the Device Tree.

So if the UART’s entry looks like this,

uart@e0001000 {
 compatible = "xlnx,ps7-uart-1.00.a";
 reg = <0xe0001000 0x1000>;
 interrupts = <0x0 0x32 0x0>;
 interrupt-parent = <&gic>;
 clock = <50000000>;
};

What does the “interrupts = <0x0 0x32 0x0>” part stand for? How do I get my definition right?

This is what I figure out. If you just want the bottom line, skip the walkthrough, which I left here for future reference (future kernels etc).

The walkthrough

These values are put into an of_irq structure, which is defined in include/linux/of_irq.h as follows:

/**
 * of_irq - container for device_node/irq_specifier pair for an irq controller
 * @controller: pointer to interrupt controller device tree node
 * @size: size of interrupt specifier
 * @specifier: array of cells @size long specifing the specific interrupt
 *
 * This structure is returned when an interrupt is mapped. The controller
 * field needs to be put() after use
 */
#define OF_MAX_IRQ_SPEC        4 /* We handle specifiers of at most 4 cells */
struct of_irq {
 struct device_node *controller; /* Interrupt controller node */
 u32 size; /* Specifier size */
 u32 specifier[OF_MAX_IRQ_SPEC]; /* Specifier copy */
}

We have of_irq_map_one() in drivers/of/irq.c fetching the “interrupt” property from the device tree, calling of_irq_map_raw() in the same file, which populates the of_irq structure with data. Specifically, we have the “specifier” field containing the data in the array exactly as it appears in the DTS file, and “size” indicating the number of elements.

Then irq_of_parse_and_map()  tears the of_irq into its three components, calling irq_create_of_mapping() with these.

This function resolves the hardware number of the interrupt by calling the function given in the dt_translate entry of the ops entry of the interrupt controller structure. In arch/arm/common/gic.c this entry is populated with the gic_irq_domain_dt_translate() function, which is defined in the same file. Note that “dt_translate” becomes “xlate” in later kernels) .

This translation function reveals what the interrupt specification means, as detailed in “the bottom line”.

irq_create_of_mapping() also calls irq_domain_to_irq() to calculate the IRQ number to be known within Linux, and more interesting, irq_set_irq_type(), which is defined in kernel/irq/chip.c. The latter function merely takes the bus lock, calls __irq_set_trigger(), and released the lock. Well, for one important exception: It does nothing if the IRQ type is set to IRQ_TYPE_NONE. In other words, the chip’s default is retained.

I didn’t bother to follow up __irq_set_trigger(), because it’s pretty obvious what this function is meant to do, and following it to the precise point is tedious, as it’s boiling down to some hardware-specific function.

The bottom line

The first value is a flag indicating if the interrupt is an SPI (shared peripheral interrupt). A nonzero value means it is an SPI.

The second value is the interrupt number. The translate function adds 16 to SPIs and 32 to non-SPIs, so for interrupts generated by fabric logic in a Zynq, the number in the DTS file should be the hardware number (as shown in Xilinx Platform Studio, XPS) minus 32.

The third value is the type of interrupt, which is ANDed wtih IRQ_TYPE_SENSE_MASK (= 0x0f), which is defined in include/linux/irq.h. Also from this file is the meaning of the IRQ types as shown at the end of this post. Refer to the values in the “enum” clause: IRQ_TYPE_LEVEL_HIGH is 4 and IRQ_TYPE_EDGE_RISING is 1.

Cortex-A9 can’t support any other interrupt types, as put in arch/arm/common/gic.c:

if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
 return -EINVAL

Returning -EINVAL means “failed because of invalid value”, so it says it all.

An important conclusion is that the common choice of zero for the third value actually means “leave it as it was”. This is not a necessarily clever choice, in particular if some bootloader fools around setting things it shouldn’t. It’s not a likely scenario, but it will be extremely difficult to spot a problem like this, since we all naturally assume that the interrupt attributes are completely set when the system boots up.

Anyhow, the reset default value for all of the relevant interrupts is an active High level. However, software is required to the interrupts for L1 cache and parity to rising-edge sensitivity. See the Zynq-7000 EPP Technical Reference Manual (TRM), section 7.2.3.

UART example revisited

Now let’s look at the interrupts = <0x0 0x32 0x0> definition again, with some help from the TRM. From the address of the registers, 0xe0001000, it’s clear that it’s UART1 (see TRM, appendix B.33). According to Table 7-3 in TRM’s section 7.2.3, UART1′s interrupt number is 82. Since the first value is 0, it’s declared as a non-SPI, so the second value should be the hardware number minus 32, which is 50, or 0x32. The third value says to leave the interrupt type as is.

The only offbeat thing here is the name of the section, under which the interrupt is listed: “Shared Peripheral Interrupts”. And still, it’s declared as a non-SPI. So it looks like this SPI thing isn’t so important.

Excerpt from irq.h

/*
 * IRQ line status.
 *
 * Bits 0-7 are the same as the IRQF_* bits in linux/interrupt.h
 *
 * IRQ_TYPE_NONE        - default, unspecified type
 * IRQ_TYPE_EDGE_RISING        - rising edge triggered
 * IRQ_TYPE_EDGE_FALLING    - falling edge triggered
 * IRQ_TYPE_EDGE_BOTH        - rising and falling edge triggered
 * IRQ_TYPE_LEVEL_HIGH        - high level triggered
 * IRQ_TYPE_LEVEL_LOW        - low level triggered
 * IRQ_TYPE_LEVEL_MASK        - Mask to filter out the level bits
 * IRQ_TYPE_SENSE_MASK        - Mask for all the above bits
 * IRQ_TYPE_PROBE        - Special flag for probing in progress
 *
 * Bits which can be modified via irq_set/clear/modify_status_flags()
 * IRQ_LEVEL            - Interrupt is level type. Will be also
 *                  updated in the code when the above trigger
 *                  bits are modified via irq_set_irq_type()
 * IRQ_PER_CPU            - Mark an interrupt PER_CPU. Will protect
 *                  it from affinity setting
 * IRQ_NOPROBE            - Interrupt cannot be probed by autoprobing
 * IRQ_NOREQUEST        - Interrupt cannot be requested via
 *                  request_irq()
 * IRQ_NOTHREAD            - Interrupt cannot be threaded
 * IRQ_NOAUTOEN            - Interrupt is not automatically enabled in
 *                  request/setup_irq()
 * IRQ_NO_BALANCING        - Interrupt cannot be balanced (affinity set)
 * IRQ_MOVE_PCNTXT        - Interrupt can be migrated from process context
 * IRQ_NESTED_TRHEAD        - Interrupt nests into another thread
 * IRQ_PER_CPU_DEVID        - Dev_id is a per-cpu variable
 */
enum {
 IRQ_TYPE_NONE        = 0x00000000,
 IRQ_TYPE_EDGE_RISING    = 0x00000001,
 IRQ_TYPE_EDGE_FALLING    = 0x00000002,
 IRQ_TYPE_EDGE_BOTH    = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
 IRQ_TYPE_LEVEL_HIGH    = 0x00000004,
 IRQ_TYPE_LEVEL_LOW    = 0x00000008,
 IRQ_TYPE_LEVEL_MASK    = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH),
 IRQ_TYPE_SENSE_MASK    = 0x0000000f,

 IRQ_TYPE_PROBE        = 0x00000010,

 IRQ_LEVEL        = (1 <<  8),
 IRQ_PER_CPU        = (1 <<  9),
 IRQ_NOPROBE        = (1 << 10),
 IRQ_NOREQUEST        = (1 << 11),
 IRQ_NOAUTOEN        = (1 << 12),
 IRQ_NO_BALANCING    = (1 << 13),
 IRQ_MOVE_PCNTXT        = (1 << 14),
 IRQ_NESTED_THREAD    = (1 << 15),
 IRQ_NOTHREAD        = (1 << 16),
 IRQ_PER_CPU_DEVID    = (1 << 17),
};

 

 

Linux kernel version magic: Adding the missing plus suffix

$
0
0

Trying to insmod a kernel module, which was just compiled against the true headers of the running kernel, I got:

# insmod mymodule.ko
insmod: error inserting 'mymodule.ko': -1 Invalid module format

And this in /var/log/syslog:

mymodule: version magic '3.3.0-xxx SMP preempt mod_unload ARMv7 ' should be '3.3.0-xxx+ SMP preempt mod_unload ARMv7 '

Really? Are you picking on me? Rejecting my module just because of a missing plus sign?!

The plus sign is there in the first place because the kernel was compiled with a git repository, which displayed modifications relative to the official tree. To avoid it, I could have compiled the kernel after renaming the .git directory to something else prior to compiling the kernel. But I forgot to do that.

In fact, I should have turned kernel magic versioning off altogether.

The plus sign is absent in the current kernel headers, because they were generated without the git repository (natively on the target). Hence the difference.

One could use modprobe –force, but isn’t it nicer to just get the version magic right?

This solution holds for the 3.3.0 kernel, but probably works on others as well.

The file you want to edit is /usr/src/kernels/{your version}/include/generated/utsrelease.h and just add the ‘+’ sign to the end of the version number. E.g.

#define UTS_RELEASE "3.3.0-xxx+"

And now all modules compiled against the kernel will load with no issues.

For those interested in the gory details: It’s mymodule.mod.c that is responsible for supplying the version magic. It’s compiled into mymodule.mod.o, and then fused together with mymodule.o to become mymodule.ko. This is what the famous “MODPOST” compilation stage does (in fact, it’s a make -f on scripts/Makefile.modpost, which in turn calls scripts/mod/modpost).

mymodule.mod.c gets the number by including linux/vermagic.h (actually, it includes other similar files as well) which in turn includes generated/utsrelease.h, which supplies the version magic explicitly.

stmmaceth: NetworkManager fails to bring up a wired Ethernet NIC

$
0
0

The problem

In short: Running linux 3.8.0 on Altera’s Cyclone V SoC, NetworkManager doesn’t bring up the Ethernet port. It also makes false accusations such as

Jan  1 00:00:17 localhost NetworkManager[1206]: <info> (eth0): driver 'stmmaceth' does not support carrier detection.

and later on also says

Jan  1 00:00:17 localhost NetworkManager[1206]: <warn> (eth0): couldn't get carrier state: (-1) unknown
Jan  1 00:00:17 localhost NetworkManager[1206]: <info> (eth0): carrier now OFF (device state 20, deferring action for 4 seconds)

And asking more directly,

# nm-tool eth0
NetworkManager Tool

State: disconnected

- Device: eth0 -----------------------------------------------------------------
  Type:              Wired
  Driver:            stmmaceth
  State:             unavailable
  Default:           no
  HW Address:        96:A7:6F:4E:DD:6D

  Capabilities:

  Wired Properties
    Carrier:         off

All of this is, of course, incorrect. Even though it’s not clear who to blame for this. But the driver detects the carrier all right:

# cat /sys/class/net/eth0/carrier
1

and as we shall see below, the ioctl() interface is also supported. Only it doesn’t work as NetworkManager expects it to.

Well, I bluffed a bit proving that the carrier detection works. Explained later.

So what went wrong?

Nothing like digging in the source code. In NetworkManager’s nm-device-ethernet.c, the function supports_ethtool_carrier_detect() goes

static gboolean
supports_ethtool_carrier_detect (NMDeviceEthernet *self)
{
	int fd;
	struct ifreq ifr;
	gboolean supports_ethtool = FALSE;
	struct ethtool_cmd edata;

	g_return_val_if_fail (self != NULL, FALSE);

	fd = socket (PF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
		nm_log_err (LOGD_HW, "couldn't open control socket.");
		return FALSE;
	}

	memset (&ifr, 0, sizeof (struct ifreq));
	strncpy (ifr.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ);

	edata.cmd = ETHTOOL_GLINK;
	ifr.ifr_data = (char *) &edata;

	errno = 0;
	if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) {
		nm_log_dbg (LOGD_HW | LOGD_ETHER, "SIOCETHTOOL failed: %d", errno);
		goto out;
	}

	supports_ethtool = TRUE;

out:
	close (fd);
	nm_log_dbg (LOGD_HW | LOGD_ETHER, "ethtool %s supported",
	            supports_ethtool ? "is" : "not");
	return supports_ethtool;
}

Obviously, this is the function that determines if the port supplies carrier detection. There is also a similar function for MII, supports_mii_carrier_detect (). A simple strace reveals what went wrong:

And indeed, in the strace log with this driver it says

socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 17
ioctl(17, SIOCETHTOOL, 0x7e93bcdc)      = -1 EBUSY (Device or resource busy)
close(17)                               = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 17
ioctl(17, SIOCGMIIPHY, 0x7e93bcfc)      = -1 EINVAL (Invalid argument)
close(17)                               = 0
open("/proc/sys/net/ipv6/conf/eth0/accept_ra", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/proc/sys/net/ipv6/conf/eth0/use_tempaddr", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
gettimeofday({4101, 753554}, NULL)      = 0
send(6, "<30>Jan  1 01:08:21 NetworkManager[1701]: <info> (eth0): driver 'stmmaceth' does not support carrier detection.", 111, MSG_NOSIGNAL) = 111

so we can see that the attempt made in supports_ethtool_carrier_detect() failed with an EBUSY, and the one made by supports_mii_carrier_detect () failed as well, with an EINVAL. In other words, the ethtool (which is loosely related to the ethtool utility) ioctl() interface was recognized, but the driver said the driver was busy (a silly return code, as we shall see later), and the MII ioctl() interface was rejected altogether.

Since NetworkManager doesn’t support carrier detection based on Sysfs, the final conclusion is that there is no carrier detection.

But why did the driver answer EBUSY in the first place?

Some kernel digging

The relevant Linux kernel is 3.8.0.

ioctl() calls to network devices are handled by the dev_ioctl() function in net/core/dev.c (not in drivers/, and it was later on moved to dev_ioctl.c) as follows:

	case SIOCETHTOOL:
		dev_load(net, ifr.ifr_name);
		rtnl_lock();
		ret = dev_ethtool(net, &ifr);
		rtnl_unlock();
		if (!ret) {
			if (colon)
				*colon = ':';
			if (copy_to_user(arg, &ifr,
					 sizeof(struct ifreq)))
				ret = -EFAULT;
		}
		return ret;

Note that the ioctl() call is based upon the name of the interface as a string (e.g. “eth0″). The call to dev_load hence loads a kernel module if the respective driver isn’t loaded yet. The dev_ethtool() function is in net/core/ethtool.c. This function first runs a few sanity checks + permissions, and may return ENODEV, EFAULT or EPERM, depending on different mishaps.

Most notably, it runs

	if (dev->ethtool_ops->begin) {
		rc = dev->ethtool_ops->begin(dev);
		if (rc  < 0)
			return rc;
	}

which in the case of stmmac is

static int stmmac_check_if_running(struct net_device *dev)
{
	if (!netif_running(dev))
		return -EBUSY;
	return 0;
}

netif_running(dev) is defined in include/linux/netdevice.h as follows:

static inline bool netif_running(const struct net_device *dev)
{
	return test_bit(__LINK_STATE_START, &dev->state);
}

This function returns true when the device is “up”, exactly in the sense of “ifconfig up”.

Say what?

NetworkManager made the SIOCETHTOOL ioctl() call before bringing up the eth0 interface in order to check if it supports carrier detect. But since it wasn’t up (why should it be? NetworkManager didn’t bring it up), the driver’s sanity check (?) failed the ioctl() call with an EBUSY, as netif_running() returns false — the interface was down. So NetworkManager marked the interface as not supporting carrier detect, and took it up even so. This made the driver say that it has detected a carrier, but since NetworkManager didn’t expect that to happen, it started fooling around, and eventually didn’t bring up the interface properly (no DHCP, in particular).

As it turns out, netif_running(dev) returns zero, which is the reason the whole thing fails with an EBUSY.

Now let’s return to the Sysfs detection of the carrier. With the eth0 interface down, it goes like this

# cat /sys/class/net/eth0/carrier
cat: /sys/class/net/eth0/carrier: Invalid argument
# ifconfig eth0 up
# cat /sys/class/net/eth0/carrier
0
# cat /sys/class/net/eth0/carrier
1

The two successive carrier detections give different results, because it takes a second or so before the carrier is detected. There was nothing changed with the hardware inbetween (no cable was plugged in or something).

So NetworkManager was partly right: There driver doesn’t support carrier detection as long as the interface isn’t brought up.

Solution

The solution is surprisingly simple. Just make sure

ifconfig eth0 up

is executed before NetworkManager is launched. That’s it. Suddenly nm-tool sees a completely different interface:

# nm-tool eth0

NetworkManager Tool

State: connected (global)

- Device: eth0  [Wired connection 1] -------------------------------------------
  Type:              Wired
  Driver:            stmmaceth
  State:             connected
  Default:           yes
  HW Address:        9E:37:A8:56:CF:EC

  Capabilities:
    Carrier Detect:  yes
    Speed:           100 Mb/s

  Wired Properties
    Carrier:         on

  IPv4 Settings:
    Address:         10.1.1.242
    Prefix:          24 (255.255.255.0)
    Gateway:         10.1.1.3

    DNS:             10.2.0.1
    DNS:             10.2.0.2

Who should we blame here? Probably NetworkManager. Since it’s bringing up the interface anyhow, why not ask it if it supports carrier detection after the interface is up? I suppose that the driver has its reasons for not cooperating while it’s down.

Epilogue

Since I started with dissecting the kernel’s code, here’s what happens with the call to dev_ethtool() mentioned above, when it passes the “sanity check”. There’s a huge case statement, with the relevant part saying

	case ETHTOOL_GLINK:
		rc = ethtool_get_link(dev, useraddr);
		break;

the rc value is propagated up when this call finishes (after some possible other operations, which are probably not relevant).

And then we have, in the same file,

static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
{
	struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };

	if (!dev->ethtool_ops->get_link)
		return -EOPNOTSUPP;

	edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev);

	if (copy_to_user(useraddr, &edata, sizeof(edata)))
		return -EFAULT;
	return 0;
}

The ethtool_value structure is defined in include/uapi/linux/ethtool.h saying

struct ethtool_value {
	__u32	cmd;
	__u32	data;
};

Note that if netif_running(dev) returns false, zero is returned on the edata entry of the answer, but the call is successful (it actually makes sense). But this never happens with the current driver, as was seen above.

It’s fairly safe to assume that drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c handles the actual call, as it has

static const struct ethtool_ops stmmac_ethtool_ops = {
	.begin = stmmac_check_if_running,
... snip ...
	.get_link = ethtool_op_get_link,
... snip ...
};

but ethtool_op_get_link() is defined in net/core/ethtool.c (we’re running in circles…) saying simply

u32 ethtool_op_get_link(struct net_device *dev)
{
	return netif_carrier_ok(dev) ? 1 : 0;
}

which bring us to include/linux/netdevice.h where it says

static inline bool netif_carrier_ok(const struct net_device *dev)
{
	return !test_bit(__LINK_STATE_NOCARRIER, &dev->state);
}

This rises the question why the driver refuses to answer ETHTOOL_GLINK requests when it’s down. It’s not even involved in answering this request. But having attempted to modify the driver, so ETHTOOL_GLINK is let through even when the interface is down, I can say that it still confused NetworkManager. I didn’t get down to why exactly.

Cache coherency on i.MX25 running Linux

$
0
0

What this blob is all about

Running some home-cooked SDMA scripts on Freescale’s Linux 2.6.28 kernel on an i.MX25 processor, I’m puzzled by the fact, that cache flushing with dma_map_single(…, DMA_TO_DEVICE) doesn’t hurt, but nothing happens if the calls are removed. On the other hand, attempting to remove cache invalidation calls, as in dma_map_single(…, DMA_FROM_DEVICE) does cause data corruption, as one would expect.

The de-facto lack of need for cache flushing could be explained by the small size of the cache: The sequence of events is typically preparing the data in the buffer, then some stuff in the middle, and only then is the SDMA script kicked off. If the cache lines are evicted naturally as a result of that “some stuff” activity, one gets away with not flushing the cache explicitly.

I’m by no means saying that cache flushing shouldn’t be done. On the contrary, I’m surprised that things don’t break when it’s removed.

So why doesn’t one get away with not invalidating the cache? In my tests, I saw 32-byte segments going wrong when I dropped the invalidation. That is, some segments, typically after a handful of successful data transactions of less than 1 kB of data.

Why does dropping the invalidation break things, and dropping the flushing doesn’t? As I said above, I’m still puzzled by this.

So I went down to the details of what these calls to dma_map_single() do. Spoiler: I didn’t find an explanation. At the end of the foodchain, there are several MCR assembly instructions, as one should expect. Both flushing and invalidation apparently does something useful.

The rest of this post is the dissection of Linux’ kernel code in this respect.

The gory details

DMA mappings and sync functions practically wrap the dma_cache_maint() function, e.g. in arch/arm/include/asm/dma-mapping.h:

static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr,
		size_t size, enum dma_data_direction dir)
{
	BUG_ON(!valid_dma_direction(dir));

	if (!arch_is_coherent())
		dma_cache_maint(cpu_addr, size, dir);

	return virt_to_dma(dev, cpu_addr);
}

It was verified with disassembly that dma_map_single() was implemented with a call to dma_cache_maint().

This function can be found in arch/arm/mm/dma-mapping.c as follows

/*
 * Make an area consistent for devices.
 * Note: Drivers should NOT use this function directly, as it will break
 * platforms with CONFIG_DMABOUNCE.
 * Use the driver DMA support - see dma-mapping.h (dma_sync_*)
 */
void dma_cache_maint(const void *start, size_t size, int direction)
{
	const void *end = start + size;

	BUG_ON(!virt_addr_valid(start) || !virt_addr_valid(end - 1));

	switch (direction) {
	case DMA_FROM_DEVICE:		/* invalidate only */
		dmac_inv_range(start, end);
		outer_inv_range(__pa(start), __pa(end));
		break;
	case DMA_TO_DEVICE:		/* writeback only */
		dmac_clean_range(start, end);
		outer_clean_range(__pa(start), __pa(end));
		break;
	case DMA_BIDIRECTIONAL:		/* writeback and invalidate */
		dmac_flush_range(start, end);
		outer_flush_range(__pa(start), __pa(end));
		break;
	default:
		BUG();
	}
}
EXPORT_SYMBOL(dma_cache_maint);

The outer_* calls are defined as null functions in arch/arm/include/asm/cacheflush.h, since the CONFIG_OUTER_CACHE kernel configuration flag isn’t set.

The dmac_* macros are defined in arch/arm/include/asm/cacheflush.h as follows:

#define dmac_inv_range			__glue(_CACHE,_dma_inv_range)
#define dmac_clean_range		__glue(_CACHE,_dma_clean_range)
#define dmac_flush_range		__glue(_CACHE,_dma_flush_range)

where __glue() simply glues the two strings together (see arch/arm/include/asm/glue.h) and _CACHE equals “arm926″ for the i.MX25, so e.g. dmac_clean_range becomes arm926_dma_clean_range.

These actual functions are implemented in assembler in arch/arm/mm/proc-arm926.S:

/*
 *	dma_inv_range(start, end)
 *
 *	Invalidate (discard) the specified virtual address range.
 *	May not write back any entries.  If 'start' or 'end'
 *	are not cache line aligned, those lines must be written
 *	back.
 *
 *	- start	- virtual start address
 *	- end	- virtual end address
 *
 * (same as v4wb)
 */
ENTRY(arm926_dma_inv_range)
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
	tst	r0, #CACHE_DLINESIZE - 1
	mcrne	p15, 0, r0, c7, c10, 1		@ clean D entry
	tst	r1, #CACHE_DLINESIZE - 1
	mcrne	p15, 0, r1, c7, c10, 1		@ clean D entry
#endif
	bic	r0, r0, #CACHE_DLINESIZE - 1
1:	mcr	p15, 0, r0, c7, c6, 1		@ invalidate D entry
	add	r0, r0, #CACHE_DLINESIZE
	cmp	r0, r1
	blo	1b
	mcr	p15, 0, r0, c7, c10, 4		@ drain WB
	mov	pc, lr

/*
 *	dma_clean_range(start, end)
 *
 *	Clean the specified virtual address range.
 *
 *	- start	- virtual start address
 *	- end	- virtual end address
 *
 * (same as v4wb)
 */
ENTRY(arm926_dma_clean_range)
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
	bic	r0, r0, #CACHE_DLINESIZE - 1
1:	mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
	add	r0, r0, #CACHE_DLINESIZE
	cmp	r0, r1
	blo	1b
#endif
	mcr	p15, 0, r0, c7, c10, 4		@ drain WB
	mov	pc, lr

/*
 *	dma_flush_range(start, end)
 *
 *	Clean and invalidate the specified virtual address range.
 *
 *	- start	- virtual start address
 *	- end	- virtual end address
 */
ENTRY(arm926_dma_flush_range)
	bic	r0, r0, #CACHE_DLINESIZE - 1
1:
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
#else
	mcr	p15, 0, r0, c7, c6, 1		@ invalidate D entry
#endif
	add	r0, r0, #CACHE_DLINESIZE
	cmp	r0, r1
	blo	1b
	mcr	p15, 0, r0, c7, c10, 4		@ drain WB
	mov	pc, lr

The CONFIG_CPU_DCACHE_WRITETHROUGH kernel configuration flag is not set, so there are no shortcuts.

Exactly the same snippet, only disassembled from the object file (using objdump -d):

000004d4 <arm926_dma_inv_range>:
 4d4:	e310001f 	tst	r0, #31
 4d8:	1e070f3a 	mcrne	15, 0, r0, cr7, cr10, {1}
 4dc:	e311001f 	tst	r1, #31
 4e0:	1e071f3a 	mcrne	15, 0, r1, cr7, cr10, {1}
 4e4:	e3c0001f 	bic	r0, r0, #31
 4e8:	ee070f36 	mcr	15, 0, r0, cr7, cr6, {1}
 4ec:	e2800020 	add	r0, r0, #32
 4f0:	e1500001 	cmp	r0, r1
 4f4:	3afffffb 	bcc	4e8 <arm926_dma_inv_range+0x14>
 4f8:	ee070f9a 	mcr	15, 0, r0, cr7, cr10, {4}
 4fc:	e1a0f00e 	mov	pc, lr

00000500 <arm926_dma_clean_range>:
 500:	e3c0001f 	bic	r0, r0, #31
 504:	ee070f3a 	mcr	15, 0, r0, cr7, cr10, {1}
 508:	e2800020 	add	r0, r0, #32
 50c:	e1500001 	cmp	r0, r1
 510:	3afffffb 	bcc	504 <arm926_dma_clean_range+0x4>
 514:	ee070f9a 	mcr	15, 0, r0, cr7, cr10, {4}
 518:	e1a0f00e 	mov	pc, lr

0000051c <arm926_dma_flush_range>:
 51c:	e3c0001f 	bic	r0, r0, #31
 520:	ee070f3e 	mcr	15, 0, r0, cr7, cr14, {1}
 524:	e2800020 	add	r0, r0, #32
 528:	e1500001 	cmp	r0, r1
 52c:	3afffffb 	bcc	520 <arm926_dma_flush_range+0x4>
 530:	ee070f9a 	mcr	15, 0, r0, cr7, cr10, {4}
 534:	e1a0f00e 	mov	pc, lr

So there’s actually little to learn from the disassembly. Or at all…

Reading the DocBook files in Linux kernel’s documentation

$
0
0

This is my short saga about my not necessarily intelligent actions for reading a DocBook paper.

So I wanted to read some documentation from my Linux kernel sources. It happened to be in DocBook format.

In the kernel source’s root, I tried

$ make htmldocs

or I could have gone

$ make pdfdocs

Or mandocs. Or sgmldocs. Or psdocs.

But that builds only the DocBook templates in Documentation/DocBook/. I need those in Documentation/sound/alsa/DocBook!

I tried to copy the Makefile to the target directory, and change the assignment of DOCBOOKS to the files I wanted handled. But that didn’t work, because the Makefile couldn’t find the script/ subdirectory. Or as “make” put it:

make: *** No rule to make target `/scripts/kernel-doc', needed by `/alsa-driver-api.tmpl'.  Stop.

OK, this was a bit too much. How about just going…

$ docbook2html writing-an-alsa-driver.tmpl

Works, creates a lot of scattered HTML files. Not so easy to read. Can’t I get it in a single document?

$ docbook2rtf writing-an-alsa-driver.tmpl

Huh? WTF? RTF? Yes, it’s apparently still alive. And hey, one can easily export it to PDF with OpenOffice!

This probably isn’t exactly the way the kernel hackers meant it to be done. On the other hand, it’s by far too much effort for just reading a document by and for the community…

I’m sure someone knowns about the obvious way I missed. Comments below, please…

 

Linux kernel platform device food chain example

$
0
0

Since the device tree is the new way to set up hardware devices on embedded platforms, I hoped that I could avoid the “platform” API for picking which driver is going to take control over what. But it looks like the /arch/arm disaster is here to stay for a while, so I need to at least understand how it works.

So for reference, here’s an example walkthrough of the SPI driver for i.MX51, declared and matched with a hardware device.

The idea is simple: The driver, which is enabled by .config (and hence the Makefile in its directory includes it for compilation) binds itself to a string during its initialization. On the other side, initialization code requests a device matching that string, and also supplies some information along with that. The example tells the story better.

The platform API is documented in the kernel tree’s Documentation/driver-model/platform.txt. There’s also a nice LWN article by Jonathan Corbet.

So let’s assume we have Freescale’s 3-stack board at hand. in arch/arm/mach-mx5/mx51_3stack.c, at the bottom, it says

MACHINE_START(MX51_3DS, "Freescale MX51 3-Stack Board")
 .fixup = fixup_mxc_board,
 .map_io = mx5_map_io,
 .init_irq = mx5_init_irq,
 .init_machine = mxc_board_init,
 .timer = &mxc_timer,
MACHINE_EN

mxc_board_init() is defined in the same file, which among many other calls goes

mxc_register_device(&mxcspi1_device, &mxcspi1_data);

with the extra info structure mxcspi1_data defined as

static struct mxc_spi_master mxcspi1_data = {
 .maxchipselect = 4,
 .spi_version = 23,
 .chipselect_active = mx51_3ds_gpio_spi_chipselect_active,
 .chipselect_inactive = mx51_3ds_gpio_spi_chipselect_inactive,
};

Now to the declaration of mxcspi1_device: In arch/arm/mach-mx5/devices.c we have

struct platform_device mxcspi1_device = {
	.name = "mxc_spi",
	.id = 0,
	.num_resources = ARRAY_SIZE(mxcspi1_resources),
	.resource = mxcspi1_resources,
	.dev = {
		.dma_mask = &spi_dma_mask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
	},
};

and before that, in the same file there was:

static struct resource mxcspi1_resources[] = {
	{
		.start = CSPI1_BASE_ADDR,
		.end = CSPI1_BASE_ADDR + SZ_4K - 1,
		.flags = IORESOURCE_MEM,
	},
	{
		.start = MXC_INT_CSPI1,
		.end = MXC_INT_CSPI1,
		.flags = IORESOURCE_IRQ,
	},
	{
		.start = MXC_DMA_CSPI1_TX,
		.end = MXC_DMA_CSPI1_TX,
		.flags = IORESOURCE_DMA,
	},
};

So that defines the magic driver string and the resources that are allocated to this device.

It’s worth noting that devices.c ends with

postcore_initcall(mxc_init_devices);

which causes a call to mxc_init_devices(), a function that messes up the addresses of the resources for some architectures. Just to add some confusion. Always watch out for those little traps!

Meanwhile, in drivers/spi/mxc_spi.c

static struct platform_driver mxc_spi_driver = {
	.driver = {
		   .name = "mxc_spi",
		   .owner = THIS_MODULE,
		   },
	.probe = mxc_spi_probe,
	.remove = mxc_spi_remove,
	.suspend = mxc_spi_suspend,
	.resume = mxc_spi_resume,
};

followed by:

static int __init mxc_spi_init(void)
{
	pr_debug("Registering the SPI Controller Driver\n");
	return platform_driver_register(&mxc_spi_driver);
}

static void __exit mxc_spi_exit(void)
{
	pr_debug("Unregistering the SPI Controller Driver\n");
	platform_driver_unregister(&mxc_spi_driver);
}

subsys_initcall(mxc_spi_init);
module_exit(mxc_spi_exit);

So this is how the driver tells Linux that it’s responsible for devices marked with the “mxc_spi” string.

As for some interaction with the device data (also in mxc_spi.c), there’s stuff like

mxc_platform_info = (struct mxc_spi_master *)pdev->dev.platform_data;

and

master_drv_data->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

going on with

if (!request_mem_region(master_drv_data->res->start,
			master_drv_data->res->end -
			master_drv_data->res->start + 1, pdev->name)) { /* Ayee! */ }

and

if (pdev->dev.dma_mask == NULL) { /* No DMA for you! */ }

and it goes on…


“Unsupported machine ID” after upgrading Linux kernel or U-boot

$
0
0

Unlike how I usually treat software tools I work with, my attitude towards U-boot is “if it works, never mind how and why”. Trying to understand the gory details of U-boot has never been very rewarding. Things work or break more or less randomly, depending on which git revision is checked out. Someone sent a patch fixing this and breaking that, and then someone else sent another patch.

So this is my story: After upgrading the Linux kernel from 3.3 to 3.12 (both having a strong Xilinx flavor, as the target is a Zynq board), but kept an old version of U-boot, I ran through the drill that usually makes the kernel boot:

zynq-uboot> fatload mmc 0 0x8000 zImage
reading zImage

2797680 bytes read
zynq-uboot> fatload mmc 0 0x1000000 devicetree.dtb
reading devicetree.dtb

5827 bytes read
zynq-uboot> go 0x8000
## Starting application at 0x00008000 ...

Error: unrecognized/unsupported machine ID (r1 = 0x1fb5662c).

Available machine support:

ID (hex)        NAME
ffffffff        Generic DT based system
ffffffff        Xilinx Zynq Platform

Please check your kernel config and/or bootloader.

So the kernel didn’t boot. Looking at the way I attempted to kick it off, one may wonder how it worked at all with kernel v3.3. But one can’t argue with the fact that it used to boot.

The first thing to understand about this error message, is that it’s fatally misleading. The real problem is that the device tree blob isn’t found by the kernel, so it reverts to looking for a machine ID in r1. And r1 just has some value. The error message comes from a piece of boot code that is never reached, if the device tree is found and used.

Now, let’s try to understand the logic behind the sequence of commands: The first command loaded the zImage into a known place in memory, 0x8000. One could ask why I didn’t use uImage. Well, why should I? zImage works, and that’s the classic way to boot a kernel.

The device tree blob is then loaded to address 0x10000000.

And then comes the fun part: U-boot just jumps to 0x8000, the beginning of the image. I think I recall that one can put zImage anywhere in memory, and it will take it from there.

But how does the kernel know that the device tree is at 0x10000000? Beats me. I suppose it’s hardcoded somewhere. But hey, it worked! At least on older kernels. And on U-boot 2012.10 and older (but not 2013.10).

For the newer kernel (say, 3.12), a completely different spell should be cast. Something like this (using U-Boot 2012.10 or 2013.10):

zynq-uboot> fatload mmc 0 0x3000000 uImage
reading uImage                                                                  

3054976 bytes read
zynq-uboot> fatload mmc 0 0x2A00000 devicetree.dtb
reading devicetree.dtb                                                          

7863 bytes read
zynq-uboot> bootm 0x3000000 - 0x2A00000
## Booting kernel from Legacy Image at 03000000 ...
   Image Name:   Linux-3.12.0-1.3-xilinx
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    3054912 Bytes = 2.9 MiB
   Load Address: 00008000
   Entry Point:  00008000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 02a00000
   Booting using the fdt blob at 0x02a00000
   Loading Kernel Image ... OK
OK
   Loading Device Tree to 1fb4f000, end 1fb53eb6 ... OK                         

Starting kernel ...                                                             

Uncompressing Linux... done, booting the kernel.
Booting Linux on physical CPU 0x0
[ ... kernel boots, bliss and happiness ... ]

OK, so I twisted it all around. All of the sudden, I use an uImage, rather than zImage. Why? Because bootm works with uImage, and bootz wasn’t supported by that specific U-boot version (or configuration, I’m not really sure).

The addresses I loaded into are different too, and I’m not sure it matters. What surely matters, is that bootm was explicitly given the address of the device tree blob, and therefore passed that through a register to the kernel. So in this case, it’s pretty obvious how the kernel finds what’s where.

Ah, but there’s a twist. The latter, bootm-based method didn’t work on the 3.3 kernel. In fact, I got a very similar error message when I tried that.

As I said in the beginning, I never cared dive deep into U-boot. So all I’m saying is — if you encounter an error message like the one above, just try the other magic spell, and hope for good.

And if someone known more about what happened in the connection between U-boot and Linux somewhere in 2012, or has a link to some mailing list discussion where this was decided upon, please comment below. :)

Kernel compilation without extra “+” or other markers in the version string

$
0
0

So there’s this “+” sign added to the kernel version (as displayed with uname -r) when the kernel is compiled with a git tree that doesn’t sit on an official version (or more precisely, not on an annotated tag). Which kinda makes sense to tell the kernel’s users that the kernel isn’t exactly the vanilla thing. Only it annoys me. Partly because modules, which are compiled under the kernel headers that are extracted from this kernel (if stored separately) will not match — their version will lack the “+” sign, since there is no git repo attached to the headers.

For a while I used to move the .git directory to .gitt before compiling, and then back again afterwards to prevent this plus sign from appearing on my kernels. And then I decided to really fix it.

So this is what to do: Edit scripts/setlocalversion and add the line marked in red, somewhere around line 38.

[ ... ]

scm_version()
{
 local short
 short=false

 cd "$srctree"

 return; # Do nothing.

  [ ... ]
}

That’s it. The git’s state is ignored, no more “+” anymore. The trick is to return immediately from the function that is supposed to add these extra version markers as necessary. So it doesn’t get the chance.

VMware Player or Workstation: Patching for Linux kernel 3.12 (or so)

$
0
0

For a reason not so clear to me, VMware doesn’t keep its drivers up to date with newer kernels, so they fail to compile against newer kernels. Consequently, there’s an insane race to patch them up. It starts with a compilation failure at the GUI level, and sooner or later it becomes clear that there’s no choice but to get down to work.

The procedure, if you don’t want to install VMware on the computer you’re working on, is to first extract the files with something like (as a non-root user)

$ bash VMware-Player-6.0.2-1744117.x86_64.bundle --extract newplayer

which just writes the files into a new directory newplayer/.

Next step is to untar the relevant directories into a fresh working directory. May I suggest:

for i in /path/to/newplayer/vmware-vmx/lib/modules/source/*.tar ; do tar -xvf $i ; done

This creates five new directories, each containing a Makefile and kernel module sources. In principle, the goal is to type “make” in all five and not have an error.

That’s where the headache is. I’ve packed up a set of patches that took me from Player 6.0.2 (or Workstation 10.0.2) to kernel 3.12 (in a rather messy way, but hey, nothing about it is really in order): Here they are as a tarball.

Once that is done, wrap it all up with

$ for i in vmblock vmci vmmon vmnet vsock ; do tar -cf $i.tar $i-only ; done

and copy the *.tar files into /usr/lib/vmware/modules/source/ (actually, replace the existing files) in an existing installation.

And then just run VMware as usual, and behave nicely when it wants to install modules.

Or if you want to see it with your own eyes (as root):

# vmware-modconfig --console --install-all

VMCI issues

As if it wasn’t enough as is, there was a problem with running the VMCI:

Starting VMware services:
 Virtual machine monitor                                 [  OK  ]
 Virtual machine communication interface                 [FAILED]
 VM communication interface socket family                [  OK  ]
 Blocking file system                                    [  OK  ]
 Virtual ethernet                                        [  OK  ]
 VMware Authentication Daemon                            [  OK  ]

And of course, so virtual machine would agree to run this way.

After a lot of messing around, it turned out that for some reason, the module wasn’t installed. Go figure.

The solution was to go back to the vmci-only directory (one of the untarred one above), compile it with “make” (it should work by now, after all), and then copy it to the running kernel’s module repository:

# cp vmci.ko /lib/modules/$(uname -r)/kernel/drivers/misc/
# depmod -a

Or maybe just create a kernel/drivers/vmware directory and copy all *.ko files that were compiled, and depmod.

I. Never. Want. To hear. About. This. Again.

Automatic mount stops after kernel upgrade and sysfs

$
0
0

I really have this thing about backward compatibility, which is why I chose to enable the CONFIG_SYSFS_DEPRECATED and CONFIG_SYSFS_DEPRECATED_V2 kernel flags when compiling kernel 3.12 for Fedora 12. After all, an old distribution with a new kernel.

This turned out to be wrong: The distribution isn’t all that old, and automounting stopped to work for USB SD cards (and possibly other stuff) as a result of this. Even though running the HAL daemon (an oldie, yes…) with debug info

# hald --daemon=no --verbose=yes

showed that it detected the insertion of the USB device. And still, no automount.

On my system, two things happen when CONFIG_SYSFS_DEPRECATED and CONFIG_SYSFS_DEPRECATED_V2 are enabled:

The directories for block devices for hard disks disappear, e.g. /sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda and also those for non-disk devices, e.g. /sys/devices/virtual/block/md0/ and /sys/devices/virtual/block/ram0.

Instead, they appear as e.g. /sys/block/sda, with a completely different outline of files. According to the Kconfig help, the parameters that are exposed are more prone to changes over time. In the non-deprecated format, there’s a symbolic link to the respective directory in /sys/devices/ instead of the full-blown directories that appear there in the deprecated mode.

So the CONFIG_SYSFS_DEPRECATED_V2 enables this feature by default, which was a mistake. To spare myself the kernel recompilation, I added sysfs.deprecated=0 to the kernel command line, and things went back to normal.

Wine: Picasa failed to start Fedora 12 after a kernel upgrade

$
0
0

I upgraded my kernel from 2.6.35 to 3.12, and Picasa 2.7 failed to start. Instead of starting, tons of winedbg processes were created at a rapid speed. If I didn’t kill everything related to Picasa within a minute or so (that is, all winedbg processes and any process having “picasa” in the string of “ps ax”) a reboot became imminent, as the system’s process table became full.

Since I’m not very fond of upgrades in general (Fedora 12, anyone?), and Google has ceased to support Picasa for Linux anyhow, I worked a bit on this.

It turned out, that going

$ wine "/opt/picasa/wine/drive_c/Program Files/Picasa2/Picasa2.exe"

did actually run Picasa, but with all my settings gone. No wonder. By default, Wine keeps its fake Windows environment under $HOME/.wine but $HOME/.picasa is the place used by Picasa.

So what about

$ WINEPREFIX=$HOME/.picasa wine "/opt/picasa/wine/drive_c/Program Files/Picasa2/Picasa2.exe"

YEY, it worked! But there were a few error messages, that may or may not be an issue:

wine: cannot find L"C:\\windows\\system32\\wineboot.exe"
err:process:start_wineboot failed to start wineboot, err 2
fixme:actctx:parse_depend_manifests Could not find dependent assembly L"Microsoft.Windows.Common-Controls" (6.0.0.0)
fixme:ntdll:find_reg_tz_info Can't find matching timezone information in the registry for bias -120, std (d/m/y): 26/10/2014, dlt (d/m/y): 28/03/2014
fixme:ole:CoResumeClassObjects stub
fixme:win:FlashWindowEx 0x329e54
Bogus message code 1006
Bogus message code 1006
Bogus message code 1006
Bogus message code 1006
Not a JPEG file: starts with 0x91 0x6b
Not a JPEG file: starts with 0xb5 0xea

[ ... tons of these ... ]

fixme:wininet:InternetSetOptionW Option INTERNET_OPTION_CONNECT_TIMEOUT (10000): STUB
fixme:wininet:InternetSetOptionW INTERNET_OPTION_SEND/RECEIVE_TIMEOUT 10000
fixme:wininet:set_cookie httponly not handled (L"HttpOnly")
fixme:wininet:set_cookie httponly not handled (L"HttpOnly")
fixme:wininet:InternetSetOptionW Option INTERNET_OPTION_CONNECT_TIMEOUT (10000): STUB
fixme:wininet:InternetSetOptionW INTERNET_OPTION_SEND/RECEIVE_TIMEOUT 10000
err:wininet:NETCON_secure_connect SSL_connect failed: 12045
fixme:wininet:INET_QueryOption Stub for 32
fixme:file:MoveFileWithProgressW MOVEFILE_WRITE_THROUGH unimplemented
fixme:file:MoveFileWithProgressW MOVEFILE_WRITE_THROUGH unimplemented
fixme:file:MoveFileWithProgressW MOVEFILE_WRITE_THROUGH unimplemented

[ .. quite a few of these too ... ]

And still, I wondered why it failed before.

Picasa’s log file

When executed using Picasa’s as in the default installation (running /opt/picasa/bin/picasa), a diagnostic log is created in $HOME/.picasa/picasa.log which said

modify_ldt: Invalid argument
modify_ldt: Invalid argument
modify_ldt: Invalid argument
modify_ldt: Invalid argument
modify_ldt: Invalid argument
wine: Unhandled page fault on write access to 0x00000010 at address 0x7ee95bd2 (thread 001d), starting debugger...

An error and then a debugger starting. Sounds familiar…

It turns out that there is indeed an issue with recent kernels, as one can see here, here and here (the two last links are to the same thread in LKML). Indeed, there’s a patch called “x86-64, modify_ldt: Ban 16-bit segments on 64-bit kernels” by H. Peter Anvin, which was targeted for 3.13 kernels, but also made its way to 3.12 stable, which I’m using. So much for “stable”. It’s a security patch, I suppose, and yet I would have lived well without it.

What the offending patch is about

The problem is that the IRET x86 machine instruction only changes the lower 16 bits of the stack pointer, if it returns to a 16-bit execution context. Since IRET is used to switch back from kernel mode to any user space program, any user space program running in 16 bit mode could get to know where the kernel’s stack is mapped (except for the lower 16 bits) just by calling the modify_ldt system call. This information could be a significant piece in the puzzle for a kernel injection exploit (but doesn’t pose a threat by itself).

This is a x86 processor bug, which has a workaround for 32-bit systems. But this workaround can be applied to 64-bit machines, which is why this patch was made. Later, Linus offered a patch that allows users to choose at runtime if they want to allow modift_ldt on 64-bit machines.

Viewing all 69 articles
Browse latest View live