GPIO Direct Register Access in Jetson TX2

Hi,

I am trying to control a GPIO pin using the Direct Register Access approach on the Jetson TX2, following what Snarky did with the Jetson Nano:
https://github.com/jwatte/jetson-gpio-example/blob/master/gpiomem.cpp

Using the GPIO registers information I found in the Nvidia Parker Series SoC TRM I wrote this:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/mman.h>

#define GPIO_392     0x02215000 // J.00

struct GPIO_mem {
    uint32_t ENABLE_CONFIG[4];
    uint32_t DEBOUNCE_THRESHOLD[4];
    uint32_t INPUT[4];
    uint32_t OUTPUT_CONTROL[4];
    uint32_t OUTPUT_VALUE[4];
    uint32_t INTERRUPT_CLEAR[4];
};

int main(void) {
    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd < 0) {
        perror("/dev/mem");
        fprintf(stderr, "Please run this program as root (for example with sudo)\n");
        exit(1);
    }

    uint32_t pagesize = getpagesize();
    uint32_t pagemask = pagesize - 1;
    void *base = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (GPIO_392 & ~pagemask));

    if (base == NULL) {
        perror("mmap()");
        exit(1);
    }
    
    GPIO_mem volatile *pin = (GPIO_mem volatile *)((char *)base + (GPIO_392 & pagemask));

    pin->OUTPUT_CONTROL[0] = 0x00000000; // Driven
    pin->ENABLE_CONFIG[0] = 0x00000003; // GPIO Enable, Out

    uint32_t val = 0x00000001;
    
    for(int i=0; i<10000; i++) {
        usleep(1000);
        val = val ^ 0x00000001;
        pin->OUTPUT_VALUE[0] = val;
    }

    munmap(base, pagesize);
    close(fd);

    return 0 ;
}

Just for testing purposes, I am trying to turn on and off the gpio392 (pin 12 of the J21 header) every 1000 microseconds. However, it is not working. I tried the same example using the sysfs approach and my logic analyzer shows the values correctly. As far as I understand, the gpio392 corresponds to the hardware pin J.00, which according to the TRM has the address 0x02215000, and that is the address I am trying to load in my test. What am I doing wrong?

Best regards

hello ajcalderont,

you might also refer to a python library that enables the use of Jetson’s GPIOs.
thanks

Hi JerryChang,

Thanks for the reference, but the Python GPIO library uses the sysfs approach. What I need is to control the GPIOs using direct register address.

Best regards

hello ajcalderont,

besides controlled via direct register address, how about using kernel APIs to control gpio pins.
for example,

<i>$l4t-r32.2/public_sources/kernel_src/kernel/kernel-4.9/arch/sh/include/asm/gpio.h</i>

static inline void gpio_set_value(unsigned gpio, int value){...}

Hi JerryChang,

That is an interesting approach, for sure I will include it in my analysis. What I want to do is to measure the timing overhead of the sysfs approach and the DRA approach in the Jetson TX2 and compare them. Now I guess I will also compare them with the kernel API approach you are suggesting. But to continue I need to solve my DRA problem.

Best regards

Hi,

I finally managed to make it work. I deleted the struct to directly modify the registers using offsets. Now I also load the address of the corresponding PADCTL to enable the GPIO:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/mman.h>

#define GPIO_392    0x02215000 // J.00 GPIO address
#define DAP1_SCLK   0x02431040 // Control pad register address

#define GPIO_ENABLE_CONFIG 0x00
#define GPIO_OUTPUT_CONTROL 0x0c
#define GPIO_OUTPUT_VALUE 0x10

int main(void) {
    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd < 0) {
        perror("/dev/mem");
        fprintf(stderr, "Please run this program as root (for example with sudo)\n");
        exit(1);
    }

    uint32_t pagesize = getpagesize();
    uint32_t pagemask = pagesize - 1;

    void *gpio_address = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (GPIO_392 & ~pagemask));
    void *pad_ctl_address = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (DAP1_SCLK & ~pagemask));

    if (gpio_address == NULL || pad_ctl_address == NULL) {
        perror("mmap()");
        exit(1);
    }
    
    volatile char *gpio_base = ((char *)gpio_address + (GPIO_392 & pagemask));
    volatile char *pad_ctl = ((char *)pad_ctl_address + (DAP1_SCLK & pagemask));

    *pad_ctl = 0x00000000; // Select GPIO

    *(gpio_base + GPIO_OUTPUT_CONTROL) = 0x00000000; // Driven
    *(gpio_base + GPIO_ENABLE_CONFIG) = 0x00000003; // GPIO Enable, Out

    uint32_t val = 0x00000001;
    volatile char *output_value = gpio_base + GPIO_OUTPUT_VALUE;
    
    for(int i=0; i<10000; i++) {
        usleep(1000);
        val = val ^ 0x00000001;
        *output_value = val;
    }

    *output_value = 0x00000000;

    munmap(gpio_address, pagesize);
    munmap(pad_ctl_address, pagesize);
    close(fd);

    return 0 ;
}

Best regards

Could you please share what frequency you are able to achieve with no usleep and best compiler optimization?

Hello ,

I couldn’t be a able to calculate address for GPIO_333. How can I find / calculate gpio address for Tx2 ?
My aim is to simulate echo 1 > /sys/class/gpio/gpio333/value with direct registry access way.

Hi ikoc,

Which pin is GPIO_333? I can’t find any reference to that GPIO number in https://www.jetsonhacks.com/nvidia-jetson-tx2-j21-header-pinout/

Hi Ajcalderont,

I am using tx2 with a custom board. We can open and connect to it with
echo 333 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio333/direction
echo 1 > /sys/class/gpio/gpio333/value

I need to find the address like you did in your code
#define GPIO_392 0x02215000 // J.00 GPIO address

but I am totally lost, for now I am opening /sys/class/gpio/gpio333/value as file and write 1 and 0 to it. But I have speed issues with that.

Does gpio 13_b meaningful?