Memory mapped I/O from Linux device driver

キャンセル
次の結果を表示 
表示  限定  | 次の代わりに検索 
もしかして: 

Memory mapped I/O from Linux device driver

ソリューションへジャンプ
11,273件の閲覧回数
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,509件の閲覧回数
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,509件の閲覧回数
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,510件の閲覧回数
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,509件の閲覧回数
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,509件の閲覧回数
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 件の賞賛
返信