diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/gpio/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpio/timbgpio.c | 342 |
3 files changed, 349 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 57ca339924ef..a019b49ecc9b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
| @@ -206,6 +206,12 @@ config GPIO_LANGWELL | |||
| 206 | help | 206 | help |
| 207 | Say Y here to support Intel Moorestown platform GPIO. | 207 | Say Y here to support Intel Moorestown platform GPIO. |
| 208 | 208 | ||
| 209 | config GPIO_TIMBERDALE | ||
| 210 | bool "Support for timberdale GPIO IP" | ||
| 211 | depends on MFD_TIMBERDALE && GPIOLIB && HAS_IOMEM | ||
| 212 | ---help--- | ||
| 213 | Add support for the GPIO IP in the timberdale FPGA. | ||
| 214 | |||
| 209 | comment "SPI GPIO expanders:" | 215 | comment "SPI GPIO expanders:" |
| 210 | 216 | ||
| 211 | config GPIO_MAX7301 | 217 | config GPIO_MAX7301 |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 270b6d7839f5..52fe4cf734c7 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
| @@ -13,6 +13,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o | |||
| 13 | obj-$(CONFIG_GPIO_PCA953X) += pca953x.o | 13 | obj-$(CONFIG_GPIO_PCA953X) += pca953x.o |
| 14 | obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o | 14 | obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o |
| 15 | obj-$(CONFIG_GPIO_PL061) += pl061.o | 15 | obj-$(CONFIG_GPIO_PL061) += pl061.o |
| 16 | obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o | ||
| 16 | obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o | 17 | obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o |
| 17 | obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o | 18 | obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o |
| 18 | obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o | 19 | obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o |
diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c new file mode 100644 index 000000000000..a4d344ba8e5c --- /dev/null +++ b/drivers/gpio/timbgpio.c | |||
| @@ -0,0 +1,342 @@ | |||
| 1 | /* | ||
| 2 | * timbgpio.c timberdale FPGA GPIO driver | ||
| 3 | * Copyright (c) 2009 Intel Corporation | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License | ||
| 15 | * along with this program; if not, write to the Free Software | ||
| 16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 17 | */ | ||
| 18 | |||
| 19 | /* Supports: | ||
| 20 | * Timberdale FPGA GPIO | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/gpio.h> | ||
| 25 | #include <linux/platform_device.h> | ||
| 26 | #include <linux/io.h> | ||
| 27 | #include <linux/timb_gpio.h> | ||
| 28 | #include <linux/interrupt.h> | ||
| 29 | |||
| 30 | #define DRIVER_NAME "timb-gpio" | ||
| 31 | |||
| 32 | #define TGPIOVAL 0x00 | ||
| 33 | #define TGPIODIR 0x04 | ||
| 34 | #define TGPIO_IER 0x08 | ||
| 35 | #define TGPIO_ISR 0x0c | ||
| 36 | #define TGPIO_IPR 0x10 | ||
| 37 | #define TGPIO_ICR 0x14 | ||
| 38 | #define TGPIO_FLR 0x18 | ||
| 39 | #define TGPIO_LVR 0x1c | ||
| 40 | |||
| 41 | struct timbgpio { | ||
| 42 | void __iomem *membase; | ||
| 43 | spinlock_t lock; /* mutual exclusion */ | ||
| 44 | struct gpio_chip gpio; | ||
| 45 | int irq_base; | ||
| 46 | }; | ||
| 47 | |||
| 48 | static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, | ||
| 49 | unsigned offset, bool enabled) | ||
| 50 | { | ||
| 51 | struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); | ||
| 52 | u32 reg; | ||
| 53 | |||
| 54 | spin_lock(&tgpio->lock); | ||
| 55 | reg = ioread32(tgpio->membase + offset); | ||
| 56 | |||
| 57 | if (enabled) | ||
| 58 | reg |= (1 << index); | ||
| 59 | else | ||
| 60 | reg &= ~(1 << index); | ||
| 61 | |||
| 62 | iowrite32(reg, tgpio->membase + offset); | ||
| 63 | spin_unlock(&tgpio->lock); | ||
| 64 | |||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) | ||
| 69 | { | ||
| 70 | return timbgpio_update_bit(gpio, nr, TGPIODIR, true); | ||
| 71 | } | ||
| 72 | |||
| 73 | static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) | ||
| 74 | { | ||
| 75 | struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); | ||
| 76 | u32 value; | ||
| 77 | |||
| 78 | value = ioread32(tgpio->membase + TGPIOVAL); | ||
| 79 | return (value & (1 << nr)) ? 1 : 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | static int timbgpio_gpio_direction_output(struct gpio_chip *gpio, | ||
| 83 | unsigned nr, int val) | ||
| 84 | { | ||
| 85 | return timbgpio_update_bit(gpio, nr, TGPIODIR, false); | ||
| 86 | } | ||
| 87 | |||
| 88 | static void timbgpio_gpio_set(struct gpio_chip *gpio, | ||
| 89 | unsigned nr, int val) | ||
| 90 | { | ||
| 91 | timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); | ||
| 92 | } | ||
| 93 | |||
| 94 | static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset) | ||
| 95 | { | ||
| 96 | struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); | ||
| 97 | |||
| 98 | if (tgpio->irq_base <= 0) | ||
| 99 | return -EINVAL; | ||
| 100 | |||
| 101 | return tgpio->irq_base + offset; | ||
| 102 | } | ||
| 103 | |||
| 104 | /* | ||
| 105 | * GPIO IRQ | ||
| 106 | */ | ||
| 107 | static void timbgpio_irq_disable(unsigned irq) | ||
| 108 | { | ||
| 109 | struct timbgpio *tgpio = get_irq_chip_data(irq); | ||
| 110 | int offset = irq - tgpio->irq_base; | ||
| 111 | |||
| 112 | timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 0); | ||
| 113 | } | ||
| 114 | |||
| 115 | static void timbgpio_irq_enable(unsigned irq) | ||
| 116 | { | ||
| 117 | struct timbgpio *tgpio = get_irq_chip_data(irq); | ||
| 118 | int offset = irq - tgpio->irq_base; | ||
| 119 | |||
| 120 | timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 1); | ||
| 121 | } | ||
| 122 | |||
| 123 | static int timbgpio_irq_type(unsigned irq, unsigned trigger) | ||
| 124 | { | ||
| 125 | struct timbgpio *tgpio = get_irq_chip_data(irq); | ||
| 126 | int offset = irq - tgpio->irq_base; | ||
| 127 | unsigned long flags; | ||
| 128 | u32 lvr, flr; | ||
| 129 | |||
| 130 | if (offset < 0 || offset > tgpio->gpio.ngpio) | ||
| 131 | return -EINVAL; | ||
| 132 | |||
| 133 | spin_lock_irqsave(&tgpio->lock, flags); | ||
| 134 | |||
| 135 | lvr = ioread32(tgpio->membase + TGPIO_LVR); | ||
| 136 | flr = ioread32(tgpio->membase + TGPIO_FLR); | ||
| 137 | |||
| 138 | if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { | ||
| 139 | flr &= ~(1 << offset); | ||
| 140 | if (trigger & IRQ_TYPE_LEVEL_HIGH) | ||
| 141 | lvr |= 1 << offset; | ||
| 142 | else | ||
| 143 | lvr &= ~(1 << offset); | ||
| 144 | } | ||
| 145 | |||
| 146 | if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) | ||
| 147 | return -EINVAL; | ||
| 148 | else { | ||
| 149 | flr |= 1 << offset; | ||
| 150 | /* opposite compared to the datasheet, but it mirrors the | ||
| 151 | * reality | ||
| 152 | */ | ||
| 153 | if (trigger & IRQ_TYPE_EDGE_FALLING) | ||
| 154 | lvr |= 1 << offset; | ||
| 155 | else | ||
| 156 | lvr &= ~(1 << offset); | ||
| 157 | } | ||
| 158 | |||
| 159 | iowrite32(lvr, tgpio->membase + TGPIO_LVR); | ||
| 160 | iowrite32(flr, tgpio->membase + TGPIO_FLR); | ||
| 161 | iowrite32(1 << offset, tgpio->membase + TGPIO_ICR); | ||
| 162 | spin_unlock_irqrestore(&tgpio->lock, flags); | ||
| 163 | |||
| 164 | return 0; | ||
| 165 | } | ||
| 166 | |||
| 167 | static void timbgpio_irq(unsigned int irq, struct irq_desc *desc) | ||
| 168 | { | ||
| 169 | struct timbgpio *tgpio = get_irq_data(irq); | ||
| 170 | unsigned long ipr; | ||
| 171 | int offset; | ||
| 172 | |||
| 173 | desc->chip->ack(irq); | ||
| 174 | ipr = ioread32(tgpio->membase + TGPIO_IPR); | ||
| 175 | iowrite32(ipr, tgpio->membase + TGPIO_ICR); | ||
| 176 | |||
| 177 | for_each_bit(offset, &ipr, tgpio->gpio.ngpio) | ||
| 178 | generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset)); | ||
| 179 | } | ||
| 180 | |||
| 181 | static struct irq_chip timbgpio_irqchip = { | ||
| 182 | .name = "GPIO", | ||
| 183 | .enable = timbgpio_irq_enable, | ||
| 184 | .disable = timbgpio_irq_disable, | ||
| 185 | .set_type = timbgpio_irq_type, | ||
| 186 | }; | ||
| 187 | |||
| 188 | static int __devinit timbgpio_probe(struct platform_device *pdev) | ||
| 189 | { | ||
| 190 | int err, i; | ||
| 191 | struct gpio_chip *gc; | ||
| 192 | struct timbgpio *tgpio; | ||
| 193 | struct resource *iomem; | ||
| 194 | struct timbgpio_platform_data *pdata = pdev->dev.platform_data; | ||
| 195 | int irq = platform_get_irq(pdev, 0); | ||
| 196 | |||
| 197 | if (!pdata || pdata->nr_pins > 32) { | ||
| 198 | err = -EINVAL; | ||
| 199 | goto err_mem; | ||
| 200 | } | ||
| 201 | |||
| 202 | iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 203 | if (!iomem) { | ||
| 204 | err = -EINVAL; | ||
| 205 | goto err_mem; | ||
| 206 | } | ||
| 207 | |||
| 208 | tgpio = kzalloc(sizeof(*tgpio), GFP_KERNEL); | ||
| 209 | if (!tgpio) { | ||
| 210 | err = -EINVAL; | ||
| 211 | goto err_mem; | ||
| 212 | } | ||
| 213 | tgpio->irq_base = pdata->irq_base; | ||
| 214 | |||
| 215 | spin_lock_init(&tgpio->lock); | ||
| 216 | |||
| 217 | if (!request_mem_region(iomem->start, resource_size(iomem), | ||
| 218 | DRIVER_NAME)) { | ||
| 219 | err = -EBUSY; | ||
| 220 | goto err_request; | ||
| 221 | } | ||
| 222 | |||
| 223 | tgpio->membase = ioremap(iomem->start, resource_size(iomem)); | ||
| 224 | if (!tgpio->membase) { | ||
| 225 | err = -ENOMEM; | ||
| 226 | goto err_ioremap; | ||
| 227 | } | ||
| 228 | |||
| 229 | gc = &tgpio->gpio; | ||
| 230 | |||
| 231 | gc->label = dev_name(&pdev->dev); | ||
| 232 | gc->owner = THIS_MODULE; | ||
| 233 | gc->dev = &pdev->dev; | ||
| 234 | gc->direction_input = timbgpio_gpio_direction_input; | ||
| 235 | gc->get = timbgpio_gpio_get; | ||
| 236 | gc->direction_output = timbgpio_gpio_direction_output; | ||
| 237 | gc->set = timbgpio_gpio_set; | ||
| 238 | gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL; | ||
| 239 | gc->dbg_show = NULL; | ||
| 240 | gc->base = pdata->gpio_base; | ||
| 241 | gc->ngpio = pdata->nr_pins; | ||
| 242 | gc->can_sleep = 0; | ||
| 243 | |||
| 244 | err = gpiochip_add(gc); | ||
| 245 | if (err) | ||
| 246 | goto err_chipadd; | ||
| 247 | |||
| 248 | platform_set_drvdata(pdev, tgpio); | ||
| 249 | |||
| 250 | /* make sure to disable interrupts */ | ||
| 251 | iowrite32(0x0, tgpio->membase + TGPIO_IER); | ||
| 252 | |||
| 253 | if (irq < 0 || tgpio->irq_base <= 0) | ||
| 254 | return 0; | ||
| 255 | |||
| 256 | for (i = 0; i < pdata->nr_pins; i++) { | ||
| 257 | set_irq_chip_and_handler_name(tgpio->irq_base + i, | ||
| 258 | &timbgpio_irqchip, handle_simple_irq, "mux"); | ||
| 259 | set_irq_chip_data(tgpio->irq_base + i, tgpio); | ||
| 260 | #ifdef CONFIG_ARM | ||
| 261 | set_irq_flags(tgpio->irq_base + i, IRQF_VALID | IRQF_PROBE); | ||
| 262 | #endif | ||
| 263 | } | ||
| 264 | |||
| 265 | set_irq_data(irq, tgpio); | ||
| 266 | set_irq_chained_handler(irq, timbgpio_irq); | ||
| 267 | |||
| 268 | return 0; | ||
| 269 | |||
| 270 | err_chipadd: | ||
| 271 | iounmap(tgpio->membase); | ||
| 272 | err_ioremap: | ||
| 273 | release_mem_region(iomem->start, resource_size(iomem)); | ||
| 274 | err_request: | ||
| 275 | kfree(tgpio); | ||
| 276 | err_mem: | ||
| 277 | printk(KERN_ERR DRIVER_NAME": Failed to register GPIOs: %d\n", err); | ||
| 278 | |||
| 279 | return err; | ||
| 280 | } | ||
| 281 | |||
| 282 | static int __devexit timbgpio_remove(struct platform_device *pdev) | ||
| 283 | { | ||
| 284 | int err; | ||
| 285 | struct timbgpio_platform_data *pdata = pdev->dev.platform_data; | ||
| 286 | struct timbgpio *tgpio = platform_get_drvdata(pdev); | ||
| 287 | struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 288 | int irq = platform_get_irq(pdev, 0); | ||
| 289 | |||
| 290 | if (irq >= 0 && tgpio->irq_base > 0) { | ||
| 291 | int i; | ||
| 292 | for (i = 0; i < pdata->nr_pins; i++) { | ||
| 293 | set_irq_chip(tgpio->irq_base + i, NULL); | ||
| 294 | set_irq_chip_data(tgpio->irq_base + i, NULL); | ||
| 295 | } | ||
| 296 | |||
| 297 | set_irq_handler(irq, NULL); | ||
| 298 | set_irq_data(irq, NULL); | ||
| 299 | } | ||
| 300 | |||
| 301 | err = gpiochip_remove(&tgpio->gpio); | ||
| 302 | if (err) | ||
| 303 | printk(KERN_ERR DRIVER_NAME": failed to remove gpio_chip\n"); | ||
| 304 | |||
| 305 | iounmap(tgpio->membase); | ||
| 306 | release_mem_region(iomem->start, resource_size(iomem)); | ||
| 307 | kfree(tgpio); | ||
| 308 | |||
| 309 | platform_set_drvdata(pdev, NULL); | ||
| 310 | |||
| 311 | return 0; | ||
| 312 | } | ||
| 313 | |||
| 314 | static struct platform_driver timbgpio_platform_driver = { | ||
| 315 | .driver = { | ||
| 316 | .name = DRIVER_NAME, | ||
| 317 | .owner = THIS_MODULE, | ||
| 318 | }, | ||
| 319 | .probe = timbgpio_probe, | ||
| 320 | .remove = timbgpio_remove, | ||
| 321 | }; | ||
| 322 | |||
| 323 | /*--------------------------------------------------------------------------*/ | ||
| 324 | |||
| 325 | static int __init timbgpio_init(void) | ||
| 326 | { | ||
| 327 | return platform_driver_register(&timbgpio_platform_driver); | ||
| 328 | } | ||
| 329 | |||
| 330 | static void __exit timbgpio_exit(void) | ||
| 331 | { | ||
| 332 | platform_driver_unregister(&timbgpio_platform_driver); | ||
| 333 | } | ||
| 334 | |||
| 335 | module_init(timbgpio_init); | ||
| 336 | module_exit(timbgpio_exit); | ||
| 337 | |||
| 338 | MODULE_DESCRIPTION("Timberdale GPIO driver"); | ||
| 339 | MODULE_LICENSE("GPL v2"); | ||
| 340 | MODULE_AUTHOR("Mocean Laboratories"); | ||
| 341 | MODULE_ALIAS("platform:"DRIVER_NAME); | ||
| 342 | |||
