blob: 8dcf3c3f5fa94eec270b0b604c5bd223e282124d [file] [log] [blame]
/* Copyright (c) 2013, ZTE-TSP. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <asm/mach/irq.h>
#include <mach/gpio.h>
#include <mach/pcu.h>
#include <mach/irqs.h>
static int zx29_gpio_get(struct gpio_chip *chip, unsigned offset)
{
unsigned int gpio=chip->base+offset;
int ret=0;
ret = zx29_gpio_input_data(gpio);
return ret;
}
static void zx29_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
{
unsigned int gpio=chip->base+offset;
zx29_gpio_output_data(gpio,(unsigned int)val);
}
static int zx29_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
unsigned int gpio=chip->base+offset;
zx29_gpio_set_direction(gpio, GPIO_IN);
return 0;
}
static int zx29_gpio_direction_output(struct gpio_chip *chip,
unsigned offset,
int val)
{
unsigned int gpio=chip->base+offset;
zx29_gpio_set_direction(gpio, GPIO_OUT);
zx29_gpio_output_data(gpio, (unsigned int)val);
return 0;
}
static int zx29_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
unsigned int gpio=chip->base+offset;
return (zx29_gpio2irq(gpio));
}
static struct gpio_chip zx29_gpio =
{
.base = BASE_GPIO,
.ngpio = NGPIO,
.direction_input = zx29_gpio_direction_input,
.direction_output = zx29_gpio_direction_output,
.get = zx29_gpio_get,
.set = zx29_gpio_set,
.to_irq = zx29_gpio_to_irq,
.request = NULL,
.free = NULL,
};
/*
* select gpio multiplex function
* gpio: gpio number
* func: PIN_FUNC_SEL_AON/PIN_FUNC_SEL_PD
* according with register defination
*/
int zx29_gpio_config(unsigned int gpio, gpio_func_id func )
{
return zx29_gpio_function_sel(gpio, func);
}
EXPORT_SYMBOL(zx29_gpio_config);
#if 0
/*add by shideyou 20130731*/
static ssize_t ap_ufi_wps_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
long value;
unsigned int tmp = 0;
unsigned int dir = 0;
struct zx29_gpio_platform_data *pdata = dev->platform_data;
dir = zx29_gpio_get_direction(pdata->ufi_wps);
tmp = zx29_gpio_function_sel_get(pdata->ufi_wps);
tmp = ((tmp>>24)&0x7);
if((tmp==0)||(tmp==2)||(tmp==4)||(tmp==6))
{
value = gpio_get_value(pdata->ufi_wps);
if (value < 0)
return value;
if (value)
if(dir)
strcpy(buf, "input highlevel!\n");
else
strcpy(buf, "output highlevel!\n");
else
if(dir)
strcpy(buf, "input lowlevel!\n");
else
strcpy(buf, "output lowlevel!\n");
return 18;
}
else
{
strcpy(buf, "it is not gpio!\n");
return 16;
}
}
static const DEVICE_ATTR(ap_ufi_wps, 0600, ap_ufi_wps_show, NULL);
static ssize_t ap_ufi_reset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
long value;
unsigned int tmp = 0;
unsigned int dir = 0;
struct zx29_gpio_platform_data *pdata = dev->platform_data;
dir = zx29_gpio_get_direction(pdata->ufi_resetkey);
tmp = zx29_gpio_function_sel_get(pdata->ufi_resetkey);
tmp = ((tmp>>24)&0x3);
if((tmp==0)||(tmp==2)||(tmp==3))
{
value = gpio_get_value(pdata->ufi_resetkey);
if (value < 0)
return value;
if (value)
if(dir)
strcpy(buf, "input highlevel!\n");
else
strcpy(buf, "output highlevel!\n");
else
if(dir)
strcpy(buf, "input lowlevel!\n");
else
strcpy(buf, "output lowlevel!\n");
return 18;
}
else
{
strcpy(buf, "it is not gpio!\n");
return 16;
}
}
static const DEVICE_ATTR(ap_ufi_reset, 0600, ap_ufi_reset_show, NULL);
static ssize_t ap_poweron_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
long value;
unsigned int tmp = 0;
unsigned int dir = 0;
struct zx29_gpio_platform_data *pdata = dev->platform_data;
dir = zx29_gpio_get_direction(pdata->power_on_int);
tmp = zx29_gpio_function_sel_get(pdata->power_on_int);
tmp = ((tmp>>24)&0x7);
if((tmp==0)||(tmp==2)||(tmp==3)||(tmp==4)||(tmp==6))
{
value = gpio_get_value(pdata->power_on_int);
if (value < 0)
return value;
if (value)
if(dir)
strcpy(buf, "input highlevel!\n");
else
strcpy(buf, "output highlevel!\n");
else
if(dir)
strcpy(buf, "input lowlevel!\n");
else
strcpy(buf, "output lowlevel!\n");
return 18;
}
else
{
strcpy(buf, "it is not gpio!\n");
return 16;
}
}
static const DEVICE_ATTR(ap_poweron, 0600, ap_poweron_show, NULL);
static ssize_t ap_wifi_hostwake_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
long value;
unsigned int tmp = 0;
unsigned int dir = 0;
struct zx29_gpio_platform_data *pdata = dev->platform_data;
dir = zx29_gpio_get_direction(pdata->wifi_host_wake);
tmp = zx29_gpio_function_sel_get(pdata->wifi_host_wake);
tmp = ((tmp>>24)&0x7);
if((tmp==0)||(tmp==2)||(tmp==4)||(tmp==6))
{
value = gpio_get_value(pdata->wifi_host_wake);
if (value < 0)
return value;
if (value)
if(dir)
strcpy(buf, "input highlevel!\n");
else
strcpy(buf, "output highlevel!\n");
else
if(dir)
strcpy(buf, "input lowlevel!\n");
else
strcpy(buf, "output lowlevel!\n");
return 18;
}
else
{
strcpy(buf, "it is not gpio!\n");
return 16;
}
}
static const DEVICE_ATTR(ap_wifi_hostwake, 0600, ap_wifi_hostwake_show, NULL);
static ssize_t ap_charger_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
long value;
unsigned int tmp = 0;
unsigned int dir = 0;
struct zx29_gpio_platform_data *pdata = dev->platform_data;
dir = zx29_gpio_get_direction(pdata->charger_pg);
tmp = zx29_gpio_function_sel_get(pdata->charger_pg);
tmp = ((tmp>>24)&0x3);
if((tmp==0)||(tmp==2))
{
value = gpio_get_value(pdata->charger_pg);
if (value < 0)
return value;
if (value)
if(dir)
strcpy(buf, "input highlevel!\n");
else
strcpy(buf, "output highlevel!\n");
else
if(dir)
strcpy(buf, "input lowlevel!\n");
else
strcpy(buf, "output lowlevel!\n");
return 18;
}
else
{
strcpy(buf, "it is not gpio!\n");
return 16;
}
}
static const DEVICE_ATTR(ap_charger, 0600, ap_charger_show, NULL);
#endif
static const struct attribute *ap_gpio_attrs[] = {
#if 0
&dev_attr_ap_ufi_reset.attr,
&dev_attr_ap_poweron.attr,
&dev_attr_ap_ufi_wps.attr,
&dev_attr_ap_wifi_hostwake.attr,
&dev_attr_ap_charger.attr,
#endif
NULL,
};
static const struct attribute_group ap_gpio_attr_group = {
.attrs = (struct attribute **) ap_gpio_attrs,
};
static int __init zx29_gpio_probe(struct platform_device *dev)
{
int ret;
zx29_gpio.label = dev->name;
ret = gpiochip_add(&zx29_gpio);
if (ret < 0)
return ret;
pr_info("[GPIO]create sysfs interface\n");
ret = sysfs_create_group(&dev->dev.kobj, &ap_gpio_attr_group);
if (ret)
printk(KERN_WARNING "[GPIO]sysfs_create_group ret %d\n", ret);
return 0;
}
static int zx29_gpio_remove(struct platform_device *dev)
{
int ret = gpiochip_remove(&zx29_gpio);
if (ret) {
dev_err(&dev->dev, "%s failed, %d\n",
"gpiochip_remove()", ret);
return ret;
}
return 0;
}
static struct platform_driver zx29_gpio_driver = {
.driver = {
.name = "zx29_gpio",
.owner = THIS_MODULE,
},
.probe = zx29_gpio_probe,
.remove = __devexit_p(zx29_gpio_remove),
};
static struct zx29_gpio_platform_data zx29_gpio_data=
{
.ufi_resetkey = GPIO_UFI_RESETKEY,
.power_on_int = GPIO_POWER_ON_INT,
.ufi_wps = GPIO_UFI_WPS,
.wifi_host_wake = GPIO_WIFI_HOST_WAKE,
.charger_pg = GPIO_CHARGER_PG,
};
static struct platform_device zx29_device_gpio =
{
.name = "zx29_gpio",
.id = -1,
.dev = {
.platform_data = &zx29_gpio_data,
},
};
static int __init zx29_gpio_init(void)
{
int rc;
rc = platform_driver_register(&zx29_gpio_driver);
if (!rc) {
rc = platform_device_register(&zx29_device_gpio);
if (rc)
platform_driver_unregister(&zx29_gpio_driver);
}
if(!rc)
pr_info("[GPIO]zx29 GPIO initialized\n");
return rc;
}
static void __exit zx29_gpio_exit(void)
{
platform_device_unregister(&zx29_device_gpio);
platform_driver_unregister(&zx29_gpio_driver);
}
postcore_initcall(zx29_gpio_init);
module_exit(zx29_gpio_exit);