• Home
  • Help
  • Search
  • Login
  • Register
Pages: [1] 2
Author Topic: Probing gpio pins from userspace using /dev/mem  (Read 18614 times)
tmk
Newbie
*

Karma: 1
Posts: 40


View Profile
« on: April 17, 2009, 10:23:20 PM »

For those who (like me) didn't realize it existed, I found a handy way to probe random memory addresses from userspace: /dev/mem.

This lets you directly read kernel and/or device memory! very useful.

A program to access it is here (not written by me): http://ftp://ftp.buici.com/pub/arm/devmem/devmem2.c

I should mention that i modified the original program to pad the output to the size of the request, or else it leaves off/truncates leading 0's. patch is here:
Code:
--- devmem2.c.orig 2009-04-17 22:20:05.226440750 -0700
+++ devmem2.c 2009-04-17 22:09:21.483230909 -0700
@@ -90,18 +90,20 @@
     switch(access_type) {
  case 'b':
  read_result = *((unsigned char *) virt_addr);
+                        printf("Value at address 0x%X (%p): 0x%02X\n", target, virt_addr, read_result, read_result);
  break;
  case 'h':
  read_result = *((unsigned short *) virt_addr);
+                        printf("Value at address 0x%X (%p): 0x%04X\n", target, virt_addr, read_result, read_result);
  break;
  case 'w':
  read_result = *((unsigned long *) virt_addr);
+                        printf("Value at address 0x%X (%p): 0x%08X\n", target, virt_addr, read_result, read_result);
  break;
  default:
  fprintf(stderr, "Illegal data type '%c'.\n", access_type);
  exit(2);
  }
-    printf("Value at address 0x%X (%p): 0x%X\n", target, virt_addr, read_result);
     fflush(stdout);
 
  if(argc > 3) {

To build, i used the GCC included in the linux host package.

Code:
gcc/bin/arm-none-linux-gnueabi-gcc devmem2.c -o devmem2

scp devmem2 user@sheevaplug:

For example, say you want to figure out how the GPIO pins are programmed.

For the HW offsets, we consult the marvell docs  MV-S104860-U0 (p773) and MV-S104859-U0 (page 53).

I am interested in the GPIO pins, 12-17, as these are shared with the SDIO. I am willing to break the SD card access if i can use those pins for GPIO.

Consulting page 774, i see that the memory offset for the register function controls is 0x10000. Based on the mmc module init message (mvsdmmc: irq =28 start f1090000) , i know that the base is 0xf1000000.

So let's query 0xf1010000 through 0xf1010008, to see how the pins are currently programmed:

Code:
root@charger:~# ./devmem2 0xf1010000 w
/dev/mem opened.
Memory mapped at address 0x4001f000.
Value at address 0xF1010000 (0x4001f000): 0x01111111

root@charger:~# ./devmem2 0xf1010004 w
/dev/mem opened.
Memory mapped at address 0x4001f000.
Value at address 0xF1010004 (0x4001f004): 0x11113322

root@charger:~# ./devmem2 0xf1010008 w
/dev/mem opened.
Memory mapped at address 0x4001f000.
Value at address 0xF1010008 (0x4001f008): 0x00001111

The right-most bits represent the lower #'d pins. So GPP pins are set as follows:
Code:

Value at address 0xF1010000: 0x01111111
 0-6 are all set to 1
 7 is set to 0

Value at address 0xF1010004: 0x11113322
 8,9 are set to 2
 10,11 are set to 3
 12-15 are set to 1

Value at address 0xF1010008: 0x00001111
 16-19 are set to 1
 20-23 are set to 0

I'm interested in 12-17. All are set to '1'. Checking page 53 of the Hardware spec, i see that this indicates they are currently set as SDIO pins. This jives with what i expect. As a sanity check, 8,9 are 2, and 10,11 are 3. These group together to form 4 pins for serial port UA0. Bingo!

I have not tried using /dev/mem to make changes to the settings, but i plan to soon. Stay tuned.

-tmk
Logged

tmk
Newbie
*

Karma: 1
Posts: 40


View Profile
« Reply #1 on: April 17, 2009, 11:46:21 PM »

WARNING WARNING

If you change the wrong bits, Very Bad Things can happen. I changed GPP pins 12-19 instead of 12-17, and 18/19 are involved in the NAND flash interface. Needless to say, i was reflashing my filesystem shortly thereafter.

Use these instructions at your own risk.

WARNING WARNING


I've gotten writes to work as well, and confirmed that my pins are correct:

Now that i've found the pins i think control SDIO/GPP for GPIO pins 12-17, i want to change their function from SDIO to GPP.

First, find where pins 12-17 start:

Code:
This is 8-15
root@charger:~# ./devmem2 0xf1010004 w
/dev/mem opened.
Memory mapped at address 0x4001f000.
Value at address 0xF1010004 (0x4001f004): 0x11113322

Slide up 2 bytes from ...4 to ...6 and you get 12-19:

root@charger:~# ./devmem2 0xf1010006 w
/dev/mem opened.
Memory mapped at address 0x4001f000.
Value at address 0xF1010006 (0x4001f006): 0x11111111

Ok, now we've found 12-19. Let's flip 12-17 to 0 (leave 18,19 alone!!!) to set them to GPIO pins.

Code:
root@charger:~# ./devmem2 0xf1010006 w 0x11000000
/dev/mem opened.
Memory mapped at address 0x4001f000.
Value at address 0xF1010006 (0x4001f006): 0x11111111
Written 0x11000000; readback 0x11000000

Let's test it.

I know my 2gb SD card works, as i've mounted it before. So i plug it in..

Code:
root@charger:~# tail /var/log/kern.log
...
Apr 18 06:33:34 charger kernel: fat: exports duplicate symbol fat_add_entries (owned by kernel)

Hmm, nothing is logged. I know for sure that it worked before...

So, let's flip it back:

Code:
root@charger:~# ./devmem2 0xf1010006 w 0x11111111
/dev/mem opened.
Memory mapped at address 0x4001f000.
Value at address 0xF1010006 (0x4001f006): 0x11000000
Written 0x11111111; readback 0x11111111

and let's see if anything happened..

Code:
root@charger:~# tail /var/log/kern.log
...
Apr 18 06:33:34 charger kernel: fat: exports duplicate symbol fat_add_entries (owned by kernel)
Apr 18 06:37:13 charger kernel: mmc0: host does not support reading read-only switch. assuming write-enable.
Apr 18 06:37:13 charger kernel: mmc0: new high speed SD card at address 1234
Apr 18 06:37:13 charger kernel: mmcblk0: mmc0:1234 SA02G 1927168KiB
Apr 18 06:37:13 charger kernel:  mmcblk0: unknown partition table

Now there's an SD card detected! No doubt that flipping these bits takes immediate effect.

-tmk
Logged

tmk
Newbie
*

Karma: 1
Posts: 40


View Profile
« Reply #2 on: April 19, 2009, 12:31:54 AM »

Today I took apart a microSD -> SD adapter card and soldered wires onto each pin.

I used an old floppy-disk cable for the wires, and as a bonus, it has a built-in breadboard (the FD connector Wink

I've tested each pad, and they all work. Now i can start playing around with the GPIO stuff in earnest.

I'll post pictures and more steps tomorrow night.

-tmk
Logged

tmk
Newbie
*

Karma: 1
Posts: 40


View Profile
« Reply #3 on: April 19, 2009, 11:59:15 PM »

Worth nothing that the above technique does not work on mainline 2.6.30-rc2.

I think the closer control of the GPIO stuff in the kernel breaks it. Each time i tried to modify the control register (to configure the pins for GPIO), it locked up on me.

also in 2.6.30-rc2, the values in the control register appear to be invalid, based on the datasheet. 18,19 are both set to '3' which is undocumented, and 16,17 are set to '2', which doesn't make sense.

-tmk
Logged

tmk
Newbie
*

Karma: 1
Posts: 40


View Profile
« Reply #4 on: April 20, 2009, 08:13:51 AM »

Here are some pictures of my on-the-cheap interface into the built-in GPIO pins:












I'm pleased to report that using the w1-gpio-custom driver from openwrt, i was able to get dallas 1-wire working using the GPIO stuff in the 2.6.30-rc2 kernel

I had a ton of issues along the way:
 * openwrt driver had some bugs which prevented module load/unload.
 * kernel didn't define the pins as gpio, had to edit the sheevaplug-setup.c file and add them
 * kernel bug described here http://lkml.indiana.edu/hypermail/linux/kernel/0904.2/00170.html bit me, had to correct it

I have a dallas temp sensor (DS 18b20), which reports and is discovered, but isn't giving valid readings (seems stuck in init mode.)

out of time for today, but heres the general steps to get it working:

Code:
modprobe w1_therm
modprobe wire
#i used pin 13:
modprobe w1-gpio-custom bus0=0,13,0
modprobe w1_gpio

cd /sys/bus/w1/devices
ls
cd 28-000001c77bdc
cat w1_slave


here's sample output. According to the data sheet, 85 celsius is the default init value

Code:
root@charger:/sys/bus/w1/devices/28-000001c77bdc# cat w1_slave
50 05 4b 46 7f ff 0c 10 1c : crc=1c YES
50 05 4b 46 7f ff 0c 10 1c t=85000
« Last Edit: April 25, 2009, 11:01:18 PM by tmk » Logged

tmk
Newbie
*

Karma: 1
Posts: 40


View Profile
« Reply #5 on: April 25, 2009, 11:00:53 PM »

Finally figured it all out.. it was a timing issue, and my parts were "parasite power only", which means i needed to do some kernel hacking to add support for that feature..

patches and such are posted over here:

http://openplug.org/plugforum/index.php?topic=163.msg940#msg940

-tmk
Logged

puterboy
Newbie
*

Karma: 0
Posts: 14


View Profile
« Reply #6 on: October 20, 2010, 11:01:06 AM »

TMK,
Since you seem to be one of the few who have mastered reading/writing GPIOs, I was hoping you could help me with a few questions.

First, I am looking for the fastest possible way to sequentially sample and/or output to the GPIO pins so I am looking for the method that has the least overhead (without resorting to assembly code or anything too hard).

I used the modified devmem2 code that you cited. It seems to read the mpp control registers just fine but I have a couple of problems/questions:
1. I get the same hang under kernel 2.6.31.8 when I try to write to those registers (and I also have the same issue with 'undocumented' states). Has any workaround been found or is it not possible to write these registers via /dev/mem
2. I see how to read/write the MPP control registers but how do you access the actual bits themselves in the corresponding GPIO lines?
3. Is /dev/mem the fastest way to read/write the GPIO lines or are there equally fast (or faster) alternatives? I have always assumed that /sys is slow but maybe I'm wrong.
4. Just out of curiosity, do you have to call mmap each time you want to re-read/write a GPIO line or does it only need to be done once at initialization and then it is just sufficient to reread the pointer to the mmapped region.

Thanks
Logged

birdman
Sr. Member
****

Karma: 4
Posts: 443


View Profile WWW
« Reply #7 on: October 20, 2010, 06:07:50 PM »

Is this of any relevance to you?
   http://plugcomputer.org/plugforum/index.php?topic=1000.0
Logged

MarkF
Full Member
***

Karma: 7
Posts: 144


View Profile
« Reply #8 on: October 22, 2010, 11:33:19 AM »

First, I am looking for the fastest possible way to sequentially sample and/or output to the GPIO pins so I am looking for the method that has the least overhead (without resorting to assembly code or anything too hard).
The input pins sampling method with the least overhead is to not sample them. Smiley Implementation involves setting the input pins up to cause an interrupt when they are changed by exterior signals.  This, however, requires kernel code (a device driver, usually) to handle the interrupt condition and user code to handle a propagated "IO change" event.

Determining a purely user space alternative requires an understanding of the maximum "IO change" event propagation delay that can be tolerated.  In short, the less delay that can be tolerated, the higher the priority of and the smaller the sleep time in the polling loop (both of which increase the overhead associated with the method).

Quote
4. Just out of curiosity, do you have to call mmap each time you want to re-read/write a GPIO line or does it only need to be done once at initialization and then it is just sufficient to reread the pointer to the mmapped region.
You only need to mmap() the registers once and then reference them via the pointer any time you want to poll the lines or change them.  The pointer will remain valid until the program terminates or you call munmap().

I hope this helps.
Logged

Mark

puterboy
Newbie
*

Karma: 0
Posts: 14


View Profile
« Reply #9 on: October 24, 2010, 12:49:54 PM »

OK I figured it out and got it working using /dev/mem!
I am able to sample (read or write) at 1MHz (1us/sample) with good accuracy (though occasionally some other thread burps and I miss a sample).

As TMK, mentioned be CAREFUL about accidentally changing the status of key dedicated GPIOs. When I was testing my code to make sure it properly did all the shifting/masking for different GPIOs,  I accidentally changed the status of one of the GPIOs controlling the NAND flash and it corrupted my flash (of course just before I was about to backup my system but I thought let me just try one more test) -- I had to reflash to fix that... Sad

Anyway, I wrote some sample code for reading and writing GPIOs that:
1. Sets the appropriate status/direction of the desired GPIO port via the appropriate control registers
2. Samples or outputs to the corresponding GPIO pin.

I tested the code on the Ionics Stratus plug and put in some guard rail code to make sure you only toggle the free GPIOs (I know that GPIOs 20-27 are safe)
The attached routines are jdevmem-gpi.c (input) and jdevmem-gpo.c (output). Obviously, they can be generalized/adapted to sample/write to multiple gpios at once etc.

Enjoy!

* jdevmem-gpi.c (0 KB - downloaded 299 times.)
* jdevmem-gpo.c (0 KB - downloaded 306 times.)
Logged

puterboy
Newbie
*

Karma: 0
Posts: 14


View Profile
« Reply #10 on: October 24, 2010, 12:54:01 PM »

First, I am looking for the fastest possible way to sequentially sample and/or output to the GPIO pins so I am looking for the method that has the least overhead (without resorting to assembly code or anything too hard).
The input pins sampling method with the least overhead is to not sample them. Smiley Implementation involves setting the input pins up to cause an interrupt when they are changed by exterior signals.  This, however, requires kernel code (a device driver, usually) to handle the interrupt condition and user code to handle a propagated "IO change" event.

Determining a purely user space alternative requires an understanding of the maximum "IO change" event propagation delay that can be tolerated.  In short, the less delay that can be tolerated, the higher the priority of and the smaller the sleep time in the polling loop (both of which increase the overhead associated with the method).
[


Thanks, I came to a similar conclusion upon reading the Marvel datasheets and seeing that there is the option to trigger inputs. I have no idea though how to write a kernel driver. Any pointers to sample code or a similar driver for another device or application that I could potentionally adapt to the plucomputer?

Thanks!
Logged

MarkF
Full Member
***

Karma: 7
Posts: 144


View Profile
« Reply #11 on: October 25, 2010, 08:55:49 AM »

Unfortunately, everything I've written was paid for (and owned) by a client so I don't have any personal sample code I can give away.  Sorry. Sad

Of course the source code for all the drivers in the kernel distribution is available.  Also, Google-ing "linux device drivers" yields a BUNCH of introductions, tutorials and books.  Just be careful that some of it is old and the kernel interface has and can change quickly.

You want to use a simple monolithic character driver with an interrupt service routine.  I can't think of a single driver that would be a good starting point since most of the latest kernel drivers are part of one or more "frameworks" and are neither simple nor monolithic.

EDIT: After increasing my blood sugar level - perhaps a driver that attaches to the input event mechanism would work for this?  If there were a small driver that sent IO change events through the evdev, the user software could open a /dev/input/eventX device and use standard read() or select() code to wait for change reports.
« Last Edit: October 25, 2010, 12:11:25 PM by MarkF » Logged

Mark

Zup
Newbie
*

Karma: 0
Posts: 6


View Profile
« Reply #12 on: October 25, 2010, 10:50:43 PM »

Does anyone know if there is a way to try to read/write to the individual SDIO pins?

I think my SDIO port is broken and it would be nice to know if any pins aren't connected properly.
Logged

puterboy
Newbie
*

Karma: 0
Posts: 14


View Profile
« Reply #13 on: October 26, 2010, 12:02:55 AM »

Does anyone know if there is a way to try to read/write to the individual SDIO pins?

I think my SDIO port is broken and it would be nice to know if any pins aren't connected properly.

Ummmm... isn't this *exactly* what I just took the trouble of posting about and even attached the actual code to do exactly what you want without any additional programming or modifications? (please tell me you read the last couple of posts...)

Just figure out which is the pin number corresponding to the sdio ports on your device and run my programs to read/write to the pins...
BE CAREFUL to get the port numbers right...
Logged

Zup
Newbie
*

Karma: 0
Posts: 6


View Profile
« Reply #14 on: October 26, 2010, 06:15:12 AM »

But I'd need some kind of hardware device to sample the output of the pins, right?
There is no way to just try to write to a pin and see if it can be set from software? Maybe some error message if something goes wrong?
I'm not too hot with designing my own hardware  Lips sealed

Something like
Set pin 1 to 1
Read pin 1: 1. OK
Set pin 2 to 1
Read pin 2: 0. ERROR
woops, line 2 is broken.
Logged

Pages: [1] 2
Print
Jump to: