diff options
| author | Thomas Abraham <thomas.abraham@linaro.org> | 2012-05-15 03:18:35 -0400 |
|---|---|---|
| committer | Kukjin Kim <kgene.kim@samsung.com> | 2012-05-15 18:03:44 -0400 |
| commit | 1e60bc0b5f488c8acde22adeb62118ccfdb49062 (patch) | |
| tree | a84b04ae45b848ae00fa8283728f678ce78d07fc | |
| parent | 9ee6af9c3fad48cddc0684d0c77d3ea1329e10a1 (diff) | |
ARM: EXYNOS: Add irq_domain support for interrupt combiner
Add irq_domain support for hardware interrupts of the interrupt combiner.
The hardware interrupts of all the instances of the combiner are grouped
in a single irq_domain.
Cc: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
| -rw-r--r-- | arch/arm/mach-exynos/common.c | 88 |
1 files changed, 50 insertions, 38 deletions
diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index 3302a8dca4e0..a688c17bc869 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c | |||
| @@ -19,6 +19,8 @@ | |||
| 19 | #include <linux/serial_core.h> | 19 | #include <linux/serial_core.h> |
| 20 | #include <linux/of.h> | 20 | #include <linux/of.h> |
| 21 | #include <linux/of_irq.h> | 21 | #include <linux/of_irq.h> |
| 22 | #include <linux/export.h> | ||
| 23 | #include <linux/irqdomain.h> | ||
| 22 | 24 | ||
| 23 | #include <asm/proc-fns.h> | 25 | #include <asm/proc-fns.h> |
| 24 | #include <asm/exception.h> | 26 | #include <asm/exception.h> |
| @@ -399,6 +401,7 @@ struct combiner_chip_data { | |||
| 399 | void __iomem *base; | 401 | void __iomem *base; |
| 400 | }; | 402 | }; |
| 401 | 403 | ||
| 404 | static struct irq_domain *combiner_irq_domain; | ||
| 402 | static struct combiner_chip_data combiner_data[MAX_COMBINER_NR]; | 405 | static struct combiner_chip_data combiner_data[MAX_COMBINER_NR]; |
| 403 | 406 | ||
| 404 | static inline void __iomem *combiner_base(struct irq_data *data) | 407 | static inline void __iomem *combiner_base(struct irq_data *data) |
| @@ -411,14 +414,14 @@ static inline void __iomem *combiner_base(struct irq_data *data) | |||
| 411 | 414 | ||
| 412 | static void combiner_mask_irq(struct irq_data *data) | 415 | static void combiner_mask_irq(struct irq_data *data) |
| 413 | { | 416 | { |
| 414 | u32 mask = 1 << (data->irq % 32); | 417 | u32 mask = 1 << (data->hwirq % 32); |
| 415 | 418 | ||
| 416 | __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); | 419 | __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); |
| 417 | } | 420 | } |
| 418 | 421 | ||
| 419 | static void combiner_unmask_irq(struct irq_data *data) | 422 | static void combiner_unmask_irq(struct irq_data *data) |
| 420 | { | 423 | { |
| 421 | u32 mask = 1 << (data->irq % 32); | 424 | u32 mask = 1 << (data->hwirq % 32); |
| 422 | 425 | ||
| 423 | __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET); | 426 | __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET); |
| 424 | } | 427 | } |
| @@ -474,36 +477,58 @@ static void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int i | |||
| 474 | irq_set_chained_handler(irq, combiner_handle_cascade_irq); | 477 | irq_set_chained_handler(irq, combiner_handle_cascade_irq); |
| 475 | } | 478 | } |
| 476 | 479 | ||
| 477 | static void __init combiner_init(unsigned int combiner_nr, void __iomem *base, | 480 | static void __init combiner_init_one(unsigned int combiner_nr, |
| 478 | unsigned int irq_start) | 481 | void __iomem *base) |
| 479 | { | 482 | { |
| 480 | unsigned int i; | ||
| 481 | unsigned int max_nr; | ||
| 482 | |||
| 483 | if (soc_is_exynos5250()) | ||
| 484 | max_nr = EXYNOS5_MAX_COMBINER_NR; | ||
| 485 | else | ||
| 486 | max_nr = EXYNOS4_MAX_COMBINER_NR; | ||
| 487 | |||
| 488 | if (combiner_nr >= max_nr) | ||
| 489 | BUG(); | ||
| 490 | |||
| 491 | combiner_data[combiner_nr].base = base; | 483 | combiner_data[combiner_nr].base = base; |
| 492 | combiner_data[combiner_nr].irq_offset = irq_start; | 484 | combiner_data[combiner_nr].irq_offset = irq_find_mapping( |
| 485 | combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER); | ||
| 493 | combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); | 486 | combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); |
| 494 | 487 | ||
| 495 | /* Disable all interrupts */ | 488 | /* Disable all interrupts */ |
| 496 | |||
| 497 | __raw_writel(combiner_data[combiner_nr].irq_mask, | 489 | __raw_writel(combiner_data[combiner_nr].irq_mask, |
| 498 | base + COMBINER_ENABLE_CLEAR); | 490 | base + COMBINER_ENABLE_CLEAR); |
| 491 | } | ||
| 492 | |||
| 493 | static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, | ||
| 494 | irq_hw_number_t hw) | ||
| 495 | { | ||
| 496 | irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq); | ||
| 497 | irq_set_chip_data(irq, &combiner_data[hw >> 3]); | ||
| 498 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | ||
| 499 | |||
| 500 | return 0; | ||
| 501 | } | ||
| 502 | |||
| 503 | static struct irq_domain_ops combiner_irq_domain_ops = { | ||
| 504 | .map = combiner_irq_domain_map, | ||
| 505 | }; | ||
| 506 | |||
| 507 | void __init combiner_init(void __iomem *combiner_base, struct device_node *np) | ||
| 508 | { | ||
| 509 | int i, irq_base; | ||
| 510 | unsigned int max_nr, nr_irq; | ||
| 511 | |||
| 512 | max_nr = soc_is_exynos5250() ? EXYNOS5_MAX_COMBINER_NR : | ||
| 513 | EXYNOS4_MAX_COMBINER_NR; | ||
| 514 | nr_irq = max_nr * MAX_IRQ_IN_COMBINER; | ||
| 515 | |||
| 516 | irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0); | ||
| 517 | if (IS_ERR_VALUE(irq_base)) { | ||
| 518 | irq_base = COMBINER_IRQ(0, 0); | ||
| 519 | pr_warning("%s: irq desc alloc failed. Continuing with %d as linux irq base\n", __func__, irq_base); | ||
| 520 | } | ||
| 499 | 521 | ||
| 500 | /* Setup the Linux IRQ subsystem */ | 522 | combiner_irq_domain = irq_domain_add_legacy(np, nr_irq, irq_base, 0, |
| 523 | &combiner_irq_domain_ops, &combiner_data); | ||
| 524 | if (WARN_ON(!combiner_irq_domain)) { | ||
| 525 | pr_warning("%s: irq domain init failed\n", __func__); | ||
| 526 | return; | ||
| 527 | } | ||
| 501 | 528 | ||
| 502 | for (i = irq_start; i < combiner_data[combiner_nr].irq_offset | 529 | for (i = 0; i < max_nr; i++) { |
| 503 | + MAX_IRQ_IN_COMBINER; i++) { | 530 | combiner_init_one(i, combiner_base + (i >> 2) * 0x10); |
| 504 | irq_set_chip_and_handler(i, &combiner_chip, handle_level_irq); | 531 | combiner_cascade_irq(i, IRQ_SPI(i)); |
| 505 | irq_set_chip_data(i, &combiner_data[combiner_nr]); | ||
| 506 | set_irq_flags(i, IRQF_VALID | IRQF_PROBE); | ||
| 507 | } | 532 | } |
| 508 | } | 533 | } |
| 509 | 534 | ||
| @@ -516,7 +541,6 @@ static const struct of_device_id exynos4_dt_irq_match[] = { | |||
| 516 | 541 | ||
| 517 | void __init exynos4_init_irq(void) | 542 | void __init exynos4_init_irq(void) |
| 518 | { | 543 | { |
| 519 | int irq; | ||
| 520 | unsigned int gic_bank_offset; | 544 | unsigned int gic_bank_offset; |
| 521 | 545 | ||
| 522 | gic_bank_offset = soc_is_exynos4412() ? 0x4000 : 0x8000; | 546 | gic_bank_offset = soc_is_exynos4412() ? 0x4000 : 0x8000; |
| @@ -528,12 +552,7 @@ void __init exynos4_init_irq(void) | |||
| 528 | of_irq_init(exynos4_dt_irq_match); | 552 | of_irq_init(exynos4_dt_irq_match); |
| 529 | #endif | 553 | #endif |
| 530 | 554 | ||
| 531 | for (irq = 0; irq < EXYNOS4_MAX_COMBINER_NR; irq++) { | 555 | combiner_init(S5P_VA_COMBINER_BASE, NULL); |
| 532 | |||
| 533 | combiner_init(irq, (void __iomem *)S5P_VA_COMBINER(irq), | ||
| 534 | COMBINER_IRQ(irq, 0)); | ||
| 535 | combiner_cascade_irq(irq, IRQ_SPI(irq)); | ||
| 536 | } | ||
| 537 | 556 | ||
| 538 | /* | 557 | /* |
| 539 | * The parameters of s5p_init_irq() are for VIC init. | 558 | * The parameters of s5p_init_irq() are for VIC init. |
| @@ -545,17 +564,10 @@ void __init exynos4_init_irq(void) | |||
| 545 | 564 | ||
| 546 | void __init exynos5_init_irq(void) | 565 | void __init exynos5_init_irq(void) |
| 547 | { | 566 | { |
| 548 | int irq; | ||
| 549 | |||
| 550 | #ifdef CONFIG_OF | 567 | #ifdef CONFIG_OF |
| 551 | of_irq_init(exynos4_dt_irq_match); | 568 | of_irq_init(exynos4_dt_irq_match); |
| 552 | #endif | 569 | #endif |
| 553 | 570 | combiner_init(S5P_VA_COMBINER_BASE, NULL); | |
| 554 | for (irq = 0; irq < EXYNOS5_MAX_COMBINER_NR; irq++) { | ||
| 555 | combiner_init(irq, (void __iomem *)S5P_VA_COMBINER(irq), | ||
| 556 | COMBINER_IRQ(irq, 0)); | ||
| 557 | combiner_cascade_irq(irq, IRQ_SPI(irq)); | ||
| 558 | } | ||
| 559 | 571 | ||
| 560 | /* | 572 | /* |
| 561 | * The parameters of s5p_init_irq() are for VIC init. | 573 | * The parameters of s5p_init_irq() are for VIC init. |
