376 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) ST-Ericsson SA 20104 *5 * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson6 * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson7 */8 9#include <linux/init.h>10#include <linux/platform_device.h>11#include <linux/slab.h>12#include <linux/gpio/driver.h>13#include <linux/of.h>14#include <linux/interrupt.h>15#include <linux/mfd/tc3589x.h>16#include <linux/bitops.h>17 18/*19 * These registers are modified under the irq bus lock and cached to avoid20 * unnecessary writes in bus_sync_unlock.21 */22enum { REG_IBE, REG_IEV, REG_IS, REG_IE, REG_DIRECT };23 24#define CACHE_NR_REGS 525#define CACHE_NR_BANKS 326 27struct tc3589x_gpio {28 struct gpio_chip chip;29 struct tc3589x *tc3589x;30 struct device *dev;31 struct mutex irq_lock;32 /* Caches of interrupt control registers for bus_lock */33 u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];34 u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];35};36 37static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned int offset)38{39 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip);40 struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;41 u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2;42 u8 mask = BIT(offset % 8);43 int ret;44 45 ret = tc3589x_reg_read(tc3589x, reg);46 if (ret < 0)47 return ret;48 49 return !!(ret & mask);50}51 52static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int val)53{54 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip);55 struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;56 u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2;57 unsigned int pos = offset % 8;58 u8 data[] = {val ? BIT(pos) : 0, BIT(pos)};59 60 tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data);61}62 63static int tc3589x_gpio_direction_output(struct gpio_chip *chip,64 unsigned int offset, int val)65{66 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip);67 struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;68 u8 reg = TC3589x_GPIODIR0 + offset / 8;69 unsigned int pos = offset % 8;70 71 tc3589x_gpio_set(chip, offset, val);72 73 return tc3589x_set_bits(tc3589x, reg, BIT(pos), BIT(pos));74}75 76static int tc3589x_gpio_direction_input(struct gpio_chip *chip,77 unsigned int offset)78{79 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip);80 struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;81 u8 reg = TC3589x_GPIODIR0 + offset / 8;82 unsigned int pos = offset % 8;83 84 return tc3589x_set_bits(tc3589x, reg, BIT(pos), 0);85}86 87static int tc3589x_gpio_get_direction(struct gpio_chip *chip,88 unsigned int offset)89{90 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip);91 struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;92 u8 reg = TC3589x_GPIODIR0 + offset / 8;93 unsigned int pos = offset % 8;94 int ret;95 96 ret = tc3589x_reg_read(tc3589x, reg);97 if (ret < 0)98 return ret;99 100 if (ret & BIT(pos))101 return GPIO_LINE_DIRECTION_OUT;102 103 return GPIO_LINE_DIRECTION_IN;104}105 106static int tc3589x_gpio_set_config(struct gpio_chip *chip, unsigned int offset,107 unsigned long config)108{109 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip);110 struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;111 /*112 * These registers are alterated at each second address113 * ODM bit 0 = drive to GND or Hi-Z (open drain)114 * ODM bit 1 = drive to VDD or Hi-Z (open source)115 */116 u8 odmreg = TC3589x_GPIOODM0 + (offset / 8) * 2;117 u8 odereg = TC3589x_GPIOODE0 + (offset / 8) * 2;118 unsigned int pos = offset % 8;119 int ret;120 121 switch (pinconf_to_config_param(config)) {122 case PIN_CONFIG_DRIVE_OPEN_DRAIN:123 /* Set open drain mode */124 ret = tc3589x_set_bits(tc3589x, odmreg, BIT(pos), 0);125 if (ret)126 return ret;127 /* Enable open drain/source mode */128 return tc3589x_set_bits(tc3589x, odereg, BIT(pos), BIT(pos));129 case PIN_CONFIG_DRIVE_OPEN_SOURCE:130 /* Set open source mode */131 ret = tc3589x_set_bits(tc3589x, odmreg, BIT(pos), BIT(pos));132 if (ret)133 return ret;134 /* Enable open drain/source mode */135 return tc3589x_set_bits(tc3589x, odereg, BIT(pos), BIT(pos));136 case PIN_CONFIG_DRIVE_PUSH_PULL:137 /* Disable open drain/source mode */138 return tc3589x_set_bits(tc3589x, odereg, BIT(pos), 0);139 default:140 break;141 }142 return -ENOTSUPP;143}144 145static const struct gpio_chip template_chip = {146 .label = "tc3589x",147 .owner = THIS_MODULE,148 .get = tc3589x_gpio_get,149 .set = tc3589x_gpio_set,150 .direction_output = tc3589x_gpio_direction_output,151 .direction_input = tc3589x_gpio_direction_input,152 .get_direction = tc3589x_gpio_get_direction,153 .set_config = tc3589x_gpio_set_config,154 .can_sleep = true,155};156 157static int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type)158{159 struct gpio_chip *gc = irq_data_get_irq_chip_data(d);160 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc);161 int offset = d->hwirq;162 int regoffset = offset / 8;163 int mask = BIT(offset % 8);164 165 if (type == IRQ_TYPE_EDGE_BOTH) {166 tc3589x_gpio->regs[REG_IBE][regoffset] |= mask;167 return 0;168 }169 170 tc3589x_gpio->regs[REG_IBE][regoffset] &= ~mask;171 172 if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)173 tc3589x_gpio->regs[REG_IS][regoffset] |= mask;174 else175 tc3589x_gpio->regs[REG_IS][regoffset] &= ~mask;176 177 if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)178 tc3589x_gpio->regs[REG_IEV][regoffset] |= mask;179 else180 tc3589x_gpio->regs[REG_IEV][regoffset] &= ~mask;181 182 return 0;183}184 185static void tc3589x_gpio_irq_lock(struct irq_data *d)186{187 struct gpio_chip *gc = irq_data_get_irq_chip_data(d);188 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc);189 190 mutex_lock(&tc3589x_gpio->irq_lock);191}192 193static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d)194{195 struct gpio_chip *gc = irq_data_get_irq_chip_data(d);196 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc);197 struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;198 static const u8 regmap[] = {199 [REG_IBE] = TC3589x_GPIOIBE0,200 [REG_IEV] = TC3589x_GPIOIEV0,201 [REG_IS] = TC3589x_GPIOIS0,202 [REG_IE] = TC3589x_GPIOIE0,203 [REG_DIRECT] = TC3589x_DIRECT0,204 };205 int i, j;206 207 for (i = 0; i < CACHE_NR_REGS; i++) {208 for (j = 0; j < CACHE_NR_BANKS; j++) {209 u8 old = tc3589x_gpio->oldregs[i][j];210 u8 new = tc3589x_gpio->regs[i][j];211 212 if (new == old)213 continue;214 215 tc3589x_gpio->oldregs[i][j] = new;216 tc3589x_reg_write(tc3589x, regmap[i] + j, new);217 }218 }219 220 mutex_unlock(&tc3589x_gpio->irq_lock);221}222 223static void tc3589x_gpio_irq_mask(struct irq_data *d)224{225 struct gpio_chip *gc = irq_data_get_irq_chip_data(d);226 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc);227 int offset = d->hwirq;228 int regoffset = offset / 8;229 int mask = BIT(offset % 8);230 231 tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask;232 tc3589x_gpio->regs[REG_DIRECT][regoffset] |= mask;233 gpiochip_disable_irq(gc, offset);234}235 236static void tc3589x_gpio_irq_unmask(struct irq_data *d)237{238 struct gpio_chip *gc = irq_data_get_irq_chip_data(d);239 struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc);240 int offset = d->hwirq;241 int regoffset = offset / 8;242 int mask = BIT(offset % 8);243 244 gpiochip_enable_irq(gc, offset);245 tc3589x_gpio->regs[REG_IE][regoffset] |= mask;246 tc3589x_gpio->regs[REG_DIRECT][regoffset] &= ~mask;247}248 249static const struct irq_chip tc3589x_gpio_irq_chip = {250 .name = "tc3589x-gpio",251 .irq_bus_lock = tc3589x_gpio_irq_lock,252 .irq_bus_sync_unlock = tc3589x_gpio_irq_sync_unlock,253 .irq_mask = tc3589x_gpio_irq_mask,254 .irq_unmask = tc3589x_gpio_irq_unmask,255 .irq_set_type = tc3589x_gpio_irq_set_type,256 .flags = IRQCHIP_IMMUTABLE,257 GPIOCHIP_IRQ_RESOURCE_HELPERS,258};259 260static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)261{262 struct tc3589x_gpio *tc3589x_gpio = dev;263 struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;264 u8 status[CACHE_NR_BANKS];265 int ret;266 int i;267 268 ret = tc3589x_block_read(tc3589x, TC3589x_GPIOMIS0,269 ARRAY_SIZE(status), status);270 if (ret < 0)271 return IRQ_NONE;272 273 for (i = 0; i < ARRAY_SIZE(status); i++) {274 unsigned int stat = status[i];275 if (!stat)276 continue;277 278 while (stat) {279 int bit = __ffs(stat);280 int line = i * 8 + bit;281 int irq = irq_find_mapping(tc3589x_gpio->chip.irq.domain,282 line);283 284 handle_nested_irq(irq);285 stat &= ~(1 << bit);286 }287 288 tc3589x_reg_write(tc3589x, TC3589x_GPIOIC0 + i, status[i]);289 }290 291 return IRQ_HANDLED;292}293 294static int tc3589x_gpio_probe(struct platform_device *pdev)295{296 struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);297 struct device_node *np = pdev->dev.of_node;298 struct tc3589x_gpio *tc3589x_gpio;299 struct gpio_irq_chip *girq;300 int ret;301 int irq;302 303 if (!np) {304 dev_err(&pdev->dev, "No Device Tree node found\n");305 return -EINVAL;306 }307 308 irq = platform_get_irq(pdev, 0);309 if (irq < 0)310 return irq;311 312 tc3589x_gpio = devm_kzalloc(&pdev->dev, sizeof(struct tc3589x_gpio),313 GFP_KERNEL);314 if (!tc3589x_gpio)315 return -ENOMEM;316 317 mutex_init(&tc3589x_gpio->irq_lock);318 319 tc3589x_gpio->dev = &pdev->dev;320 tc3589x_gpio->tc3589x = tc3589x;321 322 tc3589x_gpio->chip = template_chip;323 tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;324 tc3589x_gpio->chip.parent = &pdev->dev;325 tc3589x_gpio->chip.base = -1;326 327 girq = &tc3589x_gpio->chip.irq;328 gpio_irq_chip_set_chip(girq, &tc3589x_gpio_irq_chip);329 /* This will let us handle the parent IRQ in the driver */330 girq->parent_handler = NULL;331 girq->num_parents = 0;332 girq->parents = NULL;333 girq->default_type = IRQ_TYPE_NONE;334 girq->handler = handle_simple_irq;335 girq->threaded = true;336 337 /* Bring the GPIO module out of reset */338 ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL,339 TC3589x_RSTCTRL_GPIRST, 0);340 if (ret < 0)341 return ret;342 343 /* For tc35894, have to disable Direct KBD interrupts,344 * else IRQST will always be 0x20, IRQN low level, can't345 * clear the irq status.346 * TODO: need more test on other tc3589x chip.347 *348 */349 ret = tc3589x_reg_write(tc3589x, TC3589x_DKBDMSK,350 TC3589x_DKBDMSK_ELINT | TC3589x_DKBDMSK_EINT);351 if (ret < 0)352 return ret;353 354 ret = devm_request_threaded_irq(&pdev->dev,355 irq, NULL, tc3589x_gpio_irq,356 IRQF_ONESHOT, "tc3589x-gpio",357 tc3589x_gpio);358 if (ret) {359 dev_err(&pdev->dev, "unable to get irq: %d\n", ret);360 return ret;361 }362 363 return devm_gpiochip_add_data(&pdev->dev, &tc3589x_gpio->chip, tc3589x_gpio);364}365 366static struct platform_driver tc3589x_gpio_driver = {367 .driver.name = "tc3589x-gpio",368 .probe = tc3589x_gpio_probe,369};370 371static int __init tc3589x_gpio_init(void)372{373 return platform_driver_register(&tc3589x_gpio_driver);374}375subsys_initcall(tc3589x_gpio_init);376