Memory mapped I/O from Linux device driver

取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 
已解决

Memory mapped I/O from Linux device driver

跳至解决方案
11,285 次查看
jon_fbrj99
Contributor I

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?

标签 (1)
0 项奖励
回复
1 解答
4,521 次查看
raulen
Contributor III

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!

在原帖中查看解决方案

0 项奖励
回复
4 回复数
4,521 次查看
ghp
Contributor I

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.

0 项奖励
回复
4,522 次查看
raulen
Contributor III

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!

0 项奖励
回复
4,521 次查看
jon_fbrj99
Contributor I
I can now see why my original did not work. Writing directly via a pointer returned from ioremap (as shown by raulen) works. Writing the same value using writel() does not work. This is because writel is endian swapping the value! So to write 0x12345678 to a 32 bit memory mapped register I can either do *reg = 0x12345678 or writel(0x87654321, reg). Strange but true.
0 项奖励
回复
4,521 次查看
jkmahan
Contributor III
This is the quick and dirty way I did it to force a mapping that worked from just about anywhere.  Note that it also disables ints so that you don't accidentally use that mapping taking an irq.
0 项奖励
回复