diff options
Diffstat (limited to 'drivers/gpio')
| -rw-r--r-- | drivers/gpio/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpio/gpio-max77663.c | 672 |
3 files changed, 680 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8c2a23aa209f..2fa0b1740c0f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -236,6 +236,13 @@ config GPIO_MAX732X_IRQ Say yes here to enable the max732x to be used as an interrupt controller. It requires the driver to be built in the kernel. +config GPIO_MAX77663 + bool "GPIO suport for PMIC MAX77663" + depends on MFD_MAX77663 + help + Say yes here to enable the max77663 to be used as gpio controller. + It requires the driver to be built in the kernel. + config GPIO_MC9S08DZ60 bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions" depends on I2C && MACH_MX35_3DS diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 10784fab33a1..a2d16393170b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o +obj-$(CONFIG_GPIO_MAX77663) += gpio-max77663.o obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o diff --git a/drivers/gpio/gpio-max77663.c b/drivers/gpio/gpio-max77663.c new file mode 100644 index 000000000000..365cd16139bb --- /dev/null +++ b/drivers/gpio/gpio-max77663.c @@ -0,0 +1,672 @@ +/* + * MAXIM MAX77663 GPIO driver + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan <ldewangan@nvidia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/debugfs.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mfd/max77663-core.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> + +/* GPIO control registers */ +#define MAX77663_REG_GPIO_IRQ 0x0A +#define MAX77663_REG_GPIO_CTRL0 0x36 +#define MAX77663_REG_GPIO_CTRL1 0x37 +#define MAX77663_REG_GPIO_CTRL2 0x38 +#define MAX77663_REG_GPIO_CTRL3 0x39 +#define MAX77663_REG_GPIO_CTRL4 0x3A +#define MAX77663_REG_GPIO_CTRL5 0x3B +#define MAX77663_REG_GPIO_CTRL6 0x3C +#define MAX77663_REG_GPIO_CTRL7 0x3D +#define MAX77663_REG_GPIO_PU 0x3E +#define MAX77663_REG_GPIO_PD 0x3F +#define MAX77663_REG_GPIO_ALT 0x40 + +#define GPIO_REG_ADDR(offset) (MAX77663_REG_GPIO_CTRL0 + offset) + +#define GPIO_CTRL_DBNC_MASK (3 << 6) +#define GPIO_CTRL_DBNC_SHIFT 6 +#define GPIO_CTRL_REFE_IRQ_MASK (3 << 4) +#define GPIO_CTRL_REFE_IRQ_SHIFT 4 +#define GPIO_CTRL_DOUT_MASK (1 << 3) +#define GPIO_CTRL_DOUT_SHIFT 3 +#define GPIO_CTRL_DIN_MASK (1 << 2) +#define GPIO_CTRL_DIN_SHIFT 2 +#define GPIO_CTRL_DIR_MASK (1 << 1) +#define GPIO_CTRL_DIR_SHIFT 1 +#define GPIO_CTRL_OUT_DRV_MASK (1 << 0) +#define GPIO_CTRL_OUT_DRV_SHIFT 0 +#define GPIO_DBNC_NONE 0 +#define GPIO_DBNC_8MS 1 +#define GPIO_DBNC_16MS 2 +#define GPIO_DBNC_32MS 3 + +#define MAX77663_GPIO_IRQ(n) (MAX77663_IRQ_GPIO0 + n) + +#define GPIO_REFE_IRQ_NONE 0 +#define GPIO_REFE_IRQ_EDGE_FALLING 1 +#define GPIO_REFE_IRQ_EDGE_RISING 2 +#define GPIO_REFE_IRQ_EDGE_BOTH 3 + +struct max77663_gpio { + struct gpio_chip gpio_chip; + struct irq_chip irq_chip; + struct device *parent; + struct device *dev; + struct mutex irq_lock; + int gpio_irq; + int irq_base; + int gpio_base; + unsigned int trigger_type[MAX77663_GPIO_NR]; + u8 cache_gpio_ctrl[MAX77663_GPIO_NR]; + u8 cache_gpio_pu; + u8 cache_gpio_pd; + u8 cache_gpio_alt; +}; + +static struct max77663_gpio *max77663_gpio_chip; + +static inline struct max77663_gpio *to_max77663_gpio(struct gpio_chip *gpio) +{ + return container_of(gpio, struct max77663_gpio, gpio_chip); +} + +static inline int max77663_cache_write(struct device *dev, u8 addr, u8 mask, + u8 val, u8 *cache) +{ + u8 new_val; + int ret; + + new_val = (*cache & ~mask) | (val & mask); + if (*cache != new_val) { + ret = max77663_write(dev, addr, &new_val, 1, 0); + if (ret < 0) + return ret; + *cache = new_val; + } + return 0; +} + +static int max77663_gpio_set_pull_up(struct max77663_gpio *max77663_gpio, + int offset, int pull_up) +{ + u8 val = 0; + + if ((offset < MAX77663_GPIO0) || (MAX77663_GPIO7 < offset)) + return -EINVAL; + + if (pull_up == GPIO_PU_ENABLE) + val = (1 << offset); + + return max77663_cache_write(max77663_gpio->parent, MAX77663_REG_GPIO_PU, + (1 << offset), val, &max77663_gpio->cache_gpio_pu); +} + +static int max77663_gpio_set_pull_down(struct max77663_gpio *max77663_gpio, + int offset, int pull_down) +{ + u8 val = 0; + + if ((offset < MAX77663_GPIO0) || (MAX77663_GPIO7 < offset)) + return -EINVAL; + + if (pull_down == GPIO_PD_ENABLE) + val = (1 << offset); + + return max77663_cache_write(max77663_gpio->parent, MAX77663_REG_GPIO_PD, + (1 << offset), val, &max77663_gpio->cache_gpio_pd); +} + +static inline int max77663_gpio_is_alternate( + struct max77663_gpio *max77663_gpio, int offset) +{ + return (max77663_gpio->cache_gpio_alt & (1 << offset)) ? 1 : 0; +} + +static int max77663_gpio_config_alternate(int gpio, int alternate) +{ + struct max77663_gpio *max77663_gpio = max77663_gpio_chip; + u8 val = 0; + int ret = 0; + + if (!max77663_gpio) + return -ENXIO; + + gpio -= max77663_gpio->gpio_base; + if ((gpio < MAX77663_GPIO0) || (MAX77663_GPIO7 < gpio)) + return -EINVAL; + + if (alternate == GPIO_ALT_ENABLE) { + val = (1 << gpio); + if (gpio == MAX77663_GPIO7) { + ret = max77663_gpio_set_pull_up(max77663_gpio, gpio, 0); + if (ret < 0) + return ret; + + ret = max77663_gpio_set_pull_down(max77663_gpio, + gpio, 0); + if (ret < 0) + return ret; + } + } + + return max77663_cache_write(max77663_gpio->parent, + MAX77663_REG_GPIO_ALT, + (1 << gpio), val, &max77663_gpio->cache_gpio_alt); +} + +static int max77663_gpio_dir_input(struct gpio_chip *gpio, unsigned offset) +{ + struct max77663_gpio *max77663_gpio = to_max77663_gpio(gpio); + + if (max77663_gpio_is_alternate(max77663_gpio, offset)) { + dev_warn(max77663_gpio->dev, + "gpio%u is used as alternate mode\n", offset); + return 0; + } + + return max77663_cache_write(max77663_gpio->parent, + GPIO_REG_ADDR(offset), + GPIO_CTRL_DIR_MASK, GPIO_CTRL_DIR_MASK, + &max77663_gpio->cache_gpio_ctrl[offset]); +} + +static int max77663_gpio_get(struct gpio_chip *gpio, unsigned offset) +{ + struct max77663_gpio *max77663_gpio = to_max77663_gpio(gpio); + u8 val; + int ret; + + if (max77663_gpio_is_alternate(max77663_gpio, offset)) { + dev_warn(max77663_gpio->dev, + "gpio%u is used as alternate mode\n", offset); + return 0; + } + + ret = max77663_read(max77663_gpio->parent, GPIO_REG_ADDR(offset), + &val, 1, 0); + if (ret < 0) + return ret; + + max77663_gpio->cache_gpio_ctrl[offset] = val; + return (val & GPIO_CTRL_DIN_MASK) >> GPIO_CTRL_DIN_SHIFT; +} + +static int max77663_gpio_dir_output(struct gpio_chip *gpio, unsigned offset, + int value) +{ + struct max77663_gpio *max77663_gpio = to_max77663_gpio(gpio); + u8 mask = GPIO_CTRL_DIR_MASK | GPIO_CTRL_DOUT_MASK; + u8 val = (value ? 1 : 0) << GPIO_CTRL_DOUT_SHIFT; + + if (max77663_gpio_is_alternate(max77663_gpio, offset)) { + dev_warn(max77663_gpio->dev, + "gpio%u is used as alternate mode\n", offset); + return 0; + } + + return max77663_cache_write(max77663_gpio->parent, + GPIO_REG_ADDR(offset), mask, val, + &max77663_gpio->cache_gpio_ctrl[offset]); +} + +static int max77663_gpio_set_debounce(struct gpio_chip *gpio, + unsigned offset, unsigned debounce) +{ + struct max77663_gpio *max77663_gpio = to_max77663_gpio(gpio); + u8 shift = GPIO_CTRL_DBNC_SHIFT; + u8 val = 0; + + if (max77663_gpio_is_alternate(max77663_gpio, offset)) { + dev_warn(max77663_gpio->dev, + "gpio%u is used as alternate mode\n", offset); + return 0; + } + + if (debounce == 0) + val = 0; + else if ((0 < debounce) && (debounce <= 8)) + val = (GPIO_DBNC_8MS << shift); + else if ((8 < debounce) && (debounce <= 16)) + val = (GPIO_DBNC_16MS << shift); + else if ((16 < debounce) && (debounce <= 32)) + val = (GPIO_DBNC_32MS << shift); + else + return -EINVAL; + + return max77663_cache_write(max77663_gpio->parent, + GPIO_REG_ADDR(offset), + GPIO_CTRL_DBNC_MASK, val, + &max77663_gpio->cache_gpio_ctrl[offset]); +} + +static void max77663_gpio_set(struct gpio_chip *gpio, unsigned offset, + int value) +{ + struct max77663_gpio *max77663_gpio = to_max77663_gpio(gpio); + u8 val = (value ? 1 : 0) << GPIO_CTRL_DOUT_SHIFT; + + if (max77663_gpio_is_alternate(max77663_gpio, offset)) { + dev_warn(max77663_gpio->dev, + "gpio%u is used as alternate mode\n", offset); + return; + } + + max77663_cache_write(max77663_gpio->parent, GPIO_REG_ADDR(offset), + GPIO_CTRL_DOUT_MASK, val, + &max77663_gpio->cache_gpio_ctrl[offset]); +} + +static int max77663_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) +{ + struct max77663_gpio *max77663_gpio = to_max77663_gpio(gpio); + + return max77663_gpio->irq_base + offset; +} + +static int max77663_gpio_set_config(struct max77663_gpio *max77663_gpio, + struct max77663_gpio_config *gpio_cfg) +{ + int gpio = gpio_cfg->gpio; + u8 val = 0, mask = 0; + int ret = 0; + + if ((gpio < MAX77663_GPIO0) || (MAX77663_GPIO7 < gpio)) + return -EINVAL; + + if (gpio_cfg->pull_up != GPIO_PU_DEF) { + ret = max77663_gpio_set_pull_up(max77663_gpio, gpio, + gpio_cfg->pull_up); + if (ret < 0) { + dev_err(max77663_gpio->dev, + "Failed to set gpio%d pull-up\n", gpio); + return ret; + } + } + + if (gpio_cfg->pull_down != GPIO_PD_DEF) { + ret = max77663_gpio_set_pull_down(max77663_gpio, gpio, + gpio_cfg->pull_down); + if (ret < 0) { + dev_err(max77663_gpio->dev, + "Failed to set gpio%d pull-down\n", gpio); + return ret; + } + } + + if (gpio_cfg->dir != GPIO_DIR_DEF) { + mask = GPIO_CTRL_DIR_MASK; + if (gpio_cfg->dir == GPIO_DIR_IN) { + val |= GPIO_CTRL_DIR_MASK; + } else { + if (gpio_cfg->dout != GPIO_DOUT_DEF) { + mask |= GPIO_CTRL_DOUT_MASK; + if (gpio_cfg->dout == GPIO_DOUT_HIGH) + val |= GPIO_CTRL_DOUT_MASK; + } + + if (gpio_cfg->out_drv != GPIO_OUT_DRV_DEF) { + mask |= GPIO_CTRL_OUT_DRV_MASK; + if (gpio_cfg->out_drv == GPIO_OUT_DRV_PUSH_PULL) + val |= GPIO_CTRL_OUT_DRV_MASK; + } + } + + ret = max77663_cache_write(max77663_gpio->parent, + GPIO_REG_ADDR(gpio), mask, + val, &max77663_gpio->cache_gpio_ctrl[gpio]); + if (ret < 0) { + dev_err(max77663_gpio->dev, + "Failed to set gpio%d control\n", gpio); + return ret; + } + } + + if (gpio_cfg->alternate != GPIO_ALT_DEF) { + ret = max77663_gpio_config_alternate( + gpio + max77663_gpio->gpio_base, + gpio_cfg->alternate); + if (ret < 0) { + dev_err(max77663_gpio->dev, + "Failed to set gpio%d alternate\n", gpio); + return ret; + } + } + + return 0; +} + +static void max77663_gpio_irq_lock(struct irq_data *data) +{ + struct max77663_gpio *max77663_gpio = irq_data_get_irq_chip_data(data); + + mutex_lock(&max77663_gpio->irq_lock); +} + +static void max77663_gpio_irq_sync_unlock(struct irq_data *data) +{ + struct max77663_gpio *max77663_gpio = irq_data_get_irq_chip_data(data); + + mutex_unlock(&max77663_gpio->irq_lock); +} + +static void max77663_gpio_irq_mask(struct irq_data *data) +{ + struct max77663_gpio *max77663_gpio = irq_data_get_irq_chip_data(data); + int offset = data->irq - max77663_gpio->irq_base; + int ret; + + ret = max77663_cache_write(max77663_gpio->parent, + GPIO_REG_ADDR(offset), 0x30, 0x0, + &max77663_gpio->cache_gpio_ctrl[offset]); + if (ret < 0) + dev_err(max77663_gpio->dev, + "gpio register write failed, e %d\n", ret); +} + +static void max77663_gpio_irq_unmask(struct irq_data *data) +{ + struct max77663_gpio *max77663_gpio = irq_data_get_irq_chip_data(data); + int irq_mask = GPIO_REFE_IRQ_EDGE_FALLING << GPIO_CTRL_REFE_IRQ_SHIFT; + int offset = data->irq - max77663_gpio->irq_base; + int ret; + + if (max77663_gpio->trigger_type[offset]) + irq_mask = max77663_gpio->trigger_type[offset]; + ret = max77663_cache_write(max77663_gpio->parent, + GPIO_REG_ADDR(offset), 0x30, irq_mask, + &max77663_gpio->cache_gpio_ctrl[offset]); + if (ret < 0) + dev_err(max77663_gpio->dev, + "gpio register write failed, e %d\n", ret); +} + +static int max77663_irq_gpio_set_type(struct irq_data *data, unsigned int type) +{ + struct max77663_gpio *max77663_gpio = irq_data_get_irq_chip_data(data); + unsigned offset = data->irq - max77663_gpio->irq_base; + u8 val; + int ret; + + switch (type) { + case IRQ_TYPE_NONE: + case IRQ_TYPE_EDGE_FALLING: + val = (GPIO_REFE_IRQ_EDGE_FALLING << GPIO_CTRL_REFE_IRQ_SHIFT); + break; + + case IRQ_TYPE_EDGE_RISING: + val = (GPIO_REFE_IRQ_EDGE_RISING << GPIO_CTRL_REFE_IRQ_SHIFT); + break; + + case IRQ_TYPE_EDGE_BOTH: + val = (GPIO_REFE_IRQ_EDGE_BOTH << GPIO_CTRL_REFE_IRQ_SHIFT); + break; + + default: + return -EINVAL; + } + + max77663_gpio->trigger_type[offset] = type; + ret = max77663_cache_write(max77663_gpio->parent, + GPIO_REG_ADDR(offset), 0x30, val, + &max77663_gpio->cache_gpio_ctrl[offset]); + if (ret < 0) + dev_err(max77663_gpio->dev, + "gpio register write failed, e %d\n", ret); + + return ret; +} + +static irqreturn_t max77663_gpio_isr(int irq, void *data) +{ + struct max77663_gpio *max77663_gpio = data; + int ret; + int i; + u8 val; + + ret = max77663_read(max77663_gpio->dev, MAX77663_REG_GPIO_IRQ, + &val, 1, 0); + if (ret < 0) { + dev_err(max77663_gpio->dev, + "gpio irq reg read Failed %d\n", ret); + return IRQ_NONE; + } + + for (i = 0; i < MAX77663_GPIO_NR; ++i) { + if (val & (1 << i)) + handle_nested_irq(max77663_gpio->irq_base + i); + } + return IRQ_HANDLED; +} + +static int max77663_gpio_irq_init(struct max77663_gpio *max77663_gpio, + struct max77663_platform_data *pdata) +{ + int i; + u8 val; + int ret; + + max77663_gpio->irq_base = pdata->irq_base + MAX77663_GPIO_IRQ(0); + max77663_gpio->irq_chip.name = "max77663-gpio-irq"; + max77663_gpio->irq_chip.irq_mask = max77663_gpio_irq_mask; + max77663_gpio->irq_chip.irq_unmask = max77663_gpio_irq_unmask; + max77663_gpio->irq_chip.irq_set_type = max77663_irq_gpio_set_type; + max77663_gpio->irq_chip.irq_bus_lock = max77663_gpio_irq_lock; + max77663_gpio->irq_chip.irq_bus_sync_unlock = + max77663_gpio_irq_sync_unlock; + + for (i = 0; i < MAX77663_GPIO_NR; ++i) { + int irq = max77663_gpio->irq_base + i; + irq_set_chip_data(irq, max77663_gpio); + irq_set_chip_and_handler(irq, &max77663_gpio->irq_chip, + handle_simple_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + } + + /* IRQ_LVL2_GPIO is rea on clear */ + max77663_read(max77663_gpio->parent, MAX77663_REG_GPIO_IRQ, &val, 1, 0); + + ret = request_threaded_irq(max77663_gpio->gpio_irq, NULL, + max77663_gpio_isr, IRQF_ONESHOT, "max77663-gpio-irq", + max77663_gpio); + if (ret < 0) + dev_err(max77663_gpio->dev, "Failed to request irq %d, e %d\n", + max77663_gpio->gpio_irq, ret); + return ret; +} + +static void max77663_gpio_irq_remove(struct max77663_gpio *max77663_gpio) +{ + int gpio; + + for (gpio = 0; gpio < MAX77663_GPIO_NR; ++gpio) { + int irq = max77663_gpio->irq_base + gpio; +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); + } + free_irq(max77663_gpio->gpio_irq, max77663_gpio); +} + +static int max77663_gpio_init_regs(struct max77663_gpio *max77663_gpio, + struct max77663_platform_data *pdata) +{ + int ret; + int i; + + ret = max77663_read(max77663_gpio->parent, MAX77663_REG_GPIO_CTRL0, + &max77663_gpio->cache_gpio_ctrl, MAX77663_GPIO_NR, 0); + if (ret < 0) { + dev_err(max77663_gpio->dev, "Failed to get gpio control\n"); + return ret; + } + + ret = max77663_read(max77663_gpio->parent, MAX77663_REG_GPIO_PU, + &max77663_gpio->cache_gpio_pu, 1, 0); + if (ret < 0) { + dev_err(max77663_gpio->dev, "Failed to get gpio pull-up\n"); + return ret; + } + + ret = max77663_read(max77663_gpio->parent, MAX77663_REG_GPIO_PD, + &max77663_gpio->cache_gpio_pd, 1, 0); + if (ret < 0) { + dev_err(max77663_gpio->dev, "Failed to get gpio pull-down\n"); + return ret; + } + + ret = max77663_read(max77663_gpio->parent, MAX77663_REG_GPIO_ALT, + &max77663_gpio->cache_gpio_alt, 1, 0); + if (ret < 0) { + dev_err(max77663_gpio->dev, "Failed to get gpio alternate\n"); + return ret; + } + + for (i = 0; i < pdata->num_gpio_cfgs; i++) { + ret = max77663_gpio_set_config(max77663_gpio, + &pdata->gpio_cfgs[i]); + if (ret < 0) { + dev_err(max77663_gpio->dev, + "Failed to set gpio config\n"); + return ret; + } + } + + return 0; +} + +static int __devinit max77663_gpio_probe(struct platform_device *pdev) +{ + struct max77663_platform_data *pdata; + struct max77663_gpio *max77663_gpio; + int ret; + int gpio_irq; + + pdata = dev_get_platdata(pdev->dev.parent); + if (!pdata) { + dev_err(&pdev->dev, "Platform data not found\n"); + return -ENODEV; + } + + gpio_irq = platform_get_irq(pdev, 0); + if (gpio_irq <= 0) { + dev_err(&pdev->dev, "Gpio interrupt is not available\n"); + return -ENODEV; + } + + max77663_gpio = devm_kzalloc(&pdev->dev, + sizeof(*max77663_gpio), GFP_KERNEL); + if (!max77663_gpio) { + dev_err(&pdev->dev, "Could not allocate max77663_gpio\n"); + return -ENOMEM; + } + + mutex_init(&max77663_gpio->irq_lock); + max77663_gpio->parent = pdev->dev.parent; + max77663_gpio->dev = &pdev->dev; + max77663_gpio->gpio_irq = gpio_irq; + + max77663_gpio->gpio_chip.owner = THIS_MODULE; + max77663_gpio->gpio_chip.label = pdev->name; + max77663_gpio->gpio_chip.dev = &pdev->dev; + max77663_gpio->gpio_chip.direction_input = max77663_gpio_dir_input; + max77663_gpio->gpio_chip.get = max77663_gpio_get; + max77663_gpio->gpio_chip.direction_output = max77663_gpio_dir_output; + max77663_gpio->gpio_chip.set_debounce = max77663_gpio_set_debounce; + max77663_gpio->gpio_chip.set = max77663_gpio_set; + max77663_gpio->gpio_chip.to_irq = max77663_gpio_to_irq; + max77663_gpio->gpio_chip.ngpio = MAX77663_GPIO_NR; + max77663_gpio->gpio_chip.can_sleep = 1; + if (pdata->gpio_base) + max77663_gpio->gpio_chip.base = pdata->gpio_base; + else + max77663_gpio->gpio_chip.base = -1; + + max77663_gpio_chip = max77663_gpio; + ret = max77663_gpio_init_regs(max77663_gpio, pdata); + if (ret < 0) { + dev_err(&pdev->dev, "gpio_init regs failed\n"); + return ret; + } + + ret = gpiochip_add(&max77663_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "gpio_init: Failed to add gpiomax77663_gpio\n"); + return ret; + } + max77663_gpio->gpio_base = max77663_gpio->gpio_chip.base; + + ret = max77663_gpio_irq_init(max77663_gpio, pdata); + if (ret < 0) { + dev_err(&pdev->dev, "gpio irq init failed, e %d\n", ret); + goto fail; + } + + platform_set_drvdata(pdev, max77663_gpio); + return 0; + +fail: + gpiochip_remove(&max77663_gpio->gpio_chip); + return ret; +} + +static int __devexit max77663_gpio_remove(struct platform_device *pdev) +{ + struct max77663_gpio *max77663_gpio = platform_get_drvdata(pdev); + + max77663_gpio_irq_remove(max77663_gpio); + max77663_gpio_chip = 0; + + return gpiochip_remove(&max77663_gpio->gpio_chip); +} + +static struct platform_driver max77663_gpio_driver = { + .driver.name = "max77663-gpio", + .driver.owner = THIS_MODULE, + .probe = max77663_gpio_probe, + .remove = __devexit_p(max77663_gpio_remove), +}; + +static int __init max77663_gpio_init(void) +{ + return platform_driver_register(&max77663_gpio_driver); +} +subsys_initcall(max77663_gpio_init); + +static void __exit max77663_gpio_exit(void) +{ + platform_driver_unregister(&max77663_gpio_driver); +} +module_exit(max77663_gpio_exit); + +MODULE_ALIAS("platform:max77663-gpio"); +MODULE_DESCRIPTION("GPIO interface for MAX77663 PMIC"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL v2"); |
