国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【嵌入式Linux內(nèi)核驅(qū)動(dòng)】04_Jetson nano GPIO應(yīng)用 | 驅(qū)動(dòng)開發(fā) | 官方gpiolib、設(shè)備樹與chip_driver

這篇具有很好參考價(jià)值的文章主要介紹了【嵌入式Linux內(nèi)核驅(qū)動(dòng)】04_Jetson nano GPIO應(yīng)用 | 驅(qū)動(dòng)開發(fā) | 官方gpiolib、設(shè)備樹與chip_driver。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

GPIO子系統(tǒng)

jetson nano 驅(qū)動(dòng)開發(fā),嵌入式,# jetson nano,驅(qū)動(dòng)開發(fā),嵌入式Linux,Jetson nano,GPIO子系統(tǒng)

0.暴露給應(yīng)用層

應(yīng)用

$ echo 79 > /sys/class/gpio/export   //導(dǎo)出79號(hào)gpio 引腳,使得可在應(yīng)用層訪問
$ echo out > /sys/class/gpio/gpio79/direction  //設(shè)置 為輸出
$ echo 1 > /sys/class/gpio/gpio79/value //輸出高電平 開燈
$ echo 0 > /sys/class/gpio/gpio79/value  //輸出低電平, 關(guān)燈	
$ cat /sys/kernel/debug/gpio  //查詢gpio狀態(tài)(問題:發(fā)現(xiàn)找不到gpio文件)
$ echo 79 > unexport //取消導(dǎo)出(發(fā)現(xiàn)gpio79消失了)

解決調(diào)試目錄為空的問題

原因 //debug需要的文件系統(tǒng) debugfs沒有掛載/etc/fstab	 的后面添加一行
debugfs     /sys/kernel/debug   debugfs  defaults         0       0

調(diào)試信息

$ cat /sys/kernel/debug/gpio  //查看gpio 當(dāng)前配置情況(驅(qū)動(dòng)暴露的調(diào)試信息)
$ cat  /sys/kernel/debug/tegra_gpio  //查看GPIO 寄存器內(nèi)容(和芯片手冊(cè)進(jìn)行對(duì)應(yīng))
$ cat  /sys/kernel/debug/tegra_pinctrl_reg   //查看 pinctrl 寄存器內(nèi)容	

1.最簡(jiǎn)讀寫文件(在/SYS下)

設(shè)備樹
   sys_rw_led{  //對(duì)應(yīng)生成 /sys/devices/sys_rw_led/led_gpio
        compatible = "sys_rw_led";
        led_gpio  = <&gpio TEGRA_GPIO(J,7) GPIO_ACTIVE_HIGH>;
    };
驗(yàn)證測(cè)試
$ 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  //關(guān)燈
$ 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	
驅(qū)動(dòng)
  • of_get_named_gpio_flags//獲取設(shè)備樹節(jié)點(diǎn)的屬性gpio_is_valid//判斷是否合法devm_gpio_request//申請(qǐng)使用gpio,并調(diào)用設(shè)置pinctrl

  • device_create_file //根據(jù)設(shè)備樹節(jié)點(diǎn)屬性,創(chuàng)建相應(yīng)的屬性文件 /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> 就調(diào)用到這里了
    .show = led_show,  //cat led_gpio時(shí)調(diào)用,如果無需讀的功能,可設(shè)為NULL,  且刪除前面的S_IRUGO
};


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

  //獲取設(shè)備樹節(jié)點(diǎn)的屬性 "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"); //申請(qǐng)使用gpio(如果被占用,將申請(qǐng)失敗)
      if (ret) {
	  printk("Failed to get led_gpio gpio.\n");
	  return -1;
       } 
   }

   //根據(jù)設(shè)備樹節(jié)點(diǎn)屬性,創(chuàng)建相應(yīng)的屬性文件 /sys/devices/sys_rw_led/led_gpio
   device_create_file(&pdev->dev, &dev_attr_file);  // /device_create_file 里面是調(diào)用了 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.讀寫多個(gè)屬性文件(在SYS下)

設(shè)備樹
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
};
驗(yàn)證測(cè)試
$ 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  //關(guān)燈
$ echo 1 > led_gpio  //亮燈	
$ cat smoke_sensor_gpio  //讀管腳輸入電平,默認(rèn)讀到0,用杜邦線,把它接到前面亮燈的管腳時(shí),能成功讀到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	
驅(qū)動(dòng)
  • 定義一個(gè)結(jié)構(gòu)體指針,分配空間,指定內(nèi)容

  • device_attribute gpio_attr[]

  • sysfs_create_file //for循環(huán)讀取,和上面的數(shù)組配合實(shí)現(xiàn)讀多個(gè)屬性

#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 時(shí)會(huì)觸發(fā))
    
    return snprintf(buf, PAGE_SIZE, "%d\n",gpio_value);
}



static struct device_attribute gpio_attr[] = {  
    __ATTR(led_gpio, 0664, NULL, led_gpio_store), //寫:關(guān)聯(lián)屬性文件的寫回調(diào)函數(shù)(*store)  echo時(shí)觸發(fā)
    __ATTR(smoke_sensor_gpio, 0664, smoke_sensor_gpio_show,NULL), //讀:關(guān)聯(lián)屬性文件的讀回調(diào)函數(shù)(*show) cat 時(shí)觸發(fā)
};

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,  //創(chuàng)建設(shè)備的屬性文件
			&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.點(diǎn)多個(gè)燈(官方示例)

幫助文檔

官方有驅(qū)動(dòng)文件了,只需要參考幫助文檔改設(shè)備樹,make menuconfig對(duì)應(yīng)選配,就能用了

//Documentation/devicetree/bindings/leds/leds-gpio.txt
設(shè)備樹
//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>;  //對(duì)應(yīng)40pin 絲印 12腳  接第一個(gè)燈
  	default-state = "off"; 
  }; 
  led2 { 
  	label = "led2"; 
  	gpios = <&gpio TEGRA_GPIO(Z,0) GPIO_ACTIVE_HIGH>;  //對(duì)應(yīng)40pin 絲印 31腳  接第二個(gè)燈
  	default-state = "off"; 
  }; 
  led3 { 
  	label = "led3"; 
  	gpios = <&gpio TEGRA_GPIO(S,5) GPIO_ACTIVE_HIGH>;  //對(duì)應(yīng)40pin 絲印 29腳  接第三個(gè)燈
  	default-state = "off"; 
  }; 
};
官方驅(qū)動(dòng)
//見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);
內(nèi)核配置
$ cd ~/kernel-4.9
$ make menuconfig 
 Device Drivers ---> 
   [*] LED Support ---> 
     <*> LED Class Support 
     <*> LED Support for GPIO connected LEDs
驗(yàn)證測(cè)試
$ make dtbs
$ cp arch/arm64/boot/dts/tegra210-p3448-0000-p3449-0000-b00.dtb   /tftpboot/	
重啟板子
# cd /sys/class/leds/
# echo 1 > led1/brightness //點(diǎn)亮第一個(gè)燈
# echo 0 > led1/brightness //滅第一個(gè)燈
# echo 1 > led2/brightness //點(diǎn)亮第二個(gè)燈
# echo 0 > led2/brightness //滅第二個(gè)燈
# echo 1 > led3/brightness //點(diǎn)亮第三個(gè)燈
# echo 0 > led3/brightness //滅第三個(gè)燈	

4.GPIO庫(kù)gpiolib(對(duì)上統(tǒng)一操作接口)

//源碼 drivers/gpio/gpiolib.c:  gpio子系統(tǒng)的核心實(shí)現(xiàn),對(duì)外提供驅(qū)動(dòng)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",  //創(chuàng)建 /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");  //先申請(qǐng)(檢測(cè)是否被占用)
  gpiod_export(desc, true); //導(dǎo)出 生成 /sys/class/gpio/gpio79 文件夾(包括direction  value等)
}

static struct class_attribute gpio_class_attrs[] = {
    __ATTR(export, 0200, NULL, export_store), //寫:關(guān)聯(lián)屬性文件的寫回調(diào)函數(shù)(*store)   echo 79 > /sys/class/gpio/export 時(shí)觸發(fā)
    __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)
{
	//創(chuàng)建 /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); //注冊(cè) 生成 /sys/class/gpio 文件夾

  	gpiochip_sysfs_register(gdev);
}

postcore_initcall(gpiolib_sysfs_init); //聲明 在內(nèi)核啟動(dòng)時(shí)自動(dòng)加載 

5.GPIO設(shè)備樹

//幫助文檔  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子節(jié)點(diǎn)
    compatible = "nvidia,tegra210-gpio", "nvidia,tegra124-gpio", "nvidia,tegra30-gpio";
    reg = <0x0 0x6000d000 0x0 0x1000>; //前2個(gè)數(shù)表示起始地址(由父節(jié)點(diǎn)的#address-cells = <2> 決定)
                                       //后2個(gè)數(shù)表示長(zhǎng)度范圍(由父節(jié)點(diǎn)的#size-cells = <2> 決定)
    //域名(0:spi 1:ppi) + 中斷索引(36) + 觸發(fā)方式(高電平觸發(fā)) Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
    interrupts = <  0 32 0x04    //32: gpio編號(hào)  0x04:高電平觸發(fā)中斷 
                    0 33 0x04  
                    0 34 0x04
                    0 35 0x04
                    0 55 0x04
                    0 87 0x04
                    0 89 0x04
                    0 125 0x04>;
    status = "disabled";  //聲明 本節(jié)點(diǎn)是禁用狀態(tài)
    #gpio-cells = <2>;  //聲明 引用本節(jié)點(diǎn),用 gpios 屬性傳參時(shí) 參數(shù)占2個(gè)單位
    gpio-controller;  //聲明本節(jié)點(diǎn) 是 GPIO 控制器 
    #interrupt-cells = <2>;

    interrupt-controller;  //聲明本節(jié)點(diǎn) 是 中斷 控制器 
    gpio-ranges = <&pinmux 0 0 246>;  //設(shè)置gpio number 與 pinctrl number的 映射,詳見PinCtrl子系統(tǒng)
  };

6.GPIO芯片驅(qū)動(dòng)gpio_chip_driver

( 由原廠開發(fā))文章來源地址http://www.zghlxwxcb.cn/news/detail-731054.html

//查看驅(qū)動(dòng)信息
$ cat  /proc/devices  //查看內(nèi)核加載的驅(qū)動(dòng)設(shè)備
   254 gpiochip    
$ cat   /sys/class/gpio/gpiochip0/label //查看驅(qū)動(dòng)設(shè)備的標(biāo)簽名
   tegra-gpio  //對(duì)應(yīng) gpio-tegra.c    的  tgi->gc.label   = "tegra-gpio";

//源碼 gpio-tegra.c  英偉達(dá)原廠開發(fā)的芯片驅(qū)動(dòng)
定義類
//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對(duì)象


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);  //添加到內(nèi)核啟動(dòng)列表中
構(gòu)建對(duì)象(注冊(cè)制)
  • devm_kzalloc會(huì)自動(dòng)釋放資源,常用
static int tegra_gpio_probe(struct platform_device *pdev)
{
	struct tegra_gpio_info *tgi;  //定義對(duì)象

	//為對(duì)象分配空間
	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,   //見設(shè)備樹 interrupts =<  0 32 0x04 ...>
	                    tgi->bank_count);
	    if (!res)
	        break;
	    tgi->bank_count++;
	}

	//初始化對(duì)象:里面的變量都賦值,指針都指向?qū)嶓w,
	tgi->gc.label           = "tegra-gpio";  //對(duì)應(yīng)  /sys/class/gpio/gpiochip0/label
	tgi->gc.request         = tegra_gpio_request; //申請(qǐng)gpio(如果被占用,將申請(qǐng)失敗)
	tgi->gc.free            = tegra_gpio_free; //釋放gpio(釋放后,別的驅(qū)動(dòng)才能申請(qǐng))
	tgi->gc.direction_input     = tegra_gpio_direction_input; //
	tgi->gc.get         = tegra_gpio_get;
	tgi->gc.direction_output    = tegra_gpio_direction_output;//gpio 方向設(shè)為輸出
	tgi->gc.set         = tegra_gpio_set;
	
	//獲取MEM資源
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  //見設(shè)備樹 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); //添加 調(diào)試功能
}	

static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi,
				     u32 val, u32 reg)
{
	__raw_writel(val, tgi->regs + reg); /* 實(shí)現(xiàn)見  arch/arm64/include/asm/io.h
	
	  用C語言中嵌入?yún)R編方式   ,用str  匯編指令 實(shí)現(xiàn)寫數(shù)據(jù)
		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 宏 實(shí)現(xiàn)各gpio 地址間轉(zhuǎn)換關(guān)系(如不同端口 偏移量)
	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子系統(tǒng) 申請(qǐng)管腳 設(shè)為gpio輸出模式

    return ret;
}
支持SYS控制
#ifdef	CONFIG_DEBUG_FS  //當(dāng)打開 內(nèi)核調(diào)試關(guān),才把調(diào)試源碼編譯進(jìn)來

#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,  //創(chuàng)建調(diào)試文件 
					NULL, tgi, &debug_fops);
}

#else

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

#endif
寄存器自適應(yīng)(管腳變了,不用再該代碼,只需該改備樹)
#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 寄存器列表的值 -> 對(duì)應(yīng)芯片手冊(cè)里 
	#define GPIO3  	0x6000D200 // 第3個(gè)Bank GPIO 的基地址
	//---偏移量
	#define CNF		0x04  //配置寄存器 (0:GPIO  1:SFIO)
	#define OE   	0x14  //輸出使能寄存器 (1:使能 0:關(guān)閉)
	#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 宏,實(shí)現(xiàn)各gpio 地址間轉(zhuǎn)換關(guān)系(如不同端口 偏移量)
			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;
}

到了這里,關(guān)于【嵌入式Linux內(nèi)核驅(qū)動(dòng)】04_Jetson nano GPIO應(yīng)用 | 驅(qū)動(dòng)開發(fā) | 官方gpiolib、設(shè)備樹與chip_driver的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【嵌入式環(huán)境下linux內(nèi)核及驅(qū)動(dòng)學(xué)習(xí)筆記-(5-驅(qū)動(dòng)的并發(fā)控制機(jī)制)】

    【嵌入式環(huán)境下linux內(nèi)核及驅(qū)動(dòng)學(xué)習(xí)筆記-(5-驅(qū)動(dòng)的并發(fā)控制機(jī)制)】

    在討論并發(fā)前,先要了解以下幾個(gè)概念:執(zhí)行流,上下文,共享與臨界等。 什么叫執(zhí)行流: 【執(zhí)行流】:有開始有結(jié)束總體順序執(zhí)行的一段代碼 又稱 上下文 。 上下文分類: 【任務(wù)上下文】:普通的,具有五種狀態(tài)(就緒態(tài)、運(yùn)行態(tài)、睡眠態(tài)、暫停態(tài)、僵死態(tài)),可被阻塞

    2023年04月21日
    瀏覽(31)
  • 【嵌入式Linux內(nèi)核驅(qū)動(dòng)】05_IIC子系統(tǒng) | 硬件原理與常見面試問題 | 應(yīng)用編程 | 內(nèi)核驅(qū)動(dòng) | 總體框架

    【嵌入式Linux內(nèi)核驅(qū)動(dòng)】05_IIC子系統(tǒng) | 硬件原理與常見面試問題 | 應(yīng)用編程 | 內(nèi)核驅(qū)動(dòng) | 總體框架

    1.1 IIC 基礎(chǔ) IIC協(xié)議簡(jiǎn)介—學(xué)習(xí)筆記_iic標(biāo)準(zhǔn)協(xié)議_越吃越胖的黃的博客-CSDN博客 I2C(Inter-Integrated Circuit)是一種串行通信協(xié)議,用于連接微控制器、傳感器、存儲(chǔ)器和其他外設(shè)。 I2C使用兩條線(SDA和SCL)進(jìn)行通信,可以連接多個(gè)設(shè)備,每個(gè)設(shè)備都有一個(gè)唯一的地址。I2C總線上的

    2024年02月09日
    瀏覽(93)
  • 嵌入式Linux驅(qū)動(dòng)開發(fā) 04:基于設(shè)備樹的驅(qū)動(dòng)開發(fā)

    嵌入式Linux驅(qū)動(dòng)開發(fā) 04:基于設(shè)備樹的驅(qū)動(dòng)開發(fā)

    前面文章 《嵌入式Linux驅(qū)動(dòng)開發(fā) 03:平臺(tái)(platform)總線驅(qū)動(dòng)模型》 引入了資源和驅(qū)動(dòng)分離的概念,這篇文章將在前面基礎(chǔ)上更進(jìn)一步,引入設(shè)備樹的概念。 在平臺(tái)總線驅(qū)動(dòng)模型中資源和驅(qū)動(dòng)已經(jīng)從邏輯上和代碼組織上進(jìn)行了分離,但每次調(diào)整資源還是會(huì)涉及到內(nèi)核,所以現(xiàn)

    2024年02月16日
    瀏覽(27)
  • 嵌入式內(nèi)核及驅(qū)動(dòng)開發(fā)高級(jí)

    嵌入式內(nèi)核及驅(qū)動(dòng)開發(fā)高級(jí)

    僅devfs,導(dǎo)致開發(fā)不方便以及一些功能難以支持: 熱插拔 不支持一些針對(duì)所有設(shè)備的統(tǒng)一操作(如電源管理) 不能自動(dòng)mknod 用戶查看不了設(shè)備信息 設(shè)備信息硬編碼,導(dǎo)致驅(qū)動(dòng)代碼通用性差,即沒有分離設(shè)備和驅(qū)動(dòng) uevent機(jī)制:sysfs + uevent + udevd(上層app) sysfs用途:(類似于

    2024年02月16日
    瀏覽(36)
  • 嵌入式培訓(xùn)機(jī)構(gòu)四個(gè)月實(shí)訓(xùn)課程筆記(完整版)-Linux ARM驅(qū)動(dòng)編程第七天-內(nèi)核函數(shù)接口(物聯(lián)技術(shù)666)

    鏈接:https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd=1688 提取碼:1688 //************************************************** #include linux/module.h??? /*module_init()*/ #include linux/kernel.h??????? /* printk() */ #include linux/init.h??????????? /* __init __exit */ #include linux/fs.h????????????? /* file_opera

    2024年02月22日
    瀏覽(32)
  • 嵌入式開發(fā)之linux內(nèi)核移植

    嵌入式開發(fā)之linux內(nèi)核移植

    目錄 ?前言 一、下載內(nèi)核源碼 1.1 下載linux-3.0.1 1.2 解壓源碼文件 二、 內(nèi)核添加yaffs2文件系統(tǒng)支持 2.1 下載yaffs2 2.2 內(nèi)核添加yaffs2文件補(bǔ)丁 三、配置開發(fā)板 3.1 修改機(jī)器ID 3.2 添加開發(fā)板初始化文件 3.3 配置NandFalsh 3.3.1 添加NandFlash設(shè)備 3.3.2 添加NandFlash驅(qū)動(dòng) 3.3 修改Kconfig(支持

    2024年02月07日
    瀏覽(103)
  • 嵌入式Linux底層系統(tǒng)開發(fā) +系統(tǒng)移植+內(nèi)核文件系統(tǒng)(基礎(chǔ))

    嵌入式Linux底層系統(tǒng)開發(fā) +系統(tǒng)移植+內(nèi)核文件系統(tǒng)(基礎(chǔ))

    搭建交叉編譯開發(fā)環(huán)境 bootloader的選擇和移植 kernel的配置、編譯、移植和調(diào)試 根文件系統(tǒng)的制作 前兩個(gè)要點(diǎn)通常芯片廠家提供。后邊兩個(gè)要點(diǎn)是公司的工作重點(diǎn)。 學(xué)習(xí)方法:先整體后局部,層層推進(jìn) 如何編譯—如何添加命令和功能—如何定義自己的開發(fā)板。 移植的基本步

    2024年02月03日
    瀏覽(101)
  • 修改嵌入式 ARM Linux 內(nèi)核映像中的文件系統(tǒng)

    修改嵌入式 ARM Linux 內(nèi)核映像中的文件系統(tǒng)

    zImage 是編譯內(nèi)核后在 arch/arm/boot 目錄下生成的一個(gè)已經(jīng)壓縮過的內(nèi)核映像。通常我們不會(huì)使用編譯生成的原始內(nèi)核映像 vmlinux ,因其體積很大。因此, zImage 是我們最常見的內(nèi)核二進(jìn)制,可以直接嵌入到固件,也可以直接使用 qemu 進(jìn)行調(diào)試。當(dāng)然,在 32 位嵌入式領(lǐng)域還能見到

    2024年02月10日
    瀏覽(36)
  • 【嵌入式Linux】編譯應(yīng)用和ko內(nèi)核模塊Makefile使用記錄

    【嵌入式Linux】編譯應(yīng)用和ko內(nèi)核模塊Makefile使用記錄

    在Makefile中,變量的賦值可以使用以下幾種方式: = :最基本的賦值符號(hào),表示簡(jiǎn)單的延遲展開(lazy expansion)方式。變量的值將會(huì)在使用變量的時(shí)候進(jìn)行展開。 := :立即展開(immediate expansion)的賦值方式。變量的值在賦值的時(shí)候立即展開,并且在后續(xù)的使用中不再改變。

    2024年02月08日
    瀏覽(24)
  • 使用VSCode clangd插件進(jìn)行l(wèi)inux內(nèi)核代碼閱讀和嵌入式開發(fā)

    使用VSCode clangd插件進(jìn)行l(wèi)inux內(nèi)核代碼閱讀和嵌入式開發(fā)

    在進(jìn)行 Linux 內(nèi)核代碼閱讀和嵌入式開發(fā)時(shí),選擇合適的開發(fā)工具至關(guān)重要。VSCode 是一個(gè)流行的跨平臺(tái)編輯器,并且它的擴(kuò)展生態(tài)系統(tǒng)非常強(qiáng)大。在這篇博客中,我們將介紹如何使用 VSCode Clangd 插件來提高 Linux 內(nèi)核代碼的閱讀和嵌入式開發(fā)效率。 Clangd 是一個(gè)基于 Clang 的語言

    2024年02月09日
    瀏覽(30)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包