758 lines
19 KiB
C
Executable File
758 lines
19 KiB
C
Executable File
/* 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");
|