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)選配,就能用了文章來源:http://www.zghlxwxcb.cn/news/detail-731054.html
//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)!