/* * drivers/power/supply/axp/axp20x/axp20x-gpio.c * (C) Copyright 2010-2016 * Allwinner Technology Co., Ltd. * caiyongheng * * gpio driver of axp20x * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../../../pinctrl/core.h" #include "../axp-core.h" #include "../axp-gpio.h" #include "axp20x-gpio.h" static struct axp_gpio_irqchip *axp20x_gpio_irqchip; static int axp20x_pmu_num; static int axp20x_gpio_get_io(struct axp_dev *axp_dev, int gpio, int *io_state) { uint8_t val; struct axp_regmap *map = axp_dev->regmap; switch (gpio) { case 0: axp_regmap_read(map, AXP_GPIO0_CFG, &val); val &= 0x07; if (val < 0x02) *io_state = 1; else if (val == 0x02) *io_state = 0; else return -EIO; break; case 1: axp_regmap_read(map, AXP_GPIO1_CFG, &val); val &= 0x07; if (val < 0x02) *io_state = 1; else if (val == 0x02) *io_state = 0; else return -EIO; break; case 2: axp_regmap_read(map, AXP_GPIO2_CFG, &val); val &= 0x07; if (val < 0x02) *io_state = 1; else if (val == 0x02) *io_state = 0; else return -EIO; break; case 3: axp_regmap_read(map, AXP_GPIO3_CFG, &val); val &= 0x04; if (val == 0x00) *io_state = 1; else *io_state = 0; break; case 4: axp_regmap_read(map, AXP_GPIO4_CFG, &val); val &= 0x08; if (val == 0x00) *io_state = 0; else *io_state = 1; break; default: return -EIO; } return 0; } static int axp20x_gpio_set_io(struct axp_dev *axp_dev, int gpio, int io_state) { uint8_t val; struct axp_regmap *map = axp_dev->regmap; if (io_state == 1) { switch (gpio) { case 0: return axp_regmap_clr_bits(map, AXP_GPIO0_CFG, 0x06); case 1: return axp_regmap_clr_bits(map, AXP_GPIO1_CFG, 0x06); case 2: return axp_regmap_clr_bits(map, AXP_GPIO2_CFG, 0x06); case 3: return axp_regmap_clr_bits(map, AXP_GPIO3_CFG, 0x04); case 4: return axp_regmap_clr_bits(map, AXP_GPIO4_CFG, 0x08); default: return -ENXIO; } } else if (io_state == 0) { switch (gpio) { case 0: return axp_regmap_update_sync(map, AXP_GPIO0_CFG, 0x02, 0x07); case 1: return axp_regmap_update_sync(map, AXP_GPIO1_CFG, 0x02, 0x07); case 2: return axp_regmap_update_sync(map, AXP_GPIO2_CFG, 0x02, 0x07); case 3: return axp_regmap_set_bits(map, AXP_GPIO3_CFG, 0x04); default: return -ENXIO; } } else return -ENXIO; } static int axp20x_gpio_get_data(struct axp_dev *axp_dev, int gpio) { u8 ret; struct axp_regmap *map; int io_state = 0; map = axp_dev->regmap; if (NULL == map) { pr_err("%s: axp regmap is null\n", __func__); return -ENXIO; } ret = axp20x_gpio_get_io(axp_dev, gpio, &io_state); if (ret) return ret; if (io_state) { switch (gpio) { case 0: axp_regmap_read(map, AXP_GPIO0_CFG, &ret); ret &= 0x01; break; case 1: axp_regmap_read(map, AXP_GPIO1_CFG, &ret); ret &= 0x01; break; case 2: axp_regmap_read(map, AXP_GPIO2_CFG, &ret); ret &= 0x01; break; case 3: axp_regmap_read(map, AXP_GPIO3_STATE, &ret); ret &= 0x02; break; default: pr_err("This IO is not an output\n"); return -ENXIO; } } else { switch (gpio) { case 0: axp_regmap_read(map, AXP_GPIO012_STATE, &ret); ret &= 0x10; break; case 1: axp_regmap_read(map, AXP_GPIO012_STATE, &ret); ret &= 0x20; break; case 2: axp_regmap_read(map, AXP_GPIO012_STATE, &ret); ret &= 0x40; break; case 3: axp_regmap_read(map, AXP_GPIO3_STATE, &ret); ret &= 0x01; break; default: pr_err("This IO is not an input\n"); return -ENXIO; } } return ret; } static int axp20x_gpio_set_data(struct axp_dev *axp_dev, int gpio, int value) { u8 ret; struct axp_regmap *map; int io_state = 0; map = axp_dev->regmap; if (NULL == map) { pr_err("%s: %d axp regmap is null\n", __func__, __LINE__); return -ENXIO; } ret = axp20x_gpio_get_io(axp_dev, gpio, &io_state); if (ret) return ret; if (io_state) { if (value) { switch (gpio) { case 0: return axp_regmap_update_sync(map, AXP_GPIO0_CFG, 1, 0x7); case 1: return axp_regmap_update_sync(map, AXP_GPIO1_CFG, 1, 0x7); case 2: return axp_regmap_set_bits(map, AXP_GPIO2_CFG, 0x01); case 3: return axp_regmap_set_bits(map, AXP_GPIO3_CFG, 0x02); case 4: return axp_regmap_clr_bits(map, AXP_GPIO4_CFG, 0x30); default: return -ENXIO; } } else { switch (gpio) { case 0: return axp_regmap_update_sync(map, AXP_GPIO0_CFG, 0, 0x7); case 1: return axp_regmap_update_sync(map, AXP_GPIO1_CFG, 0, 0x7); case 2: return axp_regmap_clr_bits(map, AXP_GPIO2_CFG, 0x01); case 3: return axp_regmap_clr_bits(map, AXP_GPIO3_CFG, 0x02); case 4: return axp_regmap_set_bits(map, AXP_GPIO4_CFG, 0x30); default: pr_err("This IO is not an output\n"); return -ENXIO; } } } return -ENXIO; } static int axp20x_pmx_set(struct axp_dev *axp_dev, int gpio, int mux) { struct axp_regmap *map; map = axp_dev->regmap; if (NULL == map) { pr_err("%s: %d axp regmap is null\n", __func__, __LINE__); return -ENXIO; } if (mux == 1) { /* output */ return axp20x_gpio_set_io(axp_dev, gpio, 1); } else if (mux == 0) { /* input */ return axp20x_gpio_set_io(axp_dev, gpio, 0); } return -ENXIO; } static int axp20x_pmx_get(struct axp_dev *axp_dev, int gpio) { u8 ret; struct axp_regmap *map; int io_state = 0; map = axp_dev->regmap; if (NULL == map) { pr_err("%s: %d axp regmap is null\n", __func__, __LINE__); return -ENXIO; } ret = axp20x_gpio_get_io(axp_dev, gpio, &io_state); if (ret) return ret; return io_state; } static const struct axp_desc_pin axp20x_pins[] = { AXP_PIN_DESC(AXP_PINCTRL_GPIO(0), AXP_FUNCTION(0x0, "gpio_in"), AXP_FUNCTION(0x1, "gpio_out"), AXP_FUNCTION_IRQ(1)), AXP_PIN_DESC(AXP_PINCTRL_GPIO(1), AXP_FUNCTION(0x0, "gpio_in"), AXP_FUNCTION(0x1, "gpio_out"), AXP_FUNCTION_IRQ(1)), AXP_PIN_DESC(AXP_PINCTRL_GPIO(2), AXP_FUNCTION(0x0, "gpio_in"), AXP_FUNCTION(0x1, "gpio_out"), AXP_FUNCTION_IRQ(1)), AXP_PIN_DESC(AXP_PINCTRL_GPIO(3), AXP_FUNCTION(0x0, "gpio_in"), AXP_FUNCTION(0x1, "gpio_out"), AXP_FUNCTION_IRQ(1)), AXP_PIN_DESC(AXP_PINCTRL_GPIO(4), AXP_FUNCTION(0x0, "gpio_in"), AXP_FUNCTION(0x1, "gpio_out")), }; static struct axp_pinctrl_desc axp20x_pinctrl_pins_desc = { .pins = axp20x_pins, .npins = ARRAY_SIZE(axp20x_pins), }; static struct axp_gpio_ops axp20x_gpio_ops = { .gpio_get_data = axp20x_gpio_get_data, .gpio_set_data = axp20x_gpio_set_data, .pmx_set = axp20x_pmx_set, .pmx_get = axp20x_pmx_get, }; static int axp20x_gpio_irq_request(int gpio_no, u32 (*handler)(int, void *), void *data) { int gpio = gpio_no - AXP_PIN_BASE; if (!axp_gpio_irq_valid(&axp20x_pinctrl_pins_desc, gpio)) { pr_err("axp gpio%d irq is not valid\n", gpio); return -1; } axp20x_gpio_irqchip[gpio].gpio_no = gpio; axp20x_gpio_irqchip[gpio].handler = handler; axp20x_gpio_irqchip[gpio].data = data; return 0; } static int axp20x_gpio_irq_free(int gpio_no) { int gpio = gpio_no - AXP_PIN_BASE; if (!axp_gpio_irq_valid(&axp20x_pinctrl_pins_desc, gpio)) { pr_err("axp gpio%d irq is not valid\n", gpio); return -1; } axp20x_gpio_irqchip[gpio].gpio_no = 0; axp20x_gpio_irqchip[gpio].handler = NULL; axp20x_gpio_irqchip[gpio].data = NULL; return 0; } static int axp20x_gpio_irq_ack(int gpio_no) { struct axp_dev *cur_axp_dev = get_pmu_cur_dev(axp20x_pmu_num); struct axp_regmap *map = cur_axp_dev->regmap; int gpio = gpio_no - AXP_PIN_BASE; if (!axp_gpio_irq_valid(&axp20x_pinctrl_pins_desc, gpio)) { pr_err("axp gpio%d irq is not valid\n", gpio); return -1; } axp_regmap_write(map, AXP_GPIO0123_INTSTA, 0x1 << gpio); return 0; } static int axp20x_gpio_irq_enable(int gpio_no) { struct axp_dev *cur_axp_dev = get_pmu_cur_dev(axp20x_pmu_num); struct axp_regmap *map = cur_axp_dev->regmap; int gpio = gpio_no - AXP_PIN_BASE; u8 regval; if (!axp_gpio_irq_valid(&axp20x_pinctrl_pins_desc, gpio)) { pr_err("axp gpio%d irq is not valid\n", gpio); return -1; } axp_regmap_read(map, AXP_GPIO0123_INTEN, ®val); regval |= (0x1 << gpio); axp_regmap_write(map, AXP_GPIO0123_INTEN, regval); return 0; }; static int axp20x_gpio_irq_disable(int gpio_no) { struct axp_dev *cur_axp_dev = get_pmu_cur_dev(axp20x_pmu_num); struct axp_regmap *map = cur_axp_dev->regmap; int gpio = gpio_no - AXP_PIN_BASE; u8 regval; if (!axp_gpio_irq_valid(&axp20x_pinctrl_pins_desc, gpio)) { pr_err("axp gpio%d irq is not valid\n", gpio); return -1; } axp_regmap_read(map, AXP_GPIO0123_INTEN, ®val); regval &= ~(0x1 << gpio); axp_regmap_write(map, AXP_GPIO0123_INTEN, regval); return 0; }; static int axp20x_gpio_set_type(int gpio_no, unsigned long type) { struct axp_dev *cur_axp_dev = get_pmu_cur_dev(axp20x_pmu_num); struct axp_regmap *map = cur_axp_dev->regmap; int gpio = gpio_no - AXP_PIN_BASE; int reg = 0; u8 regval, mode = 0; if (!axp_gpio_irq_valid(&axp20x_pinctrl_pins_desc, gpio)) { pr_err("axp gpio%d irq is not valid\n", gpio); return -1; } switch (type) { case AXP_GPIO_IRQF_TRIGGER_RISING: mode = AXP_GPIO_IRQ_EDGE_RISING; break; case AXP_GPIO_IRQF_TRIGGER_FALLING: mode = AXP_GPIO_IRQ_EDGE_FALLING; break; case AXP_GPIO_IRQF_TRIGGER_RISING | AXP_GPIO_IRQF_TRIGGER_FALLING: mode = AXP_GPIO_IRQ_EDGE_RISING | AXP_GPIO_IRQ_EDGE_FALLING; break; default: return -EINVAL; } if (gpio == 0) reg = AXP_GPIO0_CFG; else if (gpio == 1) reg = AXP_GPIO1_CFG; else if (gpio == 2) reg = AXP_GPIO2_CFG; else if (gpio == 3) reg = AXP_GPIO3_CFG; axp20x_gpio_set_io(cur_axp_dev, gpio, 0); axp_regmap_read(map, reg, ®val); regval &= ~AXP_GPIO_EDGE_TRIG_MASK; regval |= mode; axp_regmap_write(map, reg, regval); return 0; }; struct axp_gpio_irq_ops axp20x_gpio_irq_ops = { .irq_request = axp20x_gpio_irq_request, .irq_free = axp20x_gpio_irq_free, .irq_ack = axp20x_gpio_irq_ack, .irq_enable = axp20x_gpio_irq_enable, .irq_disable = axp20x_gpio_irq_disable, .irq_set_type = axp20x_gpio_set_type, }; static irqreturn_t axp20x_gpio_isr(int irq, void *data) { int gpio = 0; switch (irq) { case AXP20X_IRQ_GPIO0: gpio = 0; break; case AXP20X_IRQ_GPIO1: gpio = 1; break; case AXP20X_IRQ_GPIO2: gpio = 2; break; case AXP20X_IRQ_GPIO3: gpio = 3; break; default: return IRQ_NONE; } AXP_DEBUG(AXP_INT, axp20x_pmu_num, "gpio%d input edge\n", gpio); if (axp20x_gpio_irqchip[gpio].handler != NULL) { axp20x_gpio_irqchip[gpio].handler(gpio + AXP_PIN_BASE, axp20x_gpio_irqchip[gpio].data); axp20x_gpio_irq_ack(gpio + AXP_PIN_BASE); } return IRQ_HANDLED; } static struct axp_interrupts axp20x_gpio_irq[] = { {"gpio0", axp20x_gpio_isr}, {"gpio1", axp20x_gpio_isr}, {"gpio2", axp20x_gpio_isr}, {"gpio3", axp20x_gpio_isr}, }; static int axp20x_gpio_probe(struct platform_device *pdev) { struct axp_dev *axp_dev = dev_get_drvdata(pdev->dev.parent); struct axp_pinctrl *axp20x_pin; int ret, i, irq; axp20x_pin = axp_pinctrl_register(&pdev->dev, axp_dev, &axp20x_pinctrl_pins_desc, &axp20x_gpio_ops); if (IS_ERR_OR_NULL(axp20x_pin)) goto fail; for (i = 0; i < ARRAY_SIZE(axp20x_gpio_irq); i++) { irq = platform_get_irq_byname(pdev, axp20x_gpio_irq[i].name); if (irq < 0) continue; ret = axp_gpio_irq_register(axp_dev, irq, axp20x_gpio_irq[i].isr, axp20x_pin); if (ret != 0) { dev_err(&pdev->dev, "failed to request %s IRQ %d: %d\n", axp20x_gpio_irq[i].name, irq, ret); goto out_irq; } dev_dbg(&pdev->dev, "Requested %s IRQ %d: %d\n", axp20x_gpio_irq[i].name, irq, ret); } axp20x_gpio_irqchip = kzalloc(ARRAY_SIZE(axp20x_gpio_irq) * sizeof(struct axp_gpio_irqchip), GFP_KERNEL); if (IS_ERR_OR_NULL(axp20x_gpio_irqchip)) { dev_err(&pdev->dev, "axp20x_gpio_irqchip: not enough memory\n"); i = ARRAY_SIZE(axp20x_gpio_irq); goto out_irq; } axp20x_pmu_num = axp_dev->pmu_num; axp_gpio_irq_ops_set(axp_dev->pmu_num, &axp20x_gpio_irq_ops); platform_set_drvdata(pdev, axp20x_pin); return 0; out_irq: for (i = i - 1; i >= 0; i--) { irq = platform_get_irq_byname(pdev, axp20x_gpio_irq[i].name); if (irq < 0) continue; axp_free_irq(axp_dev, irq); } fail: return -1; } static int axp20x_gpio_remove(struct platform_device *pdev) { struct axp_dev *axp_dev = dev_get_drvdata(pdev->dev.parent); struct axp_pinctrl *axp20x_pin = platform_get_drvdata(pdev); int i, irq; kfree(axp20x_gpio_irqchip); axp20x_gpio_irqchip = NULL; for (i = 0; i < ARRAY_SIZE(axp20x_gpio_irq); i++) { irq = platform_get_irq_byname(pdev, axp20x_gpio_irq[i].name); if (irq < 0) continue; axp_free_irq(axp_dev, irq); } return axp_pinctrl_unregister(axp20x_pin); } static const struct of_device_id axp20x_gpio_dt_ids[] = { { .compatible = "axp203-gpio", }, { .compatible = "axp209-gpio", }, {}, }; MODULE_DEVICE_TABLE(of, axp20x_gpio_dt_ids); static struct platform_driver axp20x_gpio_driver = { .driver = { .name = "axp20x-gpio", .of_match_table = axp20x_gpio_dt_ids, }, .probe = axp20x_gpio_probe, .remove = axp20x_gpio_remove, }; static int __init axp20x_gpio_initcall(void) { int ret; ret = platform_driver_register(&axp20x_gpio_driver); if (IS_ERR_VALUE(ret)) { pr_err("%s: failed, errno %d\n", __func__, ret); return -EINVAL; } return 0; } fs_initcall(axp20x_gpio_initcall); MODULE_DESCRIPTION("Gpio driver of axp20x"); MODULE_AUTHOR("caiyongheng"); MODULE_LICENSE("GPL");