| /* 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); |