Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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