diff options
author | Thomas Abraham <thomas.abraham@linaro.org> | 2012-05-15 03:25:23 -0400 |
---|---|---|
committer | Kukjin Kim <kgene.kim@samsung.com> | 2012-05-15 18:03:44 -0400 |
commit | e873a47c4ef3fbb2e439a6b674e73b0c40dd8248 (patch) | |
tree | 245b58e5d007f998ca6e9be39d25bec80818c12f | |
parent | 1e60bc0b5f488c8acde22adeb62118ccfdb49062 (diff) |
ARM: EXYNOS: Add device tree support for interrupt combiner
Add device tree based instantiation of the interrupt combiner controller.
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
-rw-r--r-- | Documentation/devicetree/bindings/arm/samsung/interrupt-combiner.txt | 52 | ||||
-rw-r--r-- | arch/arm/mach-exynos/common.c | 71 |
2 files changed, 116 insertions, 7 deletions
diff --git a/Documentation/devicetree/bindings/arm/samsung/interrupt-combiner.txt b/Documentation/devicetree/bindings/arm/samsung/interrupt-combiner.txt new file mode 100644 index 000000000000..f2f2171e530e --- /dev/null +++ b/Documentation/devicetree/bindings/arm/samsung/interrupt-combiner.txt | |||
@@ -0,0 +1,52 @@ | |||
1 | * Samsung Exynos Interrupt Combiner Controller | ||
2 | |||
3 | Samsung's Exynos4 architecture includes a interrupt combiner controller which | ||
4 | can combine interrupt sources as a group and provide a single interrupt request | ||
5 | for the group. The interrupt request from each group are connected to a parent | ||
6 | interrupt controller, such as GIC in case of Exynos4210. | ||
7 | |||
8 | The interrupt combiner controller consists of multiple combiners. Upto eight | ||
9 | interrupt sources can be connected to a combiner. The combiner outputs one | ||
10 | combined interrupt for its eight interrupt sources. The combined interrupt | ||
11 | is usually connected to a parent interrupt controller. | ||
12 | |||
13 | A single node in the device tree is used to describe the interrupt combiner | ||
14 | controller module (which includes multiple combiners). A combiner in the | ||
15 | interrupt controller module shares config/control registers with other | ||
16 | combiners. For example, a 32-bit interrupt enable/disable config register | ||
17 | can accommodate upto 4 interrupt combiners (with each combiner supporting | ||
18 | upto 8 interrupt sources). | ||
19 | |||
20 | Required properties: | ||
21 | - compatible: should be "samsung,exynos4210-combiner". | ||
22 | - interrupt-controller: Identifies the node as an interrupt controller. | ||
23 | - #interrupt-cells: should be <2>. The meaning of the cells are | ||
24 | * First Cell: Combiner Group Number. | ||
25 | * Second Cell: Interrupt number within the group. | ||
26 | - reg: Base address and size of interrupt combiner registers. | ||
27 | - interrupts: The list of interrupts generated by the combiners which are then | ||
28 | connected to a parent interrupt controller. The format of the interrupt | ||
29 | specifier depends in the interrupt parent controller. | ||
30 | |||
31 | Optional properties: | ||
32 | - samsung,combiner-nr: The number of interrupt combiners supported. If this | ||
33 | property is not specified, the default number of combiners is assumed | ||
34 | to be 16. | ||
35 | - interrupt-parent: pHandle of the parent interrupt controller, if not | ||
36 | inherited from the parent node. | ||
37 | |||
38 | |||
39 | Example: | ||
40 | |||
41 | The following is a an example from the Exynos4210 SoC dtsi file. | ||
42 | |||
43 | combiner:interrupt-controller@10440000 { | ||
44 | compatible = "samsung,exynos4210-combiner"; | ||
45 | interrupt-controller; | ||
46 | #interrupt-cells = <2>; | ||
47 | reg = <0x10440000 0x1000>; | ||
48 | interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>, | ||
49 | <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>, | ||
50 | <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>, | ||
51 | <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>; | ||
52 | }; | ||
diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index a688c17bc869..9900158f026a 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/of_irq.h> | 21 | #include <linux/of_irq.h> |
22 | #include <linux/export.h> | 22 | #include <linux/export.h> |
23 | #include <linux/irqdomain.h> | 23 | #include <linux/irqdomain.h> |
24 | #include <linux/of_address.h> | ||
24 | 25 | ||
25 | #include <asm/proc-fns.h> | 26 | #include <asm/proc-fns.h> |
26 | #include <asm/exception.h> | 27 | #include <asm/exception.h> |
@@ -490,6 +491,35 @@ static void __init combiner_init_one(unsigned int combiner_nr, | |||
490 | base + COMBINER_ENABLE_CLEAR); | 491 | base + COMBINER_ENABLE_CLEAR); |
491 | } | 492 | } |
492 | 493 | ||
494 | #ifdef CONFIG_OF | ||
495 | static int combiner_irq_domain_xlate(struct irq_domain *d, | ||
496 | struct device_node *controller, | ||
497 | const u32 *intspec, unsigned int intsize, | ||
498 | unsigned long *out_hwirq, | ||
499 | unsigned int *out_type) | ||
500 | { | ||
501 | if (d->of_node != controller) | ||
502 | return -EINVAL; | ||
503 | |||
504 | if (intsize < 2) | ||
505 | return -EINVAL; | ||
506 | |||
507 | *out_hwirq = intspec[0] * MAX_IRQ_IN_COMBINER + intspec[1]; | ||
508 | *out_type = 0; | ||
509 | |||
510 | return 0; | ||
511 | } | ||
512 | #else | ||
513 | static int combiner_irq_domain_xlate(struct irq_domain *d, | ||
514 | struct device_node *controller, | ||
515 | const u32 *intspec, unsigned int intsize, | ||
516 | unsigned long *out_hwirq, | ||
517 | unsigned int *out_type) | ||
518 | { | ||
519 | return -EINVAL; | ||
520 | } | ||
521 | #endif | ||
522 | |||
493 | static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, | 523 | static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, |
494 | irq_hw_number_t hw) | 524 | irq_hw_number_t hw) |
495 | { | 525 | { |
@@ -501,16 +531,26 @@ static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, | |||
501 | } | 531 | } |
502 | 532 | ||
503 | static struct irq_domain_ops combiner_irq_domain_ops = { | 533 | static struct irq_domain_ops combiner_irq_domain_ops = { |
534 | .xlate = combiner_irq_domain_xlate, | ||
504 | .map = combiner_irq_domain_map, | 535 | .map = combiner_irq_domain_map, |
505 | }; | 536 | }; |
506 | 537 | ||
507 | void __init combiner_init(void __iomem *combiner_base, struct device_node *np) | 538 | void __init combiner_init(void __iomem *combiner_base, struct device_node *np) |
508 | { | 539 | { |
509 | int i, irq_base; | 540 | int i, irq, irq_base; |
510 | unsigned int max_nr, nr_irq; | 541 | unsigned int max_nr, nr_irq; |
511 | 542 | ||
512 | max_nr = soc_is_exynos5250() ? EXYNOS5_MAX_COMBINER_NR : | 543 | if (np) { |
513 | EXYNOS4_MAX_COMBINER_NR; | 544 | if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { |
545 | pr_warning("%s: number of combiners not specified, " | ||
546 | "setting default as %d.\n", | ||
547 | __func__, EXYNOS4_MAX_COMBINER_NR); | ||
548 | max_nr = EXYNOS4_MAX_COMBINER_NR; | ||
549 | } | ||
550 | } else { | ||
551 | max_nr = soc_is_exynos5250() ? EXYNOS5_MAX_COMBINER_NR : | ||
552 | EXYNOS4_MAX_COMBINER_NR; | ||
553 | } | ||
514 | nr_irq = max_nr * MAX_IRQ_IN_COMBINER; | 554 | nr_irq = max_nr * MAX_IRQ_IN_COMBINER; |
515 | 555 | ||
516 | irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0); | 556 | irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0); |
@@ -528,13 +568,31 @@ void __init combiner_init(void __iomem *combiner_base, struct device_node *np) | |||
528 | 568 | ||
529 | for (i = 0; i < max_nr; i++) { | 569 | for (i = 0; i < max_nr; i++) { |
530 | combiner_init_one(i, combiner_base + (i >> 2) * 0x10); | 570 | combiner_init_one(i, combiner_base + (i >> 2) * 0x10); |
531 | combiner_cascade_irq(i, IRQ_SPI(i)); | 571 | irq = np ? irq_of_parse_and_map(np, i) : IRQ_SPI(i); |
572 | combiner_cascade_irq(i, irq); | ||
532 | } | 573 | } |
533 | } | 574 | } |
534 | 575 | ||
535 | #ifdef CONFIG_OF | 576 | #ifdef CONFIG_OF |
577 | int __init combiner_of_init(struct device_node *np, struct device_node *parent) | ||
578 | { | ||
579 | void __iomem *combiner_base; | ||
580 | |||
581 | combiner_base = of_iomap(np, 0); | ||
582 | if (!combiner_base) { | ||
583 | pr_err("%s: failed to map combiner registers\n", __func__); | ||
584 | return -ENXIO; | ||
585 | } | ||
586 | |||
587 | combiner_init(combiner_base, np); | ||
588 | |||
589 | return 0; | ||
590 | } | ||
591 | |||
536 | static const struct of_device_id exynos4_dt_irq_match[] = { | 592 | static const struct of_device_id exynos4_dt_irq_match[] = { |
537 | { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, | 593 | { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, |
594 | { .compatible = "samsung,exynos4210-combiner", | ||
595 | .data = combiner_of_init, }, | ||
538 | {}, | 596 | {}, |
539 | }; | 597 | }; |
540 | #endif | 598 | #endif |
@@ -552,7 +610,8 @@ void __init exynos4_init_irq(void) | |||
552 | of_irq_init(exynos4_dt_irq_match); | 610 | of_irq_init(exynos4_dt_irq_match); |
553 | #endif | 611 | #endif |
554 | 612 | ||
555 | combiner_init(S5P_VA_COMBINER_BASE, NULL); | 613 | if (!of_have_populated_dt()) |
614 | combiner_init(S5P_VA_COMBINER_BASE, NULL); | ||
556 | 615 | ||
557 | /* | 616 | /* |
558 | * The parameters of s5p_init_irq() are for VIC init. | 617 | * The parameters of s5p_init_irq() are for VIC init. |
@@ -567,8 +626,6 @@ void __init exynos5_init_irq(void) | |||
567 | #ifdef CONFIG_OF | 626 | #ifdef CONFIG_OF |
568 | of_irq_init(exynos4_dt_irq_match); | 627 | of_irq_init(exynos4_dt_irq_match); |
569 | #endif | 628 | #endif |
570 | combiner_init(S5P_VA_COMBINER_BASE, NULL); | ||
571 | |||
572 | /* | 629 | /* |
573 | * The parameters of s5p_init_irq() are for VIC init. | 630 | * The parameters of s5p_init_irq() are for VIC init. |
574 | * Theses parameters should be NULL and 0 because EXYNOS4 | 631 | * Theses parameters should be NULL and 0 because EXYNOS4 |