mirror of https://github.com/OpenIPC/firmware.git
762 lines
20 KiB
Diff
762 lines
20 KiB
Diff
diff -drupN a/drivers/gpio/gpio-sunxi.c b/drivers/gpio/gpio-sunxi.c
|
|
--- a/drivers/gpio/gpio-sunxi.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/gpio/gpio-sunxi.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -0,0 +1,757 @@
|
|
+/* driver/misc/sunxi-reg.c
|
|
+ *
|
|
+ * Copyright (C) 2011 Reuuimlla Technology Co.Ltd
|
|
+ * Charles <yanjianbo@allwinnertech.com>
|
|
+ *
|
|
+ * www.reuuimllatech.com
|
|
+ *
|
|
+ * User access to the registers driver.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ */
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/spinlock.h>
|
|
+#include <linux/rwsem.h>
|
|
+#include <linux/timer.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/ctype.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/ctype.h>
|
|
+#include <linux/sysfs.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/sunxi-gpio.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/of_gpio.h>
|
|
+#include <linux/device.h>
|
|
+#include "../base/base.h"
|
|
+
|
|
+#define PINS_PER_BANK 32
|
|
+DECLARE_RWSEM(gpio_sw_list_lock);
|
|
+LIST_HEAD(gpio_sw_list);
|
|
+static struct class *gpio_sw_class;
|
|
+static int network_led_data_suspend;
|
|
+
|
|
+struct sw_gpio_pd {
|
|
+ char name[16];
|
|
+ char link[16];
|
|
+ unsigned int light;
|
|
+};
|
|
+
|
|
+struct gpio_sw_classdev {
|
|
+ const char *name;
|
|
+ unsigned int pull, drv, cfg;
|
|
+ struct mutex class_mutex;
|
|
+ struct gpio_config *item;
|
|
+ int (*gpio_sw_cfg_set) (struct gpio_sw_classdev *gpio_sw_cdev,
|
|
+ int mul_cfg);
|
|
+ int (*gpio_sw_pull_set) (struct gpio_sw_classdev *gpio_sw_cdev,
|
|
+ int mul_cfg);
|
|
+ int (*gpio_sw_data_set) (struct gpio_sw_classdev *gpio_sw_cdev,
|
|
+ int mul_cfg);
|
|
+ int (*gpio_sw_drv_set) (struct gpio_sw_classdev *gpio_sw_cdev,
|
|
+ int mul_cfg);
|
|
+ int (*gpio_sw_cfg_get) (struct gpio_sw_classdev *gpio_sw_cdev);
|
|
+ int (*gpio_sw_pull_get) (struct gpio_sw_classdev *gpio_sw_cdev);
|
|
+ int (*gpio_sw_data_get) (struct gpio_sw_classdev *gpio_sw_cdev);
|
|
+ int (*gpio_sw_drv_get) (struct gpio_sw_classdev *gpio_sw_cdev);
|
|
+ struct device *dev;
|
|
+ struct list_head node;
|
|
+};
|
|
+
|
|
+struct sw_gpio {
|
|
+ struct sw_gpio_pd *pdata;
|
|
+ spinlock_t lock;
|
|
+ struct gpio_sw_classdev class;
|
|
+};
|
|
+
|
|
+static struct platform_device *gpio_sw_dev[256];
|
|
+static struct sw_gpio_pd *sw_pdata[256];
|
|
+struct device_node *node;
|
|
+static unsigned int easy_light_used;
|
|
+
|
|
+/*
|
|
+ * mul_cfg: 0 - inpit
|
|
+ * 1 - output
|
|
+*/
|
|
+static int gpio_sw_cfg_set(struct gpio_sw_classdev *gpio_sw_cdev, int mul_cfg)
|
|
+{
|
|
+ char pin_name[32];
|
|
+ unsigned long config;
|
|
+
|
|
+ if (mul_cfg == 0)
|
|
+ gpio_direction_input(gpio_sw_cdev->item->gpio);
|
|
+ else if (mul_cfg == 1)
|
|
+ gpio_direction_output(gpio_sw_cdev->item->gpio, 0);
|
|
+ else if (mul_cfg > 1 && mul_cfg <= 7) {
|
|
+ sunxi_gpio_to_name(gpio_sw_cdev->item->gpio, pin_name);
|
|
+ config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, mul_cfg);
|
|
+ if (gpio_sw_cdev->item->gpio < SUNXI_PL_BASE)
|
|
+ pin_config_set(SUNXI_PINCTRL, pin_name, config);
|
|
+ else
|
|
+ pin_config_set(SUNXI_R_PINCTRL, pin_name, config);
|
|
+ }
|
|
+ gpio_sw_cdev->cfg = mul_cfg;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_sw_cfg_get(struct gpio_sw_classdev *gpio_sw_cdev)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_sw_pull_set(struct gpio_sw_classdev *gpio_sw_cdev, int pull)
|
|
+{
|
|
+ char pin_name[32];
|
|
+ unsigned long config;
|
|
+
|
|
+ if (pull >= 0 && pull <= 3) {
|
|
+ sunxi_gpio_to_name(gpio_sw_cdev->item->gpio, pin_name);
|
|
+ config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, pull);
|
|
+ if (gpio_sw_cdev->item->gpio < SUNXI_PL_BASE)
|
|
+ pin_config_set(SUNXI_PINCTRL, pin_name, config);
|
|
+ else
|
|
+ pin_config_set(SUNXI_R_PINCTRL, pin_name, config);
|
|
+ }
|
|
+
|
|
+ gpio_sw_cdev->pull = pull;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_sw_pull_get(struct gpio_sw_classdev *gpio_sw_cdev)
|
|
+{
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_sw_drv_set(struct gpio_sw_classdev *gpio_sw_cdev, int drv)
|
|
+{
|
|
+ char pin_name[32];
|
|
+ unsigned long config;
|
|
+
|
|
+ if (drv >= 0 && drv <= 3) {
|
|
+ sunxi_gpio_to_name(gpio_sw_cdev->item->gpio, pin_name);
|
|
+ config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DRV, drv);
|
|
+ if (gpio_sw_cdev->item->gpio < SUNXI_PL_BASE)
|
|
+ pin_config_set(SUNXI_PINCTRL, pin_name, config);
|
|
+ else
|
|
+ pin_config_set(SUNXI_R_PINCTRL, pin_name, config);
|
|
+ }
|
|
+
|
|
+ gpio_sw_cdev->drv = drv;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_sw_drv_get(struct gpio_sw_classdev *gpio_sw_cdev)
|
|
+{
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_sw_data_set(struct gpio_sw_classdev *gpio_sw_cdev, int data)
|
|
+{
|
|
+ __gpio_set_value(gpio_sw_cdev->item->gpio, data);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_sw_data_get(struct gpio_sw_classdev *gpio_sw_cdev)
|
|
+{
|
|
+ return __gpio_get_value(gpio_sw_cdev->item->gpio);
|
|
+}
|
|
+
|
|
+static ssize_t cfg_sel_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+ int length;
|
|
+
|
|
+ mutex_lock(&gpio_sw_cdev->class_mutex);
|
|
+ gpio_sw_cdev->item->mul_sel =
|
|
+ gpio_sw_cdev->gpio_sw_drv_get(gpio_sw_cdev);
|
|
+ length = sprintf(buf, "%u\n", gpio_sw_cdev->cfg);
|
|
+ mutex_unlock(&gpio_sw_cdev->class_mutex);
|
|
+
|
|
+ return length;
|
|
+}
|
|
+
|
|
+static ssize_t pull_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+ int length;
|
|
+
|
|
+ mutex_lock(&gpio_sw_cdev->class_mutex);
|
|
+ gpio_sw_cdev->item->pull = gpio_sw_cdev->gpio_sw_drv_get(gpio_sw_cdev);
|
|
+ length = sprintf(buf, "%u\n", gpio_sw_cdev->pull);
|
|
+ mutex_unlock(&gpio_sw_cdev->class_mutex);
|
|
+
|
|
+ return length;
|
|
+}
|
|
+
|
|
+static ssize_t drv_level_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+ int length;
|
|
+
|
|
+ mutex_lock(&gpio_sw_cdev->class_mutex);
|
|
+ gpio_sw_cdev->item->drv_level =
|
|
+ gpio_sw_cdev->gpio_sw_drv_get(gpio_sw_cdev);
|
|
+ length = sprintf(buf, "%u\n", gpio_sw_cdev->drv);
|
|
+ mutex_unlock(&gpio_sw_cdev->class_mutex);
|
|
+
|
|
+ return length;
|
|
+}
|
|
+
|
|
+static ssize_t data_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+ int length;
|
|
+
|
|
+ mutex_lock(&gpio_sw_cdev->class_mutex);
|
|
+ gpio_sw_cdev->item->data = gpio_sw_cdev->gpio_sw_data_get(gpio_sw_cdev);
|
|
+ length = sprintf(buf, "%u\n", gpio_sw_cdev->item->data);
|
|
+ mutex_unlock(&gpio_sw_cdev->class_mutex);
|
|
+
|
|
+ return length;
|
|
+}
|
|
+
|
|
+static ssize_t cfg_sel_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf,
|
|
+ size_t size)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+ unsigned long cfg;
|
|
+
|
|
+ if (kstrtoul(buf, 10, &cfg))
|
|
+ return -EINVAL;
|
|
+ if (cfg > 7) {
|
|
+ return size;
|
|
+ }
|
|
+ gpio_sw_cdev->gpio_sw_cfg_set(gpio_sw_cdev, cfg);
|
|
+ return size;
|
|
+}
|
|
+
|
|
+static ssize_t pull_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf,
|
|
+ size_t size)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+ unsigned long pull;
|
|
+
|
|
+ if (kstrtoul(buf, 10, &pull))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (pull > 3) {
|
|
+ return size;
|
|
+ }
|
|
+
|
|
+ gpio_sw_cdev->gpio_sw_pull_set(gpio_sw_cdev, pull);
|
|
+ return size;
|
|
+}
|
|
+
|
|
+static ssize_t drv_level_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf,
|
|
+ size_t size)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+
|
|
+ unsigned long drv_level;
|
|
+ if (kstrtoul(buf, 10, &drv_level))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (drv_level > 3) {
|
|
+ return size;
|
|
+ }
|
|
+
|
|
+ gpio_sw_cdev->gpio_sw_drv_set(gpio_sw_cdev, drv_level);
|
|
+ return size;
|
|
+}
|
|
+
|
|
+static ssize_t data_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf,
|
|
+ size_t size)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+ unsigned long data;
|
|
+
|
|
+ if (kstrtoul(buf, 10, &data))
|
|
+ return -EINVAL;
|
|
+
|
|
+ gpio_sw_cdev->gpio_sw_data_set(gpio_sw_cdev, data);
|
|
+ return size;
|
|
+}
|
|
+
|
|
+static ssize_t light_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf,
|
|
+ size_t size)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+ struct sw_gpio_pd *pdata = dev->parent->platform_data;
|
|
+ unsigned long data;
|
|
+
|
|
+ if (kstrtoul(buf, 10, &data))
|
|
+ return -EINVAL;
|
|
+ if (data)
|
|
+ gpio_sw_cdev->gpio_sw_data_set(gpio_sw_cdev, pdata->light ? 1 : 0);
|
|
+ else
|
|
+ gpio_sw_cdev->gpio_sw_data_set(gpio_sw_cdev, pdata->light ? 0 : 1);
|
|
+
|
|
+ return size;
|
|
+}
|
|
+
|
|
+static int gpio_sw_suspend(struct device *dev, pm_message_t state)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+ struct sw_gpio_pd *pdata = dev->parent->platform_data;
|
|
+
|
|
+ if (strcmp(pdata->link, "network_led") == 0) {
|
|
+ network_led_data_suspend = gpio_sw_cdev->gpio_sw_data_get(\
|
|
+ gpio_sw_cdev);
|
|
+ gpio_sw_cdev->gpio_sw_data_set(gpio_sw_cdev, \
|
|
+ pdata->light ? 0 : 1);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_sw_resume(struct device *dev)
|
|
+{
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev = dev_get_drvdata(dev);
|
|
+ struct sw_gpio_pd *pdata = dev->parent->platform_data;
|
|
+
|
|
+ if (strcmp(pdata->link, "network_led") == 0) {
|
|
+ gpio_sw_cdev->gpio_sw_data_set(gpio_sw_cdev, network_led_data_suspend);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct device_attribute gpio_sw_class_attrs[] = {
|
|
+ __ATTR(cfg, 0664, cfg_sel_show, cfg_sel_store),
|
|
+ __ATTR(pull, 0664, pull_show, pull_store),
|
|
+ __ATTR(drv, 0664, drv_level_show, drv_level_store),
|
|
+ __ATTR(data, 0664, data_show, data_store),
|
|
+ __ATTR_NULL,
|
|
+};
|
|
+
|
|
+static struct device_attribute easy_light_attr =
|
|
+ __ATTR(light, 0664, data_show, light_store);
|
|
+
|
|
+void gpio_sw_classdev_unregister(struct gpio_sw_classdev *gpio_sw_cdev)
|
|
+{
|
|
+ mutex_destroy(&gpio_sw_cdev->class_mutex);
|
|
+ device_unregister(gpio_sw_cdev->dev);
|
|
+ down_write(&gpio_sw_list_lock);
|
|
+ list_del(&gpio_sw_cdev->node);
|
|
+ up_write(&gpio_sw_list_lock);
|
|
+}
|
|
+
|
|
+static int gpio_sw_remove(struct platform_device *dev)
|
|
+{
|
|
+ struct sw_gpio *sw_gpio_entry = platform_get_drvdata(dev);
|
|
+ struct sw_gpio_pd *pdata = dev->dev.platform_data;
|
|
+
|
|
+ if (strlen(pdata->link) != 0)
|
|
+ sysfs_remove_link(&gpio_sw_class->p->subsys.kobj, pdata->link);
|
|
+
|
|
+ gpio_sw_classdev_unregister(&sw_gpio_entry->class);
|
|
+ kfree(sw_gpio_entry->class.item);
|
|
+ kfree(sw_gpio_entry);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int
|
|
+gpio_sw_classdev_register(struct device *parent,
|
|
+ struct gpio_sw_classdev *gpio_sw_cdev)
|
|
+{
|
|
+ struct sw_gpio_pd *pdata = parent->platform_data;
|
|
+
|
|
+ gpio_sw_cdev->dev = device_create(gpio_sw_class, parent, 0,
|
|
+ gpio_sw_cdev, "%s",
|
|
+ gpio_sw_cdev->name);
|
|
+ if (IS_ERR(gpio_sw_cdev->dev))
|
|
+ return PTR_ERR(gpio_sw_cdev->dev);
|
|
+ if (easy_light_used && strlen(pdata->link)) {
|
|
+ if (sysfs_create_file(&gpio_sw_cdev->dev->kobj, &easy_light_attr.attr))
|
|
+ pr_err("gpio_sw: sysfs_create_file fail\n");
|
|
+ }
|
|
+ down_write(&gpio_sw_list_lock);
|
|
+ list_add_tail(&gpio_sw_cdev->node, &gpio_sw_list);
|
|
+ up_write(&gpio_sw_list_lock);
|
|
+ mutex_init(&gpio_sw_cdev->class_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int map_gpio_to_name(char *name, u32 gpio)
|
|
+{
|
|
+ char base;
|
|
+ int num;
|
|
+ num = gpio - SUNXI_PA_BASE;
|
|
+ if (num < 0)
|
|
+ goto map_fail;
|
|
+
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'A';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - SUNXI_PB_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'B';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - SUNXI_PC_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'C';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - SUNXI_PD_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'D';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - SUNXI_PE_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'E';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - SUNXI_PF_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'F';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - SUNXI_PG_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'G';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - SUNXI_PH_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'H';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - SUNXI_PJ_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'J';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - SUNXI_PL_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'L';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - SUNXI_PM_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'M';
|
|
+ goto map_done;
|
|
+ }
|
|
+ num = gpio - AXP_PIN_BASE;
|
|
+ if ((num >= 0) && (num < PINS_PER_BANK)) {
|
|
+ base = 'X';
|
|
+ goto map_done;
|
|
+ }
|
|
+ goto map_fail;
|
|
+map_done:
|
|
+ sprintf(name, "P%c%d", base, num);
|
|
+ return 0;
|
|
+map_fail:
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static void gpio_sw_release(struct device *dev)
|
|
+{
|
|
+ pr_info("gpio_sw_release good !\n");
|
|
+}
|
|
+
|
|
+static int gpio_suspend(struct platform_device *dev, pm_message_t state)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_resume(struct platform_device *dev)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gpio_sw_probe(struct platform_device *dev)
|
|
+{
|
|
+ struct sw_gpio *sw_gpio_entry;
|
|
+ struct sw_gpio_pd *pdata = dev->dev.platform_data;
|
|
+ int ret;
|
|
+ unsigned long flags;
|
|
+ char io_area[16];
|
|
+ int gpio;
|
|
+
|
|
+ sw_gpio_entry = kzalloc(sizeof(struct sw_gpio), GFP_KERNEL);
|
|
+ if (!sw_gpio_entry)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ sw_gpio_entry->class.item =
|
|
+ kzalloc(sizeof(struct gpio_config), GFP_KERNEL);
|
|
+ if (!sw_gpio_entry->class.item) {
|
|
+ kfree(sw_gpio_entry);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ /* pr_info("gpio_name: %s\n", pdata->name); */
|
|
+ gpio = of_get_named_gpio_flags(node, pdata->name, 0,
|
|
+ (enum of_gpio_flags *)sw_gpio_entry->
|
|
+ class.item);
|
|
+ if (!gpio_is_valid(gpio)) {
|
|
+ pr_err("get config err!\n");
|
|
+ kfree(sw_gpio_entry->class.item);
|
|
+ kfree(sw_gpio_entry);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* init the */
|
|
+ sw_gpio_entry->class.cfg = (sw_gpio_entry->class.item)->mul_sel;
|
|
+ sw_gpio_entry->class.pull = (sw_gpio_entry->class.item)->pull;
|
|
+ sw_gpio_entry->class.drv = (sw_gpio_entry->class.item)->drv_level;
|
|
+
|
|
+ ret = map_gpio_to_name(io_area, sw_gpio_entry->class.item->gpio);
|
|
+ pr_info("gpio name is %s, ret = %d\n", io_area, ret);
|
|
+
|
|
+ platform_set_drvdata(dev, sw_gpio_entry);
|
|
+ spin_lock_init(&sw_gpio_entry->lock);
|
|
+ spin_lock_irqsave(&sw_gpio_entry->lock, flags);
|
|
+ sw_gpio_entry->pdata = pdata;
|
|
+
|
|
+ if (ret == 0)
|
|
+ sw_gpio_entry->class.name = io_area;
|
|
+ else
|
|
+ sw_gpio_entry->class.name = pdata->name;
|
|
+
|
|
+ sw_gpio_entry->class.gpio_sw_cfg_set = gpio_sw_cfg_set;
|
|
+ sw_gpio_entry->class.gpio_sw_cfg_get = gpio_sw_cfg_get;
|
|
+ sw_gpio_entry->class.gpio_sw_pull_set = gpio_sw_pull_set;
|
|
+ sw_gpio_entry->class.gpio_sw_pull_get = gpio_sw_pull_get;
|
|
+ sw_gpio_entry->class.gpio_sw_drv_set = gpio_sw_drv_set;
|
|
+ sw_gpio_entry->class.gpio_sw_drv_get = gpio_sw_drv_get;
|
|
+ sw_gpio_entry->class.gpio_sw_data_set = gpio_sw_data_set;
|
|
+ sw_gpio_entry->class.gpio_sw_data_get = gpio_sw_data_get;
|
|
+
|
|
+
|
|
+ /* init the gpio form sys_config */
|
|
+ gpio_sw_cfg_set(&sw_gpio_entry->class,
|
|
+ sw_gpio_entry->class.item->mul_sel);
|
|
+ gpio_sw_data_set(&sw_gpio_entry->class,
|
|
+ sw_gpio_entry->class.item->data);
|
|
+
|
|
+ spin_unlock_irqrestore(&sw_gpio_entry->lock, flags);
|
|
+
|
|
+ ret = gpio_sw_classdev_register(&dev->dev, &sw_gpio_entry->class);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&dev->dev, "gpio_sw_classdev_register failed\n");
|
|
+ kfree(sw_gpio_entry->class.item);
|
|
+ kfree(sw_gpio_entry);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* create symbol link */
|
|
+ if (strlen(pdata->link) != 0) {
|
|
+ ret = sysfs_create_link(&gpio_sw_class->p->subsys.kobj,
|
|
+ &sw_gpio_entry->class.dev->kobj,
|
|
+ pdata->link);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct platform_driver gpio_sw_driver = {
|
|
+ .probe = gpio_sw_probe,
|
|
+ .remove = gpio_sw_remove,
|
|
+ .suspend = gpio_suspend,
|
|
+ .resume = gpio_resume,
|
|
+ .driver = {
|
|
+ .name = "gpio_sw",
|
|
+ .owner = THIS_MODULE,
|
|
+ },
|
|
+};
|
|
+
|
|
+static void __exit gpio_sw_exit(void)
|
|
+{
|
|
+ int i, cnt;
|
|
+ struct gpio_config config;
|
|
+ int gpio;
|
|
+ char gpio_name[32];
|
|
+ int ret;
|
|
+
|
|
+ platform_driver_unregister(&gpio_sw_driver);
|
|
+
|
|
+ ret = of_property_read_u32(node, "gpio_num", &cnt);
|
|
+ if (ret || !cnt) {
|
|
+ pr_info("these is zero number for gpio\n");
|
|
+ goto EXIT_END;
|
|
+ }
|
|
+ for (i = 0; i < cnt; i++) {
|
|
+ sprintf(gpio_name, "gpio_pin_%d", i + 1);
|
|
+ gpio =
|
|
+ of_get_named_gpio_flags(node, gpio_name, 0,
|
|
+ (enum of_gpio_flags *)&config);
|
|
+ if (!gpio_is_valid(gpio)) {
|
|
+ pr_err("this gpio is invalid: %d\n", gpio);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ platform_device_unregister(gpio_sw_dev[i]);
|
|
+ kfree(gpio_sw_dev[i]);
|
|
+ kfree(sw_pdata[i]);
|
|
+ gpio_free(gpio);
|
|
+ }
|
|
+
|
|
+ class_destroy(gpio_sw_class);
|
|
+EXIT_END:
|
|
+ pr_info("gpio_exit finish !\n");
|
|
+}
|
|
+
|
|
+static int sunxi_init_gpio_probe(struct platform_device *pdev)
|
|
+{
|
|
+ int i, cnt;
|
|
+ struct gpio_config config;
|
|
+ int gpio;
|
|
+ char gpio_name[32];
|
|
+ int ret;
|
|
+ const char *normal_led_pin_str = NULL;
|
|
+ const char *standby_led_pin_str = NULL;
|
|
+ const char *network_led_pin_str = NULL;
|
|
+
|
|
+ node = pdev->dev.of_node;
|
|
+ if (!node)
|
|
+ goto INIT_END;
|
|
+
|
|
+ /* create debug dir: /sys/class/gpio_sw */
|
|
+ gpio_sw_class = class_create(THIS_MODULE, "gpio_sw");
|
|
+ if (IS_ERR(gpio_sw_class))
|
|
+ return PTR_ERR(gpio_sw_class);
|
|
+
|
|
+ gpio_sw_class->suspend = gpio_sw_suspend;
|
|
+ gpio_sw_class->resume = gpio_sw_resume;
|
|
+ gpio_sw_class->class_attrs = (struct class_attribute *)gpio_sw_class_attrs;
|
|
+
|
|
+ if (of_property_read_u32(node, "easy_light_used", &easy_light_used)) {
|
|
+ easy_light_used = 0;
|
|
+ pr_err("failed to get easy_light_used assign\n");
|
|
+ }
|
|
+ if (of_property_read_string(node, "normal_led", &normal_led_pin_str))
|
|
+ pr_err("failed to get normal led pin assign\n");
|
|
+
|
|
+ if (of_property_read_string(node, "standby_led", &standby_led_pin_str))
|
|
+ pr_err("failed to get standby led pin assign\n");
|
|
+
|
|
+ if (of_property_read_string(node, "network_led", &network_led_pin_str))
|
|
+ pr_err("failed to get standby led pin assign\n");
|
|
+
|
|
+ ret = of_property_read_u32(node, "gpio_num", &cnt);
|
|
+ if (ret || !cnt) {
|
|
+ pr_err("these is zero number for gpio\n");
|
|
+ goto INIT_END;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < cnt; i++) {
|
|
+ sprintf(gpio_name, "gpio_pin_%d", i + 1);
|
|
+ gpio =
|
|
+ of_get_named_gpio_flags(node, gpio_name, 0,
|
|
+ (enum of_gpio_flags *)&config);
|
|
+ /*printk(KERN_EMERG"gpio = %d, mul = %d, drv= %d, pull= %d, data = %d\n",\
|
|
+ config.gpio, config.mul_sel, config.drv_level, config.pull, config.data);*/
|
|
+ if (gpio_request(gpio, NULL)) {
|
|
+ pr_err("gpio_pin_%d(%d) gpio_request fail\n", i + 1,
|
|
+ gpio);
|
|
+ continue;
|
|
+ }
|
|
+ pr_info("gpio_pin_%d(%d) gpio_is_valid\n", i + 1, config.gpio);
|
|
+
|
|
+ sw_pdata[i] = kzalloc(sizeof(struct sw_gpio_pd), GFP_KERNEL);
|
|
+ if (!sw_pdata[i]) {
|
|
+ pr_err("kzalloc fail for sw_pdata[%d]\n", i);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ gpio_sw_dev[i] =
|
|
+ kzalloc(sizeof(struct platform_device), GFP_KERNEL);
|
|
+ if (!gpio_sw_dev[i]) {
|
|
+ pr_err("kzalloc fail for gpio_sw_dev[%d]\n", i);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ sprintf(sw_pdata[i]->name, "gpio_pin_%d", i + 1);
|
|
+ if (normal_led_pin_str
|
|
+ && !strcmp(sw_pdata[i]->name, normal_led_pin_str)) {
|
|
+ sprintf(sw_pdata[i]->link, "%s", "normal_led");
|
|
+ if (easy_light_used)
|
|
+ of_property_read_u32(node, "normal_led_light", &sw_pdata[i]->light);
|
|
+ } else if (standby_led_pin_str
|
|
+ && !strcmp(sw_pdata[i]->name, standby_led_pin_str)) {
|
|
+ sprintf(sw_pdata[i]->link, "%s", "standby_led");
|
|
+ if (easy_light_used)
|
|
+ of_property_read_u32(node, "standby_led_light", &sw_pdata[i]->light);
|
|
+ } else if (network_led_pin_str
|
|
+ && !strcmp(sw_pdata[i]->name, network_led_pin_str)) {
|
|
+ sprintf(sw_pdata[i]->link, "%s", "network_led");
|
|
+ if (easy_light_used)
|
|
+ of_property_read_u32(node, "network_led_light", &sw_pdata[i]->light);
|
|
+ }
|
|
+
|
|
+ gpio_sw_dev[i]->name = "gpio_sw";
|
|
+ gpio_sw_dev[i]->id = i;
|
|
+ gpio_sw_dev[i]->dev.platform_data = sw_pdata[i];
|
|
+ gpio_sw_dev[i]->dev.release = gpio_sw_release;
|
|
+
|
|
+ if (platform_device_register(gpio_sw_dev[i])) {
|
|
+ pr_err("%s platform_device_register fail\n",
|
|
+ sw_pdata[i]->name);
|
|
+ goto INIT_ERR_FREE;
|
|
+ }
|
|
+ }
|
|
+ if (platform_driver_register(&gpio_sw_driver)) {
|
|
+ pr_err("gpio user platform_driver_register fail\n");
|
|
+ for (i = 0; i < cnt; i++)
|
|
+ platform_device_unregister(gpio_sw_dev[i]);
|
|
+ goto INIT_ERR_FREE;
|
|
+ }
|
|
+
|
|
+INIT_END:
|
|
+ pr_info("gpio_init finish with uesd\n");
|
|
+ return 0;
|
|
+INIT_ERR_FREE:
|
|
+ pr_err("gpio_init err\n");
|
|
+ kfree(sw_pdata[i]);
|
|
+ kfree(gpio_sw_dev[i]);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static const struct of_device_id sunxi_gpio_of_match[] = {
|
|
+ {.compatible = "allwinner,sunxi-init-gpio", .data = NULL},
|
|
+ { /* sentinel */ }
|
|
+};
|
|
+
|
|
+static struct platform_driver sunxi_gpio_driver = {
|
|
+ .driver = {
|
|
+ .name = "sunxi-init-gpio",
|
|
+ .of_match_table = of_match_ptr(sunxi_gpio_of_match),
|
|
+ },
|
|
+ .probe = sunxi_init_gpio_probe,
|
|
+};
|
|
+
|
|
+static int __init sunxi_gpio_init(void)
|
|
+{
|
|
+ if (platform_driver_register(&sunxi_gpio_driver)) {
|
|
+ pr_err("gpio user platform_driver_register fail\n");
|
|
+ return -1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+module_init(sunxi_gpio_init);
|
|
+module_exit(gpio_sw_exit);
|
|
+
|
|
+MODULE_AUTHOR("yanjianbo");
|
|
+MODULE_DESCRIPTION("SW GPIO USER driver");
|
|
+MODULE_LICENSE("GPL");
|