gpio-pl061.c revision 2de0dbc5f6830e7659083d1929f57cb88b16a3b6
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 <asm/mach/irq.h> 26 27#define GPIODIR 0x400 28#define GPIOIS 0x404 29#define GPIOIBE 0x408 30#define GPIOIEV 0x40C 31#define GPIOIE 0x410 32#define GPIORIS 0x414 33#define GPIOMIS 0x418 34#define GPIOIC 0x41C 35 36#define PL061_GPIO_NR 8 37 38struct pl061_gpio { 39 /* Each of the two spinlocks protects a different set of hardware 40 * regiters and data structurs. This decouples the code of the IRQ from 41 * the GPIO code. This also makes the case of a GPIO routine call from 42 * the IRQ code simpler. 43 */ 44 spinlock_t lock; /* GPIO registers */ 45 46 void __iomem *base; 47 int irq_base; 48 struct irq_chip_generic *irq_gc; 49 struct gpio_chip gc; 50}; 51 52static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) 53{ 54 struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); 55 unsigned long flags; 56 unsigned char gpiodir; 57 58 if (offset >= gc->ngpio) 59 return -EINVAL; 60 61 spin_lock_irqsave(&chip->lock, flags); 62 gpiodir = readb(chip->base + GPIODIR); 63 gpiodir &= ~(1 << offset); 64 writeb(gpiodir, chip->base + GPIODIR); 65 spin_unlock_irqrestore(&chip->lock, flags); 66 67 return 0; 68} 69 70static int pl061_direction_output(struct gpio_chip *gc, unsigned offset, 71 int value) 72{ 73 struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); 74 unsigned long flags; 75 unsigned char gpiodir; 76 77 if (offset >= gc->ngpio) 78 return -EINVAL; 79 80 spin_lock_irqsave(&chip->lock, flags); 81 writeb(!!value << offset, chip->base + (1 << (offset + 2))); 82 gpiodir = readb(chip->base + GPIODIR); 83 gpiodir |= 1 << offset; 84 writeb(gpiodir, chip->base + GPIODIR); 85 86 /* 87 * gpio value is set again, because pl061 doesn't allow to set value of 88 * a gpio pin before configuring it in OUT mode. 89 */ 90 writeb(!!value << offset, chip->base + (1 << (offset + 2))); 91 spin_unlock_irqrestore(&chip->lock, flags); 92 93 return 0; 94} 95 96static int pl061_get_value(struct gpio_chip *gc, unsigned offset) 97{ 98 struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); 99 100 return !!readb(chip->base + (1 << (offset + 2))); 101} 102 103static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) 104{ 105 struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); 106 107 writeb(!!value << offset, chip->base + (1 << (offset + 2))); 108} 109 110static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) 111{ 112 struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); 113 114 if (chip->irq_base <= 0) 115 return -EINVAL; 116 117 return chip->irq_base + offset; 118} 119 120static int pl061_irq_type(struct irq_data *d, unsigned trigger) 121{ 122 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 123 struct pl061_gpio *chip = gc->private; 124 int offset = d->irq - chip->irq_base; 125 unsigned long flags; 126 u8 gpiois, gpioibe, gpioiev; 127 128 if (offset < 0 || offset >= PL061_GPIO_NR) 129 return -EINVAL; 130 131 raw_spin_lock_irqsave(&gc->lock, flags); 132 133 gpioiev = readb(chip->base + GPIOIEV); 134 135 gpiois = readb(chip->base + GPIOIS); 136 if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { 137 gpiois |= 1 << offset; 138 if (trigger & IRQ_TYPE_LEVEL_HIGH) 139 gpioiev |= 1 << offset; 140 else 141 gpioiev &= ~(1 << offset); 142 } else 143 gpiois &= ~(1 << offset); 144 writeb(gpiois, chip->base + GPIOIS); 145 146 gpioibe = readb(chip->base + GPIOIBE); 147 if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) 148 gpioibe |= 1 << offset; 149 else { 150 gpioibe &= ~(1 << offset); 151 if (trigger & IRQ_TYPE_EDGE_RISING) 152 gpioiev |= 1 << offset; 153 else if (trigger & IRQ_TYPE_EDGE_FALLING) 154 gpioiev &= ~(1 << offset); 155 } 156 writeb(gpioibe, chip->base + GPIOIBE); 157 158 writeb(gpioiev, chip->base + GPIOIEV); 159 160 raw_spin_unlock_irqrestore(&gc->lock, flags); 161 162 return 0; 163} 164 165static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) 166{ 167 unsigned long pending; 168 int offset; 169 struct pl061_gpio *chip = irq_desc_get_handler_data(desc); 170 struct irq_chip *irqchip = irq_desc_get_chip(desc); 171 172 chained_irq_enter(irqchip, desc); 173 174 pending = readb(chip->base + GPIOMIS); 175 writeb(pending, chip->base + GPIOIC); 176 if (pending) { 177 for_each_set_bit(offset, &pending, PL061_GPIO_NR) 178 generic_handle_irq(pl061_to_irq(&chip->gc, offset)); 179 } 180 181 chained_irq_exit(irqchip, desc); 182} 183 184static void __init pl061_init_gc(struct pl061_gpio *chip, int irq_base) 185{ 186 struct irq_chip_type *ct; 187 188 chip->irq_gc = irq_alloc_generic_chip("gpio-pl061", 1, irq_base, 189 chip->base, handle_simple_irq); 190 chip->irq_gc->private = chip; 191 192 ct = chip->irq_gc->chip_types; 193 ct->chip.irq_mask = irq_gc_mask_clr_bit; 194 ct->chip.irq_unmask = irq_gc_mask_set_bit; 195 ct->chip.irq_set_type = pl061_irq_type; 196 ct->chip.irq_set_wake = irq_gc_set_wake; 197 ct->regs.mask = GPIOIE; 198 199 irq_setup_generic_chip(chip->irq_gc, IRQ_MSK(PL061_GPIO_NR), 200 IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0); 201} 202 203static int pl061_probe(struct amba_device *dev, const struct amba_id *id) 204{ 205 struct pl061_platform_data *pdata; 206 struct pl061_gpio *chip; 207 int ret, irq, i; 208 209 chip = kzalloc(sizeof(*chip), GFP_KERNEL); 210 if (chip == NULL) 211 return -ENOMEM; 212 213 pdata = dev->dev.platform_data; 214 if (pdata) { 215 chip->gc.base = pdata->gpio_base; 216 chip->irq_base = pdata->irq_base; 217 } else if (dev->dev.of_node) { 218 chip->gc.base = -1; 219 chip->irq_base = 0; 220 } else { 221 ret = -ENODEV; 222 goto free_mem; 223 } 224 225 if (!request_mem_region(dev->res.start, 226 resource_size(&dev->res), "pl061")) { 227 ret = -EBUSY; 228 goto free_mem; 229 } 230 231 chip->base = ioremap(dev->res.start, resource_size(&dev->res)); 232 if (chip->base == NULL) { 233 ret = -ENOMEM; 234 goto release_region; 235 } 236 237 spin_lock_init(&chip->lock); 238 239 chip->gc.direction_input = pl061_direction_input; 240 chip->gc.direction_output = pl061_direction_output; 241 chip->gc.get = pl061_get_value; 242 chip->gc.set = pl061_set_value; 243 chip->gc.to_irq = pl061_to_irq; 244 chip->gc.ngpio = PL061_GPIO_NR; 245 chip->gc.label = dev_name(&dev->dev); 246 chip->gc.dev = &dev->dev; 247 chip->gc.owner = THIS_MODULE; 248 249 ret = gpiochip_add(&chip->gc); 250 if (ret) 251 goto iounmap; 252 253 /* 254 * irq_chip support 255 */ 256 257 if (chip->irq_base <= 0) 258 return 0; 259 260 pl061_init_gc(chip, chip->irq_base); 261 262 writeb(0, chip->base + GPIOIE); /* disable irqs */ 263 irq = dev->irq[0]; 264 if (irq < 0) { 265 ret = -ENODEV; 266 goto iounmap; 267 } 268 irq_set_chained_handler(irq, pl061_irq_handler); 269 irq_set_handler_data(irq, chip); 270 271 for (i = 0; i < PL061_GPIO_NR; i++) { 272 if (pdata) { 273 if (pdata->directions & (1 << i)) 274 pl061_direction_output(&chip->gc, i, 275 pdata->values & (1 << i)); 276 else 277 pl061_direction_input(&chip->gc, i); 278 } 279 } 280 281 return 0; 282 283iounmap: 284 iounmap(chip->base); 285release_region: 286 release_mem_region(dev->res.start, resource_size(&dev->res)); 287free_mem: 288 kfree(chip); 289 290 return ret; 291} 292 293static struct amba_id pl061_ids[] = { 294 { 295 .id = 0x00041061, 296 .mask = 0x000fffff, 297 }, 298 { 0, 0 }, 299}; 300 301static struct amba_driver pl061_gpio_driver = { 302 .drv = { 303 .name = "pl061_gpio", 304 }, 305 .id_table = pl061_ids, 306 .probe = pl061_probe, 307}; 308 309static int __init pl061_gpio_init(void) 310{ 311 return amba_driver_register(&pl061_gpio_driver); 312} 313subsys_initcall(pl061_gpio_init); 314 315MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); 316MODULE_DESCRIPTION("PL061 GPIO driver"); 317MODULE_LICENSE("GPL"); 318