This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

How to access OMAP registers under Linux?

Other Parts Discussed in Thread: OMAP3530, SYSCONFIG

First, let me describe what I want to do, in case I'm headed down the wrong path.

We have a custom board with OMAP3530 on it and connected to a bunch of different peripherals. We can boot Linux (via U-Boot) and I'm quite comfortable with accessing these peripherals from U-Boot. It's pretty easy here, I just write to the correct registers and that's it. I should add that all the peripherals are hooked up to the GPMC data & address bus and use various GPMC_nCS to select operation, but I also need a handful of other I/O signals (pins configured as GPIO) to control

Now how would I go about doing this under Linux?

   The "dumb" way is for me to try and get direct access to these registers and replicate what I've done in U-Boot. Is there a "driver" or somesuch which allows direct, generic access to OMAP's memory space? I assume that generally in User Space, I'm not allowed to directly access the hardware. (And I don't think we have anything like ioperm(),inb(),outb() for OMAP, yes?)

   Or, has someone already written a framework for generic access on the GPMC & configure the GPIO interfaces? I've seen something this for other processors, so while clumsy, it has saved me a bit of work re-inventing the wheel.

   Failing that, I assume I would have to write my own driver to do what I want. In that case, what would be a good driver to start with as a basis to copy? I am hardly a Linux driver guru, I've only done 1 character device driver before for another processor and it was based off another existing driver, so I know I can at least get that done...

Access to the peripherals is pretty simple. I just want to dump some data on the GPMC bus, toggle some GPIO, then read back the status on some other GPIO.

Any tips greatly appreciated.

  • I should add, I found something relating the GPIO on OMAP in the kernel tree, as well as something for GPMC on OMAP2. I don't know if either of these are applicable, however, it doesn't compile anyways.

    In my program, I tried:

    #include <asm/arch/gpio.h>
    #include <asm/arch/gpmc.h>
    .

    .

    ...{Do some stuff}

    But when I try to make, I get:

     /home/linux/kernel_org/2.6_kernel/include/asm/arch/io.h:185: error: expected ':', ',', ';', '}' or '__attribute__' before 'offset'
    /home/linux/kernel_org/2.6_kernel/include/asm/arch/io.h:191: error: expected ':', ',', ';', '}' or '__attribute__' before 'offset'
    /home/linux/kernel_org/2.6_kernel/include/asm/arch/io.h:196: error: expected ':', ',', ';', '}' or '__attribute__' before 'offset'

     /home/linux/kernel_org/2.6_kernel/include/asm/arch/gpmc.h:89: error: expected specifier-qualifier-list before 'u16'
    /home/linux/kernel_org/2.6_kernel/include/asm/arch/gpmc.h:120: error: expected declaration specifiers or '...' before 'u32'
    /home/linux/kernel_org/2.6_kernel/include/asm/arch/gpmc.h:121: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'gpmc_cs_read_reg'

    I wonder if I'm missing an #include somewhere ...

    (The GPIO documentation doesn't seem to say anything but #include <asm/arch/gpio.h> was enough)

  • As a quick hack you can use mmap to map the desired registers into user space.  Be aware that  mmap is typically restricted to a 4K boundary (I believe) so just offset if needed from where map to desired register address.  Just depends what Linux is doing with them as well and what you are attempting to do.  Reference thread for example:

    https://community.ti.com/forums/p/4515/30131.aspx#30131

    In the end you may have to write a driver especially if Linux uses the same IO or to ensure not preempted.  There are a number of tutorials you can google.  Also be aware that some registers may need supervisor mode, which means you have to do it at the driver level regardless.

    Below is another thread that may help you as well:

    http://groups.google.com/group/beagleboard/browse_thread/thread/ff6f6aa843afd950

    Kev

     

  • Isaac,

    Please take a look at the following thread, it discusses various methods for reading/writting registers and the customer who made the original request confirmed the methods work.

    http://e2e.ti.com/forums/p/4292/15749.aspx#15749

     

  • Thanks for the helpful responses.

    I can try the "regrw" utility just for debug & testing, but it doesn't sound like it will be convenient for what we want to do. We will have an application that constantly reconfigures the GPMC to access different peripherals and may be streaming large amounts of data back sometimes. (Does my assumption about "regrw" sound correct?)

    I followed bridgenet's response and that perhaps might be more useable for our case. Most of the time our peripheral is not an actual memory device, so I do not need to map more than 4k. For the way our board is designed, I would hope that Linux doesn't access any of the register's or I/O that we're interested in. (It should have no reason to, but that doesn't mean something won't do it anyways...). But if I understand correctly, I think the general concern is if I map some physical address range, does Linux ever use that memory range? Yes?

    Also, where can I find out if any of the registers are only accessible by supervisor, thus needing kernel space driver?

  • In Linux, kernel mode normally has direct access to the hardware whereas user space normally accesses the hardware via pre-defined driver APIs.  Utilities such as regrw (which uses proc functions implemented in kernel) and mmap are ways to get around these which may be necessary for debugging, but I would not encourage them for design purposes as they seem to counter the concept behind dividing user space and kernel space.  Ideally, if you have a need to access registers from user space that is not met by any of the existing driver APIs, you would create a proprietary driver API (or ioctl, which is one of the ways you send requests to drivers in kernel space) to accomplish your task; user space application will call this API (or ioctl) and the actual register access will happen in kernel space.

    That said, hardware registers in OMAP do not understand the concept of supervisor mode, hence this is all software implementation.  Using proc-based utility or mmap, you should not run into supervisor mode issues, just keep in mind comments above.

  • Just to verify, I did try the mmap() technique and it works fine so far, just based on simple testing.

    I understand (or at least I think I do) the issue when it comes to the whole user/kernel space thing. I agree in general, what I'm doing is a "bad idea". However, that's really coming from the PC world, where there are many applications from many vendors and the underlying hardware needs to be more abstracted. For our specific applications, I think it is perhaps less bad because I need to have very intimate knowledge of the underlying hardware anyway, it is always a fixed configuration, plus it is a very customized platform that nobody else needs to ride on.

    I'm not opposed to writing up a more proper kernel driver for this thing. I've done something similar before for another processor, so I know it's not impossible [:)]. If I can find a nice example to follow, I would probably just go at it and release it (since this sort of thing seems like something everyone needs).

  • The mmap memory alignment I believe needs to be on a 4K boundary, not sure of Max length but you can always allocate multiple pointers.  As long as you don't have any drivers in Linux using the registers you access then you should be fine, if Linux does you may be able to just not load those drivers by disabling it with a new kernel build.  Also if Linux does use it, it may just be at initialization and then you can take control as long as you don't use that resource within Linux.  Regardless it is OK if Linux is writing to some of them within the block you mmap just as long as you are writing to others that are not being used.

    With regards to supervisor mode, there are some registers that you must be privilaged to access.  I'm programming the L137/8 for example and the KICK registers to enable system configuration register access is from supervisor mode.  On the OMAP3530 there is a interrupt controller configuration protection (INTCPS) register that if enabled you would only be able to access those registers from the kernal level.  The documentation for the register set you are using will tell you.  Typically things like power domains and system configuration may be treated special.  I don't know the 3530 that well but a quick google mentioned a few.  You are probably fine.

    With regards to writing User Level Drivers, there is nothing wrong with that and in fact is becoming common place with Linux 2.6, especially in an embedded environment.  You just may not have the performance that a real driver may have.  Might be easier to get it working at the user level first and then once you are more use to things, turn it into a real driver later.  Take a look at the regrw source code, not much to it.  Check out the below link to learn more:

     

    http://lwn.net/Kernel/LDD3/

     

    Kev

     

  • Whoops! I seem to have run into a little problem.

    Using mmap() to do the work seemed to be okay. But it turns out it is not the case. This is the odd thing I've found:

    I can use my program under Linux to turn "on" the GPIO (i.e. output 1), but I cannot use it to turn "off" these select GPIO (i.e. output 0).

    Except ... if I first access the same registers and write the same values , in the same order, while under U-Boot. Then upon booting Linux, my program sort of works. Then there is an odd bit. Writing 0 or 1 to the GPIO doesn't exactly work. It depends on which one goes first, then the other will toggle it, even though I am _explicitly_ writing "1" or "0" (not even using AND , OR). So depending on which one I did first, sometimes it looks like it is working correctly.

    Any clues here? Seems kind of odd. I'm also reading back the register values via the mmap allocated area, I get exactly the same values as I do in U-Boot. When the GPIO isn't toggling externally, I read back the registers and it shows that it thinks it has toggled. Hmmm.... very curious.

  • Couple of things to check, first that you are using a volatile for register access.  Also in looking at the GPIO manual briefly you need to set and clear by writing a 1 to the appropriate register, 0 will have no effect.  Don't know the register you are accessing (and I'm not using the OMAP35xx) but assuming GPIO_CLEARDATAOUT (offset 0x90) and GPIO_SETDATAOUT (offset 0x94).  

    Kev

     

  • Well, some more digging, here's the issue. Some of the register writes are not "sticky". Specifically the one that you mention (to set the GPIO output to "1" or "0").

    I use the same method to set the GPIO Interface, Functional Clocks, SYSCONFIG, etc. These values all "stay", i.e. next time I invoke the program and read back that value, it's what I last set it to.

    This doesn't seem to happen with the GPIO Data Out (I actually tried the GPIO DATA OUT , 0x03C as well as the SET and CLEAR registers, both do the same ). The bit gets set, I can read it back to verify, while within the application, and I can measure externally that the GPIO is set / clear even though the application has finished. However, next time I run the application and read back the register, it seems to think that nothing is set and all is cleared. Hmmm... strange!

    I'm going to assume my C coding is okay, since I'm just copy 'n pasting from the routine used to set all other registers, which do work. And also, I can see the GPIO toggling! This is not a "Read-to-clear" register, is it? It doesn't mention that in the TRM that I could see...

  • Hi,

    I am trying to do something smillar but on OMAP138L  linux user space program that switches MUX settings. I know that it isn't good thing etc, but I am stuck with situation almost as described above. This means that I can read and write to that PINMUX register but the mux isn't changed after next read. I ensured that address of registers are good but no change on register occurs.

    My question is what may be blocking my write, as I can see access to PINMUX is avalible only in Privileged mode so I set KICK0R KICK1R registers with unlock values by mmap but something is still missing.

    I am trying to change PINMUX4 UART2_TXD register, I can't change it in kernel configuration because it has to be changed on application runtime. I know that should be rather done by kernel module (if any ) but because of other issues I can't do it that way.  So is it possible to change mux settings from userspace ?

  • Krzysztof,

    I don't know the answer to your question, but if you don't get a solution within a couple of days, you can try the Embedded Software -> Linux forum too.

    -Tommy

  • tlee said:

    Krzysztof,

    I don't know the answer to your question, but if you don't get a solution within a couple of days, you can try the Embedded Software -> Linux forum too.

    -Tommy

    Thanks Tommy I will ask there too.

    I think I know the answer, it is because code that modifyes anything in SYSCFG0 registers must be run in Privileged mode, and this mode is reserved for Linux Kernel.

    So next question occurs, is it possible to enter kernel space and modify the MUX from userspace program, as stated before I want to avoid of writing kernel module (it could done the task at ease).

  • Hi,

    I'm in a similar situation.  I'm trying to access a memory device (256KB) we added to our OMAP3-based custom board under Linux.  I'm able to access (reads and writes) it under u-boot without any problems.  That memory device is allocated by the Xilinx Spartan 6 and hooked up to CS2 on the GPMC and mapped to physical address 0x10000000.  Under Linux, I wrote a little user-space application using mmap to access it.  On our previous board which uses the TI Davinci processor (6446), the application works fine using mmap.  On this OMAP3-based custom board, all I get are 0's on the reads and writes; no error conditions though.  Using the sample application that uses /dev/mem and mmap, I'm able to access the OMAP35x registers fine but an unable to access the 256KB memory region created from the Xilinx FPGA.  Looking at the logic analyzer, I can see the chip select when I access the memory region.   Do I need to "register" the physical address (0x10000000) with the Linux kernel for mmap to work correctly?  If so, how do I go about doing so?  I searched the kernel and see that arch/arm/mach-omap2/io.c contains virtual and physical addresses for various memory space regions of the OMAP3; I assume this is used by kernel drivers only via ioremap.  For what I'm doing, I'm not writing any driver and am just using mmap in a user-space application to access the memory device.  Any advice on what I'm doing wrong; this area is not-cacheable so I added the "O_SYNC" option.  Here's a snippet of my sample application.  Thanks.

            volatile unsigned char *p;
            size_t length = 0x10000;
            int prot = PROT_READ | PROT_WRITE;
            int flags = MAP_SHARED;
            int fd = open("/dev/mem", O_RDWR | O_SYNC);
            off_t offset;
            unsigned int offset_addr;
            int index;

            sscanf(argv[1], "%x", &offset_addr);
            offset = (off_t)(offset_addr & 0xffff0000);
            index = offset_addr & 0xffff;

            if (fd < 0) {
                  perror("open");
                  return 1;
            }

            p = (unsigned char *)mmap(NULL, length, prot, flags, fd, offset);
            if (p == MAP_FAILED) {
                  perror("mmap");
                  return 4;
            }

            // process p[index]