aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorRichard Röjfors <richard.rojfors@mocean-labs.com>2009-12-15 19:46:18 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-16 10:20:00 -0500
commit35570ac6039ef490b9c5abde1fee4803a39bf4e1 (patch)
tree59b743b2ff1b1e054fa83b91d5c0c83be98e3cfc /drivers/gpio
parent4efec6272e8e61fc77132b4d2bae56d61b289956 (diff)
gpio: add GPIO driver for the Timberdale FPGA
A GPIO driver for the Timberdale FPGA found on the Intel Atom board Russellville. The GPIO driver also has an IRQ-chip to support interrupts on the pins. Signed-off-by: Richard Röjfors <richard.rojfors@mocean-labs.com> Cc: David Brownell <david-b@pacbell.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig6
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/timbgpio.c342
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
209config 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
209comment "SPI GPIO expanders:" 215comment "SPI GPIO expanders:"
210 216
211config GPIO_MAX7301 217config 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
13obj-$(CONFIG_GPIO_PCA953X) += pca953x.o 13obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
14obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o 14obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
15obj-$(CONFIG_GPIO_PL061) += pl061.o 15obj-$(CONFIG_GPIO_PL061) += pl061.o
16obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
16obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o 17obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
17obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o 18obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o
18obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o 19obj-$(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
41struct timbgpio {
42 void __iomem *membase;
43 spinlock_t lock; /* mutual exclusion */
44 struct gpio_chip gpio;
45 int irq_base;
46};
47
48static 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
68static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
69{
70 return timbgpio_update_bit(gpio, nr, TGPIODIR, true);
71}
72
73static 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
82static 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
88static 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
94static 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 */
107static 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
115static 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
123static 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
167static 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
181static 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
188static 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
270err_chipadd:
271 iounmap(tgpio->membase);
272err_ioremap:
273 release_mem_region(iomem->start, resource_size(iomem));
274err_request:
275 kfree(tgpio);
276err_mem:
277 printk(KERN_ERR DRIVER_NAME": Failed to register GPIOs: %d\n", err);
278
279 return err;
280}
281
282static 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
314static 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
325static int __init timbgpio_init(void)
326{
327 return platform_driver_register(&timbgpio_platform_driver);
328}
329
330static void __exit timbgpio_exit(void)
331{
332 platform_driver_unregister(&timbgpio_platform_driver);
333}
334
335module_init(timbgpio_init);
336module_exit(timbgpio_exit);
337
338MODULE_DESCRIPTION("Timberdale GPIO driver");
339MODULE_LICENSE("GPL v2");
340MODULE_AUTHOR("Mocean Laboratories");
341MODULE_ALIAS("platform:"DRIVER_NAME);
342