设备树
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