summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-max77663.c672
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");