/* * MAXIM MAX77660 GPIO driver * * Copyright (c) 2012, 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 #include #define GPIO_REG_ADDR(offset) (MAX77660_REG_CNFG_GPIO0 + offset) struct max77660_gpio { struct gpio_chip gpio_chip; struct device *parent; struct device *dev; int gpio_irq; int irq_base; int gpio_base; }; static const struct regmap_irq max77660_gpio_irqs[] = { [MAX77660_IRQ_GPIO0 - MAX77660_IRQ_GPIO0] = { .mask = MAX77660_IRQ1_LVL2_GPIO_EDGE0, .type_rising_mask = MAX77660_CNFG_GPIO_INT_RISING, .type_falling_mask = MAX77660_CNFG_GPIO_INT_FALLING, .reg_offset = 0, .type_reg_offset = 0, }, [MAX77660_IRQ_GPIO1 - MAX77660_IRQ_GPIO0] = { .mask = MAX77660_IRQ1_LVL2_GPIO_EDGE1, .type_rising_mask = MAX77660_CNFG_GPIO_INT_RISING, .type_falling_mask = MAX77660_CNFG_GPIO_INT_FALLING, .reg_offset = 0, .type_reg_offset = 1, }, [MAX77660_IRQ_GPIO2 - MAX77660_IRQ_GPIO0] = { .mask = MAX77660_IRQ1_LVL2_GPIO_EDGE2, .type_rising_mask = MAX77660_CNFG_GPIO_INT_RISING, .type_falling_mask = MAX77660_CNFG_GPIO_INT_FALLING, .reg_offset = 0, .type_reg_offset = 2, }, [MAX77660_IRQ_GPIO3 - MAX77660_IRQ_GPIO0] = { .mask = MAX77660_IRQ1_LVL2_GPIO_EDGE3, .type_rising_mask = MAX77660_CNFG_GPIO_INT_RISING, .type_falling_mask = MAX77660_CNFG_GPIO_INT_FALLING, .reg_offset = 0, .type_reg_offset = 3, }, [MAX77660_IRQ_GPIO4 - MAX77660_IRQ_GPIO0] = { .mask = MAX77660_IRQ1_LVL2_GPIO_EDGE4, .type_rising_mask = MAX77660_CNFG_GPIO_INT_RISING, .type_falling_mask = MAX77660_CNFG_GPIO_INT_FALLING, .reg_offset = 0, .type_reg_offset = 4, }, [MAX77660_IRQ_GPIO5 - MAX77660_IRQ_GPIO0] = { .mask = MAX77660_IRQ1_LVL2_GPIO_EDGE5, .type_rising_mask = MAX77660_CNFG_GPIO_INT_RISING, .type_falling_mask = MAX77660_CNFG_GPIO_INT_FALLING, .reg_offset = 0, .type_reg_offset = 5, }, [MAX77660_IRQ_GPIO6 - MAX77660_IRQ_GPIO0] = { .mask = MAX77660_IRQ1_LVL2_GPIO_EDGE6, .type_rising_mask = MAX77660_CNFG_GPIO_INT_RISING, .type_falling_mask = MAX77660_CNFG_GPIO_INT_FALLING, .reg_offset = 0, .type_reg_offset = 6, }, [MAX77660_IRQ_GPIO7 - MAX77660_IRQ_GPIO0] = { .mask = MAX77660_IRQ1_LVL2_GPIO_EDGE7, .type_rising_mask = MAX77660_CNFG_GPIO_INT_RISING, .type_falling_mask = MAX77660_CNFG_GPIO_INT_FALLING, .reg_offset = 0, .type_reg_offset = 7, }, [MAX77660_IRQ_GPIO8 - MAX77660_IRQ_GPIO0] = { .mask = MAX77660_IRQ2_LVL2_GPIO_EDGE8, .type_rising_mask = MAX77660_CNFG_GPIO_INT_RISING, .type_falling_mask = MAX77660_CNFG_GPIO_INT_FALLING, .reg_offset = 1, .type_reg_offset = 8, }, [MAX77660_IRQ_GPIO9 - MAX77660_IRQ_GPIO0] = { .mask = MAX77660_IRQ2_LVL2_GPIO_EDGE9, .type_rising_mask = MAX77660_CNFG_GPIO_INT_RISING, .type_falling_mask = MAX77660_CNFG_GPIO_INT_FALLING, .reg_offset = 1, .type_reg_offset = 9, }, }; static struct regmap_irq_chip max77660_gpio_irq_chip = { .name = "max77660-gpio", .irqs = max77660_gpio_irqs, .num_irqs = ARRAY_SIZE(max77660_gpio_irqs), .num_regs = 2, .num_type_reg = 10, .irq_reg_stride = 1, .type_reg_stride = 1, .status_base = MAX77660_REG_IRQ1_LVL2_GPIO, .type_base = MAX77660_REG_CNFG_GPIO0, }; static inline struct max77660_gpio *to_max77660_gpio(struct gpio_chip *gpio) { return container_of(gpio, struct max77660_gpio, gpio_chip); } static int max77660_gpio_dir_input(struct gpio_chip *gpio, unsigned offset) { struct max77660_gpio *max77660_gpio = to_max77660_gpio(gpio); struct device *dev = max77660_gpio->dev; struct device *parent = max77660_gpio->parent; int ret; ret = max77660_reg_update(parent, MAX77660_PWR_SLAVE, GPIO_REG_ADDR(offset), MAX77660_CNFG_GPIO_DIR_INPUT, MAX77660_CNFG_GPIO_DIR_MASK); if (ret < 0) dev_err(dev, "CNFG_GPIOx dir update failed: %d\n", ret); return ret; } static int max77660_gpio_get(struct gpio_chip *gpio, unsigned offset) { struct max77660_gpio *max77660_gpio = to_max77660_gpio(gpio); struct device *dev = max77660_gpio->dev; struct device *parent = max77660_gpio->parent; u8 val; int ret; ret = max77660_reg_read(parent, MAX77660_PWR_SLAVE, GPIO_REG_ADDR(offset), &val); if (ret < 0) { dev_err(dev, "CNFG_GPIOx read failed: %d\n", ret); return ret; } return !!(val & MAX77660_CNFG_GPIO_INPUT_VAL_MASK); } static int max77660_gpio_dir_output(struct gpio_chip *gpio, unsigned offset, int value) { struct max77660_gpio *max77660_gpio = to_max77660_gpio(gpio); struct device *dev = max77660_gpio->dev; struct device *parent = max77660_gpio->parent; u8 val; int ret; if (value) val = MAX77660_CNFG_GPIO_OUTPUT_VAL_HIGH; else val = MAX77660_CNFG_GPIO_OUTPUT_VAL_LOW; ret = max77660_reg_update(parent, MAX77660_PWR_SLAVE, GPIO_REG_ADDR(offset), val, MAX77660_CNFG_GPIO_OUTPUT_VAL_MASK); if (ret < 0) { dev_err(dev, "CNFG_GPIOx val update failed: %d\n", ret); return ret; } ret = max77660_reg_update(parent, MAX77660_PWR_SLAVE, GPIO_REG_ADDR(offset), MAX77660_CNFG_GPIO_DIR_OUTPUT, MAX77660_CNFG_GPIO_DIR_MASK); if (ret < 0) dev_err(dev, "CNFG_GPIOx dir update failed: %d\n", ret); return ret; } static int max77660_gpio_set_debounce(struct gpio_chip *gpio, unsigned offset, unsigned debounce) { struct max77660_gpio *max77660_gpio = to_max77660_gpio(gpio); struct device *dev = max77660_gpio->dev; struct device *parent = max77660_gpio->parent; u8 val; int ret; /* ES 1.0 errata: GPIO1 debaunce does not worked in ES1.0 */ if (max77660_is_es_1_0(parent) && (offset == 1)) { dev_err(dev, "ES1.0: GPIO1 Debaunce is not supported in HW\n"); return -EINVAL; } if (debounce == 0) val = MAX77660_CNFG_GPIO_DBNC_None; else if ((0 < debounce) && (debounce <= 8)) val = MAX77660_CNFG_GPIO_DBNC_8ms; else if ((8 < debounce) && (debounce <= 16)) val = MAX77660_CNFG_GPIO_DBNC_16ms; else if ((16 < debounce) && (debounce <= 32)) val = MAX77660_CNFG_GPIO_DBNC_32ms; else { dev_err(dev, "%s(): illegal value %u\n", __func__, debounce); return -EINVAL; } ret = max77660_reg_update(parent, MAX77660_PWR_SLAVE, GPIO_REG_ADDR(offset), val, MAX77660_CNFG_GPIO_DBNC_MASK); if (ret < 0) dev_err(dev, "CNFG_GPIOx debounce update failed: %d\n", ret); return ret; } static void max77660_gpio_set(struct gpio_chip *gpio, unsigned offset, int value) { struct max77660_gpio *max77660_gpio = to_max77660_gpio(gpio); struct device *dev = max77660_gpio->dev; struct device *parent = max77660_gpio->parent; u8 val; int ret; if (value) val = MAX77660_CNFG_GPIO_OUTPUT_VAL_HIGH; else val = MAX77660_CNFG_GPIO_OUTPUT_VAL_LOW; ret = max77660_reg_update(parent, MAX77660_PWR_SLAVE, GPIO_REG_ADDR(offset), val, MAX77660_CNFG_GPIO_OUTPUT_VAL_MASK); if (ret < 0) dev_err(dev, "CNFG_GPIOx val update failed: %d\n", ret); } static int max77660_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) { struct max77660_gpio *max77660_gpio = to_max77660_gpio(gpio); return max77660_gpio->irq_base + offset; } static int max77660_gpio_irq_init(struct max77660_gpio *max77660_gpio, struct max77660_platform_data *pdata) { struct device *parent = max77660_gpio->parent; struct max77660_chip *chip = dev_get_drvdata(parent); int ret; max77660_gpio->irq_base = pdata->irq_base + MAX77660_IRQ_GPIO0; ret = regmap_add_irq_chip(chip->rmap[MAX77660_PWR_SLAVE], max77660_gpio->gpio_irq, IRQF_ONESHOT, max77660_gpio->irq_base, &max77660_gpio_irq_chip, &chip->gpio_irq_data); if (ret < 0) { dev_err(max77660_gpio->dev, "Failed to add gpio irq_chip %d\n", ret); return ret; } return 0; } static void max77660_gpio_irq_remove(struct max77660_gpio *max77660_gpio) { struct max77660_chip *chip; chip = dev_get_drvdata(max77660_gpio->dev->parent); regmap_del_irq_chip(max77660_gpio->gpio_irq, chip->gpio_irq_data); chip->gpio_irq_data = NULL; } static int max77660_gpio_probe(struct platform_device *pdev) { struct max77660_platform_data *pdata; struct max77660_gpio *max77660_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; } max77660_gpio = devm_kzalloc(&pdev->dev, sizeof(*max77660_gpio), GFP_KERNEL); if (!max77660_gpio) { dev_err(&pdev->dev, "Could not allocate max77660_gpio\n"); return -ENOMEM; } max77660_gpio->parent = pdev->dev.parent; max77660_gpio->dev = &pdev->dev; max77660_gpio->gpio_irq = gpio_irq; max77660_gpio->gpio_chip.owner = THIS_MODULE; max77660_gpio->gpio_chip.label = pdev->name; max77660_gpio->gpio_chip.dev = &pdev->dev; max77660_gpio->gpio_chip.direction_input = max77660_gpio_dir_input; max77660_gpio->gpio_chip.get = max77660_gpio_get; max77660_gpio->gpio_chip.direction_output = max77660_gpio_dir_output; max77660_gpio->gpio_chip.set_debounce = max77660_gpio_set_debounce; max77660_gpio->gpio_chip.set = max77660_gpio_set; max77660_gpio->gpio_chip.to_irq = max77660_gpio_to_irq; max77660_gpio->gpio_chip.ngpio = MAX77660_GPIO_NR; max77660_gpio->gpio_chip.can_sleep = 1; if (pdata->gpio_base) max77660_gpio->gpio_chip.base = pdata->gpio_base; else max77660_gpio->gpio_chip.base = -1; platform_set_drvdata(pdev, max77660_gpio); ret = gpiochip_add(&max77660_gpio->gpio_chip); if (ret < 0) { dev_err(&pdev->dev, "gpio_init: Failed to add max77660_gpio\n"); return ret; } max77660_gpio->gpio_base = max77660_gpio->gpio_chip.base; ret = max77660_gpio_irq_init(max77660_gpio, pdata); if (ret < 0) { dev_err(&pdev->dev, "gpio irq init failed: %d\n", ret); goto fail; } return 0; fail: ret = gpiochip_remove(&max77660_gpio->gpio_chip); return ret; } static int max77660_gpio_remove(struct platform_device *pdev) { struct max77660_gpio *max77660_gpio = platform_get_drvdata(pdev); int ret; max77660_gpio_irq_remove(max77660_gpio); ret = gpiochip_remove(&max77660_gpio->gpio_chip); if (ret < 0) dev_err(max77660_gpio->dev, "gpiochip_remove failed: %d\n", ret); return ret; } static struct platform_driver max77660_gpio_driver = { .driver.name = "max77660-gpio", .driver.owner = THIS_MODULE, .probe = max77660_gpio_probe, .remove = max77660_gpio_remove, }; static int __init max77660_gpio_init(void) { return platform_driver_register(&max77660_gpio_driver); } subsys_initcall(max77660_gpio_init); static void __exit max77660_gpio_exit(void) { platform_driver_unregister(&max77660_gpio_driver); } module_exit(max77660_gpio_exit); MODULE_DESCRIPTION("GPIO interface for MAX77660 PMIC"); MODULE_VERSION("1.0"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Maxim Integrated"); MODULE_ALIAS("platform:max77660-gpio");