【嵌入式Linux】Jetson nano GPIO应用 | 驱动开发 | 官方gpiolib、设备树与chip_driver

news2024/9/26 3:23:28

GPIO子系统

image-20230415163259138

0.暴露给应用层

应用

$ echo 79 > /sys/class/gpio/export   //导出79号gpio 引脚,使得可在应用层访问
$ echo out > /sys/class/gpio/gpio79/direction  //设置 为输出
$ echo 1 > /sys/class/gpio/gpio79/value //输出高电平 开灯
$ echo 0 > /sys/class/gpio/gpio79/value  //输出低电平, 关灯	
$ cat /sys/kernel/debug/gpio  //查询gpio状态(问题:发现找不到gpio文件)
$ echo 79 > unexport //取消导出(发现gpio79消失了)

解决调试目录为空的问题

原因 //debug需要的文件系统 debugfs没有挂载/etc/fstab	 的后面添加一行
debugfs     /sys/kernel/debug   debugfs  defaults         0       0

调试信息

$ cat /sys/kernel/debug/gpio  //查看gpio 当前配置情况(驱动暴露的调试信息)
$ cat  /sys/kernel/debug/tegra_gpio  //查看GPIO 寄存器内容(和芯片手册进行对应)
$ cat  /sys/kernel/debug/tegra_pinctrl_reg   //查看 pinctrl 寄存器内容	

1.最简读写文件(在/SYS下)

设备树

   sys_rw_led{  //对应生成 /sys/devices/sys_rw_led/led_gpio
        compatible = "sys_rw_led";
        led_gpio  = <&gpio TEGRA_GPIO(J,7) GPIO_ACTIVE_HIGH>;
    };

验证测试

$ cd sys_rw_led
$ make
$ cp led.ko /nfs/rootfs

$ cd ~/kernel-4.9
$ make dtbs
$ cp arch/arm64/boot/dts/tegra210-p3448-0000-p3449-0000-b00.dtb   /tftpboot/	

重启板子
$ insmod led.ko
$ cd /sys/devices/sys_rw_led
$ echo 0 > led_gpio  //关灯
$ echo 1 > led_gpio  //亮灯

编译文件

//Makefile
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= ~/kernel-4.9 
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules* a.out *.bak

else
    obj-m := led.o
endif	

驱动

  • of_get_named_gpio_flags//获取设备树节点的属性gpio_is_valid//判断是否合法devm_gpio_request//申请使用gpio,并调用设置pinctrl

  • device_create_file //根据设备树节点属性,创建相应的属性文件 /sys/devices/sys_rw_led/led_gpio

    • static struct device_attribute dev_attr_file //属性文件的描述
//led.c
#include <linux/init.h>                        
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>

#include <linux/of_gpio.h>
#include <linux/gpio.h>

#include <linux/platform_device.h>
#include <asm/io.h>


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

#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#endif

int  led_gpio;
static ssize_t led_store(struct device *dev, struct device_attribute *attr,
              const char *buf, size_t count) {
              
    //写文件,控制 gpio 输出 ( echo 1 >   led_gpio)                
    if (buf[0] == '0') {
        gpio_direction_output(led_gpio, 0);
    }else if (buf[0] == '1') {
        gpio_direction_output(led_gpio, 1);
    }    

    printk(KERN_ERR "led_gpio_store %c \n",buf[0]);   
    return count;
}

ssize_t led_show(struct device *dev, struct device_attribute *attr,char *buf){  
	printk("led_show go \n");	
 	return 0;
}
//属性文件的描述
static struct device_attribute dev_attr_file = {    
    .attr = {        
       .name = "led_gpio",        
       .mode = (S_IRUGO | S_IWUSR)  
    },   
    .store = led_store, //echo 1> 就调用到这里了
    .show = led_show,  //cat led_gpio时调用,如果无需读的功能,可设为NULL,  且删除前面的S_IRUGO
};


int leds_probe(struct platform_device *pdev)
{
   int  ret = 0;
	
   enum of_gpio_flags flags;

  //获取设备树节点的属性 "led_gpio"
   led_gpio = of_get_named_gpio_flags(pdev->dev.of_node, "led_gpio", 0, &flags);
   if (gpio_is_valid(led_gpio)) //判断是否合法
   {
      ret = devm_gpio_request(&pdev->dev,led_gpio, "led_gpio"); //申请使用gpio(如果被占用,将申请失败)
      if (ret) {
	  printk("Failed to get led_gpio gpio.\n");
	  return -1;
       } 
   }

   //根据设备树节点属性,创建相应的属性文件 /sys/devices/sys_rw_led/led_gpio
   device_create_file(&pdev->dev, &dev_attr_file);  // /device_create_file 里面是调用了 sysfs_create_file
   printk("leds_probe 1 ok\n");
   return 0;
}

int leds_remove(struct platform_device *pdev)
{   
    device_remove_file(&pdev->dev, &dev_attr_file);
    printk("leds_remove  ok\n");    
    return 0;
}

static const struct of_device_id of_led_match[] = {
    { .compatible = "sys_rw_led", },
    {},
};

MODULE_DEVICE_TABLE(of, of_led_match);

struct platform_driver leds_drv = {
   .driver = {
      .owner = THIS_MODULE,
      .name = "sys_rw_led driver" ,
      .of_match_table = of_led_match,
   },
   .probe = leds_probe,
   .remove = leds_remove,    
};
module_platform_driver(leds_drv);
MODULE_LICENSE("GPL");

2.读写多个属性文件(在SYS下)

设备树

sys_rw_gpio{
    compatible = "bbcen,sys_rw_gpio";
    led_gpio  = <&gpio TEGRA_GPIO(J,7) GPIO_ACTIVE_HIGH>;     //40pin 丝印 12
    smoke_sensor_gpio = <&gpio TEGRA_GPIO(S,5) GPIO_ACTIVE_HIGH>; //40pin 丝印 29
};

验证测试

$ cd sys_read_write
$ make
$ cp gpio.ko /nfs/rootfs

$ cd ~/kernel-4.9
$ make dtbs
$ cp arch/arm64/boot/dts/tegra210-p3448-0000-p3449-0000-b00.dtb   /tftpboot/	

重启板子
$ insmod gpio.ko
$ cd /sys/devices/sys_rw_gpio
$ echo 0 > led_gpio  //关灯
$ echo 1 > led_gpio  //亮灯	
$ cat smoke_sensor_gpio  //读管脚输入电平,默认读到0,用杜邦线,把它接到前面亮灯的管脚时,能成功读到1

编译文件

ifeq ($(KERNELRELEASE),)
KERNELDIR ?= ~/kernel-4.9 
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules* a.out *.bak

else
    obj-m := gpio.o
endif	

驱动

  • 定义一个结构体指针,分配空间,指定内容

  • device_attribute gpio_attr[]

  • sysfs_create_file //for循环读取,和上面的数组配合实现读多个属性

#include <linux/init.h>                        
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>

#include <linux/delay.h>
#include <linux/err.h>

#include <linux/of_gpio.h>
#include <linux/gpio.h>

#include <linux/platform_device.h>
#include <asm/irq.h>
#include <asm/io.h>

#include <linux/delay.h>

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

#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#endif

struct gpio_dev{
     struct platform_device *pdev;
     int led_gpio;        
     int led_gpio_direction;	 
     int smoke_sensor_gpio;
};

static struct gpio_dev *gpio_dev = NULL;

static ssize_t led_gpio_store(struct device *dev, struct device_attribute *attr,
              const char *buf, size_t count) {
              
    //写文件,控制 gpio 输出 ( echo 1 >   led_gpio)        
    struct gpio_dev *pdata = gpio_dev;            
    if (buf[0] == '0') {
        pdata->led_gpio_direction = 0;
        gpio_direction_output(pdata->led_gpio, pdata->led_gpio_direction);
    }else if (buf[0] == '1') {
        pdata->led_gpio_direction = 1;
        gpio_direction_output(pdata->led_gpio, pdata->led_gpio_direction);
    }    

    printk(KERN_ERR "led_gpio_store %d \n",pdata->led_gpio_direction);   
    return count;
}

static ssize_t smoke_sensor_gpio_show(struct device *dev, struct device_attribute *attr, char *buf) {
    struct gpio_dev *pdata = gpio_dev; 
    int gpio_value = 0;

    gpio_value = gpio_get_value(pdata->smoke_sensor_gpio); //获取 gpio的输入值(cat  smoke_sensor_gpio 时会触发)
    
    return snprintf(buf, PAGE_SIZE, "%d\n",gpio_value);
}



static struct device_attribute gpio_attr[] = {  
    __ATTR(led_gpio, 0664, NULL, led_gpio_store), //写:关联属性文件的写回调函数(*store)  echo时触发
    __ATTR(smoke_sensor_gpio, 0664, smoke_sensor_gpio_show,NULL), //读:关联属性文件的读回调函数(*show) cat 时触发
};

static int  gpio_init_sysfs(struct device *dev)
{
  int i, ret;
  for (i = 0; i < ARRAY_SIZE(gpio_attr); i++) {
    ret = sysfs_create_file(&dev->kobj,  //创建设备的属性文件
			&gpio_attr[i].attr);
     if (ret){
	dev_err(dev, "create charger node(%s) error\n",gpio_attr[i].attr.name);
        return -1;
    }
  }
  return 0;
}

static int gpio_dt(struct gpio_dev *pdata) {
    int gpio;
    int ret;
    enum of_gpio_flags flags;
    struct device *dev = &pdata->pdev->dev;
    struct device_node *node = dev->of_node;
    
    gpio = of_get_named_gpio_flags(node, "led_gpio", 0, &flags);
    if (gpio_is_valid(gpio))
    {
	    pdata->led_gpio           = gpio;
	    pdata->led_gpio_direction = (flags == GPIO_ACTIVE_HIGH)? 1:0;	    
	    printk(KERN_ERR"led_gpio: %d\n", pdata->led_gpio);
	    
	    ret = devm_gpio_request(dev,gpio, "led_gpio");
	    if (ret) {
		printk("Failed to get led_gpio gpio.\n");
		return -1;
	    }
    }


    gpio = of_get_named_gpio_flags(node, "smoke_sensor_gpio", 0, &flags);
    if (gpio_is_valid(gpio))
    {
	pdata->smoke_sensor_gpio        = gpio;
	printk(KERN_ERR"smoke_sensor_gpio: %d\n", pdata->smoke_sensor_gpio);

	ret = devm_gpio_request(dev,gpio, "smoke_sensor_gpio");
	if (ret) {
	   printk("Failed to get smoke_sensor_gpio gpio.\n");
	   return -1;
	}

	ret = gpio_direction_input(gpio);
	if (ret) {
	   printk("Failed to set flame_sensor_gpio gpio.\n");
	   return -1;
	}
    }

    return 0;
}

static void gpio_set_default(struct gpio_dev *pdata) {
    if (pdata->led_gpio) {
        gpio_direction_output(pdata->led_gpio, pdata->led_gpio_direction);
    }
}


static int gpio_probe(struct platform_device *pdev) {
     int ret=0;	
     printk(KERN_ALERT "%s \n",__func__);      
     gpio_dev = kmalloc(sizeof(struct gpio_dev), GFP_KERNEL);
     if (gpio_dev == NULL) {
        printk(KERN_ERR"kmalloc struct gpio_dev err \n");
        return -ENOMEM;
     }

     memset(gpio_dev, 0, sizeof(struct gpio_dev));

     gpio_dev->pdev = pdev;

     ret = gpio_dt(gpio_dev);
     if(ret<0){
	  printk(KERN_ERR"gpio_dt err \n");	
	  return -1;
     }
     gpio_set_default(gpio_dev);
     ret = gpio_init_sysfs(&gpio_dev->pdev->dev);
     if(ret<0){
	  printk(KERN_ERR"gpio_init_sysfs  err \n");	
	  return -1;
     }	     
     printk(KERN_ALERT "%s  ok !!\n",__func__);
     return 0;
}

static int  gpio_remove(struct platform_device *pdev){
     kfree(gpio_dev);
     return 0;
}


static struct of_device_id gpio_of_match[] = {
	{ .compatible = "bbcen,sys_rw_gpio" },
	{ }
};

static struct platform_driver gpio_driver = {
	.driver		= {
		.name		= "sys_rw_gpio driver ",
		.of_match_table	= of_match_ptr(gpio_of_match),
	},
	.probe		= gpio_probe,
	.remove         = gpio_remove,
};

3.点多个灯(官方示例)

帮助文档

官方有驱动文件了,只需要参考帮助文档改设备树,make menuconfig对应选配,就能用了

//Documentation/devicetree/bindings/leds/leds-gpio.txt

设备树

//tegra210-p3448-0000-p3449-0000-b00.dts
yhai-gpio-led { 
  compatible = "gpio-leds"; 
  led1 {
  	label = "led1"; 
  	gpios = <&gpio TEGRA_GPIO(J,7) GPIO_ACTIVE_HIGH>;  //对应40pin 丝印 12脚  接第一个灯
  	default-state = "off"; 
  }; 
  led2 { 
  	label = "led2"; 
  	gpios = <&gpio TEGRA_GPIO(Z,0) GPIO_ACTIVE_HIGH>;  //对应40pin 丝印 31脚  接第二个灯
  	default-state = "off"; 
  }; 
  led3 { 
  	label = "led3"; 
  	gpios = <&gpio TEGRA_GPIO(S,5) GPIO_ACTIVE_HIGH>;  //对应40pin 丝印 29脚  接第三个灯
  	default-state = "off"; 
  }; 
};

官方驱动

//见drivers/leds/leds-gpio.c   

static const struct of_device_id of_gpio_leds_match[] = { 
    { .compatible = "gpio-leds", },
    {}, 
};

static struct platform_driver gpio_led_driver = {
    .probe      = gpio_led_probe,
    .driver     = {
        .name   = "leds-gpio",
        .of_match_table = of_gpio_leds_match,
    },
};
module_platform_driver(gpio_led_driver);

内核配置

$ cd ~/kernel-4.9
$ make menuconfig 
 Device Drivers ---> 
   [*] LED Support ---> 
     <*> LED Class Support 
     <*> LED Support for GPIO connected LEDs

验证测试

$ make dtbs
$ cp arch/arm64/boot/dts/tegra210-p3448-0000-p3449-0000-b00.dtb   /tftpboot/	
重启板子
# cd /sys/class/leds/
# echo 1 > led1/brightness //点亮第一个灯
# echo 0 > led1/brightness //灭第一个灯
# echo 1 > led2/brightness //点亮第二个灯
# echo 0 > led2/brightness //灭第二个灯
# echo 1 > led3/brightness //点亮第三个灯
# echo 0 > led3/brightness //灭第三个灯	

4.GPIO库gpiolib(对上统一操作接口)

//源码 drivers/gpio/gpiolib.c:  gpio子系统的核心实现,对外提供驱动API接口
EXPORT_SYMBOL_GPL(gpiod_get);
EXPORT_SYMBOL_GPL(gpiod_direction_output);
EXPORT_SYMBOL_GPL(gpiod_set_value);
//源码 drivers/gpio/gpiolib-sysfs.c
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{ 
	device_create_with_groups(&gpio_class, &gdev->dev,
                    MKDEV(0, 0), data, gpio_groups,
                    ioname ? ioname : "gpio%u",  //创建 /sys/class/gpio/gpio79 文件夹
                    desc_to_gpio(desc));
}


static ssize_t export_store(struct class *class,
                struct class_attribute *attr,
                const char *buf, size_t len)
{
  gpio_to_desc(gpio);
  gpiod_request(desc, "sysfs");  //先申请(检测是否被占用)
  gpiod_export(desc, true); //导出 生成 /sys/class/gpio/gpio79 文件夹(包括direction  value等)
}

static struct class_attribute gpio_class_attrs[] = {
    __ATTR(export, 0200, NULL, export_store), //写:关联属性文件的写回调函数(*store)   echo 79 > /sys/class/gpio/export 时触发
    __ATTR(unexport, 0200, NULL, unexport_store),
    __ATTR_NULL,
};

static struct class gpio_class = {
    .name =     "gpio",
    .owner =    THIS_MODULE,
    .class_attrs =  gpio_class_attrs,
};

int gpiochip_sysfs_register(struct gpio_device *gdev)
{
	//创建 /sys/class/gpio/gpiochip0 文件夹
	device_create_with_groups(&gpio_class, parent,
	                    MKDEV(0, 0),
	                    chip, gpiochip_groups,
	                    "gpiochip%d", chip->base);	
}

static int __init gpiolib_sysfs_init(void)
{
	struct gpio_device *gdev;
  	class_register(&gpio_class); //注册 生成 /sys/class/gpio 文件夹

  	gpiochip_sysfs_register(gdev);
}

postcore_initcall(gpiolib_sysfs_init); //声明 在内核启动时自动加载 

5.GPIO设备树

//帮助文档  Documentation/devicetree/bindings/gpio/nvidia,tegra186-gpio.txt

//tegra210-soc-base.dtsi   cpu的基本配置
//位置: public_sources\hardware\nvidia\soc\t210\kernel-dts\tegra210-soc\
gpio: gpio@6000d000 {//gpio子节点
    compatible = "nvidia,tegra210-gpio", "nvidia,tegra124-gpio", "nvidia,tegra30-gpio";
    reg = <0x0 0x6000d000 0x0 0x1000>; //前2个数表示起始地址(由父节点的#address-cells = <2> 决定)
                                       //后2个数表示长度范围(由父节点的#size-cells = <2> 决定)
    //域名(0:spi 1:ppi) + 中断索引(36) + 触发方式(高电平触发) Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
    interrupts = <  0 32 0x04    //32: gpio编号  0x04:高电平触发中断 
                    0 33 0x04  
                    0 34 0x04
                    0 35 0x04
                    0 55 0x04
                    0 87 0x04
                    0 89 0x04
                    0 125 0x04>;
    status = "disabled";  //声明 本节点是禁用状态
    #gpio-cells = <2>;  //声明 引用本节点,用 gpios 属性传参时 参数占2个单位
    gpio-controller;  //声明本节点 是 GPIO 控制器 
    #interrupt-cells = <2>;

    interrupt-controller;  //声明本节点 是 中断 控制器 
    gpio-ranges = <&pinmux 0 0 246>;  //设置gpio number 与 pinctrl number的 映射,详见PinCtrl子系统
  };

6.GPIO芯片驱动gpio_chip_driver

( 由原厂开发)

//查看驱动信息
$ cat  /proc/devices  //查看内核加载的驱动设备
   254 gpiochip    
$ cat   /sys/class/gpio/gpiochip0/label //查看驱动设备的标签名
   tegra-gpio  //对应 gpio-tegra.c    的  tgi->gc.label   = "tegra-gpio";

//源码 gpio-tegra.c  英伟达原厂开发的芯片驱动

定义类

//gpio-tegra.c	
struct tegra_gpio_info { //定义gpio类
    struct device               *dev;
    void __iomem                *regs;
    struct irq_domain           *irq_domain;
    struct tegra_gpio_bank          *bank_info;
    const struct tegra_gpio_soc_config  *soc;
    struct gpio_chip            gc; //gpio控制器 
    struct irq_chip             ic; //irq控制器
    u32                 bank_count;
};
static struct tegra_gpio_info *gpio_info; //定义 gpio对象


static const struct of_device_id tegra_gpio_of_match[] = {               
    { .compatible = "nvidia,tegra210-gpio", .data = &tegra210_gpio_config },                                                           
};  

static struct platform_driver tegra_gpio_driver = {                      
    .driver     = {                                                      
        .name   = "tegra-gpio",                                          
        .of_match_table = tegra_gpio_of_match,
    },
    .probe      = tegra_gpio_probe,                                      
};      
    
static int __init tegra_gpio_init(void)                                  
{
    return platform_driver_register(&tegra_gpio_driver);                 
}
subsys_initcall(tegra_gpio_init);  //添加到内核启动列表中

构建对象(注册制)

  • devm_kzalloc会自动释放资源,常用
static int tegra_gpio_probe(struct platform_device *pdev)
{
	struct tegra_gpio_info *tgi;  //定义对象

	//为对象分配空间
	tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL);
	if (!tgi)
	    return -ENODEV;
	gpio_info = tgi;

	//获取所有bank的中断资源
	for (;;) {
	    res = platform_get_resource(pdev, IORESOURCE_IRQ,   //见设备树 interrupts =<  0 32 0x04 ...>
	                    tgi->bank_count);
	    if (!res)
	        break;
	    tgi->bank_count++;
	}

	//初始化对象:里面的变量都赋值,指针都指向实体,
	tgi->gc.label           = "tegra-gpio";  //对应  /sys/class/gpio/gpiochip0/label
	tgi->gc.request         = tegra_gpio_request; //申请gpio(如果被占用,将申请失败)
	tgi->gc.free            = tegra_gpio_free; //释放gpio(释放后,别的驱动才能申请)
	tgi->gc.direction_input     = tegra_gpio_direction_input; //
	tgi->gc.get         = tegra_gpio_get;
	tgi->gc.direction_output    = tegra_gpio_direction_output;//gpio 方向设为输出
	tgi->gc.set         = tegra_gpio_set;
	
	//获取MEM资源
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  //见设备树 reg = <0x0 0x6000d000 0x0 0x1000>;
	tgi->regs = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(tgi->regs))
		return PTR_ERR(tgi->regs);	
	
	
	tegra_gpio_debuginit(tgi); //添加 调试功能
}	

static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi,
				     u32 val, u32 reg)
{
	__raw_writel(val, tgi->regs + reg); /* 实现见  arch/arm64/include/asm/io.h
	
	  用C语言中嵌入汇编方式   ,用str  汇编指令 实现写数据
		static inline void __raw_writel(u32 val, volatile void __iomem *addr)
		{
			asm volatile("str %w0, [%1]" : : "rZ" (val), "r" (addr));
		}
			
	  */
}

static void tegra_gpio_mask_write(struct tegra_gpio_info *tgi, u32 reg,
				  int gpio, int value)
{
	tegra_gpio_writel(tgi, val, reg);
}

static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
	//通过 GPIO_MSK_OUT 宏 实现各gpio 地址间转换关系(如不同端口 偏移量)
	tegra_gpio_mask_write(tgi, GPIO_MSK_OUT(tgi, offset), offset, value);
}

static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
                    int value)
{
    struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
    int ret;

    tegra_gpio_set(chip, offset, value);
    tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1);
    tegra_gpio_enable(tgi, offset);

    ret = pinctrl_gpio_direction_output(chip->base + offset); //向pinctrl子系统 申请管脚 设为gpio输出模式

    return ret;
}

支持SYS控制

#ifdef	CONFIG_DEBUG_FS  //当打开 内核调试关,才把调试源码编译进来

#include <linux/debugfs.h>
#include <linux/seq_file.h>

static int dbg_gpio_open(struct inode *inode, struct file *file)
{
	return single_open(file, dbg_gpio_show, inode->i_private);
}

static const struct file_operations debug_fops = {
	.open		= dbg_gpio_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

static void tegra_gpio_debuginit(struct tegra_gpio_info *tgi)
{
	(void) debugfs_create_file("tegra_gpio", S_IRUGO,  //创建调试文件 
					NULL, tgi, &debug_fops);
}

#else

static inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi)
{
}

#endif

寄存器自适应(管脚变了,不用再该代码,只需该改备树)

#include <linux/gpio.h>  //for gpio_chip

#define GPIO_BANK(x)        ((x) >> 5)
#define GPIO_PORT(x)        (((x) >> 3) & 0x3)
#define GPIO_BIT(x)     ((x) & 0x7)

#define GPIO_REG(tgi, x)    (GPIO_BANK(x) * tgi->soc->bank_stride + \
                    GPIO_PORT(x) * 4)

#define GPIO_CNF(t, x)      (GPIO_REG(t, x) + 0x00)
#define GPIO_OE(t, x)       (GPIO_REG(t, x) + 0x10)
#define GPIO_OUT(t, x)      (GPIO_REG(t, x) + 0X20)
#define GPIO_IN(t, x)       (GPIO_REG(t, x) + 0x30)
#define GPIO_INT_STA(t, x)  (GPIO_REG(t, x) + 0x40)
#define GPIO_INT_ENB(t, x)  (GPIO_REG(t, x) + 0x50)
#define GPIO_INT_LVL(t, x)  (GPIO_REG(t, x) + 0x60)
#define GPIO_INT_CLR(t, x)  (GPIO_REG(t, x) + 0x70)
#define GPIO_DBC_CNT(t, x)  (GPIO_REG(t, x) + 0xF0)


#define GPIO_MSK_CNF(t, x)  (GPIO_REG(t, x) + t->soc->upper_offset + 0x00)
#define GPIO_MSK_OE(t, x)   (GPIO_REG(t, x) + t->soc->upper_offset + 0x10)
#define GPIO_MSK_OUT(t, x)  (GPIO_REG(t, x) + t->soc->upper_offset + 0X20)
#define GPIO_MSK_DBC_EN(t, x)   (GPIO_REG(t, x) + t->soc->upper_offset + 0x30)
#define GPIO_MSK_INT_STA(t, x)  (GPIO_REG(t, x) + t->soc->upper_offset + 0x40)
#define GPIO_MSK_INT_ENB(t, x)  (GPIO_REG(t, x) + t->soc->upper_offset + 0x50)
#define GPIO_MSK_INT_LVL(t, x)  (GPIO_REG(t, x) + t->soc->upper_offset + 0x60)

#define GPIO_INT_LVL_MASK           0x010101
#define GPIO_INT_LVL_EDGE_RISING    0x000101
#define GPIO_INT_LVL_EDGE_FALLING   0x000100
#define GPIO_INT_LVL_EDGE_BOTH      0x010100
#define GPIO_INT_LVL_LEVEL_HIGH     0x000001
#define GPIO_INT_LVL_LEVEL_LOW      0x000000	

struct tegra_gpio_soc_config {
	bool debounce_supported;
	u32 bank_stride;
	u32 upper_offset;
};

static const struct tegra_gpio_soc_config tegra210_gpio_config = {
	.debounce_supported = true,
	.bank_stride = 0x100,
	.upper_offset = 0x80,
};

static const struct of_device_id tegra_gpio_of_match[] = {
	{ .compatible = "nvidia,tegra210-gpio", .data = &tegra210_gpio_config },
}

/*打印 各bank gpio 寄存器列表的值 -> 对应芯片手册里 
	#define GPIO3  	0x6000D200 // 第3个Bank GPIO 的基地址
	//---偏移量
	#define CNF		0x04  //配置寄存器 (0:GPIO  1:SFIO)
	#define OE   	0x14  //输出使能寄存器 (1:使能 0:关闭)
	#define OUT  	0x24  //输出寄存器(1:高电平 0:低电平)
	#define MSK_CNF 0x84  //配置屏蔽寄存器(高位1:屏蔽 高位0:不屏蔽   低位1:GPIO模式 低位0:SFIO模式)
	#define MSK_OE  0x94  //输出使能屏蔽寄存器(高位1:禁止写   低位1:使能)
	#define MSK_OUT 0xA4  //输出屏蔽寄存器(高位1:禁止写   低位1:高电平)
*/
static int dbg_gpio_show(struct seq_file *s, void *unused)
{
	struct tegra_gpio_info *tgi = s->private;
	int i;
	int j;
	char x, y;

	x = ' ';
	y = 'A';

	seq_printf(s, "Name:Bank:Port CNF OE OUT IN INT_STA INT_ENB INT_LVL\n");
	for (i = 0; i < tgi->bank_count; i++) {
		for (j = 0; j < 4; j++) {
		int gpio = tegra_gpio_compose(i, j, 0);

		seq_printf(s,
		     "%c%c: %d:%d %02x %02x %02x %02x %02x %02x %06x\n",
			x, y, i, j,
			tegra_gpio_readl(tgi, GPIO_CNF(tgi, gpio)), //用GPIO_CNF 宏,实现各gpio 地址间转换关系(如不同端口 偏移量)
			tegra_gpio_readl(tgi, GPIO_OE(tgi, gpio)),
			tegra_gpio_readl(tgi, GPIO_OUT(tgi, gpio)),
			tegra_gpio_readl(tgi, GPIO_IN(tgi, gpio)),
			tegra_gpio_readl(tgi, GPIO_INT_STA(tgi, gpio)),
			tegra_gpio_readl(tgi, GPIO_INT_ENB(tgi, gpio)),
			tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio)));

		if (x != ' ')
			x++;
		if (y == 'Z') {
			y = 'A';
			x = 'A';
		} else {
			y++;
		};
		}
	}
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/430340.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

win10系统部署-zabbix客户端

一、下载win10系统 zabbix客户端 下载地址&#xff1a; 根据自己需求下载 https://www.zabbix.com/cn/download二、win系统安装客户端 1、解压安装包 c:\zabbix 2、编辑zabbix\conf\zabbix_agentd.conf文件 Server127.0.0.1,IP IP是你的zabbix服务器端地址 ServerActive127…

C++的GUI库

1. Qt Qt是一个跨平台的C应用程序开发框架。广泛用于开发GUI程序&#xff0c;这种情况下又被称为部件工具箱。也可用于开发非GUI程序&#xff0c;例如控制台工具和服务器。 2. MFC 微软基础类库&#xff08;英语&#xff1a;Microsoft Foundation Classes&#xff0c;简称MFC&a…

罗丹明-聚乙二醇-生物素RB-PEG-Biotin;Biotin-PEG-Rhodamine,PEG2000

RB-PEG-Biotin 罗丹明-聚乙二醇-生物素 中文名称&#xff1a;罗丹明-聚乙二醇-生物素 英文名称&#xff1a;RB-PEG-Biotin 分子量&#xff08;PEG &#xff09;&#xff1a;2000、3400、5000&#xff0c;其他分子量可以定制。 用 途&#xff1a;仅供科研实验使用。 性状&…

TiDB 6.1/6.5 在 Rocky Linux 8 中的部署升级与 PITR 初体验

作者&#xff1a; 沈阳盛京征信有限公司 原文来源&#xff1a; https://tidb.net/blog/5fa1612a 本文档的主要内容为&#xff1a; TiDB v6.1.0 在 Rocky Linux 8.7 中的离线部署 TiDB v6.1.0 -> TiDB v6.5.1 升级 TiFlash 扩缩容 Haproxy 部署 br 物理备份与恢复 基…

递归算法(JS实现代码)

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;数据结构与算法 &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; 文章目录递归算法递归的思想递归三要素递归的编程模型递归一般应用场景递归经典案例…

​力扣解法汇总1026. 节点与其祖先之间的最大差值

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 给定二叉树的根节点 root&#xff0c;找出存在于 不同 节点 A 和 B 之间的最大值…

05、SpringBoot开发实用篇

一、热部署什么是热部署&#xff1f;简单说就是你程序改了&#xff0c;现在要重新启动服务器&#xff0c;嫌麻烦&#xff1f;不用重启&#xff0c;服务器会自己悄悄的把更新后的程序给重新加载一遍&#xff0c;这就是热部署。热部署的功能是如何实现的呢&#xff1f;这就要分两…

【技术】封装自己的 Maven Archetype Maven 原型

封装自己的 Maven Archetype Maven 原型 为什么要封装 Maven Archetype&#xff1f;如何封装 Maven Archetype&#xff1f;核心步骤具体步骤构建项目构建原型 archetypeIDEA 导入自定义原型 如何删除自定义的 Maven Archetype ? 为什么要封装 Maven Archetype&#xff1f; 用…

尚融宝19-Nuxt.js入门

目录 一、搜索引擎优化 1、什么是SEO 2、搜索引擎工作流程 二、服务端渲染和客户端渲染 1、什么是服务端渲染 2、什么是客户端渲染 3、两种方式各有什么优缺点&#xff1f; 三、Nuxt.js 1、Nuxt.js介绍 2、Nuxt.js服务器端渲染 四、安装和运行 五、页面、导航和路…

倒计时 1 天!IoTDB X EMQ 智能汽车主题 Meetup 明日不见不散!

期待已久的智能汽车主题 Meetup 活动明日即将举办&#xff01;天谋科技联手 EMQ、通过数据基础设施平台的实践经验分享&#xff0c;共同为行业用户带来可靠高效的智能制造、智慧车联数据解决方案&#xff0c;快来预约直播&#xff0c;不要错过这场干货满满的智能汽车主题 Meetu…

类中自定义函数并调用and使用钩子函数打印类中变量

在一个类中自定义一个函数A&#xff0c;并在前向传播函数forword中调用这个函数 假设您正在编写一个PyTorch模型&#xff0c;您可以按照以下方式在类中定义函数A&#xff0c;并在forward函数中调用它&#xff1a; import torch import torch.nn as nnclass MyModel(nn.Module)…

Android修行手册 - Android Studio提升性能效率

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册点击跳转>Scratch编程案例 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断…

【Linux】NanoPi-NEO2外接spi-lcd

这是目录一、显示接口1.1、LCD接口1.2、核心板接口二、添加驱动2.1、确认驱动型号2.2、添加驱动三、测试四、附加4.1、交叉编译器安装4.2、内核和module编译4.3、扩展rootfs大小本文使用环境&#xff1a; 电脑&#xff1a;Ubuntu 18.04.5 LTS 开发板&#xff1a;NanoPi-NEO2 50…

这款知名开车软件,居然暗藏大量病毒

想必大家见多了网上有关 Windows 系统宝藏神级软件的种种推荐。 其中有这么一款软件一直占据推荐榜单前列&#xff0c;并且坐拥无数好评。 它就是在 Steam 上售价仅 19 元&#xff0c;表面看起来平平无奇的 Wallpaper Engine &#xff08;壁纸引擎&#xff09;。 别看它价格不…

常见分布式锁4:zookeeper 瞬时znode节点 + watcher监听机制,ChatGPT回复的解决死锁的方案

原文地址在这里 临时节点具备数据自动删除的功能。当client与ZooKeeper连接和session断掉时&#xff0c;相应的临时节点就会被删除。zk有瞬时和持久节点&#xff0c;瞬时节点不可以有子节点。会话结束之后瞬时节点就会消失&#xff0c;基于zk的瞬时有序节点实现分布式锁&#x…

windows 环境下安装ITOP

该文章修改自旧版本的教程&#xff0c;如有侵权或其他问题请及时联系 windows 环境下安装ITOP 1、安装环境的下载 安装的相关文件列表&#xff0c;自用的程序安装包是版本3.0.2-1&#xff0c;环境是3.2.6版本&#xff1b;也可以直接通过下面链接找到最新的版本进行下载 1.1…

GitLab与jekins结合构建持续集成(cl)环境(2)

目录 GItlab配置邮箱 绑定邮箱 创建群组 添加人员 创建一个项目 添加文件 新建分支 如何拉取代码 Git bash 演示 Git GUI演示 安装jenkins 更改插件镜像源 配置jenkins使用gitlab更新代码 安装jekins插件 配置jenkins免密拉取gatlab代码 jenkins创建项目 将代码…

一种vivado联合vcs仿真以及verdi查看波形的方法

上一篇中提到vivado仿真xilinx官方的axi vip耗时过长、且每次缩放波形时加载慢的问题。后来用了正点原子的AXI DDR例程&#xff0c;将AXI DDR换成了AXI RAM进行读写测试&#xff0c;用以学习了解AXI的工作方式。详见此文读写AXI4接口RAM的简单示例_给米PHY的博客-CSDN博客。 在…

力扣题库刷题笔记20-有效的括号

1、题目如下&#xff1a; 2、个人Python代码实现如下&#xff1a; 第一次读题就理解错了题意&#xff0c;以为是只判断小括号闭合&#xff0c;大括号、中括号只是干扰元素。再次读题后&#xff0c;代码实现如下&#xff1a; 以上代码仍旧是没有理解清楚题意&#xff0c;以为是只…

Linux Shell 实现一键部署二进制docker+docker_compose

docker 前言 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows 机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口。 d…