How to calculate MMIO GPIO addresses for devmem2/mmap

0

I am trying to access some MMIO GPIO addresses for LED control and battery monitoring. But I am not sure how to calculate the right addresses.

The documentation states the following:

COMMUINTY_BASE = Bus 0 Device 0x0d Function 0 Reg. 0x10
GPIO North Community PORT ID = 0xC5
GPIO North West Community PORT ID = 0xC4
GPIO_PADBAR = COMMUNITY_BASE + PORT ID << 16 + 0x500
GPO Reg = GPIO_PADBAR + GPIO_Offset Bit [0] (LEDs)
GPI Reg = GPIO_PADBAR + GPIO_Offset Bit [1] (Battery)

Example: Reg LED_USER1_RD_N_P1V8A = 0xD0C506A0

Documentation

My question here is what is the difference between the North and North West Community Port ID? When do I need to use which? I am also failing to come up with the example address when following the documented formula.

I wrote the following c program to play around with the calculation:

int main(void) {
    u_int32_t COMMUINTY_BASE = 0x10;
    u_int32_t GPIO_NORTH_PORT = 0xC5;
    u_int32_t GPIO_WEST_PORT = 0xC4;

    u_int32_t GPIO_PADBAR =  (COMMUINTY_BASE + GPIO_NORTH_PORT) << 16 + 0x500;
    u_int32_t GPIO_REG = GPIO_PADBAR + 0x01;

    printf("0x%04x\n", GPIO_REG);
}

But the output is 0xd50001instead of 0xD0C506A0.

If I try to read the example address using devmem2 it returns the register with all bits set to one.

sudo devmem2 0xD0C506A0 Result read: 0xFFFFFFFF

Any help would be greatly appreciated.

c
embedded
intel
mmap
gpio
asked on Stack Overflow Aug 24, 2019 by c3ntry • edited Aug 24, 2019 by c3ntry

1 Answer

1

I don't understand how they got 0xD0C506A0 exactly, but only to the extent I don't understand why it's in the address starting with 0xD0000000 to start with.

You have an error in how you're calculating everything else. The << operator has higher precedence than +, so the equation should be parenthesized as COMMUNITY_BASE + (PORT ID << 16) + 0x500. Calculating PORT_ID << 16 for the port in question yields COMMUNITY_BASE + 0xC50000 + 0x500 = COMMUNITY_BASE + 0xC50500. To that you add the red user 1 LED offset of 0x1A0 to get COMMUNITY_BASE + 0xC506A0.

I mostly do ARM architectures at this level, so I don't understand how the device 0x0D information gets turned into COMMUNITY_BASE being 0xD0000000, but there you have it.

If you look at the documentation you'll also see that the address is write-only ("output"), which explains why you're unable to read from the example address. If you're unfamiliar with using write-only GPIO registers, they aren't that uncommon. The usual approach is to either just not read them, or to maintain an in-memory copy of what you think they SHOULD be, then abstract the object so that you "read" from your private value of the register and "write" to the actual GPIO pin and update your private value.

answered on Stack Overflow Sep 1, 2019 by Julie in Austin

User contributions licensed under CC BY-SA 3.0