diff options
Diffstat (limited to 'drivers/gpio/gpio-langwell.c')
-rw-r--r-- | drivers/gpio/gpio-langwell.c | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c new file mode 100644 index 000000000000..e7a7ea760efc --- /dev/null +++ b/drivers/gpio/gpio-langwell.c | |||
@@ -0,0 +1,458 @@ | |||
1 | /* | ||
2 | * Moorestown platform Langwell chip GPIO driver | ||
3 | * | ||
4 | * Copyright (c) 2008 - 2009, Intel Corporation. | ||
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 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | */ | ||
19 | |||
20 | /* Supports: | ||
21 | * Moorestown platform Langwell chip. | ||
22 | * Medfield platform Penwell chip. | ||
23 | * Whitney point. | ||
24 | */ | ||
25 | |||
26 | #include <linux/module.h> | ||
27 | #include <linux/pci.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/delay.h> | ||
31 | #include <linux/stddef.h> | ||
32 | #include <linux/interrupt.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <linux/irq.h> | ||
35 | #include <linux/io.h> | ||
36 | #include <linux/gpio.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include <linux/pm_runtime.h> | ||
39 | |||
40 | /* | ||
41 | * Langwell chip has 64 pins and thus there are 2 32bit registers to control | ||
42 | * each feature, while Penwell chip has 96 pins for each block, and need 3 32bit | ||
43 | * registers to control them, so we only define the order here instead of a | ||
44 | * structure, to get a bit offset for a pin (use GPDR as an example): | ||
45 | * | ||
46 | * nreg = ngpio / 32; | ||
47 | * reg = offset / 32; | ||
48 | * bit = offset % 32; | ||
49 | * reg_addr = reg_base + GPDR * nreg * 4 + reg * 4; | ||
50 | * | ||
51 | * so the bit of reg_addr is to control pin offset's GPDR feature | ||
52 | */ | ||
53 | |||
54 | enum GPIO_REG { | ||
55 | GPLR = 0, /* pin level read-only */ | ||
56 | GPDR, /* pin direction */ | ||
57 | GPSR, /* pin set */ | ||
58 | GPCR, /* pin clear */ | ||
59 | GRER, /* rising edge detect */ | ||
60 | GFER, /* falling edge detect */ | ||
61 | GEDR, /* edge detect result */ | ||
62 | }; | ||
63 | |||
64 | struct lnw_gpio { | ||
65 | struct gpio_chip chip; | ||
66 | void *reg_base; | ||
67 | spinlock_t lock; | ||
68 | unsigned irq_base; | ||
69 | struct pci_dev *pdev; | ||
70 | }; | ||
71 | |||
72 | static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, | ||
73 | enum GPIO_REG reg_type) | ||
74 | { | ||
75 | struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); | ||
76 | unsigned nreg = chip->ngpio / 32; | ||
77 | u8 reg = offset / 32; | ||
78 | void __iomem *ptr; | ||
79 | |||
80 | ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); | ||
81 | return ptr; | ||
82 | } | ||
83 | |||
84 | static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
85 | { | ||
86 | void __iomem *gplr = gpio_reg(chip, offset, GPLR); | ||
87 | |||
88 | return readl(gplr) & BIT(offset % 32); | ||
89 | } | ||
90 | |||
91 | static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
92 | { | ||
93 | void __iomem *gpsr, *gpcr; | ||
94 | |||
95 | if (value) { | ||
96 | gpsr = gpio_reg(chip, offset, GPSR); | ||
97 | writel(BIT(offset % 32), gpsr); | ||
98 | } else { | ||
99 | gpcr = gpio_reg(chip, offset, GPCR); | ||
100 | writel(BIT(offset % 32), gpcr); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | ||
105 | { | ||
106 | struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); | ||
107 | void __iomem *gpdr = gpio_reg(chip, offset, GPDR); | ||
108 | u32 value; | ||
109 | unsigned long flags; | ||
110 | |||
111 | if (lnw->pdev) | ||
112 | pm_runtime_get(&lnw->pdev->dev); | ||
113 | |||
114 | spin_lock_irqsave(&lnw->lock, flags); | ||
115 | value = readl(gpdr); | ||
116 | value &= ~BIT(offset % 32); | ||
117 | writel(value, gpdr); | ||
118 | spin_unlock_irqrestore(&lnw->lock, flags); | ||
119 | |||
120 | if (lnw->pdev) | ||
121 | pm_runtime_put(&lnw->pdev->dev); | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int lnw_gpio_direction_output(struct gpio_chip *chip, | ||
127 | unsigned offset, int value) | ||
128 | { | ||
129 | struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); | ||
130 | void __iomem *gpdr = gpio_reg(chip, offset, GPDR); | ||
131 | unsigned long flags; | ||
132 | |||
133 | lnw_gpio_set(chip, offset, value); | ||
134 | |||
135 | if (lnw->pdev) | ||
136 | pm_runtime_get(&lnw->pdev->dev); | ||
137 | |||
138 | spin_lock_irqsave(&lnw->lock, flags); | ||
139 | value = readl(gpdr); | ||
140 | value |= BIT(offset % 32); | ||
141 | writel(value, gpdr); | ||
142 | spin_unlock_irqrestore(&lnw->lock, flags); | ||
143 | |||
144 | if (lnw->pdev) | ||
145 | pm_runtime_put(&lnw->pdev->dev); | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int lnw_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | ||
151 | { | ||
152 | struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); | ||
153 | return lnw->irq_base + offset; | ||
154 | } | ||
155 | |||
156 | static int lnw_irq_type(struct irq_data *d, unsigned type) | ||
157 | { | ||
158 | struct lnw_gpio *lnw = irq_data_get_irq_chip_data(d); | ||
159 | u32 gpio = d->irq - lnw->irq_base; | ||
160 | unsigned long flags; | ||
161 | u32 value; | ||
162 | void __iomem *grer = gpio_reg(&lnw->chip, gpio, GRER); | ||
163 | void __iomem *gfer = gpio_reg(&lnw->chip, gpio, GFER); | ||
164 | |||
165 | if (gpio >= lnw->chip.ngpio) | ||
166 | return -EINVAL; | ||
167 | |||
168 | if (lnw->pdev) | ||
169 | pm_runtime_get(&lnw->pdev->dev); | ||
170 | |||
171 | spin_lock_irqsave(&lnw->lock, flags); | ||
172 | if (type & IRQ_TYPE_EDGE_RISING) | ||
173 | value = readl(grer) | BIT(gpio % 32); | ||
174 | else | ||
175 | value = readl(grer) & (~BIT(gpio % 32)); | ||
176 | writel(value, grer); | ||
177 | |||
178 | if (type & IRQ_TYPE_EDGE_FALLING) | ||
179 | value = readl(gfer) | BIT(gpio % 32); | ||
180 | else | ||
181 | value = readl(gfer) & (~BIT(gpio % 32)); | ||
182 | writel(value, gfer); | ||
183 | spin_unlock_irqrestore(&lnw->lock, flags); | ||
184 | |||
185 | if (lnw->pdev) | ||
186 | pm_runtime_put(&lnw->pdev->dev); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static void lnw_irq_unmask(struct irq_data *d) | ||
192 | { | ||
193 | } | ||
194 | |||
195 | static void lnw_irq_mask(struct irq_data *d) | ||
196 | { | ||
197 | } | ||
198 | |||
199 | static struct irq_chip lnw_irqchip = { | ||
200 | .name = "LNW-GPIO", | ||
201 | .irq_mask = lnw_irq_mask, | ||
202 | .irq_unmask = lnw_irq_unmask, | ||
203 | .irq_set_type = lnw_irq_type, | ||
204 | }; | ||
205 | |||
206 | static DEFINE_PCI_DEVICE_TABLE(lnw_gpio_ids) = { /* pin number */ | ||
207 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f), .driver_data = 64 }, | ||
208 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f), .driver_data = 96 }, | ||
209 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a), .driver_data = 96 }, | ||
210 | { 0, } | ||
211 | }; | ||
212 | MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); | ||
213 | |||
214 | static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) | ||
215 | { | ||
216 | struct irq_data *data = irq_desc_get_irq_data(desc); | ||
217 | struct lnw_gpio *lnw = irq_data_get_irq_handler_data(data); | ||
218 | struct irq_chip *chip = irq_data_get_irq_chip(data); | ||
219 | u32 base, gpio, mask; | ||
220 | unsigned long pending; | ||
221 | void __iomem *gedr; | ||
222 | |||
223 | /* check GPIO controller to check which pin triggered the interrupt */ | ||
224 | for (base = 0; base < lnw->chip.ngpio; base += 32) { | ||
225 | gedr = gpio_reg(&lnw->chip, base, GEDR); | ||
226 | pending = readl(gedr); | ||
227 | while (pending) { | ||
228 | gpio = __ffs(pending) - 1; | ||
229 | mask = BIT(gpio); | ||
230 | pending &= ~mask; | ||
231 | /* Clear before handling so we can't lose an edge */ | ||
232 | writel(mask, gedr); | ||
233 | generic_handle_irq(lnw->irq_base + base + gpio); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | chip->irq_eoi(data); | ||
238 | } | ||
239 | |||
240 | #ifdef CONFIG_PM | ||
241 | static int lnw_gpio_runtime_resume(struct device *dev) | ||
242 | { | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static int lnw_gpio_runtime_suspend(struct device *dev) | ||
247 | { | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static int lnw_gpio_runtime_idle(struct device *dev) | ||
252 | { | ||
253 | int err = pm_schedule_suspend(dev, 500); | ||
254 | |||
255 | if (!err) | ||
256 | return 0; | ||
257 | |||
258 | return -EBUSY; | ||
259 | } | ||
260 | |||
261 | #else | ||
262 | #define lnw_gpio_runtime_suspend NULL | ||
263 | #define lnw_gpio_runtime_resume NULL | ||
264 | #define lnw_gpio_runtime_idle NULL | ||
265 | #endif | ||
266 | |||
267 | static const struct dev_pm_ops lnw_gpio_pm_ops = { | ||
268 | .runtime_suspend = lnw_gpio_runtime_suspend, | ||
269 | .runtime_resume = lnw_gpio_runtime_resume, | ||
270 | .runtime_idle = lnw_gpio_runtime_idle, | ||
271 | }; | ||
272 | |||
273 | static int __devinit lnw_gpio_probe(struct pci_dev *pdev, | ||
274 | const struct pci_device_id *id) | ||
275 | { | ||
276 | void *base; | ||
277 | int i; | ||
278 | resource_size_t start, len; | ||
279 | struct lnw_gpio *lnw; | ||
280 | u32 irq_base; | ||
281 | u32 gpio_base; | ||
282 | int retval = 0; | ||
283 | |||
284 | retval = pci_enable_device(pdev); | ||
285 | if (retval) | ||
286 | goto done; | ||
287 | |||
288 | retval = pci_request_regions(pdev, "langwell_gpio"); | ||
289 | if (retval) { | ||
290 | dev_err(&pdev->dev, "error requesting resources\n"); | ||
291 | goto err2; | ||
292 | } | ||
293 | /* get the irq_base from bar1 */ | ||
294 | start = pci_resource_start(pdev, 1); | ||
295 | len = pci_resource_len(pdev, 1); | ||
296 | base = ioremap_nocache(start, len); | ||
297 | if (!base) { | ||
298 | dev_err(&pdev->dev, "error mapping bar1\n"); | ||
299 | goto err3; | ||
300 | } | ||
301 | irq_base = *(u32 *)base; | ||
302 | gpio_base = *((u32 *)base + 1); | ||
303 | /* release the IO mapping, since we already get the info from bar1 */ | ||
304 | iounmap(base); | ||
305 | /* get the register base from bar0 */ | ||
306 | start = pci_resource_start(pdev, 0); | ||
307 | len = pci_resource_len(pdev, 0); | ||
308 | base = ioremap_nocache(start, len); | ||
309 | if (!base) { | ||
310 | dev_err(&pdev->dev, "error mapping bar0\n"); | ||
311 | retval = -EFAULT; | ||
312 | goto err3; | ||
313 | } | ||
314 | |||
315 | lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); | ||
316 | if (!lnw) { | ||
317 | dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); | ||
318 | retval = -ENOMEM; | ||
319 | goto err4; | ||
320 | } | ||
321 | lnw->reg_base = base; | ||
322 | lnw->irq_base = irq_base; | ||
323 | lnw->chip.label = dev_name(&pdev->dev); | ||
324 | lnw->chip.direction_input = lnw_gpio_direction_input; | ||
325 | lnw->chip.direction_output = lnw_gpio_direction_output; | ||
326 | lnw->chip.get = lnw_gpio_get; | ||
327 | lnw->chip.set = lnw_gpio_set; | ||
328 | lnw->chip.to_irq = lnw_gpio_to_irq; | ||
329 | lnw->chip.base = gpio_base; | ||
330 | lnw->chip.ngpio = id->driver_data; | ||
331 | lnw->chip.can_sleep = 0; | ||
332 | lnw->pdev = pdev; | ||
333 | pci_set_drvdata(pdev, lnw); | ||
334 | retval = gpiochip_add(&lnw->chip); | ||
335 | if (retval) { | ||
336 | dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); | ||
337 | goto err5; | ||
338 | } | ||
339 | irq_set_handler_data(pdev->irq, lnw); | ||
340 | irq_set_chained_handler(pdev->irq, lnw_irq_handler); | ||
341 | for (i = 0; i < lnw->chip.ngpio; i++) { | ||
342 | irq_set_chip_and_handler_name(i + lnw->irq_base, &lnw_irqchip, | ||
343 | handle_simple_irq, "demux"); | ||
344 | irq_set_chip_data(i + lnw->irq_base, lnw); | ||
345 | } | ||
346 | |||
347 | spin_lock_init(&lnw->lock); | ||
348 | |||
349 | pm_runtime_put_noidle(&pdev->dev); | ||
350 | pm_runtime_allow(&pdev->dev); | ||
351 | |||
352 | goto done; | ||
353 | err5: | ||
354 | kfree(lnw); | ||
355 | err4: | ||
356 | iounmap(base); | ||
357 | err3: | ||
358 | pci_release_regions(pdev); | ||
359 | err2: | ||
360 | pci_disable_device(pdev); | ||
361 | done: | ||
362 | return retval; | ||
363 | } | ||
364 | |||
365 | static struct pci_driver lnw_gpio_driver = { | ||
366 | .name = "langwell_gpio", | ||
367 | .id_table = lnw_gpio_ids, | ||
368 | .probe = lnw_gpio_probe, | ||
369 | .driver = { | ||
370 | .pm = &lnw_gpio_pm_ops, | ||
371 | }, | ||
372 | }; | ||
373 | |||
374 | |||
375 | static int __devinit wp_gpio_probe(struct platform_device *pdev) | ||
376 | { | ||
377 | struct lnw_gpio *lnw; | ||
378 | struct gpio_chip *gc; | ||
379 | struct resource *rc; | ||
380 | int retval = 0; | ||
381 | |||
382 | rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
383 | if (!rc) | ||
384 | return -EINVAL; | ||
385 | |||
386 | lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); | ||
387 | if (!lnw) { | ||
388 | dev_err(&pdev->dev, | ||
389 | "can't allocate whitneypoint_gpio chip data\n"); | ||
390 | return -ENOMEM; | ||
391 | } | ||
392 | lnw->reg_base = ioremap_nocache(rc->start, resource_size(rc)); | ||
393 | if (lnw->reg_base == NULL) { | ||
394 | retval = -EINVAL; | ||
395 | goto err_kmalloc; | ||
396 | } | ||
397 | spin_lock_init(&lnw->lock); | ||
398 | gc = &lnw->chip; | ||
399 | gc->label = dev_name(&pdev->dev); | ||
400 | gc->owner = THIS_MODULE; | ||
401 | gc->direction_input = lnw_gpio_direction_input; | ||
402 | gc->direction_output = lnw_gpio_direction_output; | ||
403 | gc->get = lnw_gpio_get; | ||
404 | gc->set = lnw_gpio_set; | ||
405 | gc->to_irq = NULL; | ||
406 | gc->base = 0; | ||
407 | gc->ngpio = 64; | ||
408 | gc->can_sleep = 0; | ||
409 | retval = gpiochip_add(gc); | ||
410 | if (retval) { | ||
411 | dev_err(&pdev->dev, "whitneypoint gpiochip_add error %d\n", | ||
412 | retval); | ||
413 | goto err_ioremap; | ||
414 | } | ||
415 | platform_set_drvdata(pdev, lnw); | ||
416 | return 0; | ||
417 | err_ioremap: | ||
418 | iounmap(lnw->reg_base); | ||
419 | err_kmalloc: | ||
420 | kfree(lnw); | ||
421 | return retval; | ||
422 | } | ||
423 | |||
424 | static int __devexit wp_gpio_remove(struct platform_device *pdev) | ||
425 | { | ||
426 | struct lnw_gpio *lnw = platform_get_drvdata(pdev); | ||
427 | int err; | ||
428 | err = gpiochip_remove(&lnw->chip); | ||
429 | if (err) | ||
430 | dev_err(&pdev->dev, "failed to remove gpio_chip.\n"); | ||
431 | iounmap(lnw->reg_base); | ||
432 | kfree(lnw); | ||
433 | platform_set_drvdata(pdev, NULL); | ||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | static struct platform_driver wp_gpio_driver = { | ||
438 | .probe = wp_gpio_probe, | ||
439 | .remove = __devexit_p(wp_gpio_remove), | ||
440 | .driver = { | ||
441 | .name = "wp_gpio", | ||
442 | .owner = THIS_MODULE, | ||
443 | }, | ||
444 | }; | ||
445 | |||
446 | static int __init lnw_gpio_init(void) | ||
447 | { | ||
448 | int ret; | ||
449 | ret = pci_register_driver(&lnw_gpio_driver); | ||
450 | if (ret < 0) | ||
451 | return ret; | ||
452 | ret = platform_driver_register(&wp_gpio_driver); | ||
453 | if (ret < 0) | ||
454 | pci_unregister_driver(&lnw_gpio_driver); | ||
455 | return ret; | ||
456 | } | ||
457 | |||
458 | device_initcall(lnw_gpio_init); | ||