linux/drivers/gpio/gpio-pl061.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2008, 2009 Provigent Ltd.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)
   9 *
  10 * Data sheet: ARM DDI 0190B, September 2000
  11 */
  12#include <linux/spinlock.h>
  13#include <linux/errno.h>
  14#include <linux/module.h>
  15#include <linux/io.h>
  16#include <linux/ioport.h>
  17#include <linux/irq.h>
  18#include <linux/bitops.h>
  19#include <linux/workqueue.h>
  20#include <linux/gpio.h>
  21#include <linux/device.h>
  22#include <linux/amba/bus.h>
  23#include <linux/amba/pl061.h>
  24#include <linux/slab.h>
  25#include <linux/pm.h>
  26#include <asm/mach/irq.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
  39#ifdef CONFIG_PM
  40struct pl061_context_save_regs {
  41        u8 gpio_data;
  42        u8 gpio_dir;
  43        u8 gpio_is;
  44        u8 gpio_ibe;
  45        u8 gpio_iev;
  46        u8 gpio_ie;
  47};
  48#endif
  49
  50struct pl061_gpio {
  51        spinlock_t              lock;
  52
  53        void __iomem            *base;
  54        int                     irq_base;
  55        struct irq_chip_generic *irq_gc;
  56        struct gpio_chip        gc;
  57
  58#ifdef CONFIG_PM
  59        struct pl061_context_save_regs csave_regs;
  60#endif
  61};
  62
  63static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
  64{
  65        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
  66        unsigned long flags;
  67        unsigned char gpiodir;
  68
  69        if (offset >= gc->ngpio)
  70                return -EINVAL;
  71
  72        spin_lock_irqsave(&chip->lock, flags);
  73        gpiodir = readb(chip->base + GPIODIR);
  74        gpiodir &= ~(1 << offset);
  75        writeb(gpiodir, chip->base + GPIODIR);
  76        spin_unlock_irqrestore(&chip->lock, flags);
  77
  78        return 0;
  79}
  80
  81static int pl061_direction_output(struct gpio_chip *gc, unsigned offset,
  82                int value)
  83{
  84        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
  85        unsigned long flags;
  86        unsigned char gpiodir;
  87
  88        if (offset >= gc->ngpio)
  89                return -EINVAL;
  90
  91        spin_lock_irqsave(&chip->lock, flags);
  92        writeb(!!value << offset, chip->base + (1 << (offset + 2)));
  93        gpiodir = readb(chip->base + GPIODIR);
  94        gpiodir |= 1 << offset;
  95        writeb(gpiodir, chip->base + GPIODIR);
  96
  97        /*
  98         * gpio value is set again, because pl061 doesn't allow to set value of
  99         * a gpio pin before configuring it in OUT mode.
 100         */
 101        writeb(!!value << offset, chip->base + (1 << (offset + 2)));
 102        spin_unlock_irqrestore(&chip->lock, flags);
 103
 104        return 0;
 105}
 106
 107static int pl061_get_value(struct gpio_chip *gc, unsigned offset)
 108{
 109        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
 110
 111        return !!readb(chip->base + (1 << (offset + 2)));
 112}
 113
 114static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value)
 115{
 116        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
 117
 118        writeb(!!value << offset, chip->base + (1 << (offset + 2)));
 119}
 120
 121static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
 122{
 123        struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
 124
 125        if (chip->irq_base <= 0)
 126                return -EINVAL;
 127
 128        return chip->irq_base + offset;
 129}
 130
 131static int pl061_irq_type(struct irq_data *d, unsigned trigger)
 132{
 133        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 134        struct pl061_gpio *chip = gc->private;
 135        int offset = d->irq - chip->irq_base;
 136        unsigned long flags;
 137        u8 gpiois, gpioibe, gpioiev;
 138
 139        if (offset < 0 || offset >= PL061_GPIO_NR)
 140                return -EINVAL;
 141
 142        raw_spin_lock_irqsave(&gc->lock, flags);
 143
 144        gpioiev = readb(chip->base + GPIOIEV);
 145
 146        gpiois = readb(chip->base + GPIOIS);
 147        if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
 148                gpiois |= 1 << offset;
 149                if (trigger & IRQ_TYPE_LEVEL_HIGH)
 150                        gpioiev |= 1 << offset;
 151                else
 152                        gpioiev &= ~(1 << offset);
 153        } else
 154                gpiois &= ~(1 << offset);
 155        writeb(gpiois, chip->base + GPIOIS);
 156
 157        gpioibe = readb(chip->base + GPIOIBE);
 158        if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
 159                gpioibe |= 1 << offset;
 160        else {
 161                gpioibe &= ~(1 << offset);
 162                if (trigger & IRQ_TYPE_EDGE_RISING)
 163                        gpioiev |= 1 << offset;
 164                else if (trigger & IRQ_TYPE_EDGE_FALLING)
 165                        gpioiev &= ~(1 << offset);
 166        }
 167        writeb(gpioibe, chip->base + GPIOIBE);
 168
 169        writeb(gpioiev, chip->base + GPIOIEV);
 170
 171        raw_spin_unlock_irqrestore(&gc->lock, flags);
 172
 173        return 0;
 174}
 175
 176static void pl061_irq_handler(unsigned irq, struct irq_desc *desc)
 177{
 178        unsigned long pending;
 179        int offset;
 180        struct pl061_gpio *chip = irq_desc_get_handler_data(desc);
 181        struct irq_chip *irqchip = irq_desc_get_chip(desc);
 182
 183        chained_irq_enter(irqchip, desc);
 184
 185        pending = readb(chip->base + GPIOMIS);
 186        writeb(pending, chip->base + GPIOIC);
 187        if (pending) {
 188                for_each_set_bit(offset, &pending, PL061_GPIO_NR)
 189                        generic_handle_irq(pl061_to_irq(&chip->gc, offset));
 190        }
 191
 192        chained_irq_exit(irqchip, desc);
 193}
 194
 195static void __init pl061_init_gc(struct pl061_gpio *chip, int irq_base)
 196{
 197        struct irq_chip_type *ct;
 198
 199        chip->irq_gc = irq_alloc_generic_chip("gpio-pl061", 1, irq_base,
 200                                              chip->base, handle_simple_irq);
 201        chip->irq_gc->private = chip;
 202
 203        ct = chip->irq_gc->chip_types;
 204        ct->chip.irq_mask = irq_gc_mask_clr_bit;
 205        ct->chip.irq_unmask = irq_gc_mask_set_bit;
 206        ct->chip.irq_set_type = pl061_irq_type;
 207        ct->chip.irq_set_wake = irq_gc_set_wake;
 208        ct->regs.mask = GPIOIE;
 209
 210        irq_setup_generic_chip(chip->irq_gc, IRQ_MSK(PL061_GPIO_NR),
 211                               IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0);
 212}
 213
 214static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
 215{
 216        struct device *dev = &adev->dev;
 217        struct pl061_platform_data *pdata = dev->platform_data;
 218        struct pl061_gpio *chip;
 219        int ret, irq, i;
 220
 221        chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
 222        if (chip == NULL)
 223                return -ENOMEM;
 224
 225        if (pdata) {
 226                chip->gc.base = pdata->gpio_base;
 227                chip->irq_base = pdata->irq_base;
 228        } else if (adev->dev.of_node) {
 229                chip->gc.base = -1;
 230                chip->irq_base = 0;
 231        } else
 232                return -ENODEV;
 233
 234        if (!devm_request_mem_region(dev, adev->res.start,
 235                                resource_size(&adev->res), "pl061"))
 236                return -EBUSY;
 237
 238        chip->base = devm_ioremap(dev, adev->res.start,
 239                                resource_size(&adev->res));
 240        if (chip->base == NULL)
 241                return -ENOMEM;
 242
 243        spin_lock_init(&chip->lock);
 244
 245        chip->gc.direction_input = pl061_direction_input;
 246        chip->gc.direction_output = pl061_direction_output;
 247        chip->gc.get = pl061_get_value;
 248        chip->gc.set = pl061_set_value;
 249        chip->gc.to_irq = pl061_to_irq;
 250        chip->gc.ngpio = PL061_GPIO_NR;
 251        chip->gc.label = dev_name(dev);
 252        chip->gc.dev = dev;
 253        chip->gc.owner = THIS_MODULE;
 254
 255        ret = gpiochip_add(&chip->gc);
 256        if (ret)
 257                return ret;
 258
 259        /*
 260         * irq_chip support
 261         */
 262
 263        if (chip->irq_base <= 0)
 264                return 0;
 265
 266        pl061_init_gc(chip, chip->irq_base);
 267
 268        writeb(0, chip->base + GPIOIE); /* disable irqs */
 269        irq = adev->irq[0];
 270        if (irq < 0)
 271                return -ENODEV;
 272
 273        irq_set_chained_handler(irq, pl061_irq_handler);
 274        irq_set_handler_data(irq, chip);
 275
 276        for (i = 0; i < PL061_GPIO_NR; i++) {
 277                if (pdata) {
 278                        if (pdata->directions & (1 << i))
 279                                pl061_direction_output(&chip->gc, i,
 280                                                pdata->values & (1 << i));
 281                        else
 282                                pl061_direction_input(&chip->gc, i);
 283                }
 284        }
 285
 286        amba_set_drvdata(adev, chip);
 287
 288        return 0;
 289}
 290
 291#ifdef CONFIG_PM
 292static int pl061_suspend(struct device *dev)
 293{
 294        struct pl061_gpio *chip = dev_get_drvdata(dev);
 295        int offset;
 296
 297        chip->csave_regs.gpio_data = 0;
 298        chip->csave_regs.gpio_dir = readb(chip->base + GPIODIR);
 299        chip->csave_regs.gpio_is = readb(chip->base + GPIOIS);
 300        chip->csave_regs.gpio_ibe = readb(chip->base + GPIOIBE);
 301        chip->csave_regs.gpio_iev = readb(chip->base + GPIOIEV);
 302        chip->csave_regs.gpio_ie = readb(chip->base + GPIOIE);
 303
 304        for (offset = 0; offset < PL061_GPIO_NR; offset++) {
 305                if (chip->csave_regs.gpio_dir & (1 << offset))
 306                        chip->csave_regs.gpio_data |=
 307                                pl061_get_value(&chip->gc, offset) << offset;
 308        }
 309
 310        return 0;
 311}
 312
 313static int pl061_resume(struct device *dev)
 314{
 315        struct pl061_gpio *chip = dev_get_drvdata(dev);
 316        int offset;
 317
 318        for (offset = 0; offset < PL061_GPIO_NR; offset++) {
 319                if (chip->csave_regs.gpio_dir & (1 << offset))
 320                        pl061_direction_output(&chip->gc, offset,
 321                                        chip->csave_regs.gpio_data &
 322                                        (1 << offset));
 323                else
 324                        pl061_direction_input(&chip->gc, offset);
 325        }
 326
 327        writeb(chip->csave_regs.gpio_is, chip->base + GPIOIS);
 328        writeb(chip->csave_regs.gpio_ibe, chip->base + GPIOIBE);
 329        writeb(chip->csave_regs.gpio_iev, chip->base + GPIOIEV);
 330        writeb(chip->csave_regs.gpio_ie, chip->base + GPIOIE);
 331
 332        return 0;
 333}
 334
 335static const struct dev_pm_ops pl061_dev_pm_ops = {
 336        .suspend = pl061_suspend,
 337        .resume = pl061_resume,
 338        .freeze = pl061_suspend,
 339        .restore = pl061_resume,
 340};
 341#endif
 342
 343static struct amba_id pl061_ids[] = {
 344        {
 345                .id     = 0x00041061,
 346                .mask   = 0x000fffff,
 347        },
 348        { 0, 0 },
 349};
 350
 351MODULE_DEVICE_TABLE(amba, pl061_ids);
 352
 353static struct amba_driver pl061_gpio_driver = {
 354        .drv = {
 355                .name   = "pl061_gpio",
 356#ifdef CONFIG_PM
 357                .pm     = &pl061_dev_pm_ops,
 358#endif
 359        },
 360        .id_table       = pl061_ids,
 361        .probe          = pl061_probe,
 362};
 363
 364static int __init pl061_gpio_init(void)
 365{
 366        return amba_driver_register(&pl061_gpio_driver);
 367}
 368module_init(pl061_gpio_init);
 369
 370MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
 371MODULE_DESCRIPTION("PL061 GPIO driver");
 372MODULE_LICENSE("GPL");
 373
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.