aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-s5pv310/irq-combiner.c
diff options
context:
space:
mode:
authorChanghwan Youn <chaos.youn@samsung.com>2010-07-15 23:12:07 -0400
committerKukjin Kim <kgene.kim@samsung.com>2010-08-05 05:32:41 -0400
commit84bbc16c1f6210b2dfc39344b132d5801c357a70 (patch)
treee60e91c0e6c08913909e50006c1eb289818e829d /arch/arm/mach-s5pv310/irq-combiner.c
parentc8bef14051b261f86278fad84ccc23c891242d25 (diff)
ARM: S5PV310: Add IRQ support
This patch adds IRQ support for S5PV310. ARM GIC is installed in S5PV310 instead of VIC which is in every other CPUs in S5P series. Several irq combiners are used to resolve the lack of irq lines in current implementation. Signed-off-by: Changhwan Youn <chaos.youn@samsung.com> Signed-off-by: Hyuk Lee <hyuk1.lee@samsung.com> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'arch/arm/mach-s5pv310/irq-combiner.c')
-rw-r--r--arch/arm/mach-s5pv310/irq-combiner.c125
1 files changed, 125 insertions, 0 deletions
diff --git a/arch/arm/mach-s5pv310/irq-combiner.c b/arch/arm/mach-s5pv310/irq-combiner.c
new file mode 100644
index 000000000000..0f7052164f23
--- /dev/null
+++ b/arch/arm/mach-s5pv310/irq-combiner.c
@@ -0,0 +1,125 @@
1/* linux/arch/arm/mach-s5pv310/irq-combiner.c
2 *
3 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
5 *
6 * Based on arch/arm/common/gic.c
7 *
8 * IRQ COMBINER support
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15#include <linux/io.h>
16
17#include <asm/mach/irq.h>
18
19#define COMBINER_ENABLE_SET 0x0
20#define COMBINER_ENABLE_CLEAR 0x4
21#define COMBINER_INT_STATUS 0xC
22
23static DEFINE_SPINLOCK(irq_controller_lock);
24
25struct combiner_chip_data {
26 unsigned int irq_offset;
27 void __iomem *base;
28};
29
30static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];
31
32static inline void __iomem *combiner_base(unsigned int irq)
33{
34 struct combiner_chip_data *combiner_data = get_irq_chip_data(irq);
35 return combiner_data->base;
36}
37
38static void combiner_mask_irq(unsigned int irq)
39{
40 u32 mask = 1 << (irq % 32);
41
42 __raw_writel(mask, combiner_base(irq) + COMBINER_ENABLE_CLEAR);
43}
44
45static void combiner_unmask_irq(unsigned int irq)
46{
47 u32 mask = 1 << (irq % 32);
48
49 __raw_writel(mask, combiner_base(irq) + COMBINER_ENABLE_SET);
50}
51
52static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
53{
54 struct combiner_chip_data *chip_data = get_irq_data(irq);
55 struct irq_chip *chip = get_irq_chip(irq);
56 unsigned int cascade_irq, combiner_irq;
57 unsigned long status;
58
59 /* primary controller ack'ing */
60 chip->ack(irq);
61
62 spin_lock(&irq_controller_lock);
63 status = __raw_readl(chip_data->base + COMBINER_INT_STATUS);
64 spin_unlock(&irq_controller_lock);
65
66 if (status == 0)
67 goto out;
68
69 for (combiner_irq = 0; combiner_irq < 32; combiner_irq++) {
70 if (status & 0x1)
71 break;
72 status >>= 1;
73 }
74
75 cascade_irq = combiner_irq + (chip_data->irq_offset & ~31);
76 if (unlikely(cascade_irq >= NR_IRQS))
77 do_bad_IRQ(cascade_irq, desc);
78 else
79 generic_handle_irq(cascade_irq);
80
81 out:
82 /* primary controller unmasking */
83 chip->unmask(irq);
84}
85
86static struct irq_chip combiner_chip = {
87 .name = "COMBINER",
88 .mask = combiner_mask_irq,
89 .unmask = combiner_unmask_irq,
90};
91
92void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq)
93{
94 if (combiner_nr >= MAX_COMBINER_NR)
95 BUG();
96 if (set_irq_data(irq, &combiner_data[combiner_nr]) != 0)
97 BUG();
98 set_irq_chained_handler(irq, combiner_handle_cascade_irq);
99}
100
101void __init combiner_init(unsigned int combiner_nr, void __iomem *base,
102 unsigned int irq_start)
103{
104 unsigned int i;
105
106 if (combiner_nr >= MAX_COMBINER_NR)
107 BUG();
108
109 combiner_data[combiner_nr].base = base;
110 combiner_data[combiner_nr].irq_offset = irq_start;
111
112 /* Disable all interrupts */
113
114 __raw_writel(0xffffffff, base + COMBINER_ENABLE_CLEAR);
115
116 /* Setup the Linux IRQ subsystem */
117
118 for (i = irq_start; i < combiner_data[combiner_nr].irq_offset
119 + MAX_IRQ_IN_COMBINER; i++) {
120 set_irq_chip(i, &combiner_chip);
121 set_irq_chip_data(i, &combiner_data[combiner_nr]);
122 set_irq_handler(i, handle_level_irq);
123 set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
124 }
125}