Search code examples
embedded-linuxmmapgpioopenwrtlow-level

How to use mmap for devmemon MT7620n


I`m trying to access the GPIOs of a MT7620n via register settings. So far I can access them by using /sys/class/gpio/... but that is not fast enough for me.

In the Programming guide of the MT7620 page 84 you can see that the GPIO base address is at 0x10000600 and the single registers have an offset of 4 Bytes.

MT7620 Programming Guide

Something like: devmem 0x10000600 from the shell works absolutely fine but I cannot access it from inside of a c Programm.

Here is my code:

#define GPIOCHIP_0_ADDDRESS 0x10000600 // base address
#define GPIO_BLOCK 4

volatile unsigned long *gpiochip_0_Address;
int gpioSetup()
{
int  m_mfd;
if ((m_mfd = open("/dev/mem", O_RDWR)) < 0)
{
    printf("ERROR open\n");
    return -1;
}
gpiochip_0_Address = (unsigned long*)mmap(NULL, GPIO_BLOCK, PROT_READ|PROT_WRITE, MAP_SHARED, m_mfd, GPIOCHIP_0_ADDDRESS);

close(m_mfd);

if(gpiochip_0_Address == MAP_FAILED)
{
    printf("mmap() failed at phsical address:%d %s\n", GPIOCHIP_0_ADDDRESS, strerror(errno));
    return -2;
}

return 0;
}

The Output I get is:

mmap() failed at phsical address:268436992 Invalid argument

What do I have to take care of? Do I have to make the memory accessable before? I´m running as root.

Thanks

EDIT

Peter Cordes is right, thank you so much.

Here is my final solution, if somebody finds a bug, please tell me ;)

#define GPIOCHIP_0_ADDDRESS 0x10000600 // base address

volatile unsigned long *gpiochip_0_Address;
int gpioSetup()
{
const size_t pagesize = sysconf(_SC_PAGE_SIZE);
unsigned long gpiochip_pageAddress = GPIOCHIP_0_ADDDRESS & ~(pagesize-1); //get the closest page-sized-address
const unsigned long gpiochip_0_offset = GPIOCHIP_0_ADDDRESS - gpiochip_pageAddress;  //calculate the offset between the physical address and the page-sized-address

int  m_mfd;
if ((m_mfd = open("/dev/mem", O_RDWR)) < 0)
{
    printf("ERROR open\n");
    return -1;
}
page_virtual_start_Address = (unsigned long*)mmap(NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, m_mfd, gpiochip_pageAddress);


close(m_mfd);

if(page_virtual_start_Address == MAP_FAILED)
{
    printf("ERROR mmap\n");
    printf("mmap() failed at phsical address:%d %d\n", GPIOCHIP_0_ADDDRESS, strerror(errno));
    return -2;
}

gpiochip_0_Address = page_virtual_start_Address + (gpiochip_0_offset/sizeof(long));


return 0;
}

Solution

  • mmap's file offset argument has to be page-aligned, and that's one of the documented reasons for mmap to fail with EINVAL.

    0x10000600 is not a multiple of 4k, or even 1k, so that's almost certainly your problem. I don't think any systems have pages as small as 512B.

    mmap a whole page that includes the address you want, and access the MMIO registers at an offset within that page.

    Either hard-code it, or maybe do something like GPIOCHIP_0_ADDDRESS & ~(page_size-1) to round down an address to a page-aligned boundary. You should be able to do something that gets the page size as a compile-time constant so it still compiles efficiently.