linux/drivers/gpio/pl061.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/gpio/pl061.c
   3 *
   4 *  Copyright (C) 2008, 2009 Provigent Ltd.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)
  11 *
  12 * Data sheet: ARM DDI 0190B, September 2000
  13 */
  14#include <linux/spinlock.h>
  15#include <linux/errno.h>
  16#include <linux/module.h>
  17#include <linux/list.h>
  18#include <linux/io.h>
  19#include <linux/ioport.h>
  20#include <linux/irq.h>
  21#include <linux/bitops.h>
  22#include <linux/workqueue.h>
  23#include <linux/gpio.h>
  24#include <linux/device.h>
  25#include <linux/amba/bus.h>
  26#include <linux/amba/pl061.h>
  27
  28#define GPIODIR 0x400
  29#define GPIOIS  0x404
  30#define GPIOIBE 0x408
  31#define GPIOIEV 0x40C
  32#define GPIOIE  0x410
  33#define GPIORIS 0x414
  34#define GPIOMIS 0x418
  35#define GPIOIC  0x41C
  36
  37#define PL061_GPIO_NR   8
  38
  39struct pl061_gpio {
  40        /* We use a list of pl061_gpio structs for each trigger IRQ in the main
  41         * interrupts controller of the system. We need this to support systems
  42         * in which more that one PL061s are connected to the same IRQ. The ISR
  43         * interates through this list to find the source of the interrupt.
  44         */
  45        struct list_head        list;
  46
  47        /* Each of the two spinlocks protects a different set of hardware
  48         * regiters and data structurs. This decouples the code of the IRQ from
  49         * the GPIO code. This also makes the case of a GPIO routine call from
  50         * the IRQ code simpler.
  51         */
  52        spinlock_t              lock;           /* GPIO registers */
  53        spinlock_t              irq_lock;       /* IRQ registers */
  54
  55        void __iomem            *base;
  56        unsigned                irq_base;
  57        struct gpio_chip        gc;
  58};
  59
  60static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
  61{
  62        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
  63        unsigned long flags;
  64        unsigned char gpiodir;
  65
  66        if (offset >= gc->ngpio)
  67                return -EINVAL;
  68
  69        spin_lock_irqsave(&chip->lock, flags);
  70        gpiodir = readb(chip->base + GPIODIR);
  71        gpiodir &= ~(1 << offset);
  72        writeb(gpiodir, chip->base + GPIODIR);
  73        spin_unlock_irqrestore(&chip->lock, flags);
  74
  75        return 0;
  76}
  77
  78static int pl061_direction_output(struct gpio_chip *gc, unsigned offset,
  79                int value)
  80{
  81        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
  82        unsigned long flags;
  83        unsigned char gpiodir;
  84
  85        if (offset >= gc->ngpio)
  86                return -EINVAL;
  87
  88        spin_lock_irqsave(&chip->lock, flags);
  89        writeb(!!value << offset, chip->base + (1 << (offset + 2)));
  90        gpiodir = readb(chip->base + GPIODIR);
  91        gpiodir |= 1 << offset;
  92        writeb(gpiodir, chip->base + GPIODIR);
  93        spin_unlock_irqrestore(&chip->lock, flags);
  94
  95        return 0;
  96}
  97
  98static int pl061_get_value(struct gpio_chip *gc, unsigned offset)
  99{
 100        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
 101
 102        return !!readb(chip->base + (1 << (offset + 2)));
 103}
 104
 105static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value)
 106{
 107        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
 108
 109        writeb(!!value << offset, chip->base + (1 << (offset + 2)));
 110}
 111
 112static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
 113{
 114        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
 115
 116        if (chip->irq_base == (unsigned) -1)
 117                return -EINVAL;
 118
 119        return chip->irq_base + offset;
 120}
 121
 122/*
 123 * PL061 GPIO IRQ
 124 */
 125static void pl061_irq_disable(unsigned irq)
 126{
 127        struct pl061_gpio *chip = get_irq_chip_data(irq);
 128        int offset = irq - chip->irq_base;
 129        unsigned long flags;
 130        u8 gpioie;
 131
 132        spin_lock_irqsave(&chip->irq_lock, flags);
 133        gpioie = readb(chip->base + GPIOIE);
 134        gpioie &= ~(1 << offset);
 135        writeb(gpioie, chip->base + GPIOIE);
 136        spin_unlock_irqrestore(&chip->irq_lock, flags);
 137}
 138
 139static void pl061_irq_enable(unsigned irq)
 140{
 141        struct pl061_gpio *chip = get_irq_chip_data(irq);
 142        int offset = irq - chip->irq_base;
 143        unsigned long flags;
 144        u8 gpioie;
 145
 146        spin_lock_irqsave(&chip->irq_lock, flags);
 147        gpioie = readb(chip->base + GPIOIE);
 148        gpioie |= 1 << offset;
 149        writeb(gpioie, chip->base + GPIOIE);
 150        spin_unlock_irqrestore(&chip->irq_lock, flags);
 151}
 152
 153static int pl061_irq_type(unsigned irq, unsigned trigger)
 154{
 155        struct pl061_gpio *chip = get_irq_chip_data(irq);
 156        int offset = irq - chip->irq_base;
 157        unsigned long flags;
 158        u8 gpiois, gpioibe, gpioiev;
 159
 160        if (offset < 0 || offset > PL061_GPIO_NR)
 161                return -EINVAL;
 162
 163        spin_lock_irqsave(&chip->irq_lock, flags);
 164
 165        gpioiev = readb(chip->base + GPIOIEV);
 166
 167        gpiois = readb(chip->base + GPIOIS);
 168        if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
 169                gpiois |= 1 << offset;
 170                if (trigger & IRQ_TYPE_LEVEL_HIGH)
 171                        gpioiev |= 1 << offset;
 172                else
 173                        gpioiev &= ~(1 << offset);
 174        } else
 175                gpiois &= ~(1 << offset);
 176        writeb(gpiois, chip->base + GPIOIS);
 177
 178        gpioibe = readb(chip->base + GPIOIBE);
 179        if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
 180                gpioibe |= 1 << offset;
 181        else {
 182                gpioibe &= ~(1 << offset);
 183                if (trigger & IRQ_TYPE_EDGE_RISING)
 184                        gpioiev |= 1 << offset;
 185                else
 186                        gpioiev &= ~(1 << offset);
 187        }
 188        writeb(gpioibe, chip->base + GPIOIBE);
 189
 190        writeb(gpioiev, chip->base + GPIOIEV);
 191
 192        spin_unlock_irqrestore(&chip->irq_lock, flags);
 193
 194        return 0;
 195}
 196
 197static struct irq_chip pl061_irqchip = {
 198        .name           = "GPIO",
 199        .enable         = pl061_irq_enable,
 200        .disable        = pl061_irq_disable,
 201        .set_type       = pl061_irq_type,
 202};
 203
 204static void pl061_irq_handler(unsigned irq, struct irq_desc *desc)
 205{
 206        struct list_head *chip_list = get_irq_chip_data(irq);
 207        struct list_head *ptr;
 208        struct pl061_gpio *chip;
 209
 210        desc->chip->ack(irq);
 211        list_for_each(ptr, chip_list) {
 212                unsigned long pending;
 213                int offset;
 214
 215                chip = list_entry(ptr, struct pl061_gpio, list);
 216                pending = readb(chip->base + GPIOMIS);
 217                writeb(pending, chip->base + GPIOIC);
 218
 219                if (pending == 0)
 220                        continue;
 221
 222                for_each_bit(offset, &pending, PL061_GPIO_NR)
 223                        generic_handle_irq(pl061_to_irq(&chip->gc, offset));
 224        }
 225        desc->chip->unmask(irq);
 226}
 227
 228static int __init pl061_probe(struct amba_device *dev, struct amba_id *id)
 229{
 230        struct pl061_platform_data *pdata;
 231        struct pl061_gpio *chip;
 232        struct list_head *chip_list;
 233        int ret, irq, i;
 234        static DECLARE_BITMAP(init_irq, NR_IRQS);
 235
 236        pdata = dev->dev.platform_data;
 237        if (pdata == NULL)
 238                return -ENODEV;
 239
 240        chip = kzalloc(sizeof(*chip), GFP_KERNEL);
 241        if (chip == NULL)
 242                return -ENOMEM;
 243
 244        if (!request_mem_region(dev->res.start,
 245                                resource_size(&dev->res), "pl061")) {
 246                ret = -EBUSY;
 247                goto free_mem;
 248        }
 249
 250        chip->base = ioremap(dev->res.start, resource_size(&dev->res));
 251        if (chip->base == NULL) {
 252                ret = -ENOMEM;
 253                goto release_region;
 254        }
 255
 256        spin_lock_init(&chip->lock);
 257        spin_lock_init(&chip->irq_lock);
 258        INIT_LIST_HEAD(&chip->list);
 259
 260        chip->gc.direction_input = pl061_direction_input;
 261        chip->gc.direction_output = pl061_direction_output;
 262        chip->gc.get = pl061_get_value;
 263        chip->gc.set = pl061_set_value;
 264        chip->gc.to_irq = pl061_to_irq;
 265        chip->gc.base = pdata->gpio_base;
 266        chip->gc.ngpio = PL061_GPIO_NR;
 267        chip->gc.label = dev_name(&dev->dev);
 268        chip->gc.dev = &dev->dev;
 269        chip->gc.owner = THIS_MODULE;
 270
 271        chip->irq_base = pdata->irq_base;
 272
 273        ret = gpiochip_add(&chip->gc);
 274        if (ret)
 275                goto iounmap;
 276
 277        /*
 278         * irq_chip support
 279         */
 280
 281        if (chip->irq_base == (unsigned) -1)
 282                return 0;
 283
 284        writeb(0, chip->base + GPIOIE); /* disable irqs */
 285        irq = dev->irq[0];
 286        if (irq < 0) {
 287                ret = -ENODEV;
 288                goto iounmap;
 289        }
 290        set_irq_chained_handler(irq, pl061_irq_handler);
 291        if (!test_and_set_bit(irq, init_irq)) { /* list initialized? */
 292                chip_list = kmalloc(sizeof(*chip_list), GFP_KERNEL);
 293                if (chip_list == NULL) {
 294                        clear_bit(irq, init_irq);
 295                        ret = -ENOMEM;
 296                        goto iounmap;
 297                }
 298                INIT_LIST_HEAD(chip_list);
 299                set_irq_chip_data(irq, chip_list);
 300        } else
 301                chip_list = get_irq_chip_data(irq);
 302        list_add(&chip->list, chip_list);
 303
 304        for (i = 0; i < PL061_GPIO_NR; i++) {
 305                if (pdata->directions & (1 << i))
 306                        pl061_direction_output(&chip->gc, i,
 307                                        pdata->values & (1 << i));
 308                else
 309                        pl061_direction_input(&chip->gc, i);
 310
 311                set_irq_chip(i+chip->irq_base, &pl061_irqchip);
 312                set_irq_handler(i+chip->irq_base, handle_simple_irq);
 313                set_irq_flags(i+chip->irq_base, IRQF_VALID);
 314                set_irq_chip_data(i+chip->irq_base, chip);
 315        }
 316
 317        return 0;
 318
 319iounmap:
 320        iounmap(chip->base);
 321release_region:
 322        release_mem_region(dev->res.start, resource_size(&dev->res));
 323free_mem:
 324        kfree(chip);
 325
 326        return ret;
 327}
 328
 329static struct amba_id pl061_ids[] __initdata = {
 330        {
 331                .id     = 0x00041061,
 332                .mask   = 0x000fffff,
 333        },
 334        { 0, 0 },
 335};
 336
 337static struct amba_driver pl061_gpio_driver = {
 338        .drv = {
 339                .name   = "pl061_gpio",
 340        },
 341        .id_table       = pl061_ids,
 342        .probe          = pl061_probe,
 343};
 344
 345static int __init pl061_gpio_init(void)
 346{
 347        return amba_driver_register(&pl061_gpio_driver);
 348}
 349subsys_initcall(pl061_gpio_init);
 350
 351MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
 352MODULE_DESCRIPTION("PL061 GPIO driver");
 353MODULE_LICENSE("GPL");
 354