IMX8M增加gpio 接口驱动

源码:android9
硬件平台:imx8mm

  1. 设备树
gpio_para {
                device_type = "gpio_para";
                compatible = "user,user-init-gpio";
                status = "okay";
                gpio_num = <2>;
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_gpio_grps>;
                gpio_1 = <&gpio5 3 GPIO_ACTIVE_HIGH>;
                gpio_2 = <&gpio5 4 GPIO_ACTIVE_HIGH>;
        };


pinctrl_gpio_grps: gpiogrps {
                   fsl,pins = <
                            	MX8MM_IOMUXC_SPDIF_RX_GPIO5_IO4 0x19
                                MX8MM_IOMUXC_SPDIF_TX_GPIO5_IO3 0x19
                   >;
                };
  1. 驱动
#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/gpio.h>
#define GPIOX_MAX_NUM 30

struct gpiox_device_t {
	int gpio;
	struct device * dev;
	
	int active_low;
	int direction;
	int edge;
	int value;
};

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"))
        {
                if(xdev->direction == 0)
                        return strlcpy(buf, "0\n", 3);
                else
                        return strlcpy(buf, "1\n", 3);
        }
        return strlcpy(buf, "0\n", 3);
}

static ssize_t gpiox_direction_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, "direction"))
        {
                if(on)
                        gpio_direction_output(xdev->gpio, 1);
                else
                        gpio_direction_output(xdev->gpio, 0);
        }
        xdev->direction = on ? 1 : 0;
        return count;
}

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;

	printk("this is gpio %d data\n",xdev->gpio);
        if(!strcmp(attr->attr.name, "value"))
        {
                if(xdev->value == 0)
                        return strlcpy(buf, "0\n", 3);
                else
                        return strlcpy(buf, "1\n", 3);
        }
        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,
};

#if 1
static int init_device_data(struct gpiox_device_t *dev,int gpio,int active,int direction,int edge,int value)
{
	if(dev == NULL)
		return -1;

	dev->gpio = gpio;
        dev->active_low = active;
        dev->direction = direction;
        dev->edge = edge;
        dev->value = value;

	if(direction)
                gpio_direction_output(dev->gpio, 0);
        else
                gpio_direction_output(dev->gpio, 1);

	return 0;
}
#endif
static int create_device_nodes(struct gpiox_class_t *cls,int gpio_num)
{
	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,0,1,0,0);

	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, "gpio_num", &cnt);
        if (ret || !cnt) {
                pr_err("these is zero number for gpio\n");
        	goto INIT_ERR_FREE;
	}
	printk("ZWQ get gpio_num:%d\n",cnt);
  
	for (i = 0; i < cnt; i++) {
                sprintf(gpio_name, "gpio_%d", i + 1);
                gpio =
                    of_get_named_gpio_flags(node, gpio_name, 0,&flags);

                if (gpio_request(gpio, NULL)) {
                        pr_err("gpio_%d(%d) gpio_request fail\n",i + 1,gpio);
                        continue;
                }
                printk("gpio_%d(%d) gpio_is_valid\n", i + 1, gpio);
		create_device_nodes(gpiox_class,gpio);
	}
	
	dev_set_drvdata(&pdev->dev, gpiox_class);
        pr_info("gpio_init finish with uesd\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, [email protected]");
MODULE_LICENSE("GPL");

  1. device/fsl/imx8m/evk_8mm/BoardConfig.mk修改
 BOARD_VENDOR_KERNEL_MODULES += \
     $(KERNEL_OUT)/drivers/net/wireless/bcmdhd/bcmdhd.ko \
+    $(KERNEL_OUT)/drivers/gpio/gpio-user.ko
  1. 使用
加载驱动后,生成节点 /sys/class/gpio_usr/gpio*
简单测试: echo 1 > /sys/class/gpio_usr/gpio*/value

其中gpio* 对应生成的gpio节点

猜你喜欢

转载自blog.csdn.net/weixin_43479963/article/details/106739608
今日推荐