/* * MAXIM MAX77663 GPIO driver * * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. * Author: Laxman dewangan * * 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 . */ #include #include #include #include #include #include #include #include #include #include /* 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 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: if (gpiochip_remove(&max77663_gpio->gpio_chip)) dev_err(&pdev->dev, "%s gpiochip_remove failed\n", __func__); return ret; } static int 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 = 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 "); MODULE_LICENSE("GPL v2");