Cannot access GPIO registers on Jetson Nano

I am creating a kernel module for controlling the GPIOs for learning purpose. I have written the code such that if anyone sends “4,1” to /proc/lll-gpio, pin 37 should turn on.

I am developing a kernel module to control GPIOs for educational purposes. My module is designed so that sending the string “4,1” to **/proc/lll-gpio** should turn on GPIO pin 37. As I don’t have any previous experience writing kernel modules and accessing registers, I decided to read the values from the register first to verify that I am indeed accessing the correct the memory locations.

To do this, I used the Jetson.GPIO Python library to toggle pin 37 and then checked the register values using my kernel module. My goal was to ensure that my kernel module was correctly accessing the relevant registers by observing changes in register values corresponding to GPIO operation.

However, I am encountering an issue: toggling GPIO pin 37 using the Jetson.GPIO library does not seem to produce any change in the register values read by my kernel module. GPIO pin 37 is indeed toggling but the values read by my kernel module are having no change.

Could let me know what I am doing wrong here.

#include <asm/io.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#define LLL_MAX_USER_SIZE 1024

#define GPIO_ADDR                0x6000d000
#define GPIO_CONTROLLER_1_OFFSET 0x0
#define GPIO_CONTROLLER_2_OFFSET 0x100
#define GPIO_CONTROLLER_3_OFFSET 0x200
#define GPIO_CONTROLLER_4_OFFSET 0x300
#define GPIO_CONTROLLER_5_OFFSET 0x400
#define GPIO_CONTROLLER_6_OFFSET 0x500
#define GPIO_CONTROLLER_7_OFFSET 0x600

#define GPIO_PORT_1_OFFSET 0x00
#define GPIO_PORT_2_OFFSET 0x04
#define GPIO_PORT_3_OFFSET 0x08
#define GPIO_PORT_4_OFFSET 0x0C

#define GPIO_REG_GPIO_CNF      0x00
#define GPIO_REG_GPIO_OE       0x10
#define GPIO_REG_GPIO_OUT      0x20
#define GPIO_REG_GPIO_IN       0x30
#define GPIO_REG_GPIO_INT_STAT 0x40
#define GPIO_REG_GPIO_ENB      0x50
#define GPIO_REG_GPIO_LVL      0x60
#define GPIO_REG_GPIO_CLR      0x70

static unsigned int *gpio_registers    = NULL;
static struct proc_dir_entry *lll_proc = NULL;
static char data_buffer[LLL_MAX_USER_SIZE];

ssize_t lll_read(struct file *file, char __user *user, size_t size, loff_t *off);
ssize_t lll_write(struct file *file, const char __user *user, size_t size, loff_t *off);

static void gpio_on(unsigned int pin) {
    volatile unsigned int *GPIO_CNF_REG = gpio_registers + GPIO_CONTROLLER_1_OFFSET + GPIO_PORT_2_OFFSET +
                                          GPIO_REG_GPIO_CNF;
    volatile unsigned int *GPIO_OE_REG = gpio_registers + GPIO_CONTROLLER_1_OFFSET + GPIO_PORT_2_OFFSET +
                                         GPIO_REG_GPIO_OE;
    volatile unsigned int *GPIO_ENB_REG = gpio_registers + GPIO_CONTROLLER_1_OFFSET + GPIO_PORT_2_OFFSET +
                                          GPIO_REG_GPIO_ENB;

    volatile unsigned int *GPIO_IN_REG = gpio_registers + GPIO_CONTROLLER_1_OFFSET + GPIO_PORT_2_OFFSET +
                                          GPIO_REG_GPIO_IN;
    // *GPIO_CNF_REG              = *GPIO_CNF_REG | 1 << 4;
    // *GPIO_ENB_REG              = *GPIO_ENB_REG | 1 << 4;
    // *GPIO_OE_REG               = *GPIO_OE_REG | 1 << 4;
    printk("GPIO CNG addr : %X\n", *GPIO_CNF_REG);
    printk("GPIO OE addr : %X\n", *GPIO_OE_REG);
    printk("GPIO ENB addr : %X\n", *GPIO_ENB_REG);
    printk("GPIO IN addr : %X\n", *GPIO_IN_REG );
}

static void gpio_off(unsigned int pin) {
}

ssize_t lll_read(struct file *file, char __user *user, size_t size, loff_t *off) {
    int ret = 0;
    ret     = copy_to_user(user, "Hello!\n", 7);
    return 7;
}

ssize_t lll_write(struct file *file, const char __user *user, size_t size, loff_t *off) {
    int ret            = 0;
    unsigned int pin   = UINT_MAX;
    unsigned int value = UINT_MAX;

    memset(data_buffer, 0x0, sizeof(data_buffer));

    if (size > LLL_MAX_USER_SIZE)
        size = LLL_MAX_USER_SIZE;

    ret = copy_from_user(data_buffer, user, size);

    printk("You said '%s' !", data_buffer);

    if (sscanf(data_buffer, "%d,%d", &pin, &value) != 2) {
        printk("Improper format\n");
    }
    printk("You said pin %d value %d!\n", pin, value);
    if (pin > 0 && pin < 8) {
        gpio_on(pin);
    }

    return size;
}

static const struct file_operations lll_proc_fops = {
    .read  = lll_read,
    .write = lll_write,
};

static int __init gpio_driver_init(void) {
    printk("Welcome to my driver!\n");

    gpio_registers = (int *)ioremap(GPIO_ADDR, 1024);

    if (gpio_registers == NULL) {
        printk("Failed to initialize GPIO");
        return -1;
    }
    printk("Successfully GPIO");
    // Create proc file
    lll_proc = proc_create("lll-gpio", 0666, NULL, &lll_proc_fops);

    if (lll_proc == NULL)
        return -1;
    return 0;
}

static void __exit gpio_driver_exit(void) {
    printk("Leaving to my driver!\n");
    iounmap(gpio_registers);
    proc_remove(lll_proc);
    return;
}

module_init(gpio_driver_init);
module_exit(gpio_driver_exit);

MODULE_LICENSE("GPL");

Read value as seen in dmesg

[  +0.000007] GPIO CNG addr : 40
[  +0.000005] GPIO OE addr : 0
[  +0.000005] GPIO ENB addr : 0
[  +0.000004] GPIO IN addr : 0

hello 283982,

please also share your L4T version, you may check release tag for confirmation, $ cat /etc/nv_tegra_release
may I also confirm which GPIO pin you would like to controlled? you may also dig into pinmux cfg for the pin address.
here’s Xavier’s pinmux cfg as an example, $OUT/Linux_for_Tegra/bootloader/tegra19x-mb1-pinmux-p2888-0000-a04-p2822-0000-b01.cfg

L4T version

R32 (release), REVISION: 7.5, GCID: 36557527, BOARD: t210ref, EABI: aarch64

I am trying to control pin 37 on Jetson Nano. My goal is to actually control any GPIO based on the value that I write to the proc file. For eg: If I write “4,1” to the file, pin 4 should be high and it is “4,0”, pin 4 should be low.

hello 283982,

pin-37 it’s PB.04 according to 40-Pin Header (J6), right?
you may see-also Accessing GPIOs via “gpio” Device Labels for controlling the pin directly.

Yes it is PB.04. According to the Tegra X1 Technical reference manual, I should be able to access the GPIO_CNF register using 0x6000d000 + 0x04 address and the GPIO_OE register by using 0x6000d000 + 0x014.

So when I set the pin HIGH using Jetson.GPIO in a python, my kernel module should be able the read GPIO_OE register and let me know that the 4th bit is HIGH. Right ?

that’s correct.
BTW, I don’t have Nano pinmux cfg for checking at the moment, please see-also Topic 220249 for reference.

After looking through the internet, I found out that I should have first called request_mem_region(). So I updated the code, ran it and found out request_mem_region fails which indicates that something else is already accessing those physical address.

As you suggest I refer the Topic 220249. I ran the following command

sudo devmem2 0x6000D004 w 0x10

And I got the following error

/dev/mem opened.
Memory mapped at address 0x7fb219400.
Bus error