i.MX5x device register access
Published on February 26, 2011
When developing device drivers and porting code to new hardware, programmers will often operate with source code in one hand and data sheet in the other, and it's often necessary to look at the state of a device's register set.
Under Linux, you can access registers, or any area of physical memory through the /dev/mem
pseudo-device and the wonders of the mmap
system call. To use it, you open the /dev/mem
device, mmap
the page in which a register is located, then use the pointer returned to read and/or write the data.
I suspect that you can find one of these in every embedded programmer's toolkit, but at Ezurio (formerly Boundary Devices), we've developed a tool known as devregs
that allows you to put a little structure around this facility. It allows you to give names to particular physical memory areas and to describe the bits within a register in the text file /etc/devregs.dat
.
If you look in the git repository, you'll see two files, named devregs_imx51.dat
and devregs_imx53.dat
which contain a subset of register definitions for the i.MX51 and i.MX53 processors.
We don't have a proper repository for this type of tool, but you can currently find the source code for our tool in our i.MX utility repository. The file devregs.cpp
is self-contained, so I won't explain compilation.
Usage for reads:
To read one or more registers, use devregs
with a single parameter that's either an address or a register name.
root@freescale ~$ devregs 0x73f88000
:0x73f88000 =0x803dffaf
If a register address matches a register in /etc/devregs.dat
, you'll see the register name:
root@freescale ~$ devregs 0x73f88000
GPIO2_DR:0x73f88000 =0x803dffaf
If used with a register name, any bitfields defined will be shown:
root@freescale ~$ devregs UART1_UFCR
UART1_UFCR:0x73fc0090 =0x0801
TXTL 10-15 =0x2
RFDIV 7- 9 =0x0
DCEDTE 6- 6 =0x0
RXTL 0- 5 =0x1
Usage for writes:
To write to a register, use devregs
with two parameters, the address, register, or field name and the value.
The following example shows how to change GPIO2:2
from an input to an output by tweaking the GPIO2_GDIR
.
root@freescale ~$ devregs GPIO2_GDIR
GPIO2_GDIR:0x73f88004 =0x0002c0a4
root@freescale ~$ devregs GPIO2_GDIR 0x2c0a0
GPIO2_GDIR:0x73f88004 =0x0002c0a4
GPIO2_GDIR:0x73f88004 == 0x0002c0a4...0x0002c0a0
root@freescale ~$ devregs GPIO2_GDIR
GPIO2_GDIR:0x73f88004 =0x0002c0a0
Note that we try to use register names exactly as defined in the data sheets within the devregs.dat
files. This makes it much easier when looking up values.
Format of /etc/devregs.dat
As mentioned earlier, the file /etc/devregs.dat is a text file which defines a set of registers. The simplest entries in the file simply have register names and addresses separated by whitespace as shown in this example for the registers that control GPIO bank 2. Each line defines
GPIO2_BASE 0x53F88000
GPIO2_DR 0x53F88000
GPIO2_GDIR 0x53F88004
GPIO2_PSR 0x53F88008
GPIO2_ICR1 0x53F8800C
GPIO2_ICR2 0x53F88010
GPIO2_IMR 0x53F88014
GPIO2_ISR 0x53F88018
GPIO2_EDGESEL 0x53F8801C
Some registers on the i.MX5x aren't four-byte addressable though (notably the UART and I2C registers). They must be read with word (2-byte) or single byte accesses. You can define such registers by appending either .W
or .B
to the address. This works either on the command line:
root@freescale ~$ devregs 0X73F98000.W
WDOG1_BASE_ADDR:0x73f98000 =0x0030
or in the /etc/devregs.dat
file:
WDOG1_BASE_ADDR 0X73F98000.W
Fields
To save time and mental energy reading and writing bitfields in a register, you can specify bitfields on the command-line.
Fields are specified in the form start-end
in either order:
root@freescale ~$ devregs GPIO2_GDIR:2-0
GPIO2_GDIR:0x73f88004 =0x0002c0a4
2-0 0- 2 =0x4
root@freescale ~$ devregs GPIO2_GDIR:0-2
GPIO2_GDIR:0x73f88004 =0x0002c0a4
0-2 0- 2 =0x4
You can also specify a single bit, which is useful when toggling a single bit as I did in the previous example:
root@freescale ~$ devregs GPIO2_GDIR:2 0
GPIO2_GDIR:0x73f88004 =0x0002c0a4
2 2- 2 =0x1
GPIO2_GDIR:0x73f88004 == 0x0002c0a4...0x0002c0a0
root@freescale ~$ devregs GPIO2_GDIR:2
GPIO2_GDIR:0x73f88004 =0x0002c0a0
2 2- 2 =0x0
Inside /etc/devregs.dat
, you can also give bit fields names by starting a line with a colon (the whitespace is just for readability). When specified after a register, the bit fields apply to that single register.
IOMUXC_GPR2 0x53FA8008
:COUNTER_RESET_VAL:21-20
:LVDS_CLK_SHIFT:18-16
:BGREF_RRMODE:15
:DI1_VS_POLARITY:10
:DI0_VS_POLARITY:9
:BIT_MAPPING_CH1:8
:DATA_WIDTH_CH1:7
:BIT_MAPPING_CH0:6
:DATA_WIDTH_CH0:5
:SPLIT_MODE_EN:4
:CH1_MODE:3-2
:CH0_MODE:1-0
Templates
Because the layout of a register is often repeated for multiple instances of UARTs or I2C devices for example, the parser for /etc/devregs.dat
allows you to define a named set of bit fields and then use it for multiple register definitions.
The set of bit fields is defined by starting a line in the file with a slash (/) and following it with a set of fields:
/uart_urxd
:CHARRDY:15
:ERR:14
:OVRRUN:13
:FRMERR:12
:BRK:11
:PRERR:10
:DATA:7-0
You can then refer to the named set of fields using this form:
UART0_URXD 0x73FBC000.W
:uart_urxd/
Fine print:
Please note that using /dev/mem
is completely dangerous and only available when running as root
. Only do it if you know what you're doing.
Okay, now don't say I didn't warn you.