jetson agx xavier gpio-user驱动

这篇博客详细介绍了如何在Linux系统中为Tegra平台实现用户空间GPIO驱动。内容涉及设备树配置、内核源码分析以及驱动程序的编写,包括GPIO方向、边沿触发、活动电平的读写操作。通过示例展示了如何创建设备节点并将其挂载到sysfs文件系统,以便用户空间应用进行交互。最后,提供了编译和加载驱动的步骤以及测试验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设备树

Linux_for_Tegra/source/public/hardware/nvidia/platform/t19x/galen/kernel-dts/tegra194-p2888-0001-p2822-0000.dts

#include "common/tegra194-p2888-0001-p2822-0000-common.dtsi"
#include "common/tegra194-p2822-camera-modules.dtsi"
#include "t19x-common-modules/tegra194-camera-plugin-manager.dtsi"
#include "user/user-gpio.dtsi"

Linux_for_Tegra/source/public/hardware/nvidia/platform/t19x/galen/kernel-dts/user/user-gpio.dtsi

#include <dt-bindings/gpio/tegra194-gpio.h>

/ {
        user-gpios {
                compatible = "user,user-init-gpio";
                status = "okay";
                
                out-gpios-num = <2>;
                out-1-gpios = <&tegra_main_gpio TEGRA194_MAIN_GPIO(N, 1) GPIO_ACTIVE_HIGH>;
                out-2-gpios = <&tegra_main_gpio TEGRA194_MAIN_GPIO(H, 7) GPIO_ACTIVE_LOW>;

                input-gpios-num = <1>;
                in-1-gpios = <&tegra_main_gpio TEGRA194_MAIN_GPIO(H, 0) GPIO_ACTIVE_HIGH>;
        };
};

驱动

Linux_for_Tegra/source/public/kernel/kernel-4.9/drivers/gpio/gpio-user.c

#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/kthread.h>

#include <dt-bindings/gpio/tegra194-gpio.h>

#define GPIOX_MAX_NUM 30

enum direction_type_t {
	GPIO_IN,
	GPIO_OUT,
	GPIO_IRQ
};

struct gpiox_device_t {
	int gpio;
	struct device * dev;

	int active_low;
	int direction;
	int edge;
	int value;
	int irq_num;
};

struct gpiox_class_t {
	struct class *gpio_class;
	struct gpiox_device_t gpio_devs[GPIOX_MAX_NUM];
	int gpio_cnt;
};


static ssize_t gpiox_active_low_show(struct device * dev, struct device_attribute * attr, char * buf)
{
	struct gpiox_device_t * xdev = dev_get_drvdata(dev);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "active_low")){
		if(xdev->active_low == 0)
			return strlcpy(buf, "0\n", 3);
		else
			return strlcpy(buf, "1\n", 3);
	}
	return strlcpy(buf, "0\n", 3);
}

static ssize_t gpiox_active_low_store(struct device * dev, struct device_attribute * attr, const char * buf, size_t count)
{
	struct gpiox_device_t * xdev = dev_get_drvdata(dev);
	unsigned long on = simple_strtoul(buf, NULL, 10);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "active_low")){
	}
	xdev->active_low = on ? 1 : 0;
	return count;
}

static ssize_t gpiox_direction_show(struct device * dev, struct device_attribute * attr, char * buf)
{
	struct gpiox_device_t * xdev = dev_get_drvdata(dev);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "direction")){
		switch(xdev->direction){
		case GPIO_IN:
			return strlcpy(buf, "in\n", 4);
		break;
		case GPIO_OUT:
			return strlcpy(buf, "out\n", 5);
		break;
		case GPIO_IRQ:
			return strlcpy(buf,"irq\n", 5);
		break;
		}
	}
	return strlcpy(buf, "none\n", 6);
}

static ssize_t gpiox_direction_store(struct device * dev, struct device_attribute * attr, const char * buf, size_t count)
{
	int direction = 0;
	struct gpiox_device_t * xdev = dev_get_drvdata(dev);
	unsigned long on = simple_strtoul(buf, NULL, 10);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "direction")){
		direction = (on==1)? GPIO_OUT :(on==0)? GPIO_IN:GPIO_IRQ; /* not used */
	}

	return 0;
}

static ssize_t gpiox_edge_show(struct device * dev, struct device_attribute * attr, char * buf)
{
	struct gpiox_device_t * xdev = dev_get_drvdata(dev);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "edge")){
		if(xdev->edge == 0)
			return strlcpy(buf, "0\n", 3);
		else
			return strlcpy(buf, "1\n", 3);
	}
	return strlcpy(buf, "0\n", 3);
}

static ssize_t gpiox_edge_store(struct device * dev, struct device_attribute * attr, const char * buf, size_t count)
{
	struct gpiox_device_t * xdev = dev_get_drvdata(dev);
	unsigned long on = simple_strtoul(buf, NULL, 10);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "edge")){
		if(on)
			gpio_direction_output(xdev->gpio, 1);
		else
			gpio_direction_output(xdev->gpio, 0);
	}
	xdev->edge = on ? 1 : 0;
	return count;
}


static ssize_t gpiox_value_show(struct device * dev, struct device_attribute * attr, char * buf)
{
	struct gpiox_device_t * xdev = dev_get_drvdata(dev);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "value")){
		switch(xdev->direction){
		case GPIO_IN:
			xdev->value = gpio_get_value(xdev->gpio);
		case GPIO_OUT:
			if(xdev->value == 0)
				return strlcpy(buf, "0\n", 3);
			else
				return strlcpy(buf, "1\n", 3);
		break;
		case GPIO_IRQ:
		break;
		}
	}
	return strlcpy(buf, "0\n", 3);
}

static ssize_t gpiox_value_store(struct device * dev, struct device_attribute * attr, const char * buf, size_t count)
{
	struct gpiox_device_t * xdev = dev_get_drvdata(dev);

	unsigned long on = simple_strtoul(buf, NULL, 10);
	if(xdev == NULL)
		return 0;

	if(!strcmp(attr->attr.name, "value")){
		if(on)
			gpio_direction_output(xdev->gpio, 1);
		else
			gpio_direction_output(xdev->gpio, 0);
	}
	xdev->value = on ? 1 : 0;
	return count;
}

static DEVICE_ATTR(value, 0664, gpiox_value_show, gpiox_value_store);
static DEVICE_ATTR(direction, 0664, gpiox_direction_show, gpiox_direction_store);
static DEVICE_ATTR(edge, 0664, gpiox_edge_show, gpiox_edge_store);
static DEVICE_ATTR(active_low, 0664, gpiox_active_low_show, gpiox_active_low_store);
static struct attribute * gpiox_attrs[] = {
	&dev_attr_value.attr,
	&dev_attr_direction.attr,
	&dev_attr_edge.attr,
	&dev_attr_active_low.attr,
	NULL
};


static const struct attribute_group gpiox_group = {
	.attrs = gpiox_attrs,
};

static int init_device_data(struct gpiox_device_t *dev,int gpio,int active,int direction,int edge,int value)
{
	int irq_num;

	if(dev == NULL)
		return -1;

	value =(value==0)?1:0;
	dev->gpio = gpio;
	dev->active_low = active;
	dev->direction = direction;
	dev->edge = edge;
	dev->value = value;

	switch(direction){ 
	case GPIO_IN:
		printk("set gpio%d direction input \n",dev->gpio);
		gpio_direction_input(dev->gpio);
	break;
	case GPIO_OUT:
		printk("set gpio%d direction output[%d] \n",dev->gpio,value);
		gpio_direction_output(dev->gpio,value);
	break;
	case GPIO_IRQ:
		irq_num = gpio_to_irq(dev->gpio);
		dev->irq_num = (irq_num>0)?irq_num:-1;
	break;
	}

	return 0;
}

static int create_device_nodes(struct gpiox_class_t *cls,int gpio_num,enum direction_type_t direction,int flag)
{
	int ret = 0;
	char name[10];
	if(cls == NULL)
		return -1;
	if(cls->gpio_cnt>=GPIOX_MAX_NUM)	
		return -2;
	
	memset(name,0,sizeof(name));
	sprintf(name,"gpio%d",gpio_num);
	cls->gpio_devs[cls->gpio_cnt].dev = device_create(cls->gpio_class, NULL,MKDEV(0, cls->gpio_cnt), NULL, "%s", name);
	ret = sysfs_create_group(&cls->gpio_devs[cls->gpio_cnt].dev->kobj,&gpiox_group);

	init_device_data(&cls->gpio_devs[cls->gpio_cnt],gpio_num,1,direction,0,flag);
	
	dev_set_drvdata(cls->gpio_devs[cls->gpio_cnt].dev, &cls->gpio_devs[cls->gpio_cnt]);

	cls->gpio_cnt++;

	return 0;
}

static int user_gpio_probe(struct platform_device * pdev)
{
	struct device_node * node = pdev->dev.of_node;
	struct gpiox_class_t * gpiox_class;
	enum of_gpio_flags flags;
	int gpio;
	int ret,i;
	int cnt = 0;
	char gpio_name[32];

	if(!node)
		return -ENODEV;

	gpiox_class = kzalloc(sizeof(struct gpiox_class_t), GFP_KERNEL);
	if (!gpiox_class){
		printk("%s -ENOMEM\n",__func__);
		return -ENOMEM;
	}
	gpiox_class->gpio_cnt = 0;
	gpiox_class->gpio_class = class_create(THIS_MODULE, "gpio_usr");
	
	ret = of_property_read_u32(node, "out-gpios-num", &cnt);
	if (ret || !cnt) {
		pr_err("no gpio\n");
		goto INIT_ERR_FREE;
	}
	printk("get out-gpios-num:%d\n",cnt);
  
	for (i = 0; i < cnt; i++) {
		sprintf(gpio_name, "out-%d-gpios", i + 1);
		//gpio = of_get_named_gpio(node, gpio_name,0);
		gpio = of_get_named_gpio_flags(node, gpio_name,0,&flags);

		if (gpio_request(gpio, NULL)) {
			pr_err("out-%d-gpios(%d) gpio_request fail\n",i + 1,gpio);
			continue;
		}
		printk("out-%d-gpios(%d) gpio_is_valid\n", i + 1, gpio);
		create_device_nodes(gpiox_class,gpio,GPIO_OUT,flags);
	}
	
	ret = of_property_read_u32(node, "input-gpios-num", &cnt);
	if (ret || !cnt) {
		pr_err("no in gpio\n");
		cnt = 0;
	}
	printk("get input-gpios-num:%d\n",cnt);

	for (i = 0; i < cnt; i++) {
		sprintf(gpio_name, "in-%d-gpios", i + 1);
		gpio = of_get_named_gpio(node, gpio_name,0);

		if (gpio_request(gpio, NULL)) {
			pr_err("in-%d-gpios(%d) gpio_request fail\n",i + 1,gpio);
			continue;
		}
		printk("in-%d-gpios(%d) gpio_is_valid\n", i + 1, gpio);
		create_device_nodes(gpiox_class,gpio,GPIO_IN,flags);
	}

	dev_set_drvdata(&pdev->dev, gpiox_class);
	pr_info("gpio_init finish\n");
	return 0;

INIT_ERR_FREE:
	pr_err("gpio_init err\n");
	kfree(gpiox_class);
	return -1;
}

static int user_gpio_remove(struct platform_device *pdev)
{
	int i = 0;
	struct gpiox_class_t * xdev = dev_get_drvdata(&pdev->dev);
	if(xdev != NULL){
		for(i=0;i<xdev->gpio_cnt;i++){
	 		gpio_free(xdev->gpio_devs[i].gpio);
			printk("gpio%d free %d\n",i,xdev->gpio_devs[i].gpio);
			sysfs_remove_group(&xdev->gpio_devs[i].dev->kobj, &gpiox_group);
			
			device_destroy(xdev->gpio_class,MKDEV(0, i));
		}
		class_destroy(xdev->gpio_class);
	}
	return 0;
}

#ifdef CONFIG_PM
static int user_gpio_suspend(struct device *dev)
{
	return 0;
}

static int user_gpio_resume(struct device *dev)
{
	return 0;
}
#else
#define user_gpio_suspend NULL
#define user_gpio_resume NULL
#endif

static const struct dev_pm_ops user_gpio_pm_ops = {
	.suspend = user_gpio_suspend,
	.resume = user_gpio_resume,
};

static struct of_device_id user_gpio_of_match[] = {
	{ .compatible = "user,user-init-gpio" },
	{},
};
MODULE_DEVICE_TABLE(of, user_gpio_of_match);

static struct platform_driver user_gpio_driver = {
	.driver		= {
		.name	= "user-gpio",
		.owner	= THIS_MODULE,
		.pm	= &user_gpio_pm_ops,
		.of_match_table	= of_match_ptr(user_gpio_of_match),
	},
	.probe		= user_gpio_probe,
	.remove		= user_gpio_remove,
};
module_platform_driver(user_gpio_driver);

MODULE_DESCRIPTION("user gpio driver");
MODULE_AUTHOR("zhengweiqing, 1548889230@qq.com");
MODULE_LICENSE("GPL");

Makefile

Linux_for_Tegra/source/public/kernel/kernel-4.9/drivers/gpio/Makefile
最后添加:

obj-$(CONFIG_GPIO_USER)    += gpio-user.o

Kconfig

Linux_for_Tegra/source/public/kernel/kernel-4.9/drivers/gpio/Kconfig
增加GPIO_USER部分:

config GPIO_LOONGSON1
        tristate "Loongson1 GPIO support"
        depends on MACH_LOONGSON32
        select GPIO_GENERIC
        help
          Say Y or M here to support GPIO on Loongson1 SoCs.

config GPIO_USER
        tristate "USER GPIO support"
        depends on GPIOLIB
        default m
        help
          Say Y or M here to support USER GPIO .

endmenu

menu "Port-mapped I/O GPIO drivers"

tegra_defconfig

Linux_for_Tegra/source/public/kernel/kernel-4.9/arch/arm64/configs/tegra_defconfig
增加

CONFIG_GPIO_USER=m

编译

cd  Linux_for_Tegra/source/public/kernel/kernel-4.9
./build.sh

build.sh内容:

export CROSS_COMPILE=aarch64-linux-gnu-
make ARCH=arm64 tegra_defconfig
make ARCH=arm64 -j12

#TOP_PATH=/home/z/nvidia/Linux_for_Tegra
TOP_PATH=`pwd`/../../../../
cp arch/arm64/boot/Image $TOP_PATH/kernel/Image
cp arch/arm64/boot/dts/* $TOP_PATH/kernel/dtb/

sudo make ARCH=arm64 modules_install INSTALL_MOD_PATH=$TOP_PATH/rootfs/

更新设备树以及驱动上传

agx进入Recovery模式后,执行

cd  Linux_for_Tegra

./flash.sh -k kernel-dtb jetson-agx-xavier-devkit mmcblk0p1

更新完成后,重启板子,将生成驱动文件 gpio-user.ko放到板子中加载。

测试

sudo insmod gpio-user.ko 

ls /sys/class/gpio_usr/
gpio344  gpio351  gpio393

~$ cat /sys/class/gpio_usr/gpio351/direction 
out
~$ cat /sys/class/gpio_usr/gpio393/direction 
out
~$ cat /sys/class/gpio_usr/gpio344/direction 
in

~$ cat /sys/class/gpio_usr/gpio344/value
0
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值