1

i am having the following register address: 0x18040028. Since I am pretty new to that stuff, how am I supposed to access that register address, change that one bit and then write it back (all other entries should stay the same)?
Do I need to write a program, or can I do it from the terminal ?

I am doing that on a linux (openwrt)

thanks

rimes
  • 669
  • 1
  • 8
  • 24

2 Answers2

0

The quick and dirty way is to try and mmap the memory region out of /dev/mem. See these two questions:

Accessing hardware registers in Linux userspace

Accessing physical address from user space

However, I would not suggest you do this for a production system. What is cleaner would be to write a kernel driver. One option is write a regular kernel module that uses ioremap and ioread/iowrite operations, and exposes the registers via sysfs.

Using IO Memory

Sysfs Documentation

Another option that is particularly slick in my opinion, is to have these registers exposed through the platform_device system in your board support files.

Platform Devices

Basically you need to read the value, and then do:

readvalue |= 1<<1;

Then write the value back to the register.

Community
  • 1
  • 1
bodangly
  • 2,375
  • 14
  • 27
  • this seems pretty complex. Just to explain, in my case I need to enable a pin on my microcontroller, and in the manual it says i have to do it through the SoC internal registers. But when I check the links that you sent, it seems pretty complicated for such trivial need i have (to enable the pin). I guess i will need more time on that then i thought – rimes May 15 '16 at 15:58
  • @rimes Well if it is just one register that is one thing, you COULD try to use the first couple links to mmap /dev/mem. – bodangly May 15 '16 at 16:06
  • @rimes Does the pin need to always be on, starting at boot? If that is the case I would turn it on in the gpio_config file in your uboot source code. Again, these suggestions are best for production as they scale nicely and give end users a common interface. The platform_device method does have a lot of boilerplate code, but it is extremely nice because it lets you expose everything you need through sysfs, and define appropriate permissions on all the sysfs entries. – bodangly May 15 '16 at 16:08
  • yes it should always be on. Actually the pin is on, I need to change the function of it (so I can use serial communication). The pin is a multiplexed GPIO pin that is also used as TX_UART Interface (thats where I connect my device). So I need to turn on that TX_UART Interface (because now it is used as regular GPIO). Here http://www.black-swift.ru/files/AR9331.pdf in the section 6.4.11 I found that I need to set the Bit 1, (UART_EN) so I enable my needed interface – rimes May 15 '16 at 16:13
  • In that case, I would change this functionality in your Uboot source code. Can you reflash your uboot? – bodangly May 15 '16 at 16:15
  • Sadly, I guess I am too much noob to follow you on that (I am also using linux, or its light version openwrt, just for the last couple days). But I guess the code thats being executed on start? I am not sure if you ask, do I know how to do it, or does the board allows me to do it (sorry for the dumb question) – rimes May 15 '16 at 16:20
  • @rimes Uboot is the bootloader that starts up OpenWRT, but it sounds like it would not be a good idea for you to modify that if you are new to this as doing so can brick your board. In that case, follow the code from one of the first two StackOverflow questions in my answer and change the address to match your register. – bodangly May 15 '16 at 16:31
  • Ok thanks. But I need to write a C program which will always be executed on start, or is it enough to execute it once? EDIT: can use just that: http://stackoverflow.com/questions/13196387/how-to-change-a-32bit-registers-specific-bits-without-changing-other-bits where I put my address and the line you provided for OR manipulation ? – rimes May 15 '16 at 16:56
0

Option 1

On many linux builds you can access the GPIO pins directly using the sysfs. On openwrt in particular it is nearly always exposed.

on the openwrt device's terminal, see if this folder exists:

ls -Al /sys/class/gpio

If it is there, you usually need to export the specific GPIO pin you want to access. I looked at http://www.black-swift.ru/files/AR9331.pdf and can't figure it out. The problem is I don't think that particular pin on that register is exposed that way - at least I can't find it. I would have to be on that system digging around in the GPIO controller to have a chance to figure it out.

The UART pins you want to enable are exposed - GPIO9 (RX) and GPIO10 (TX). So maybe this info will be helpful regardless.

Option 2, below, is pretty much guaranteed to work; I found that after I had written this part.

Depending on your kernel version, you either use that GPIO # directly, or add in an offset. Try:

cat /sys/class/gpio/gpiochip*/base | head -n1

If it returns a number, thats the offset. Take your GPIO #, add that offset (making up 200 as an example) and use that as your GPIO #.

Using whatever your GPIO number is (either with or without offset), enter the following (using 210 just as an example; 200 offset, GPIO10)

echo "210" > /sys/class/gpio/export

then the GPIO pin will exposed in this directory, again using 210 as example, /sys/class/gpio210/ There will be at least two files created after the export, /sys/class/gpio240/value and /sys/class/gpio240/direction
You can interact with them using cat to read them, e.g. cat /sys/class/gpio240/value and modify it using echo: echo 1 > /sys/class/gpio240/value

There is a lot more info on accessing gpio via sysfs in the links I put at the bottom.

This will work easily if you find the right GPIO #, but if not (or even if you do), use:

Option 2

Someone has already done all the work. There are instructions on how to interface with this register to disable / enable the UART. It requires adding a package to your openwrt install called io, and they provide a script that toggles the value of the bit. You can modify that if you want to just echo out, always set it to a certain value, etc. It's written in bash.

You can add either the contents of the script or call the script itself from /etc/rc.local and it will get run at each boot; addressing that concern.

Since you already know you have the AR9331, you can cut out a lot of the script, e.g.:

    #!/bin/bash -

    # Bitwise operations: & = And, | = Or, ^ = xOr, << = Left Shift

    func_addr="0x18040028"
    func_value=0x`io -4 $func_addr | cut -f3 -d' 
    case_bit="1<<1"

    # This is where you would make a change if you wanted to 
    # just set it to 1 every time. 

    # To always set it to a '1', change from xor ^ to or |; like this
    #    io -4 $func_addr $(printf "0x%8.8x" $(($func_value | $case_bit)))

    # to always be 0 change the operator to and &, and use a mask
    # with just bit 1 set to 0
    mask32_b1="0xFFFFFFFD"
    # io -4 $func_addr $(printf "0x%8.8x" $(($func_value $ $mask32_b1)))
    # we using Bitwise xOr operation to switching bit# state (0 or 1)
    io -4 $func_addr $(printf "0x%8.8x" $(($func_value ^ $case_bit)))

    # read bit# state and depending on the state - print some info
    if [ $(($func_value & $case_bit)) = $(($case_bit)) ]; then
        echo "Hardware UART is turned OFF"
        # You can use this line for automatic configuring GPIOs via sysfs
        # or you can load other modules that use these GPIOs
    else
        echo "Hardware UART is turned ON"
    fi

It could be a one liner(fairly ugly one):

io -4  "0x18040028" $(printf "0x%8.8x" $(("0x"$((io -4   "0x18040028" | cut -f3 -d )) ^ "0x2")))

Toggle uart_en on AR9331

I couldn't test any of this, so I could have made a mistake anywhere, but if you follow that guide, and make the modifications to always set to 1 that I put in there (commented out at the moment), then you should be good. Make sure you only leave in one option to modify that register.

Sources / more info:
OpenWrt Wiki
Working with GPIOs
Kernel.org

Argonauts
  • 199
  • 10
  • Thanks for the input. The second option did it. Although I still have a problem with my display, when I connect it to the GPIO10/UART_TX I get weird sounds and characters on it. I thought it is because the UART is not turned on by default, but it seems that it is bcz when I execute the script it says UART turned off. I will have to search more for the problem. Thanks again for the help on this – rimes May 16 '16 at 12:28
  • Beyond the obvious (toggle that bit in that register), its not at all clear to me what you are intending to use the other 2 gpio pins for, etc. Unless that was a mistype, it sounds like you have a 16550 uart TX line connected to a display- which unless its a circa 1960 dumb terminal display makes no sense at all. You should probably ask a new question encompassing the end goal you are trying to achieve in detail, and I'm sure you'll get help. Sounds interesting at the very least. – Argonauts May 16 '16 at 14:04
  • What do you mean by "1960 dumb terminal display"? I have a serial lcd display and my problem was/is that when i connect it to the TX on the Black Swift and fire it up i get random characters and sounds (the display has also a speaker) from the display. So I thought that maybe UART is not enabled on that pin. But nowI have tried now to write on the display and, for my own surprise, it works. I can also clear it and all these random characters are gone. The problem now is just that these random sounds an characters appear always on start (and the dont go until i clear the display) – rimes May 16 '16 at 15:07
  • Actually that basically is a dumb terminal. http://www.linfo.org/dumb_terminal.html. Its not an insult; just an older approach and the only thing that came to mind with the info I had. I guessed right – Argonauts May 16 '16 at 18:21