PB thru PH series gpios control begins at memory address 0x01C20800
PL series gpios control begins at memory address 0x01F02C00
These are the equivalents of the PERI_BASE address seen with Raspberry Pis, and there is a porting effort of the Pi-Gpio library, where the programming side is mapping to the PI-2-Bus connectors as similar as possible to what is happening on the Raspberry Pi.
The following example program shows how to flash a LED connected to port PC4 (pin P32)
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define GPIO_BASE_ADDR_PB 0x01c20800
#define GPIO_BASE_ADDR_PL 0x01f02c00
int main()
{
int fd;
void *p_mem;
volatile uint32_t *gpio; /* The registers in gpio-space are 32-bits wide. */
int j;
uint64_t addr_offset;
uint64_t addr_start; /* page-aligned start-address of mmapped space. */
uint64_t page_size; /* Page-size from system, it reports 0x1000 here. */
uint64_t page_mask; /* Mask to separate out page-alignment bits */
uint64_t port_io_base; /* input GPIOs base-address, doesn't have to be page-aligned */
uint64_t offset;
port_io_base = GPIO_BASE_ADDR_PB; /* For example for ports PB-PH */
page_size = sysconf(_SC_PAGESIZE); /* 0x1000 */
page_mask = (~(page_size-1)); /* 0xFFFFFFFFFFFFF000 */
addr_start = port_io_base & page_mask; /* gives the page-aligned start address 0x01c20000, last 24 bits are 0 */
addr_offset = port_io_base & ~page_mask; /* mask with 0FFF so we get the 0800 offset into the page */
fd = open("/dev/mem", O_RDWR | O_SYNC);
if(fd == -1)
{
/* Very likely if you forget to run this as root ... */
perror("/dev/mem");
return(0);
}
p_mem = mmap(NULL, page_size * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr_start);
if(p_mem == NULL)
{
perror("mmap");
close(fd);
return(0);
}
close(fd);
offset = addr_offset + 0x48; /* Will aim at the first of the PC control registers, which controls PC7-PC0 */
/* Page 377 in the manual */
gpio = (volatile uint32_t *) ( (char *) (p_mem) + offset);
printf("GPIOs at %p\n", gpio);
*(gpio) &= ~(7 << 16); /* Set SELECT to 000 for enable or input */
*(gpio) |= (1 << 16); /* Set SELECT to 001 for output */
offset = addr_offset + 0x58; /* Will aim at the the PC data register, which controls state of PC18-PC0 wires */
/* Page 380 in the manual. */
gpio = (volatile uint32_t *) ( (char *) (p_mem) + offset);
printf("GPIOs at %p\n", gpio);
/* flash on and off 20 times */
for(j = 0; j<20; j++)
{
*(gpio) &= ~(0x10); /* Set bit 4 for PC4 to 0 */
usleep(500000);
*(gpio) |= (0x10); /* Set bit 4 for PC4 to 1 */
usleep(500000);
}
munmap(p_mem, page_size*2);
return(0);
}
The GPIO Control registers each consist of 8 3-bit values for each of the gpio lines, which gives a function-number for that gpio line. Function number 0 (000) means this is a general-purpose input, function number 1 (001) means it is a general-purpose output and function number 7 (111) means that this line is disabled. Function numbers 2 through 6 refer to various special functions, which will be different for each gpio line. Many of these are marked as reserved. The info on which are which is in the Allwinner_A64_User_Manual_V1.0.pdf sections 3.21 and 3.22 (starting at page 376).