Cannot control gpio via gpioset in Thor

Hi Nvidia Team,

I am using a Jetson AGX Thor module on a custom carrier board with JetPack 7.2. In the pinmux I have configured GPIO64 as a GPIO output; that pad is GPIO3_PU.01. I am trying to drive it with gpioset (including --mode=wait), but the pin value does not change.

Also output of “sudo cat /sys/kernel/debug/gpio | grep PU.01” shows 579 and same for PU.00 is 578 but in Jetson Thor Adaptation and Bring-Up — NVIDIA Jetson Linux Developer Guide mentions 356.

I also tried controlling it by creating a driver.

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>

/* Meta Information */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("DSC PU.01 GNU/Linux");
MODULE_DESCRIPTION("A simple gpio driver for enabling/disabling PU.01 UART S/W Select");

/* Variables for device and device class */
static dev_t my_device_nr;
static struct class *my_class;
static struct cdev my_device;

#define DRIVER_NAME "PU.01"
#define DRIVER_CLASS "GPIO579Class"

/**
 * @brief Read data out of the buffer
 */
static ssize_t driver_read(struct file *File, char *user_buffer, size_t count, loff_t *offs) {
	int to_copy, not_copied, delta;
	char tmp[3] = " \n";

	/* Get amount of data to copy */
	to_copy = min(count, sizeof(tmp));

	/* Read value of button */
	printk("Value of button: %d\n", gpio_get_value(579));
	tmp[0] = gpio_get_value(579) + '0';

	/* Copy data to user */
	not_copied = copy_to_user(user_buffer, &tmp, to_copy);

	/* Calculate data */
	delta = to_copy - not_copied;

	return delta;
}

/**
 * @brief Write data to buffer
 */
static ssize_t driver_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) {
	int to_copy, not_copied, delta;
	char value;

	/* Get amount of data to copy */
	to_copy = min(count, sizeof(value));

	/* Copy data to user */
	not_copied = copy_from_user(&value, user_buffer, to_copy);

	/* Setting the LED */
	switch(value) {
		case '0':
			gpio_set_value(579, 0);
			break;
		case '1':
			gpio_set_value(579, 1);
			break;
		default:
			printk("Invalid Input!\n");
			break;
	}

	/* Calculate data */
	delta = to_copy - not_copied;

	return delta;
}

/**
 * @brief This function is called, when the device file is opened
 */
static int driver_open(struct inode *device_file, struct file *instance) {
	printk("dev_nr - open was called!\n");
	return 0;
}

/**
 * @brief This function is called, when the device file is opened
 */
static int driver_close(struct inode *device_file, struct file *instance) {
	printk("dev_nr - close was called!\n");
	return 0;
}

static struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = driver_open,
	.release = driver_close,
	.read = driver_read,
	.write = driver_write
};

/**
 * @brief This function is called, when the module is loaded into the kernel
 */
static int __init ModuleInit(void) {
	printk("Hello, Kernel!\n");

	/* Allocate a device nr */
	if( alloc_chrdev_region(&my_device_nr, 0, 1, DRIVER_NAME) < 0) {
		printk("Device Nr. could not be allocated!\n");
		return -1;
	}
	printk("read_write - Device Nr. Major: %d, Minor: %d was registered!\n", my_device_nr >> 20, my_device_nr && 0xfffff);

	/* Create device class */
	if((my_class = class_create(DRIVER_CLASS)) == NULL) {
		printk("Device class can not be created!\n");
		goto ClassError;
	}

	/* create device file */
	if(device_create(my_class, NULL, my_device_nr, NULL, DRIVER_NAME) == NULL) {
		printk("Can not create device file!\n");
		goto FileError;
	}

	/* Initialize device file */
	cdev_init(&my_device, &fops);

	/* Regisering device to kernel */
	if(cdev_add(&my_device, my_device_nr, 1) == -1) {
		printk("Registering of device to kernel failed!\n");
		goto AddError;
	}

	/* GPIO 579 init */
	if(gpio_request(579, "UART5_SW_SEL")) {
		printk("Can not allocate GPIO 579\n");
		goto AddError;
	}

	/* Set GPIO 579 direction */
	if(gpio_direction_output(579, 0)) {
		printk("Can not set GPIO 579 to output!\n");
		goto Gpio579Error;
	}

	return 0;
Gpio579Error:
	gpio_free(579);
AddError:
	device_destroy(my_class, my_device_nr);
FileError:
	class_destroy(my_class);
ClassError:
	unregister_chrdev_region(my_device_nr, 1);
	return -1;
}

/**
 * @brief This function is called, when the module is removed from the kernel
 */
static void __exit ModuleExit(void) {
	gpio_set_value(579, 0);
	gpio_free(579);
	cdev_del(&my_device);
	device_destroy(my_class, my_device_nr);
	class_destroy(my_class);
	unregister_chrdev_region(my_device_nr, 1);
	printk("Goodbye, Kernel\n");
}

module_init(ModuleInit);
module_exit(ModuleExit);

and tried controlling it using:

sudo insmod /etc/dsc/gpio_driver_modules/PU01.ko
sudo chmod 666 /dev/PU.01
printf "1" | sudo tee /dev/PU.01 > /dev/null

But no change in state was observed.

Kindly guide me on how to control this gpio’s.

Regards,

Vrushti

Hi,

Could you verify whether you can control the GPIO using libgpiod?

For example, try the following command:

sudo gpioset "$(gpiofind PU.01)"=1

If that works, it would indicate that the GPIO and pinmux are configured correctly, and the issue is likely related to the driver implementation.

I would not recommend using a hard-coded global GPIO number (for example, 579). Instead, I recommend using the GPIO descriptor API, which is the approach used by Linux kernel drivers.

First, define a Device Tree node for your driver and describe the GPIO using a GPIO property:

```
uart_sw_ctrl: uart-sw {
    compatible = "dabhi,uart-sw";
    status = "okay";
    uart-sw-gpios = <&tegra_main_gpio
                     TEGRA264_MAIN_GPIO(U, 1) // This is the GPIO information
                     GPIO_ACTIVE_HIGH>;
 
};

Then, in your driver, obtain the GPIO descriptor from the Device Tree using devm_gpiod_get(...)
Once you have the GPIO descriptor, you can drive the pin using:

gpiod_set_value_cansleep(...);

For example:

#include <linux/module.h>
#include  <linux/platform_device.h>
#include  <linux/of.h>
#include  <linux/gpio/consumer.h>
#include  <linux/mutex.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("DSC");
MODULE_DESCRIPTION("GPIO driver");
static int gpio_value;
static struct gpio_desc *uart_sw_gpio;
static DEFINE_MUTEX(uart_sw_lock);

static int gpio_value_set(const char *val,
const struct kernel_param *kp)
{
int new_value;
int ret;

	ret = kstrtoint(val, 0, &new_value);
	if (ret)
		return ret;

	if (new_value != 0 && new_value != 1)
		return -EINVAL;

	mutex_lock(&uart_sw_lock);

	gpio_value = new_value;

	if (uart_sw_gpio)
		gpiod_set_value_cansleep(uart_sw_gpio, gpio_value);

	mutex_unlock(&uart_sw_lock);

	pr_info("uart_sw: gpio_value=%d\n", gpio_value);

	return 0;

}

static int gpio_value_get(char *buffer,
const struct kernel_param *kp)
{
	return sysfs_emit(buffer, "%d\n", gpio_value);
}

static const struct kernel_param_ops gpio_value_ops = {
	.set = gpio_value_set,
	.get = gpio_value_get,
};

module_param_cb(gpio_value, &gpio_value_ops, &gpio_value, 0644);
MODULE_PARM_DESC(gpio_value, "UART5 software select GPIO value (0 or 1)");

static int uart_sw_probe(struct platform_device *pdev)
{
	int ret;

	dev_info(&pdev->dev, "UART switch driver probe\n");


	uart_sw_gpio = devm_gpiod_get(&pdev->dev,
					  "uart-sw",
					  GPIOD_OUT_LOW);

	if (IS_ERR(uart_sw_gpio)) {
		ret = PTR_ERR(uart_sw_gpio);
		uart_sw_gpio = NULL;

		dev_err(&pdev->dev,
			"Failed to get uart-sw GPIO: %d\n",
			ret);

		return ret;
	}


	mutex_lock(&uart_sw_lock);

	gpiod_set_value_cansleep(uart_sw_gpio, gpio_value);

	mutex_unlock(&uart_sw_lock);

	dev_info(&pdev->dev,
		 "UART switch GPIO initialized to %d\n",
		 gpio_value);

	return 0;

}

static void uart_sw_remove(struct platform_device *pdev)
{

}

static const struct of_device_id uart_sw_of_match
 = {
	{
		.compatible = "dabhi,uart-sw",
	},
	{ }
};

MODULE_DEVICE_TABLE(of, uart_sw_of_match);

static struct platform_driver uart_sw_driver = {
	.probe = uart_sw_probe,
	.remove = uart_sw_remove,
	.driver = {
		.name = "uart-sw",
		.of_match_table = uart_sw_of_match,
	},
};

module_platform_driver(uart_sw_driver);

For convenience, this example exposes the GPIO value as a module parameter. This allows you to set the GPIO state when loading the module without modifying the driver:

sudo insmod gpio_driver_modules gpio_value=0
# or
sudo insmod gpio_driver_modules gpio_value=1

Manuel Leiva
Embedded SW Engineer at RidgeRun
Contact us: support@ridgerun.com
Developers wiki: https://developer.ridgerun.com
Website: www.ridgerun.com

Hi vrushti.dabhi,

How do you configure the default state for this GPIO/Output? Drive 0 or 1?

Could you try running the following command to control this pin?

# gpioset --mode=wait `gpiofind "PU.01"`=1
# gpioset --mode=wait `gpiofind "PU.01"`=0

Hi @KevinFFF , with default state set to Drive 1 i am able to set gpio value high, but using commands :

# gpioset --mode=wait `gpiofind "PU.01"`=1
# gpioset --mode=wait `gpiofind "PU.01"`=0

observing no change in state

Could you try configuring it as Drive 0? Does the pin state default to low?

Additionally, please verify whether any other driver is claiming this pin concurrently or if the custom board is affecting it..

Hi @KevinFFF , the default state for the pin is high. Also after setting drive 1 from pinmux i am able to change the state using gpioset commands.

Is is necessary to have default state set for a gpio/pin in pimux if we want to control it?

The default state Drive 0 or Drive 1 is required for the GPIO/Output.

Please refer to Table 2. GPIO Direction and Initial State Combination in Jetson AGX Series Module Pinmux Application Note for details.