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/irq.h>
  15#include <linux/interrupt.h>
  16#include <linux/mfd/tc3589x.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 };
  23
  24#define CACHE_NR_REGS   4
  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
  33        int irq_base;
  34
  35        /* Caches of interrupt control registers for bus_lock */
  36        u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
  37        u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
  38};
  39
  40static inline struct tc3589x_gpio *to_tc3589x_gpio(struct gpio_chip *chip)
  41{
  42        return container_of(chip, struct tc3589x_gpio, chip);
  43}
  44
  45static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned offset)
  46{
  47        struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
  48        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
  49        u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2;
  50        u8 mask = 1 << (offset % 8);
  51        int ret;
  52
  53        ret = tc3589x_reg_read(tc3589x, reg);
  54        if (ret < 0)
  55                return ret;
  56
  57        return ret & mask;
  58}
  59
  60static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
  61{
  62        struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
  63        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
  64        u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2;
  65        unsigned pos = offset % 8;
  66        u8 data[] = {!!val << pos, 1 << pos};
  67
  68        tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data);
  69}
  70
  71static int tc3589x_gpio_direction_output(struct gpio_chip *chip,
  72                                         unsigned offset, int val)
  73{
  74        struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
  75        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
  76        u8 reg = TC3589x_GPIODIR0 + offset / 8;
  77        unsigned pos = offset % 8;
  78
  79        tc3589x_gpio_set(chip, offset, val);
  80
  81        return tc3589x_set_bits(tc3589x, reg, 1 << pos, 1 << pos);
  82}
  83
  84static int tc3589x_gpio_direction_input(struct gpio_chip *chip,
  85                                        unsigned offset)
  86{
  87        struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
  88        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
  89        u8 reg = TC3589x_GPIODIR0 + offset / 8;
  90        unsigned pos = offset % 8;
  91
  92        return tc3589x_set_bits(tc3589x, reg, 1 << pos, 0);
  93}
  94
  95static int tc3589x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
  96{
  97        struct tc3589x_gpio *tc3589x_gpio = to_tc3589x_gpio(chip);
  98
  99        return tc3589x_gpio->irq_base + offset;
 100}
 101
 102static struct gpio_chip template_chip = {
 103        .label                  = "tc3589x",
 104        .owner                  = THIS_MODULE,
 105        .direction_input        = tc3589x_gpio_direction_input,
 106        .get                    = tc3589x_gpio_get,
 107        .direction_output       = tc3589x_gpio_direction_output,
 108        .set                    = tc3589x_gpio_set,
 109        .to_irq                 = tc3589x_gpio_to_irq,
 110        .can_sleep              = 1,
 111};
 112
 113static int tc3589x_gpio_irq_set_type(struct irq_data *d, unsigned int type)
 114{
 115        struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
 116        int offset = d->irq - tc3589x_gpio->irq_base;
 117        int regoffset = offset / 8;
 118        int mask = 1 << (offset % 8);
 119
 120        if (type == IRQ_TYPE_EDGE_BOTH) {
 121                tc3589x_gpio->regs[REG_IBE][regoffset] |= mask;
 122                return 0;
 123        }
 124
 125        tc3589x_gpio->regs[REG_IBE][regoffset] &= ~mask;
 126
 127        if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
 128                tc3589x_gpio->regs[REG_IS][regoffset] |= mask;
 129        else
 130                tc3589x_gpio->regs[REG_IS][regoffset] &= ~mask;
 131
 132        if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
 133                tc3589x_gpio->regs[REG_IEV][regoffset] |= mask;
 134        else
 135                tc3589x_gpio->regs[REG_IEV][regoffset] &= ~mask;
 136
 137        return 0;
 138}
 139
 140static void tc3589x_gpio_irq_lock(struct irq_data *d)
 141{
 142        struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
 143
 144        mutex_lock(&tc3589x_gpio->irq_lock);
 145}
 146
 147static void tc3589x_gpio_irq_sync_unlock(struct irq_data *d)
 148{
 149        struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
 150        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
 151        static const u8 regmap[] = {
 152                [REG_IBE]       = TC3589x_GPIOIBE0,
 153                [REG_IEV]       = TC3589x_GPIOIEV0,
 154                [REG_IS]        = TC3589x_GPIOIS0,
 155                [REG_IE]        = TC3589x_GPIOIE0,
 156        };
 157        int i, j;
 158
 159        for (i = 0; i < CACHE_NR_REGS; i++) {
 160                for (j = 0; j < CACHE_NR_BANKS; j++) {
 161                        u8 old = tc3589x_gpio->oldregs[i][j];
 162                        u8 new = tc3589x_gpio->regs[i][j];
 163
 164                        if (new == old)
 165                                continue;
 166
 167                        tc3589x_gpio->oldregs[i][j] = new;
 168                        tc3589x_reg_write(tc3589x, regmap[i] + j * 8, new);
 169                }
 170        }
 171
 172        mutex_unlock(&tc3589x_gpio->irq_lock);
 173}
 174
 175static void tc3589x_gpio_irq_mask(struct irq_data *d)
 176{
 177        struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
 178        int offset = d->irq - tc3589x_gpio->irq_base;
 179        int regoffset = offset / 8;
 180        int mask = 1 << (offset % 8);
 181
 182        tc3589x_gpio->regs[REG_IE][regoffset] &= ~mask;
 183}
 184
 185static void tc3589x_gpio_irq_unmask(struct irq_data *d)
 186{
 187        struct tc3589x_gpio *tc3589x_gpio = irq_data_get_irq_chip_data(d);
 188        int offset = d->irq - tc3589x_gpio->irq_base;
 189        int regoffset = offset / 8;
 190        int mask = 1 << (offset % 8);
 191
 192        tc3589x_gpio->regs[REG_IE][regoffset] |= mask;
 193}
 194
 195static struct irq_chip tc3589x_gpio_irq_chip = {
 196        .name                   = "tc3589x-gpio",
 197        .irq_bus_lock           = tc3589x_gpio_irq_lock,
 198        .irq_bus_sync_unlock    = tc3589x_gpio_irq_sync_unlock,
 199        .irq_mask               = tc3589x_gpio_irq_mask,
 200        .irq_unmask             = tc3589x_gpio_irq_unmask,
 201        .irq_set_type           = tc3589x_gpio_irq_set_type,
 202};
 203
 204static irqreturn_t tc3589x_gpio_irq(int irq, void *dev)
 205{
 206        struct tc3589x_gpio *tc3589x_gpio = dev;
 207        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
 208        u8 status[CACHE_NR_BANKS];
 209        int ret;
 210        int i;
 211
 212        ret = tc3589x_block_read(tc3589x, TC3589x_GPIOMIS0,
 213                                 ARRAY_SIZE(status), status);
 214        if (ret < 0)
 215                return IRQ_NONE;
 216
 217        for (i = 0; i < ARRAY_SIZE(status); i++) {
 218                unsigned int stat = status[i];
 219                if (!stat)
 220                        continue;
 221
 222                while (stat) {
 223                        int bit = __ffs(stat);
 224                        int line = i * 8 + bit;
 225
 226                        handle_nested_irq(tc3589x_gpio->irq_base + line);
 227                        stat &= ~(1 << bit);
 228                }
 229
 230                tc3589x_reg_write(tc3589x, TC3589x_GPIOIC0 + i, status[i]);
 231        }
 232
 233        return IRQ_HANDLED;
 234}
 235
 236static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio)
 237{
 238        int base = tc3589x_gpio->irq_base;
 239        int irq;
 240
 241        for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) {
 242                irq_set_chip_data(irq, tc3589x_gpio);
 243                irq_set_chip_and_handler(irq, &tc3589x_gpio_irq_chip,
 244                                         handle_simple_irq);
 245                irq_set_nested_thread(irq, 1);
 246#ifdef CONFIG_ARM
 247                set_irq_flags(irq, IRQF_VALID);
 248#else
 249                irq_set_noprobe(irq);
 250#endif
 251        }
 252
 253        return 0;
 254}
 255
 256static void tc3589x_gpio_irq_remove(struct tc3589x_gpio *tc3589x_gpio)
 257{
 258        int base = tc3589x_gpio->irq_base;
 259        int irq;
 260
 261        for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) {
 262#ifdef CONFIG_ARM
 263                set_irq_flags(irq, 0);
 264#endif
 265                irq_set_chip_and_handler(irq, NULL, NULL);
 266                irq_set_chip_data(irq, NULL);
 267        }
 268}
 269
 270static int __devinit tc3589x_gpio_probe(struct platform_device *pdev)
 271{
 272        struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
 273        struct tc3589x_gpio_platform_data *pdata;
 274        struct tc3589x_gpio *tc3589x_gpio;
 275        int ret;
 276        int irq;
 277
 278        pdata = tc3589x->pdata->gpio;
 279        if (!pdata)
 280                return -ENODEV;
 281
 282        irq = platform_get_irq(pdev, 0);
 283        if (irq < 0)
 284                return irq;
 285
 286        tc3589x_gpio = kzalloc(sizeof(struct tc3589x_gpio), GFP_KERNEL);
 287        if (!tc3589x_gpio)
 288                return -ENOMEM;
 289
 290        mutex_init(&tc3589x_gpio->irq_lock);
 291
 292        tc3589x_gpio->dev = &pdev->dev;
 293        tc3589x_gpio->tc3589x = tc3589x;
 294
 295        tc3589x_gpio->chip = template_chip;
 296        tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;
 297        tc3589x_gpio->chip.dev = &pdev->dev;
 298        tc3589x_gpio->chip.base = pdata->gpio_base;
 299
 300        tc3589x_gpio->irq_base = tc3589x->irq_base + TC3589x_INT_GPIO(0);
 301
 302        /* Bring the GPIO module out of reset */
 303        ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL,
 304                               TC3589x_RSTCTRL_GPIRST, 0);
 305        if (ret < 0)
 306                goto out_free;
 307
 308        ret = tc3589x_gpio_irq_init(tc3589x_gpio);
 309        if (ret)
 310                goto out_free;
 311
 312        ret = request_threaded_irq(irq, NULL, tc3589x_gpio_irq, IRQF_ONESHOT,
 313                                   "tc3589x-gpio", tc3589x_gpio);
 314        if (ret) {
 315                dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
 316                goto out_removeirq;
 317        }
 318
 319        ret = gpiochip_add(&tc3589x_gpio->chip);
 320        if (ret) {
 321                dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
 322                goto out_freeirq;
 323        }
 324
 325        if (pdata->setup)
 326                pdata->setup(tc3589x, tc3589x_gpio->chip.base);
 327
 328        platform_set_drvdata(pdev, tc3589x_gpio);
 329
 330        return 0;
 331
 332out_freeirq:
 333        free_irq(irq, tc3589x_gpio);
 334out_removeirq:
 335        tc3589x_gpio_irq_remove(tc3589x_gpio);
 336out_free:
 337        kfree(tc3589x_gpio);
 338        return ret;
 339}
 340
 341static int __devexit tc3589x_gpio_remove(struct platform_device *pdev)
 342{
 343        struct tc3589x_gpio *tc3589x_gpio = platform_get_drvdata(pdev);
 344        struct tc3589x *tc3589x = tc3589x_gpio->tc3589x;
 345        struct tc3589x_gpio_platform_data *pdata = tc3589x->pdata->gpio;
 346        int irq = platform_get_irq(pdev, 0);
 347        int ret;
 348
 349        if (pdata->remove)
 350                pdata->remove(tc3589x, tc3589x_gpio->chip.base);
 351
 352        ret = gpiochip_remove(&tc3589x_gpio->chip);
 353        if (ret < 0) {
 354                dev_err(tc3589x_gpio->dev,
 355                        "unable to remove gpiochip: %d\n", ret);
 356                return ret;
 357        }
 358
 359        free_irq(irq, tc3589x_gpio);
 360        tc3589x_gpio_irq_remove(tc3589x_gpio);
 361
 362        platform_set_drvdata(pdev, NULL);
 363        kfree(tc3589x_gpio);
 364
 365        return 0;
 366}
 367
 368static struct platform_driver tc3589x_gpio_driver = {
 369        .driver.name    = "tc3589x-gpio",
 370        .driver.owner   = THIS_MODULE,
 371        .probe          = tc3589x_gpio_probe,
 372        .remove         = __devexit_p(tc3589x_gpio_remove),
 373};
 374
 375static int __init tc3589x_gpio_init(void)
 376{
 377        return platform_driver_register(&tc3589x_gpio_driver);
 378}
 379subsys_initcall(tc3589x_gpio_init);
 380
 381static void __exit tc3589x_gpio_exit(void)
 382{
 383        platform_driver_unregister(&tc3589x_gpio_driver);
 384}
 385module_exit(tc3589x_gpio_exit);
 386
 387MODULE_LICENSE("GPL v2");
 388MODULE_DESCRIPTION("TC3589x GPIO driver");
 389MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
 390
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.