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).