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 4dfd459b738cf1f65b3eac4e0a9b19bc93cc91c6 353 lines 8.7 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 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");