源码:android9
硬件平台:imx8mm
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
>;
};
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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");
BOARD_VENDOR_KERNEL_MODULES += \
$(KERNEL_OUT)/drivers/net/wireless/bcmdhd/bcmdhd.ko \
$(KERNEL_OUT)/drivers/gpio/gpio-user.ko
加载驱动后,生成节点 /sys/class/gpio_usr/gpio*
简单测试: echo 1 > /sys/class/gpio_usr/gpio*/value
其中gpio* 对应生成的gpio节点