linux/drivers/gpio/gpio-tc3589x.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) ST-Ericsson SA 2010
   3 *
   4 * License Terms: GNU General Public License, version 2
   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/module.h>
  10#include <linux/init.h>
  11#include <linux/platform_device.h>
  12#include <linux/slab.h>
  13#include <linux/gpio.h>
  14#include <linux/of.h>
  15#include <linux/irq.h>
  16#include <linux/irqdomain.h>
  17#include <linux/interrupt.h>
  18#include <linux/mfd/tc3589x.h>
  19
  20/*
  21 * These registers are modified under the irq bus lock and cached to avoid
  22 * unnecessary writes in bus_sync_unlock.
  23 */
  24enum { REG_IBE, REG_IEV, REG_IS, REG_IE };
  25
  26#define CACHE_NR_REGS   4
  27#define CACHE_NR_BANKS  3
  28
  29struct tc3589x_gpio {
  30        struct gpio_chip chip;
  31        struct tc3589x *tc3589x;
  32        struct device *dev;
  33        struct mutex irq_lock;
  34        struct irq_domain *domain;
  35
  36        int irq_base;
  37
  38        /* Caches of interrupt control registers for bus_lock */
  39        u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
  40        u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
  41};
  42
  43static inline struct tc3589x_gpio *to_tc3589x_gpio(struct gpio_chip *chip)
  44{
  45        return container_of(chip, struct tc3589x_gpio, chip);
  46}
  47
  48static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned offset)
  49{
  50        struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
  51        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
  52        u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2;
  53        u8 mask = 1 << (offset % 8);
  54        int ret;
  55
  56        ret = tc3589x_reg_read(tc3589x, reg);
  57        if (ret < 0)
  58                return ret;
  59
  60        return ret & mask;
  61}
  62
  63static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
  64{
  65        struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
  66        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
  67        u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2;
  68        unsigned pos = offset % 8;
  69        u8 data[] = {!!val << pos, 1 << pos};
  70
  71        tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data);
  72}
  73
  74static int tc3589x_gpio_direction_output(struct gpio_chip *chip,
  75                                         unsigned offset, int val)
  76{
  77        struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
  78        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
  79        u8 reg = TC3589x_GPIODIR0 + offset / 8;
  80        unsigned pos = offset % 8;
  81
  82        tc3589x_gpio_set(chip, offset, val);
  83
  84        return tc3589x_set_bits(tc3589x, reg, 1 << pos, 1 << pos);
  85}
  86
  87static int tc3589x_gpio_direction_input(struct gpio_chip *chip,
  88                                        unsigned offset)
  89{
  90        struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
  91        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
  92        u8 reg = TC3589x_GPIODIR0 + offset / 8;
  93        unsigned pos = offset % 8;
  94
  95        return tc3589x_set_bits(tc3589x, reg, 1 << pos, 0);
  96}
  97
  98/**
  99 * tc3589x_gpio_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
 100 *
 101 * @tc3589x_gpio: tc3589x_gpio_irq controller to operate on.
 102 * @irq: index of the interrupt requested in the chip IRQs
 103 *
 104 * Useful for drivers to request their own IRQs.
 105 */
 106static int tc3589x_gpio_irq_get_virq(struct tc3589x_gpio *tc3589x_gpio,
 107                                     int irq)
 108{
 109        if (!tc3589x_gpio)
 110                return -EINVAL;
 111
 112        return irq_create_mapping(tc3589x_gpio->domain, irq);
 113}
 114
 115static int tc3589x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
 116{
 117        struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
 118
 119        return tc3589x_gpio_irq_get_virq(tc3589x_gpio, offset);
 120}
 121
 122static struct gpio_chip template_chip = {
 123        .label                  = "tc3589x",
 124        .owner                  = THIS_MODULE,
 125        .direction_input        = tc3589x_gpio_direction_input,
 126        .get                    = tc3589x_gpio_get,
 127        .direction_output       = tc3589x_gpio_direction_output,
 128        .set                    = tc3589x_gpio_set,
 129        .to_irq                 = tc3589x_gpio_to_irq,
 130        .can_sleep              = 1,
 131};
 132
 133static int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type)
 134{
 135        struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
 136        int offset = d->hwirq;
 137        int regoffset = offset / 8;
 138        int mask = 1 << (offset % 8);
 139
 140        if (type == IRQ_TYPE_EDGE_BOTH) {
 141                tc3589x_gpio->regs[REG_IBE][regoffset] |= mask;
 142                return 0;
 143        }
 144
 145        tc3589x_gpio->regs[REG_IBE][regoffset] &= ~mask;
 146
 147        if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
 148                tc3589x_gpio->regs[REG_IS][regoffset] |= mask;
 149        else
 150                tc3589x_gpio->regs[REG_IS][regoffset] &= ~mask;
 151
 152        if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
 153                tc3589x_gpio->regs[REG_IEV][regoffset] |= mask;
 154        else
 155                tc3589x_gpio->regs[REG_IEV][regoffset] &= ~mask;
 156
 157        return 0;
 158}
 159
 160static void tc3589x_gpio_irq_lock(struct irq_data *d)
 161{
 162        struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
 163
 164        mutex_lock(&tc3589x_gpio->irq_lock);
 165}
 166
 167static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d)
 168{
 169        struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
 170        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
 171        static const u8 regmap[] = {
 172                [REG_IBE]       = TC3589x_GPIOIBE0,
 173                [REG_IEV]       = TC3589x_GPIOIEV0,
 174                [REG_IS]        = TC3589x_GPIOIS0,
 175                [REG_IE]        = TC3589x_GPIOIE0,
 176        };
 177        int i, j;
 178
 179        for (i = 0; i < CACHE_NR_REGS; i++) {
 180                for (j = 0; j < CACHE_NR_BANKS; j++) {
 181                        u8 old = tc3589x_gpio->oldregs[i][j];
 182                        u8 new = tc3589x_gpio->regs[i][j];
 183
 184                        if (new == old)
 185                                continue;
 186
 187                        tc3589x_gpio->oldregs[i][j] = new;
 188                        tc3589x_reg_write(tc3589x, regmap[i] + j * 8, new);
 189                }
 190        }
 191
 192        mutex_unlock(&tc3589x_gpio->irq_lock);
 193}
 194
 195static void tc3589x_gpio_irq_mask(struct irq_data *d)
 196{
 197        struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
 198        int offset = d->hwirq;
 199        int regoffset = offset / 8;
 200        int mask = 1 << (offset % 8);
 201
 202        tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask;
 203}
 204
 205static void tc3589x_gpio_irq_unmask(struct irq_data *d)
 206{
 207        struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
 208        int offset = d->hwirq;
 209        int regoffset = offset / 8;
 210        int mask = 1 << (offset % 8);
 211
 212        tc3589x_gpio->regs[REG_IE][regoffset] |= mask;
 213}
 214
 215static struct irq_chip tc3589x_gpio_irq_chip = {
 216        .name                   = "tc3589x-gpio",
 217        .irq_bus_lock           = tc3589x_gpio_irq_lock,
 218        .irq_bus_sync_unlock    = tc3589x_gpio_irq_sync_unlock,
 219        .irq_mask               = tc3589x_gpio_irq_mask,
 220        .irq_unmask             = tc3589x_gpio_irq_unmask,
 221        .irq_set_type           = tc3589x_gpio_irq_set_type,
 222};
 223
 224static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
 225{
 226        struct tc3589x_gpio *tc3589x_gpio = dev;
 227        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
 228        u8 status[CACHE_NR_BANKS];
 229        int ret;
 230        int i;
 231
 232        ret = tc3589x_block_read(tc3589x, TC3589x_GPIOMIS0,
 233                                 ARRAY_SIZE(status), status);
 234        if (ret < 0)
 235                return IRQ_NONE;
 236
 237        for (i = 0; i < ARRAY_SIZE(status); i++) {
 238                unsigned int stat = status[i];
 239                if (!stat)
 240                        continue;
 241
 242                while (stat) {
 243                        int bit = __ffs(stat);
 244                        int line = i * 8 + bit;
 245                        int virq = tc3589x_gpio_irq_get_virq(tc3589x_gpio, line);
 246
 247                        handle_nested_irq(virq);
 248                        stat &= ~(1 << bit);
 249                }
 250
 251                tc3589x_reg_write(tc3589x, TC3589x_GPIOIC0 + i, status[i]);
 252        }
 253
 254        return IRQ_HANDLED;
 255}
 256
 257static int tc3589x_gpio_irq_map(struct irq_domain *d, unsigned int virq,
 258                                irq_hw_number_t hwirq)
 259{
 260        struct tc3589x *tc3589x_gpio = d->host_data;
 261
 262        irq_set_chip_data(virq, tc3589x_gpio);
 263        irq_set_chip_and_handler(virq, &tc3589x_gpio_irq_chip,
 264                                handle_simple_irq);
 265        irq_set_nested_thread(virq, 1);
 266#ifdef CONFIG_ARM
 267        set_irq_flags(virq, IRQF_VALID);
 268#else
 269        irq_set_noprobe(virq);
 270#endif
 271
 272        return 0;
 273}
 274
 275static void tc3589x_gpio_irq_unmap(struct irq_domain *d, unsigned int virq)
 276{
 277#ifdef CONFIG_ARM
 278        set_irq_flags(virq, 0);
 279#endif
 280        irq_set_chip_and_handler(virq, NULL, NULL);
 281        irq_set_chip_data(virq, NULL);
 282}
 283
 284static struct irq_domain_ops tc3589x_irq_ops = {
 285        .map    = tc3589x_gpio_irq_map,
 286        .unmap  = tc3589x_gpio_irq_unmap,
 287        .xlate  = irq_domain_xlate_twocell,
 288};
 289
 290static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio,
 291                                struct device_node *np)
 292{
 293        int base = tc3589x_gpio->irq_base;
 294
 295        if (base) {
 296                tc3589x_gpio->domain = irq_domain_add_legacy(
 297                        NULL, tc3589x_gpio->chip.ngpio, base,
 298                        0, &tc3589x_irq_ops, tc3589x_gpio);
 299        }
 300        else {
 301                tc3589x_gpio->domain = irq_domain_add_linear(
 302                        np, tc3589x_gpio->chip.ngpio,
 303                        &tc3589x_irq_ops, tc3589x_gpio);
 304        }
 305
 306        if (!tc3589x_gpio->domain) {
 307                dev_err(tc3589x_gpio->dev, "Failed to create irqdomain\n");
 308                return -ENOSYS;
 309        }
 310
 311        return 0;
 312}
 313
 314static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
 315{
 316        struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
 317        struct tc3589x_gpio_platform_data *pdata;
 318        struct device_node *np = pdev->dev.of_node;
 319        struct tc3589x_gpio *tc3589x_gpio;
 320        int ret;
 321        int irq;
 322
 323        pdata = tc3589x->pdata->gpio;
 324
 325        if (!(pdata || np)) {
 326                dev_err(&pdev->dev, "No platform data or Device Tree found\n");
 327                return -EINVAL;
 328        }
 329
 330        irq = platform_get_irq(pdev, 0);
 331        if (irq < 0)
 332                return irq;
 333
 334        tc3589x_gpio = kzalloc(sizeof(struct tc3589x_gpio), GFP_KERNEL);
 335        if (!tc3589x_gpio)
 336                return -ENOMEM;
 337
 338        mutex_init(&tc3589x_gpio->irq_lock);
 339
 340        tc3589x_gpio->dev = &pdev->dev;
 341        tc3589x_gpio->tc3589x = tc3589x;
 342
 343        tc3589x_gpio->chip = template_chip;
 344        tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;
 345        tc3589x_gpio->chip.dev = &pdev->dev;
 346        tc3589x_gpio->chip.base = (pdata) ? pdata->gpio_base : -1;
 347
 348#ifdef CONFIG_OF_GPIO
 349        tc3589x_gpio->chip.of_node = np;
 350#endif
 351
 352        tc3589x_gpio->irq_base = tc3589x->irq_base ?
 353                tc3589x->irq_base + TC3589x_INT_GPIO(0) : 0;
 354
 355        /* Bring the GPIO module out of reset */
 356        ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL,
 357                               TC3589x_RSTCTRL_GPIRST, 0);
 358        if (ret < 0)
 359                goto out_free;
 360
 361        ret = tc3589x_gpio_irq_init(tc3589x_gpio, np);
 362        if (ret)
 363                goto out_free;
 364
 365        ret = request_threaded_irq(irq, NULL, tc3589x_gpio_irq, IRQF_ONESHOT,
 366                                   "tc3589x-gpio", tc3589x_gpio);
 367        if (ret) {
 368                dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
 369                goto out_free;
 370        }
 371
 372        ret = gpiochip_add(&tc3589x_gpio->chip);
 373        if (ret) {
 374                dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
 375                goto out_freeirq;
 376        }
 377
 378        if (pdata && pdata->setup)
 379                pdata->setup(tc3589x, tc3589x_gpio->chip.base);
 380
 381        platform_set_drvdata(pdev, tc3589x_gpio);
 382
 383        return 0;
 384
 385out_freeirq:
 386        free_irq(irq, tc3589x_gpio);
 387out_free:
 388        kfree(tc3589x_gpio);
 389        return ret;
 390}
 391
 392static int __devexit tc3589x_gpio_remove(struct platform_device *pdev)
 393{
 394        struct tc3589x_gpio *tc3589x_gpio = platform_get_drvdata(pdev);
 395        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
 396        struct tc3589x_gpio_platform_data *pdata = tc3589x->pdata->gpio;
 397        int irq = platform_get_irq(pdev, 0);
 398        int ret;
 399
 400        if (pdata && pdata->remove)
 401                pdata->remove(tc3589x, tc3589x_gpio->chip.base);
 402
 403        ret = gpiochip_remove(&tc3589x_gpio->chip);
 404        if (ret < 0) {
 405                dev_err(tc3589x_gpio->dev,
 406                        "unable to remove gpiochip: %d\n", ret);
 407                return ret;
 408        }
 409
 410        free_irq(irq, tc3589x_gpio);
 411
 412        platform_set_drvdata(pdev, NULL);
 413        kfree(tc3589x_gpio);
 414
 415        return 0;
 416}
 417
 418static struct platform_driver tc3589x_gpio_driver = {
 419        .driver.name    = "tc3589x-gpio",
 420        .driver.owner   = THIS_MODULE,
 421        .probe          = tc3589x_gpio_probe,
 422        .remove         = __devexit_p(tc3589x_gpio_remove),
 423};
 424
 425static int __init tc3589x_gpio_init(void)
 426{
 427        return platform_driver_register(&tc3589x_gpio_driver);
 428}
 429subsys_initcall(tc3589x_gpio_init);
 430
 431static void __exit tc3589x_gpio_exit(void)
 432{
 433        platform_driver_unregister(&tc3589x_gpio_driver);
 434}
 435module_exit(tc3589x_gpio_exit);
 436
 437MODULE_LICENSE("GPL v2");
 438MODULE_DESCRIPTION("TC3589x GPIO driver");
 439MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
 440
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.