diff options
Diffstat (limited to 'drivers/mfd/twl4030-irq.c')
-rw-r--r-- | drivers/mfd/twl4030-irq.c | 107 |
1 files changed, 60 insertions, 47 deletions
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index b69bb517b102..5d656e814358 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c | |||
@@ -28,10 +28,12 @@ | |||
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include <linux/init.h> | 30 | #include <linux/init.h> |
31 | #include <linux/export.h> | ||
31 | #include <linux/interrupt.h> | 32 | #include <linux/interrupt.h> |
32 | #include <linux/irq.h> | 33 | #include <linux/irq.h> |
33 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
34 | 35 | #include <linux/of.h> | |
36 | #include <linux/irqdomain.h> | ||
35 | #include <linux/i2c/twl.h> | 37 | #include <linux/i2c/twl.h> |
36 | 38 | ||
37 | #include "twl-core.h" | 39 | #include "twl-core.h" |
@@ -53,13 +55,14 @@ | |||
53 | * base + 8 .. base + 15 SIH for PWR_INT | 55 | * base + 8 .. base + 15 SIH for PWR_INT |
54 | * base + 16 .. base + 33 SIH for GPIO | 56 | * base + 16 .. base + 33 SIH for GPIO |
55 | */ | 57 | */ |
58 | #define TWL4030_CORE_NR_IRQS 8 | ||
59 | #define TWL4030_PWR_NR_IRQS 8 | ||
56 | 60 | ||
57 | /* PIH register offsets */ | 61 | /* PIH register offsets */ |
58 | #define REG_PIH_ISR_P1 0x01 | 62 | #define REG_PIH_ISR_P1 0x01 |
59 | #define REG_PIH_ISR_P2 0x02 | 63 | #define REG_PIH_ISR_P2 0x02 |
60 | #define REG_PIH_SIR 0x03 /* for testing */ | 64 | #define REG_PIH_SIR 0x03 /* for testing */ |
61 | 65 | ||
62 | |||
63 | /* Linux could (eventually) use either IRQ line */ | 66 | /* Linux could (eventually) use either IRQ line */ |
64 | static int irq_line; | 67 | static int irq_line; |
65 | 68 | ||
@@ -111,7 +114,8 @@ static int nr_sih_modules; | |||
111 | #define TWL4030_MODULE_INT_PWR TWL4030_MODULE_INT | 114 | #define TWL4030_MODULE_INT_PWR TWL4030_MODULE_INT |
112 | 115 | ||
113 | 116 | ||
114 | /* Order in this table matches order in PIH_ISR. That is, | 117 | /* |
118 | * Order in this table matches order in PIH_ISR. That is, | ||
115 | * BIT(n) in PIH_ISR is sih_modules[n]. | 119 | * BIT(n) in PIH_ISR is sih_modules[n]. |
116 | */ | 120 | */ |
117 | /* sih_modules_twl4030 is used both in twl4030 and twl5030 */ | 121 | /* sih_modules_twl4030 is used both in twl4030 and twl5030 */ |
@@ -288,7 +292,6 @@ static unsigned twl4030_irq_base; | |||
288 | */ | 292 | */ |
289 | static irqreturn_t handle_twl4030_pih(int irq, void *devid) | 293 | static irqreturn_t handle_twl4030_pih(int irq, void *devid) |
290 | { | 294 | { |
291 | int module_irq; | ||
292 | irqreturn_t ret; | 295 | irqreturn_t ret; |
293 | u8 pih_isr; | 296 | u8 pih_isr; |
294 | 297 | ||
@@ -299,16 +302,18 @@ static irqreturn_t handle_twl4030_pih(int irq, void *devid) | |||
299 | return IRQ_NONE; | 302 | return IRQ_NONE; |
300 | } | 303 | } |
301 | 304 | ||
302 | /* these handlers deal with the relevant SIH irq status */ | 305 | while (pih_isr) { |
303 | for (module_irq = twl4030_irq_base; | 306 | unsigned long pending = __ffs(pih_isr); |
304 | pih_isr; | 307 | unsigned int irq; |
305 | pih_isr >>= 1, module_irq++) { | 308 | |
306 | if (pih_isr & 0x1) | 309 | pih_isr &= ~BIT(pending); |
307 | handle_nested_irq(module_irq); | 310 | irq = pending + twl4030_irq_base; |
311 | handle_nested_irq(irq); | ||
308 | } | 312 | } |
309 | 313 | ||
310 | return IRQ_HANDLED; | 314 | return IRQ_HANDLED; |
311 | } | 315 | } |
316 | |||
312 | /*----------------------------------------------------------------------*/ | 317 | /*----------------------------------------------------------------------*/ |
313 | 318 | ||
314 | /* | 319 | /* |
@@ -337,7 +342,6 @@ static int twl4030_init_sih_modules(unsigned line) | |||
337 | memset(buf, 0xff, sizeof buf); | 342 | memset(buf, 0xff, sizeof buf); |
338 | sih = sih_modules; | 343 | sih = sih_modules; |
339 | for (i = 0; i < nr_sih_modules; i++, sih++) { | 344 | for (i = 0; i < nr_sih_modules; i++, sih++) { |
340 | |||
341 | /* skip USB -- it's funky */ | 345 | /* skip USB -- it's funky */ |
342 | if (!sih->bytes_ixr) | 346 | if (!sih->bytes_ixr) |
343 | continue; | 347 | continue; |
@@ -352,7 +356,8 @@ static int twl4030_init_sih_modules(unsigned line) | |||
352 | pr_err("twl4030: err %d initializing %s %s\n", | 356 | pr_err("twl4030: err %d initializing %s %s\n", |
353 | status, sih->name, "IMR"); | 357 | status, sih->name, "IMR"); |
354 | 358 | ||
355 | /* Maybe disable "exclusive" mode; buffer second pending irq; | 359 | /* |
360 | * Maybe disable "exclusive" mode; buffer second pending irq; | ||
356 | * set Clear-On-Read (COR) bit. | 361 | * set Clear-On-Read (COR) bit. |
357 | * | 362 | * |
358 | * NOTE that sometimes COR polarity is documented as being | 363 | * NOTE that sometimes COR polarity is documented as being |
@@ -382,7 +387,8 @@ static int twl4030_init_sih_modules(unsigned line) | |||
382 | if (sih->irq_lines <= line) | 387 | if (sih->irq_lines <= line) |
383 | continue; | 388 | continue; |
384 | 389 | ||
385 | /* Clear pending interrupt status. Either the read was | 390 | /* |
391 | * Clear pending interrupt status. Either the read was | ||
386 | * enough, or we need to write those bits. Repeat, in | 392 | * enough, or we need to write those bits. Repeat, in |
387 | * case an IRQ is pending (PENDDIS=0) ... that's not | 393 | * case an IRQ is pending (PENDDIS=0) ... that's not |
388 | * uncommon with PWR_INT.PWRON. | 394 | * uncommon with PWR_INT.PWRON. |
@@ -398,7 +404,8 @@ static int twl4030_init_sih_modules(unsigned line) | |||
398 | status = twl_i2c_write(sih->module, buf, | 404 | status = twl_i2c_write(sih->module, buf, |
399 | sih->mask[line].isr_offset, | 405 | sih->mask[line].isr_offset, |
400 | sih->bytes_ixr); | 406 | sih->bytes_ixr); |
401 | /* else COR=1 means read sufficed. | 407 | /* |
408 | * else COR=1 means read sufficed. | ||
402 | * (for most SIH modules...) | 409 | * (for most SIH modules...) |
403 | */ | 410 | */ |
404 | } | 411 | } |
@@ -410,7 +417,8 @@ static int twl4030_init_sih_modules(unsigned line) | |||
410 | static inline void activate_irq(int irq) | 417 | static inline void activate_irq(int irq) |
411 | { | 418 | { |
412 | #ifdef CONFIG_ARM | 419 | #ifdef CONFIG_ARM |
413 | /* ARM requires an extra step to clear IRQ_NOREQUEST, which it | 420 | /* |
421 | * ARM requires an extra step to clear IRQ_NOREQUEST, which it | ||
414 | * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE. | 422 | * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE. |
415 | */ | 423 | */ |
416 | set_irq_flags(irq, IRQF_VALID); | 424 | set_irq_flags(irq, IRQF_VALID); |
@@ -620,33 +628,24 @@ static irqreturn_t handle_twl4030_sih(int irq, void *data) | |||
620 | return IRQ_HANDLED; | 628 | return IRQ_HANDLED; |
621 | } | 629 | } |
622 | 630 | ||
623 | static unsigned twl4030_irq_next; | 631 | /* returns the first IRQ used by this SIH bank, or negative errno */ |
624 | 632 | int twl4030_sih_setup(struct device *dev, int module, int irq_base) | |
625 | /* returns the first IRQ used by this SIH bank, | ||
626 | * or negative errno | ||
627 | */ | ||
628 | int twl4030_sih_setup(int module) | ||
629 | { | 633 | { |
630 | int sih_mod; | 634 | int sih_mod; |
631 | const struct sih *sih = NULL; | 635 | const struct sih *sih = NULL; |
632 | struct sih_agent *agent; | 636 | struct sih_agent *agent; |
633 | int i, irq; | 637 | int i, irq; |
634 | int status = -EINVAL; | 638 | int status = -EINVAL; |
635 | unsigned irq_base = twl4030_irq_next; | ||
636 | 639 | ||
637 | /* only support modules with standard clear-on-read for now */ | 640 | /* only support modules with standard clear-on-read for now */ |
638 | for (sih_mod = 0, sih = sih_modules; | 641 | for (sih_mod = 0, sih = sih_modules; sih_mod < nr_sih_modules; |
639 | sih_mod < nr_sih_modules; | ||
640 | sih_mod++, sih++) { | 642 | sih_mod++, sih++) { |
641 | if (sih->module == module && sih->set_cor) { | 643 | if (sih->module == module && sih->set_cor) { |
642 | if (!WARN((irq_base + sih->bits) > NR_IRQS, | 644 | status = 0; |
643 | "irq %d for %s too big\n", | ||
644 | irq_base + sih->bits, | ||
645 | sih->name)) | ||
646 | status = 0; | ||
647 | break; | 645 | break; |
648 | } | 646 | } |
649 | } | 647 | } |
648 | |||
650 | if (status < 0) | 649 | if (status < 0) |
651 | return status; | 650 | return status; |
652 | 651 | ||
@@ -654,8 +653,6 @@ int twl4030_sih_setup(int module) | |||
654 | if (!agent) | 653 | if (!agent) |
655 | return -ENOMEM; | 654 | return -ENOMEM; |
656 | 655 | ||
657 | status = 0; | ||
658 | |||
659 | agent->irq_base = irq_base; | 656 | agent->irq_base = irq_base; |
660 | agent->sih = sih; | 657 | agent->sih = sih; |
661 | agent->imr = ~0; | 658 | agent->imr = ~0; |
@@ -671,8 +668,6 @@ int twl4030_sih_setup(int module) | |||
671 | activate_irq(irq); | 668 | activate_irq(irq); |
672 | } | 669 | } |
673 | 670 | ||
674 | twl4030_irq_next += i; | ||
675 | |||
676 | /* replace generic PIH handler (handle_simple_irq) */ | 671 | /* replace generic PIH handler (handle_simple_irq) */ |
677 | irq = sih_mod + twl4030_irq_base; | 672 | irq = sih_mod + twl4030_irq_base; |
678 | irq_set_handler_data(irq, agent); | 673 | irq_set_handler_data(irq, agent); |
@@ -680,26 +675,43 @@ int twl4030_sih_setup(int module) | |||
680 | status = request_threaded_irq(irq, NULL, handle_twl4030_sih, 0, | 675 | status = request_threaded_irq(irq, NULL, handle_twl4030_sih, 0, |
681 | agent->irq_name ?: sih->name, NULL); | 676 | agent->irq_name ?: sih->name, NULL); |
682 | 677 | ||
683 | pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", sih->name, | 678 | dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name, |
684 | irq, irq_base, twl4030_irq_next - 1); | 679 | irq, irq_base, irq_base + i - 1); |
685 | 680 | ||
686 | return status < 0 ? status : irq_base; | 681 | return status < 0 ? status : irq_base; |
687 | } | 682 | } |
688 | 683 | ||
689 | /* FIXME need a call to reverse twl4030_sih_setup() ... */ | 684 | /* FIXME need a call to reverse twl4030_sih_setup() ... */ |
690 | 685 | ||
691 | |||
692 | /*----------------------------------------------------------------------*/ | 686 | /*----------------------------------------------------------------------*/ |
693 | 687 | ||
694 | /* FIXME pass in which interrupt line we'll use ... */ | 688 | /* FIXME pass in which interrupt line we'll use ... */ |
695 | #define twl_irq_line 0 | 689 | #define twl_irq_line 0 |
696 | 690 | ||
697 | int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | 691 | int twl4030_init_irq(struct device *dev, int irq_num) |
698 | { | 692 | { |
699 | static struct irq_chip twl4030_irq_chip; | 693 | static struct irq_chip twl4030_irq_chip; |
694 | int status, i; | ||
695 | int irq_base, irq_end, nr_irqs; | ||
696 | struct device_node *node = dev->of_node; | ||
700 | 697 | ||
701 | int status; | 698 | /* |
702 | int i; | 699 | * TWL core and pwr interrupts must be contiguous because |
700 | * the hwirqs numbers are defined contiguously from 1 to 15. | ||
701 | * Create only one domain for both. | ||
702 | */ | ||
703 | nr_irqs = TWL4030_PWR_NR_IRQS + TWL4030_CORE_NR_IRQS; | ||
704 | |||
705 | irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); | ||
706 | if (IS_ERR_VALUE(irq_base)) { | ||
707 | dev_err(dev, "Fail to allocate IRQ descs\n"); | ||
708 | return irq_base; | ||
709 | } | ||
710 | |||
711 | irq_domain_add_legacy(node, nr_irqs, irq_base, 0, | ||
712 | &irq_domain_simple_ops, NULL); | ||
713 | |||
714 | irq_end = irq_base + TWL4030_CORE_NR_IRQS; | ||
703 | 715 | ||
704 | /* | 716 | /* |
705 | * Mask and clear all TWL4030 interrupts since initially we do | 717 | * Mask and clear all TWL4030 interrupts since initially we do |
@@ -711,7 +723,8 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
711 | 723 | ||
712 | twl4030_irq_base = irq_base; | 724 | twl4030_irq_base = irq_base; |
713 | 725 | ||
714 | /* install an irq handler for each of the SIH modules; | 726 | /* |
727 | * Install an irq handler for each of the SIH modules; | ||
715 | * clone dummy irq_chip since PIH can't *do* anything | 728 | * clone dummy irq_chip since PIH can't *do* anything |
716 | */ | 729 | */ |
717 | twl4030_irq_chip = dummy_irq_chip; | 730 | twl4030_irq_chip = dummy_irq_chip; |
@@ -725,14 +738,14 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
725 | irq_set_nested_thread(i, 1); | 738 | irq_set_nested_thread(i, 1); |
726 | activate_irq(i); | 739 | activate_irq(i); |
727 | } | 740 | } |
728 | twl4030_irq_next = i; | 741 | |
729 | pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", "PIH", | 742 | dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", "PIH", |
730 | irq_num, irq_base, twl4030_irq_next - 1); | 743 | irq_num, irq_base, irq_end); |
731 | 744 | ||
732 | /* ... and the PWR_INT module ... */ | 745 | /* ... and the PWR_INT module ... */ |
733 | status = twl4030_sih_setup(TWL4030_MODULE_INT); | 746 | status = twl4030_sih_setup(dev, TWL4030_MODULE_INT, irq_end); |
734 | if (status < 0) { | 747 | if (status < 0) { |
735 | pr_err("twl4030: sih_setup PWR INT --> %d\n", status); | 748 | dev_err(dev, "sih_setup PWR INT --> %d\n", status); |
736 | goto fail; | 749 | goto fail; |
737 | } | 750 | } |
738 | 751 | ||
@@ -741,11 +754,11 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) | |||
741 | IRQF_ONESHOT, | 754 | IRQF_ONESHOT, |
742 | "TWL4030-PIH", NULL); | 755 | "TWL4030-PIH", NULL); |
743 | if (status < 0) { | 756 | if (status < 0) { |
744 | pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status); | 757 | dev_err(dev, "could not claim irq%d: %d\n", irq_num, status); |
745 | goto fail_rqirq; | 758 | goto fail_rqirq; |
746 | } | 759 | } |
747 | 760 | ||
748 | return status; | 761 | return irq_base; |
749 | fail_rqirq: | 762 | fail_rqirq: |
750 | /* clean up twl4030_sih_setup */ | 763 | /* clean up twl4030_sih_setup */ |
751 | fail: | 764 | fail: |