Memory Mapped Access to /dev/mem GPIO2 fails, GPIO1 OK

I am trying to access, via mmap on /dev/mem, the GPIOs at addresses 0x44e07000, 0x4804c000, 0x481ac000 and 0x481ae000. These are GPIO banks 0,1,2 and 3 respectively.

Access is successful on GPIO bank 0 and 1 (0x44e07000 and 0x4804c000) but fails consistently with a “Bus Error” on GPIO bank 2 and 3 (0x481ac000 and 0x481ae000).

Does anybody have any idea why this might be happening? Sample C code which reproduces the error below`` - basically it is trying to read the GPIO_REVISION at offset 0 at the start of each GPIO bank. The expected value returned should be 0x50600801 - but a “Bus Error” is returned for GPIO banks 2 and 3.

This seems to happen on standard 3.8.13-bone47 on a BBB rev C right out of the CircuitCo box and also on an 3.14 ubuntu kernel.

I would really appreciate any insights you may have

`
// c code to test access to the /dev/mem file

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#define GPIO_BANK_0 0x44E07000
#define GPIO_BANK_1 0x4804C000
#define GPIO_BANK_2 0x481AC000
#define GPIO_BANK_3 0x481AE000

int gpio_mmap_test(int gpioBank, int sizeToFetch);

int main()
{
gpio_mmap_test(GPIO_BANK_0, 0xfff);
gpio_mmap_test(GPIO_BANK_1, 0xfff);
gpio_mmap_test(GPIO_BANK_2, 0xfff);
gpio_mmap_test(GPIO_BANK_3, 0xfff);
return 0;
}

int gpio_mmap_test(int gpioBank, int sizeToFetch)
{

int gpio_fd2 = open("/dev/mem", O_RDWR | O_SYNC);

if (gpio_fd2 < 0)
{
printf(“Could not open GPIO memory fd\n”);
return 0;
}

volatile ulong *gpio;

gpio = (ulong*) mmap(NULL, sizeToFetch, PROT_READ | PROT_WRITE, MAP_SHARED, gpio_fd2, gpioBank);
if (gpio == MAP_FAILED)
{
printf (“GPIO Mapping failed\n”);
close(gpio_fd2);
return 0;
}

// offset 0 is the GPIO_REVISION field
// expected value is 0x50600801
int gpioRevision = gpio[0];

printf(“bank %04x, gpioRevision = %04x\n”, gpioBank, gpioRevision);

close(gpio_fd2);
}

`

Check their functional clocks are enabled in the CPM_PER_GPIO(2/3)_CLKCTRL registers.

Thanks. I have had a look in the Device Tree file and the only references to the CM_PER_GPIO2_CLKCTRL seem to be the clocks being set up identically in the ocp branch (shown below). Looking it up in the TRM, I also dug around at location 0x44e00000 positions 0xac, 0xb0 and 0xb4) and only zeros were returned. I am not sure what they are supposed to be.

Another difference in the device tree is that the ocp definitions of the gpio’s themselves are mostly identical except that gpio2 and gpio3 do not have a phandle reference. Is it possible this has something to do with it? Adding a phandle to those two did not fix the problem.

Is there a better place to check to see if these clocks are enabled? I appreciate any insights you can give me as I track this down.

`
gpio0_dbclk_mux_ck {
#clock-cells = <0x0>;
compatible = “ti,mux-clock”;
clocks = <0x14 0x15 0x12>;
reg = <0x53c>;
linux,phandle = <0x18>;
phandle = <0x18>;
};

gpio0_dbclk {
#clock-cells = <0x0>;
compatible = “ti,gate-clock”;
clocks = <0x18>;
ti,bit-shift = <0x12>;
reg = <0x408>;
};

gpio1_dbclk {
#clock-cells = <0x0>;
compatible = “ti,gate-clock”;
clocks = <0x12>;
ti,bit-shift = <0x12>;
reg = <0xac>;
};

gpio2_dbclk {
#clock-cells = <0x0>;
compatible = “ti,gate-clock”;
clocks = <0x12>;
ti,bit-shift = <0x12>;
reg = <0xb0>;
};

gpio3_dbclk {
#clock-cells = <0x0>;
compatible = “ti,gate-clock”;
clocks = <0x12>;
ti,bit-shift = <0x12>;
reg = <0xb4>;
};

`

This is out of a program I use -

`
Enter code here…
uint32_t *clock_gpio3;
clock_gpio3 = (uint32_t *) mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x44E00000);
clock_gpio3[0x2d] |= 0x40002; // 2d is b4 divide by 4 because it is a byte offset

`

Success!!! Thank you very much! - this works. I really appreciate your advice. I absolutely would not have got this sorted without it.

I am going to write up a summary and post it on this thread so that anybody else that runs into this issue can find it. However I do have a couple more questions - if you (or anybody) have the time to provide any insights you might have.

  • The CM_PER_GPIO1_CLKCTRL at 0x44e00000[0xac] for Bank GPIO1 is already set, yet the equivalents for GPIO2 and GPIO3 are not. Any idea how CM_PER_GPIO1_CLKCTRL got set?
  • Are there any Device Tree settings that you know of that can set the CLKCTRLs for GPIO2 and GPIO3 at boot up?
  • There is no CM_PER_GPIO0_CLKCTRL for the GPIO0 bank. I gather GPIO0 is something of a special case due to the wakeup capabilities it contains. Is the GPIO0 bank always enabled? Any thoughts on why GPIO0 is not there?
  • Your example OR’ed with 0x40002 - presumably to place it in standby - rather than fully active. I am curious - under what circumstances would you want to put a CLKCTRL in standby mode?
  • Do you have any idea why /dev/mem would fail to access GPIO3 if the CLKCTRL was not set and yet SYSFS can read the GPIO states on GPIO3? Obviously SYSFS is a device driver and does not need to hit /dev/mem but even so it appears the CLKCTRL is used to transfer the state off the physical pin and into the GPIO bank so why would SYSFS work if the clock is disabled.
  • You must have run into this issue yourself. How on earth did you figure out that it was the CLKCTRL registers? The reason I ask I I would like to know how to diagnose this sort of thing myself and it all seems such a mystery at the moment.

Once again… many thanks.

I have found that accessing GPIOs via mmap() of /dev/mem works without accessing additional control register settings if you export one GPIO pin in each bank so that the GPIO kernel driver “unlocks” each bank and wakes it up. GPIO1 is awake by default, but you can get the other GPIO0, GPIO2, and GPIO3 working via the following commands:

echo 5 > /sys/class/gpio/export
echo 65 > /sys/class/gpio/export
echo 105 > /sys/class/gpio/export

I do the same with Android in the init.{ro.hardware}.rc file:

write /sys/class/gpio/export 5
write /sys/class/gpio/export 65
write /sys/class/gpio/export 105

As for running those commands under Linux, I usually place the commands to export the pins into a shell script that also launches the application that uses the GPIOs. Since you have to be root to mmap() the pages with GPIO control registers, you can run the shell script via sudo and have the script export the pins and then launch your binary. You could also place the commands in the /etc/rc.local startup script or the like, if you want.

Andrew

I have found that accessing GPIOs via mmap() of /dev/mem works without accessing additional control register settings if you export one GPIO pin in each bank so that the GPIO kernel driver “unlocks” each bank and wakes it up. GPIO1 is awake by default, but you can get the other GPIO0, GPIO2, and GPIO3 working via the following commands:

echo 5 > /sys/class/gpio/export
echo 65 > /sys/class/gpio/export
echo 105 > /sys/class/gpio/export

I do the same with Android in the init.{ro.hardware}.rc file:

write /sys/class/gpio/export 5
write /sys/class/gpio/export 65
write /sys/class/gpio/export 105

As for running those commands under Linux, I usually place the commands to export the pins into a shell script that also launches the application that uses the GPIOs. Since you have to be root to mmap() the pages with GPIO control registers, you can run the shell script via sudo and have the script export the pins and then launch your binary. You could also place the commands in the /etc/rc.local startup script or the like, if you want.

Running applications as root is a very bad idea. if you change the group for /dev/mem and add your userid to that group, you don’t have to run your app as root.

Regards,
John

Except that running those commands as root at startup( init scripts ) has no detrimental effect.

Except that running those commands as root at startup( init scripts ) has no detrimental effect.

He is running the script as root and in that script, he is launching his application, so his application is running as root.

echo 5 > /sys/class/gpio/export
echo 65 > /sys/class/gpio/export
echo 105 > /sys/class/gpio/export

I do the same with Android in the init.{ro.hardware}.rc file:

write /sys/class/gpio/export 5
write /sys/class/gpio/export 65
write /sys/class/gpio/export 105

As for running those commands under Linux, I usually place the commands to export the pins into a shell script that also launches the application that uses the GPIOs. Since you have to be root to mmap() the pages with GPIO control registers, you can run the shell script via sudo and have the script export the pins and then launch your binary. You could also place the commands in the /etc/rc.local startup script or the like, if you want.

Read carefully.

echo 5 > /sys/class/gpio/export
echo 65 > /sys/class/gpio/export
echo 105 > /sys/class/gpio/export

I do the same with Android in the init.{ro.hardware}.rc file:

write /sys/class/gpio/export 5
write /sys/class/gpio/export 65
write /sys/class/gpio/export 105

As for running those commands under Linux, I usually place the commands to export the pins into a shell script that also launches the application that uses the GPIOs. Since you have to be root to mmap() the pages with GPIO control registers, you can run the shell script via sudo and have the script export the pins and then launch your binary. You could also place the commands in the /etc/rc.local startup script or the like, if you want.

Read carefully.

you can run the shell script via sudo and have the script export the pins and then launch your binary
Either way, it is ambiguous. I read it as “you can run the shell script via sudo and have that same script export the pins and then have the same shell script launch your binary”. Anyway, my comment was to not run his app as root and that is still valid.

Very interesting. I can confirm this also works for me. C code that would formerly fail with a “Bus Error” accessing GPIO2 and GPIO3 via /dev/mem and mmap() has no problems if any GPIO is exported from that bank via SYSFS. It would seem that the driver is setting the CLKCTRL on the bank.

Thanks for this. Besides being a useful way of enabling the CLKCTRL it is also one of those tricky little gotchas to watch out for that could make code work just fine in a test env but fail seemingly randomly out in the world.

As mentioned above. Here is a summary of the issue and its resolution. I am posting it here so that anybody else that runs into this might be able to find it with a search.

BASIC PROBLEM: Using /dev/mem with a mmap() call to access the GPIOs in banks 2 and 3 fail with a bus error. Identical access to the GPIO’s in banks 0 and 1 works just fine.

SOLUTION: Access to the GPIO banks 2 and 3 fails because the clocks CM_PER_GPIO2_CLKCTRL and CM_PER_GPIO3_CLKCTRL are not enabled. These can readily be enabled by accessing memory location 0x44e00000 (see the TRM) and turning them on. If you do that, access to the GPIO2 and 3 at locations 0x481AC000 and 0x481AE000 will be successful.

EXPLANATION and EXAMPLES

Example C code for memory mapped access to the GPIOs is below – it assumes we wish to access GPIO_BANK_1

`
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0)
{
printf(“Could not open GPIO memory fd\n”);
return 0;
}

volatile ulong *gpio;
int gpioBank = 0x4804C000;
int sizeToFetch =0xfff;

gpio = (ulong*) mmap(NULL, sizeToFetch, PROT_READ | PROT_WRITE, MAP_SHARED, fd, gpioBank);
if (gpio == MAP_FAILED)
{
printf (“GPIO Mapping failed\n”);
close(fd);
return 0;
}
// offset 0 is the GPIO_REVISION field
// expected value is 0x50600801
int gpioRevision = gpio[0];
printf(“bank %04x, gpioRevision = %04x\n”, gpioBank, gpioRevision);

munmap((void*)gpio,sizeToFetch);
close(fd);

`

Accessing GPIO_BANK_2 or GPIO_BANK_2 will, in most circumstances, result in a failure with a “Bus error” message on the call to

int gpioRevision = gpio[0];

Everything else works fine, the /dev/mem file opens and the mmap() call is successful but access to any value in the mapped meory will fail. It seems that this is due to the clock which is used to shift the physical pin state into the GPIO_BANK bit is not enabled by default.

One way to get around this is to note that using SYSFS to export any GPIO in that bank (echo 105 > /sys/class/gpio/export) will enable the clock on that BANK and all subsequent accesses via mmap() will succeed.

The other way is to enable the clock yourself. This is done by writing to a memory location and hence requires yet another mmap() on /dev/mem. The relevant memory location is 0x44e00000 and the offset of interest is entitled CM_PER_GPIO?_CLKCTRL (where the ? Is 1, 2, or 3 for GPIO_BANK_1,2 or 3). If you wish to look this up in the TRM, search for CM_PER_GPIO2_CLKCTRL – it is in Section 8 – which deals with the Power Clock and Reset Managment. Here is some sample code that will properly set the CM_PER_GPIO2_CLKCTRL register.

`
uint32_t *clock_gpio2;
clock_gpio2 = (uint32_t *) mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x44E00000);
// 0xb0 is CM_PER_GPIO2_CLKCTRL as given in the TRM, use 0xb4 for GPIO_3 (see the TRM)
int offsetInMemory = 0xb0;
// get the value, we divide by 4 because it is a byte offset
int memValue = clock_gpio2[(offsetInMemory/4)];
// print it – it will probably be 0x030000 if the clock has never been enabled
printf(“Value = %04x\n”, memValue);
// now set it, this enables the memory
clock_gpio2[(offsetInMemory/4)] = 0x02;

`

Once the clock has been set as above, the previous C code will be able to access the GPIO_BANK via a statement like.

int gpioRevision = gpio[0];

On my system GPIO_BANK_1 was already enabled, but GPIO_BANK_2 and GPIO_BANK_3 needed to be enabled.

Pada Selasa, 28 Oktober 2014 01.39.00 UTC+8, Nic Cyn menulis:

I am trying to access, via mmap on /dev/mem, the GPIOs at addresses 0x44e07000, 0x4804c000, 0x481ac000 and 0x481ae000. These are GPIO banks 0,1,2 and 3 respectively.

Access is successful on GPIO bank 0 and 1 (0x44e07000 and 0x4804c000) but fails consistently with a "Bus Error" on GPIO bank 2 and 3 (0x481ac000 and 0x481ae000).

Does anybody have any idea why this might be happening? Sample C code which reproduces the error below - basically it is trying to read the GPIO_REVISION at offset 0 at the start of each GPIO bank. The expected value returned should be 0x50600801 - but a "Bus Error" is returned for GPIO banks 2 and 3.

This seems to happen on standard 3.8.13-bone47 on a BBB rev C right out of the CircuitCo box and also on an 3.14 ubuntu kernel.

I would really appreciate any insights you may have

// c code to test access to the /dev/mem file

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#define GPIO_BANK_0 0x44E07000
#define GPIO_BANK_1 0x4804C000
#define GPIO_BANK_2 0x481AC000
#define GPIO_BANK_3 0x481AE000

int gpio_mmap_test(int gpioBank, int sizeToFetch);

int main()
{
gpio_mmap_test(GPIO_BANK_0, 0xfff);
gpio_mmap_test(GPIO_BANK_1, 0xfff);
gpio_mmap_test(GPIO_BANK_2, 0xfff);
gpio_mmap_test(GPIO_BANK_3, 0xfff);
return 0;
}

int gpio_mmap_test(int gpioBank, int sizeToFetch)
{

int gpio_fd2 = open("/dev/mem", O_RDWR | O_SYNC);

if (gpio_fd2 < 0)
{
printf("Could not open GPIO memory fd\n");
return 0;
}

volatile ulong *gpio;

gpio = (ulong*) mmap(NULL, sizeToFetch, PROT_READ | PROT_WRITE, MAP_SHARED, gpio_fd2, gpioBank);
if (gpio == MAP_FAILED)
{
printf ("GPIO Mapping failed\n");
close(gpio_fd2);
return 0;
}

// offset 0 is the GPIO_REVISION field
// expected value is 0x50600801
int gpioRevision = gpio[0];

printf("bank %04x, gpioRevision = %04x\n", gpioBank, gpioRevision);

close(gpio_fd2);
}

World Bank