linux/drivers/gpio/gpio-tc3589x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) ST-Ericsson SA 2010
   4 *
   5 * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
   6 * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
   7 */
   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 avoid
  20 * unnecessary writes in bus_sync_unlock.
  21 */
  22enum { REG_IBE, REG_IEV, REG_IS, REG_IE, REG_DIRECT };
  23
  24#define CACHE_NR_REGS   5
  25#define CACHE_NR_BANKS  3
  26
  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 address
 113         * 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        else
 175                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        else
 180                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}
 234
 235static void tc3589x_gpio_irq_unmask(struct irq_data *d)
 236{
 237        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
 238        struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(gc);
 239        int offset = d->hwirq;
 240        int regoffset = offset / 8;
 241        int mask = BIT(offset % 8);
 242
 243        tc3589x_gpio->regs[REG_IE][regoffset] |= mask;
 244        tc3589x_gpio->regs[REG_DIRECT][regoffset] &= ~mask;
 245}
 246
 247static struct irq_chip tc3589x_gpio_irq_chip = {
 248        .name                   = "tc3589x-gpio",
 249        .irq_bus_lock           = tc3589x_gpio_irq_lock,
 250        .irq_bus_sync_unlock    = tc3589x_gpio_irq_sync_unlock,
 251        .irq_mask               = tc3589x_gpio_irq_mask,
 252        .irq_unmask             = tc3589x_gpio_irq_unmask,
 253        .irq_set_type           = tc3589x_gpio_irq_set_type,
 254};
 255
 256static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
 257{
 258        struct tc3589x_gpio *tc3589x_gpio = dev;
 259        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
 260        u8 status[CACHE_NR_BANKS];
 261        int ret;
 262        int i;
 263
 264        ret = tc3589x_block_read(tc3589x, TC3589x_GPIOMIS0,
 265                                 ARRAY_SIZE(status), status);
 266        if (ret < 0)
 267                return IRQ_NONE;
 268
 269        for (i = 0; i < ARRAY_SIZE(status); i++) {
 270                unsigned int stat = status[i];
 271                if (!stat)
 272                        continue;
 273
 274                while (stat) {
 275                        int bit = __ffs(stat);
 276                        int line = i * 8 + bit;
 277                        int irq = irq_find_mapping(tc3589x_gpio->chip.irq.domain,
 278                                                   line);
 279
 280                        handle_nested_irq(irq);
 281                        stat &= ~(1 << bit);
 282                }
 283
 284                tc3589x_reg_write(tc3589x, TC3589x_GPIOIC0 + i, status[i]);
 285        }
 286
 287        return IRQ_HANDLED;
 288}
 289
 290static int tc3589x_gpio_probe(struct platform_device *pdev)
 291{
 292        struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
 293        struct device_node *np = pdev->dev.of_node;
 294        struct tc3589x_gpio *tc3589x_gpio;
 295        struct gpio_irq_chip *girq;
 296        int ret;
 297        int irq;
 298
 299        if (!np) {
 300                dev_err(&pdev->dev, "No Device Tree node found\n");
 301                return -EINVAL;
 302        }
 303
 304        irq = platform_get_irq(pdev, 0);
 305        if (irq < 0)
 306                return irq;
 307
 308        tc3589x_gpio = devm_kzalloc(&pdev->dev, sizeof(struct tc3589x_gpio),
 309                                    GFP_KERNEL);
 310        if (!tc3589x_gpio)
 311                return -ENOMEM;
 312
 313        mutex_init(&tc3589x_gpio->irq_lock);
 314
 315        tc3589x_gpio->dev = &pdev->dev;
 316        tc3589x_gpio->tc3589x = tc3589x;
 317
 318        tc3589x_gpio->chip = template_chip;
 319        tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;
 320        tc3589x_gpio->chip.parent = &pdev->dev;
 321        tc3589x_gpio->chip.base = -1;
 322        tc3589x_gpio->chip.of_node = np;
 323
 324        girq = &tc3589x_gpio->chip.irq;
 325        girq->chip = &tc3589x_gpio_irq_chip;
 326        /* This will let us handle the parent IRQ in the driver */
 327        girq->parent_handler = NULL;
 328        girq->num_parents = 0;
 329        girq->parents = NULL;
 330        girq->default_type = IRQ_TYPE_NONE;
 331        girq->handler = handle_simple_irq;
 332        girq->threaded = true;
 333
 334        /* Bring the GPIO module out of reset */
 335        ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL,
 336                               TC3589x_RSTCTRL_GPIRST, 0);
 337        if (ret < 0)
 338                return ret;
 339
 340         /* For tc35894, have to disable Direct KBD interrupts,
 341          * else IRQST will always be 0x20, IRQN low level, can't
 342          * clear the irq status.
 343          * TODO: need more test on other tc3589x chip.
 344          *
 345          */
 346        ret = tc3589x_reg_write(tc3589x, TC3589x_DKBDMSK,
 347                        TC3589x_DKBDMSK_ELINT | TC3589x_DKBDMSK_EINT);
 348        if (ret < 0)
 349                return ret;
 350
 351        ret = devm_request_threaded_irq(&pdev->dev,
 352                                        irq, NULL, tc3589x_gpio_irq,
 353                                        IRQF_ONESHOT, "tc3589x-gpio",
 354                                        tc3589x_gpio);
 355        if (ret) {
 356                dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
 357                return ret;
 358        }
 359
 360        return devm_gpiochip_add_data(&pdev->dev, &tc3589x_gpio->chip, tc3589x_gpio);
 361}
 362
 363static struct platform_driver tc3589x_gpio_driver = {
 364        .driver.name    = "tc3589x-gpio",
 365        .probe          = tc3589x_gpio_probe,
 366};
 367
 368static int __init tc3589x_gpio_init(void)
 369{
 370        return platform_driver_register(&tc3589x_gpio_driver);
 371}
 372subsys_initcall(tc3589x_gpio_init);
 373