I'm new to Linux device driver writing. Can anyone offer any advice on the proper way to access memory mapped hardware registers?
I'm tinkering with stuff using MCF54455EVB. This has a 7-segment LED display. So, from u-boot I can display "12" on it just by doing:
-> mw.l 0x09000014 0x12
Now I'm trying to do the same thing from a Linux device driver. According to books/online sources this should work:
#define LED_REG_ADDRESS 0x09000014
void __iomem *reg;
request_mem_region(LED_REG_ADDRESS, 4, "LED");
reg = ioremap_nocache(LED_REG_ADDRESS, 4);
printk(KERN_INFO "LED phys = %x, virtual = %x\n",
LED_REG_ADDRESS,
(unsigned int)reg);
writel(0x00000012, reg);
I'm assuming that ioremap_nocache should program the MMU to give a translation from kernal allocated virtual address to the actual physical register addess with no cacheing. When it runs it prints the following, but the LED does not change.
LED phys = 9000014, virtual = e1002014
and /proc/iomem shows me that the address has been registered. Have tried all sorts of variations on the above code, but nothing works. Anybody have any ideas?
Solved! Go to Solution.
In order to write a value in a memory mapped register I do the following:
(for instance)
#define LED_WRITE 0x90000002 /* memory mapped address */
static unsigned char ls_leds = 0xFF;
unsigned char *la_writeLed;
la_writeLed = ioremap ((volatile unsigned long)LED_WRITE, 4); /* use IOREMAP */
...
...
*(la_writeLed) = ls_leds; /* write the desirable value in the register */
Hope this helps!
I remember having the same problem trying to use ioremap_nocache() per the Linux device drivers books. Finally I searched through the Freescale Coldfire kernel code and found this in arch/m68k/coldfire/mcf5445x-pci.c:
/* setup FPGA to route PCI to IRQ3(67), SW7 to IRQ7, SW6 to IRQ4 */
set_fpga(FPGA_PCI_IRQ_ENABLE, 0x00000000);
set_fpga(FPGA_PCI_IRQ_ROUTE, 0x00000039);
set_fpga(FPGA_SEVEN_LED, 0x000000FF);
The set_fpga() function is defined as an assembly routine in arch/m68k/coldfire/head.S. It manipulates the Access Control Register ACR0:
/*
* set_fpga(addr,val)
*
* Map in 0x00000000 -> 0x0fffffff and then do the write.
*/
set_fpga:
movew %sr,%d1
movew #0x2700,%sr
movel ACR0_FPGA, %d0
movec %d0, %acr0
nop
moveal 4(%sp),%a0
movel 8(%sp),%a0@
movel ACR0_DEFAULT, %d0
movec %d0, %acr0
nop
movew %d1,%sr
rts
.data
.align 4
Using this function should allow you write to the 7-seg using the 0x09000014 physical address directly.
In order to write a value in a memory mapped register I do the following:
(for instance)
#define LED_WRITE 0x90000002 /* memory mapped address */
static unsigned char ls_leds = 0xFF;
unsigned char *la_writeLed;
la_writeLed = ioremap ((volatile unsigned long)LED_WRITE, 4); /* use IOREMAP */
...
...
*(la_writeLed) = ls_leds; /* write the desirable value in the register */
Hope this helps!