diff options
author | Rohit Vaswani <rvaswani@codeaurora.org> | 2013-06-10 18:50:21 -0400 |
---|---|---|
committer | David Brown <davidb@codeaurora.org> | 2013-06-12 17:50:12 -0400 |
commit | 43f68444bce70e360ff6ce3653a54e511625f651 (patch) | |
tree | 31a695accad61e88f5f584acca4d9abc0d708eea /drivers/gpio/gpio-msm-v2.c | |
parent | eda9dcfa56f4864e0e4340f5af3f3e39f514b726 (diff) |
gpio: msm: Add device tree and irqdomain support for gpio-msm-v2
This cleans up the gpio-msm-v2 driver of all the global define usage.
The number of gpios are now defined in the device tree. This enables
adding irqdomain support as well.
Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
Acked-by: Grant Likely <grant.likely@linaro.org>
Signed-off-by: David Brown <davidb@codeaurora.org>
Diffstat (limited to 'drivers/gpio/gpio-msm-v2.c')
-rw-r--r-- | drivers/gpio/gpio-msm-v2.c | 190 |
1 files changed, 113 insertions, 77 deletions
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c index 75cc8215f15b..f4491a497cc8 100644 --- a/drivers/gpio/gpio-msm-v2.c +++ b/drivers/gpio/gpio-msm-v2.c | |||
@@ -19,17 +19,21 @@ | |||
19 | 19 | ||
20 | #include <linux/bitmap.h> | 20 | #include <linux/bitmap.h> |
21 | #include <linux/bitops.h> | 21 | #include <linux/bitops.h> |
22 | #include <linux/err.h> | ||
22 | #include <linux/gpio.h> | 23 | #include <linux/gpio.h> |
23 | #include <linux/init.h> | 24 | #include <linux/init.h> |
24 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
25 | #include <linux/io.h> | 26 | #include <linux/io.h> |
26 | #include <linux/irqchip/chained_irq.h> | 27 | #include <linux/irqchip/chained_irq.h> |
27 | #include <linux/irq.h> | 28 | #include <linux/irq.h> |
29 | #include <linux/irqdomain.h> | ||
28 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/of_address.h> | ||
29 | #include <linux/platform_device.h> | 32 | #include <linux/platform_device.h> |
30 | #include <linux/spinlock.h> | 33 | #include <linux/spinlock.h> |
34 | #include <linux/slab.h> | ||
31 | 35 | ||
32 | #include <mach/msm_iomap.h> | 36 | #define MAX_NR_GPIO 300 |
33 | 37 | ||
34 | /* Bits of interest in the GPIO_IN_OUT register. | 38 | /* Bits of interest in the GPIO_IN_OUT register. |
35 | */ | 39 | */ |
@@ -76,13 +80,6 @@ enum { | |||
76 | TARGET_PROC_NONE = 7, | 80 | TARGET_PROC_NONE = 7, |
77 | }; | 81 | }; |
78 | 82 | ||
79 | |||
80 | #define GPIO_INTR_CFG_SU(gpio) (MSM_TLMM_BASE + 0x0400 + (0x04 * (gpio))) | ||
81 | #define GPIO_CONFIG(gpio) (MSM_TLMM_BASE + 0x1000 + (0x10 * (gpio))) | ||
82 | #define GPIO_IN_OUT(gpio) (MSM_TLMM_BASE + 0x1004 + (0x10 * (gpio))) | ||
83 | #define GPIO_INTR_CFG(gpio) (MSM_TLMM_BASE + 0x1008 + (0x10 * (gpio))) | ||
84 | #define GPIO_INTR_STATUS(gpio) (MSM_TLMM_BASE + 0x100c + (0x10 * (gpio))) | ||
85 | |||
86 | /** | 83 | /** |
87 | * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure | 84 | * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure |
88 | * | 85 | * |
@@ -101,11 +98,27 @@ enum { | |||
101 | */ | 98 | */ |
102 | struct msm_gpio_dev { | 99 | struct msm_gpio_dev { |
103 | struct gpio_chip gpio_chip; | 100 | struct gpio_chip gpio_chip; |
104 | DECLARE_BITMAP(enabled_irqs, NR_GPIO_IRQS); | 101 | DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO); |
105 | DECLARE_BITMAP(wake_irqs, NR_GPIO_IRQS); | 102 | DECLARE_BITMAP(wake_irqs, MAX_NR_GPIO); |
106 | DECLARE_BITMAP(dual_edge_irqs, NR_GPIO_IRQS); | 103 | DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO); |
104 | struct irq_domain *domain; | ||
105 | unsigned int summary_irq; | ||
106 | void __iomem *msm_tlmm_base; | ||
107 | }; | 107 | }; |
108 | 108 | ||
109 | struct msm_gpio_dev msm_gpio; | ||
110 | |||
111 | #define GPIO_INTR_CFG_SU(gpio) (msm_gpio.msm_tlmm_base + 0x0400 + \ | ||
112 | (0x04 * (gpio))) | ||
113 | #define GPIO_CONFIG(gpio) (msm_gpio.msm_tlmm_base + 0x1000 + \ | ||
114 | (0x10 * (gpio))) | ||
115 | #define GPIO_IN_OUT(gpio) (msm_gpio.msm_tlmm_base + 0x1004 + \ | ||
116 | (0x10 * (gpio))) | ||
117 | #define GPIO_INTR_CFG(gpio) (msm_gpio.msm_tlmm_base + 0x1008 + \ | ||
118 | (0x10 * (gpio))) | ||
119 | #define GPIO_INTR_STATUS(gpio) (msm_gpio.msm_tlmm_base + 0x100c + \ | ||
120 | (0x10 * (gpio))) | ||
121 | |||
109 | static DEFINE_SPINLOCK(tlmm_lock); | 122 | static DEFINE_SPINLOCK(tlmm_lock); |
110 | 123 | ||
111 | static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip) | 124 | static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip) |
@@ -168,27 +181,19 @@ static void msm_gpio_free(struct gpio_chip *chip, unsigned offset) | |||
168 | 181 | ||
169 | static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | 182 | static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset) |
170 | { | 183 | { |
171 | return MSM_GPIO_TO_INT(chip->base + offset); | 184 | struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip); |
185 | struct irq_domain *domain = g_dev->domain; | ||
186 | |||
187 | return irq_create_mapping(domain, offset); | ||
172 | } | 188 | } |
173 | 189 | ||
174 | static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq) | 190 | static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq) |
175 | { | 191 | { |
176 | return irq - MSM_GPIO_TO_INT(chip->base); | 192 | struct irq_data *irq_data = irq_get_irq_data(irq); |
193 | |||
194 | return irq_data->hwirq; | ||
177 | } | 195 | } |
178 | 196 | ||
179 | static struct msm_gpio_dev msm_gpio = { | ||
180 | .gpio_chip = { | ||
181 | .base = 0, | ||
182 | .ngpio = NR_GPIO_IRQS, | ||
183 | .direction_input = msm_gpio_direction_input, | ||
184 | .direction_output = msm_gpio_direction_output, | ||
185 | .get = msm_gpio_get, | ||
186 | .set = msm_gpio_set, | ||
187 | .to_irq = msm_gpio_to_irq, | ||
188 | .request = msm_gpio_request, | ||
189 | .free = msm_gpio_free, | ||
190 | }, | ||
191 | }; | ||
192 | 197 | ||
193 | /* For dual-edge interrupts in software, since the hardware has no | 198 | /* For dual-edge interrupts in software, since the hardware has no |
194 | * such support: | 199 | * such support: |
@@ -226,9 +231,9 @@ static void msm_gpio_update_dual_edge_pos(unsigned gpio) | |||
226 | if (intstat || val == val2) | 231 | if (intstat || val == val2) |
227 | return; | 232 | return; |
228 | } while (loop_limit-- > 0); | 233 | } while (loop_limit-- > 0); |
229 | pr_err("dual-edge irq failed to stabilize, " | 234 | pr_err("%s: dual-edge irq failed to stabilize, " |
230 | "interrupts dropped. %#08x != %#08x\n", | 235 | "interrupts dropped. %#08x != %#08x\n", |
231 | val, val2); | 236 | __func__, val, val2); |
232 | } | 237 | } |
233 | 238 | ||
234 | static void msm_gpio_irq_ack(struct irq_data *d) | 239 | static void msm_gpio_irq_ack(struct irq_data *d) |
@@ -315,10 +320,10 @@ static void msm_summary_irq_handler(unsigned int irq, struct irq_desc *desc) | |||
315 | 320 | ||
316 | chained_irq_enter(chip, desc); | 321 | chained_irq_enter(chip, desc); |
317 | 322 | ||
318 | for_each_set_bit(i, msm_gpio.enabled_irqs, NR_GPIO_IRQS) { | 323 | for_each_set_bit(i, msm_gpio.enabled_irqs, MAX_NR_GPIO) { |
319 | if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS)) | 324 | if (readl(GPIO_INTR_STATUS(i)) & BIT(INTR_STATUS)) |
320 | generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip, | 325 | generic_handle_irq(irq_find_mapping(msm_gpio.domain, |
321 | i)); | 326 | i)); |
322 | } | 327 | } |
323 | 328 | ||
324 | chained_irq_exit(chip, desc); | 329 | chained_irq_exit(chip, desc); |
@@ -329,13 +334,13 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) | |||
329 | int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq); | 334 | int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq); |
330 | 335 | ||
331 | if (on) { | 336 | if (on) { |
332 | if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS)) | 337 | if (bitmap_empty(msm_gpio.wake_irqs, MAX_NR_GPIO)) |
333 | irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 1); | 338 | irq_set_irq_wake(msm_gpio.summary_irq, 1); |
334 | set_bit(gpio, msm_gpio.wake_irqs); | 339 | set_bit(gpio, msm_gpio.wake_irqs); |
335 | } else { | 340 | } else { |
336 | clear_bit(gpio, msm_gpio.wake_irqs); | 341 | clear_bit(gpio, msm_gpio.wake_irqs); |
337 | if (bitmap_empty(msm_gpio.wake_irqs, NR_GPIO_IRQS)) | 342 | if (bitmap_empty(msm_gpio.wake_irqs, MAX_NR_GPIO)) |
338 | irq_set_irq_wake(TLMM_SCSS_SUMMARY_IRQ, 0); | 343 | irq_set_irq_wake(msm_gpio.summary_irq, 0); |
339 | } | 344 | } |
340 | 345 | ||
341 | return 0; | 346 | return 0; |
@@ -350,30 +355,86 @@ static struct irq_chip msm_gpio_irq_chip = { | |||
350 | .irq_set_wake = msm_gpio_irq_set_wake, | 355 | .irq_set_wake = msm_gpio_irq_set_wake, |
351 | }; | 356 | }; |
352 | 357 | ||
353 | static int msm_gpio_probe(struct platform_device *dev) | 358 | static struct lock_class_key msm_gpio_lock_class; |
359 | |||
360 | static int msm_gpio_irq_domain_map(struct irq_domain *d, unsigned int irq, | ||
361 | irq_hw_number_t hwirq) | ||
362 | { | ||
363 | irq_set_lockdep_class(irq, &msm_gpio_lock_class); | ||
364 | irq_set_chip_and_handler(irq, &msm_gpio_irq_chip, | ||
365 | handle_level_irq); | ||
366 | set_irq_flags(irq, IRQF_VALID); | ||
367 | |||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static const struct irq_domain_ops msm_gpio_irq_domain_ops = { | ||
372 | .xlate = irq_domain_xlate_twocell, | ||
373 | .map = msm_gpio_irq_domain_map, | ||
374 | }; | ||
375 | |||
376 | static int msm_gpio_probe(struct platform_device *pdev) | ||
354 | { | 377 | { |
355 | int i, irq, ret; | 378 | int ret, ngpio; |
379 | struct resource *res; | ||
380 | |||
381 | if (!of_property_read_u32(pdev->dev.of_node, "ngpio", &ngpio)) { | ||
382 | dev_err(&pdev->dev, "%s: ngpio property missing\n", __func__); | ||
383 | return -EINVAL; | ||
384 | } | ||
385 | |||
386 | if (ngpio > MAX_NR_GPIO) | ||
387 | WARN(1, "ngpio exceeds the MAX_NR_GPIO. Increase MAX_NR_GPIO\n"); | ||
388 | |||
389 | bitmap_zero(msm_gpio.enabled_irqs, MAX_NR_GPIO); | ||
390 | bitmap_zero(msm_gpio.wake_irqs, MAX_NR_GPIO); | ||
391 | bitmap_zero(msm_gpio.dual_edge_irqs, MAX_NR_GPIO); | ||
392 | |||
393 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
394 | msm_gpio.msm_tlmm_base = devm_ioremap_resource(&pdev->dev, res); | ||
395 | if (IS_ERR(msm_gpio.msm_tlmm_base)) | ||
396 | return PTR_ERR(msm_gpio.msm_tlmm_base); | ||
397 | |||
398 | msm_gpio.gpio_chip.ngpio = ngpio; | ||
399 | msm_gpio.gpio_chip.label = pdev->name; | ||
400 | msm_gpio.gpio_chip.dev = &pdev->dev; | ||
401 | msm_gpio.gpio_chip.base = 0; | ||
402 | msm_gpio.gpio_chip.direction_input = msm_gpio_direction_input; | ||
403 | msm_gpio.gpio_chip.direction_output = msm_gpio_direction_output; | ||
404 | msm_gpio.gpio_chip.get = msm_gpio_get; | ||
405 | msm_gpio.gpio_chip.set = msm_gpio_set; | ||
406 | msm_gpio.gpio_chip.to_irq = msm_gpio_to_irq; | ||
407 | msm_gpio.gpio_chip.request = msm_gpio_request; | ||
408 | msm_gpio.gpio_chip.free = msm_gpio_free; | ||
356 | 409 | ||
357 | bitmap_zero(msm_gpio.enabled_irqs, NR_GPIO_IRQS); | ||
358 | bitmap_zero(msm_gpio.wake_irqs, NR_GPIO_IRQS); | ||
359 | bitmap_zero(msm_gpio.dual_edge_irqs, NR_GPIO_IRQS); | ||
360 | msm_gpio.gpio_chip.label = dev->name; | ||
361 | ret = gpiochip_add(&msm_gpio.gpio_chip); | 410 | ret = gpiochip_add(&msm_gpio.gpio_chip); |
362 | if (ret < 0) | 411 | if (ret < 0) { |
412 | dev_err(&pdev->dev, "gpiochip_add failed with error %d\n", ret); | ||
363 | return ret; | 413 | return ret; |
414 | } | ||
364 | 415 | ||
365 | for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) { | 416 | msm_gpio.summary_irq = platform_get_irq(pdev, 0); |
366 | irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i); | 417 | if (msm_gpio.summary_irq < 0) { |
367 | irq_set_chip_and_handler(irq, &msm_gpio_irq_chip, | 418 | dev_err(&pdev->dev, "No Summary irq defined for msmgpio\n"); |
368 | handle_level_irq); | 419 | return msm_gpio.summary_irq; |
369 | set_irq_flags(irq, IRQF_VALID); | ||
370 | } | 420 | } |
371 | 421 | ||
372 | irq_set_chained_handler(TLMM_SCSS_SUMMARY_IRQ, | 422 | msm_gpio.domain = irq_domain_add_linear(pdev->dev.of_node, ngpio, |
373 | msm_summary_irq_handler); | 423 | &msm_gpio_irq_domain_ops, |
424 | &msm_gpio); | ||
425 | if (!msm_gpio.domain) | ||
426 | return -ENODEV; | ||
427 | |||
428 | irq_set_chained_handler(msm_gpio.summary_irq, msm_summary_irq_handler); | ||
429 | |||
374 | return 0; | 430 | return 0; |
375 | } | 431 | } |
376 | 432 | ||
433 | static struct of_device_id msm_gpio_of_match[] = { | ||
434 | { .compatible = "qcom,msm-gpio", }, | ||
435 | { }, | ||
436 | }; | ||
437 | |||
377 | static int msm_gpio_remove(struct platform_device *dev) | 438 | static int msm_gpio_remove(struct platform_device *dev) |
378 | { | 439 | { |
379 | int ret = gpiochip_remove(&msm_gpio.gpio_chip); | 440 | int ret = gpiochip_remove(&msm_gpio.gpio_chip); |
@@ -381,7 +442,7 @@ static int msm_gpio_remove(struct platform_device *dev) | |||
381 | if (ret < 0) | 442 | if (ret < 0) |
382 | return ret; | 443 | return ret; |
383 | 444 | ||
384 | irq_set_handler(TLMM_SCSS_SUMMARY_IRQ, NULL); | 445 | irq_set_handler(msm_gpio.summary_irq, NULL); |
385 | 446 | ||
386 | return 0; | 447 | return 0; |
387 | } | 448 | } |
@@ -392,36 +453,11 @@ static struct platform_driver msm_gpio_driver = { | |||
392 | .driver = { | 453 | .driver = { |
393 | .name = "msmgpio", | 454 | .name = "msmgpio", |
394 | .owner = THIS_MODULE, | 455 | .owner = THIS_MODULE, |
456 | .of_match_table = msm_gpio_of_match, | ||
395 | }, | 457 | }, |
396 | }; | 458 | }; |
397 | 459 | ||
398 | static struct platform_device msm_device_gpio = { | 460 | module_platform_driver(msm_gpio_driver) |
399 | .name = "msmgpio", | ||
400 | .id = -1, | ||
401 | }; | ||
402 | |||
403 | static int __init msm_gpio_init(void) | ||
404 | { | ||
405 | int rc; | ||
406 | |||
407 | rc = platform_driver_register(&msm_gpio_driver); | ||
408 | if (!rc) { | ||
409 | rc = platform_device_register(&msm_device_gpio); | ||
410 | if (rc) | ||
411 | platform_driver_unregister(&msm_gpio_driver); | ||
412 | } | ||
413 | |||
414 | return rc; | ||
415 | } | ||
416 | |||
417 | static void __exit msm_gpio_exit(void) | ||
418 | { | ||
419 | platform_device_unregister(&msm_device_gpio); | ||
420 | platform_driver_unregister(&msm_gpio_driver); | ||
421 | } | ||
422 | |||
423 | postcore_initcall(msm_gpio_init); | ||
424 | module_exit(msm_gpio_exit); | ||
425 | 461 | ||
426 | MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>"); | 462 | MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>"); |
427 | MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs"); | 463 | MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs"); |