summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip/exynos-combiner.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-05-07 14:28:42 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-07 14:28:42 -0400
commitbb9055b2744ada735a2fe555c4196ad39a83ef2a (patch)
tree014e0f462217cfa49988872a5134ae41b90b2e9c /drivers/irqchip/exynos-combiner.c
parent1bf25e78af317e6d5d9b5594dfeb0036e0d589d6 (diff)
parent241a9871263f3114717c0ed416a1bd1d2415d1fb (diff)
Merge tag 'multiplatform-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull late ARM Exynos multiplatform changes from Arnd Bergmann: "These continue the multiplatform support for exynos, adding support for building most of the essential drivers (clocksource, clk, irqchip) when combined with other platforms. As a result, it should become really easy to add full multiplatform exynos support in 3.11, although we don't yet enable it for 3.10. The changes were not included in the earlier multiplatform series in order to avoid clashes with the other Exynos updates. This also includes work from Tomasz Figa to fix the pwm clocksource code on Exynos, which is not strictly required for multiplatform, but related to the other patches in this set and needed as a bug fix for at least one board." * tag 'multiplatform-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (22 commits) ARM: dts: exynops4210: really add universal_c210 dts ARM: dts: exynos4210: Add basic dts file for universal_c210 board ARM: dts: exynos4: Add node for PWM device ARM: SAMSUNG: Do not register legacy timer interrupts on Exynos clocksource: samsung_pwm_timer: Work around rounding errors in clockevents core clocksource: samsung_pwm_timer: Correct programming of clock events clocksource: samsung_pwm_timer: Use proper clockevents max_delta clocksource: samsung_pwm_timer: Add support for non-DT platforms clocksource: samsung_pwm_timer: Drop unused samsung_pwm struct clocksource: samsung_pwm_timer: Keep all driver data in a structure clocksource: samsung_pwm_timer: Make PWM spinlock global clocksource: samsung_pwm_timer: Let platforms select the driver Documentation: Add device tree bindings for Samsung PWM timers clocksource: add samsung pwm timer driver irqchip: exynos: look up irq using irq_find_mapping irqchip: exynos: pass irq_base from platform irqchip: exynos: localize irq lookup for ATAGS irqchip: exynos: allocate combiner_data dynamically irqchip: exynos: pass max combiner number to combiner_init ARM: exynos: add missing properties for combiner IRQs ...
Diffstat (limited to 'drivers/irqchip/exynos-combiner.c')
-rw-r--r--drivers/irqchip/exynos-combiner.c125
1 files changed, 64 insertions, 61 deletions
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index 02492ab20d22..a9d2b2fa4afd 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -12,13 +12,16 @@
12#include <linux/export.h> 12#include <linux/export.h>
13#include <linux/init.h> 13#include <linux/init.h>
14#include <linux/io.h> 14#include <linux/io.h>
15#include <linux/slab.h>
15#include <linux/irqdomain.h> 16#include <linux/irqdomain.h>
16#include <linux/irqchip/chained_irq.h> 17#include <linux/irqchip/chained_irq.h>
17#include <linux/of_address.h> 18#include <linux/of_address.h>
18#include <linux/of_irq.h> 19#include <linux/of_irq.h>
19#include <asm/mach/irq.h> 20#include <asm/mach/irq.h>
20 21
22#ifdef CONFIG_EXYNOS_ATAGS
21#include <plat/cpu.h> 23#include <plat/cpu.h>
24#endif
22 25
23#include "irqchip.h" 26#include "irqchip.h"
24 27
@@ -26,17 +29,18 @@
26#define COMBINER_ENABLE_CLEAR 0x4 29#define COMBINER_ENABLE_CLEAR 0x4
27#define COMBINER_INT_STATUS 0xC 30#define COMBINER_INT_STATUS 0xC
28 31
32#define IRQ_IN_COMBINER 8
33
29static DEFINE_SPINLOCK(irq_controller_lock); 34static DEFINE_SPINLOCK(irq_controller_lock);
30 35
31struct combiner_chip_data { 36struct combiner_chip_data {
32 unsigned int irq_offset; 37 unsigned int hwirq_offset;
33 unsigned int irq_mask; 38 unsigned int irq_mask;
34 void __iomem *base; 39 void __iomem *base;
35 unsigned int parent_irq; 40 unsigned int parent_irq;
36}; 41};
37 42
38static struct irq_domain *combiner_irq_domain; 43static struct irq_domain *combiner_irq_domain;
39static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];
40 44
41static inline void __iomem *combiner_base(struct irq_data *data) 45static inline void __iomem *combiner_base(struct irq_data *data)
42{ 46{
@@ -77,11 +81,11 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
77 if (status == 0) 81 if (status == 0)
78 goto out; 82 goto out;
79 83
80 combiner_irq = __ffs(status); 84 combiner_irq = chip_data->hwirq_offset + __ffs(status);
85 cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq);
81 86
82 cascade_irq = combiner_irq + (chip_data->irq_offset & ~31); 87 if (unlikely(!cascade_irq))
83 if (unlikely(cascade_irq >= NR_IRQS)) 88 do_bad_IRQ(irq, desc);
84 do_bad_IRQ(cascade_irq, desc);
85 else 89 else
86 generic_handle_irq(cascade_irq); 90 generic_handle_irq(cascade_irq);
87 91
@@ -113,40 +117,25 @@ static struct irq_chip combiner_chip = {
113#endif 117#endif
114}; 118};
115 119
116static unsigned int max_combiner_nr(void) 120static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data,
117{
118 if (soc_is_exynos5250())
119 return EXYNOS5_MAX_COMBINER_NR;
120 else if (soc_is_exynos4412())
121 return EXYNOS4412_MAX_COMBINER_NR;
122 else if (soc_is_exynos4212())
123 return EXYNOS4212_MAX_COMBINER_NR;
124 else
125 return EXYNOS4210_MAX_COMBINER_NR;
126}
127
128static void __init combiner_cascade_irq(unsigned int combiner_nr,
129 unsigned int irq) 121 unsigned int irq)
130{ 122{
131 if (combiner_nr >= max_combiner_nr()) 123 if (irq_set_handler_data(irq, combiner_data) != 0)
132 BUG();
133 if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0)
134 BUG(); 124 BUG();
135 irq_set_chained_handler(irq, combiner_handle_cascade_irq); 125 irq_set_chained_handler(irq, combiner_handle_cascade_irq);
136} 126}
137 127
138static void __init combiner_init_one(unsigned int combiner_nr, 128static void __init combiner_init_one(struct combiner_chip_data *combiner_data,
129 unsigned int combiner_nr,
139 void __iomem *base, unsigned int irq) 130 void __iomem *base, unsigned int irq)
140{ 131{
141 combiner_data[combiner_nr].base = base; 132 combiner_data->base = base;
142 combiner_data[combiner_nr].irq_offset = irq_find_mapping( 133 combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER;
143 combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER); 134 combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3);
144 combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); 135 combiner_data->parent_irq = irq;
145 combiner_data[combiner_nr].parent_irq = irq;
146 136
147 /* Disable all interrupts */ 137 /* Disable all interrupts */
148 __raw_writel(combiner_data[combiner_nr].irq_mask, 138 __raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
149 base + COMBINER_ENABLE_CLEAR);
150} 139}
151 140
152#ifdef CONFIG_OF 141#ifdef CONFIG_OF
@@ -162,7 +151,7 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
162 if (intsize < 2) 151 if (intsize < 2)
163 return -EINVAL; 152 return -EINVAL;
164 153
165 *out_hwirq = intspec[0] * MAX_IRQ_IN_COMBINER + intspec[1]; 154 *out_hwirq = intspec[0] * IRQ_IN_COMBINER + intspec[1];
166 *out_type = 0; 155 *out_type = 0;
167 156
168 return 0; 157 return 0;
@@ -181,6 +170,8 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
181static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, 170static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
182 irq_hw_number_t hw) 171 irq_hw_number_t hw)
183{ 172{
173 struct combiner_chip_data *combiner_data = d->host_data;
174
184 irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq); 175 irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
185 irq_set_chip_data(irq, &combiner_data[hw >> 3]); 176 irq_set_chip_data(irq, &combiner_data[hw >> 3]);
186 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 177 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
@@ -193,8 +184,12 @@ static struct irq_domain_ops combiner_irq_domain_ops = {
193 .map = combiner_irq_domain_map, 184 .map = combiner_irq_domain_map,
194}; 185};
195 186
196static unsigned int exynos4x12_combiner_extra_irq(int group) 187static unsigned int combiner_lookup_irq(int group)
197{ 188{
189#ifdef CONFIG_EXYNOS_ATAGS
190 if (group < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
191 return IRQ_SPI(group);
192
198 switch (group) { 193 switch (group) {
199 case 16: 194 case 16:
200 return IRQ_SPI(107); 195 return IRQ_SPI(107);
@@ -204,53 +199,46 @@ static unsigned int exynos4x12_combiner_extra_irq(int group)
204 return IRQ_SPI(48); 199 return IRQ_SPI(48);
205 case 19: 200 case 19:
206 return IRQ_SPI(42); 201 return IRQ_SPI(42);
207 default:
208 return 0;
209 } 202 }
203#endif
204 return 0;
210} 205}
211 206
212void __init combiner_init(void __iomem *combiner_base, 207void __init combiner_init(void __iomem *combiner_base,
213 struct device_node *np) 208 struct device_node *np,
209 unsigned int max_nr,
210 int irq_base)
214{ 211{
215 int i, irq, irq_base; 212 int i, irq;
216 unsigned int max_nr, nr_irq; 213 unsigned int nr_irq;
214 struct combiner_chip_data *combiner_data;
217 215
218 max_nr = max_combiner_nr(); 216 nr_irq = max_nr * IRQ_IN_COMBINER;
219 217
220 if (np) { 218 combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL);
221 if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { 219 if (!combiner_data) {
222 pr_info("%s: number of combiners not specified, " 220 pr_warning("%s: could not allocate combiner data\n", __func__);
223 "setting default as %d.\n", 221 return;
224 __func__, max_nr);
225 }
226 }
227
228 nr_irq = max_nr * MAX_IRQ_IN_COMBINER;
229
230 irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0);
231 if (IS_ERR_VALUE(irq_base)) {
232 irq_base = COMBINER_IRQ(0, 0);
233 pr_warning("%s: irq desc alloc failed. Continuing with %d as linux irq base\n", __func__, irq_base);
234 } 222 }
235 223
236 combiner_irq_domain = irq_domain_add_legacy(np, nr_irq, irq_base, 0, 224 combiner_irq_domain = irq_domain_add_simple(np, nr_irq, irq_base,
237 &combiner_irq_domain_ops, &combiner_data); 225 &combiner_irq_domain_ops, combiner_data);
238 if (WARN_ON(!combiner_irq_domain)) { 226 if (WARN_ON(!combiner_irq_domain)) {
239 pr_warning("%s: irq domain init failed\n", __func__); 227 pr_warning("%s: irq domain init failed\n", __func__);
240 return; 228 return;
241 } 229 }
242 230
243 for (i = 0; i < max_nr; i++) { 231 for (i = 0; i < max_nr; i++) {
244 if (i < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
245 irq = IRQ_SPI(i);
246 else
247 irq = exynos4x12_combiner_extra_irq(i);
248#ifdef CONFIG_OF 232#ifdef CONFIG_OF
249 if (np) 233 if (np)
250 irq = irq_of_parse_and_map(np, i); 234 irq = irq_of_parse_and_map(np, i);
235 else
251#endif 236#endif
252 combiner_init_one(i, combiner_base + (i >> 2) * 0x10, irq); 237 irq = combiner_lookup_irq(i);
253 combiner_cascade_irq(i, irq); 238
239 combiner_init_one(&combiner_data[i], i,
240 combiner_base + (i >> 2) * 0x10, irq);
241 combiner_cascade_irq(&combiner_data[i], irq);
254 } 242 }
255} 243}
256 244
@@ -259,6 +247,8 @@ static int __init combiner_of_init(struct device_node *np,
259 struct device_node *parent) 247 struct device_node *parent)
260{ 248{
261 void __iomem *combiner_base; 249 void __iomem *combiner_base;
250 unsigned int max_nr = 20;
251 int irq_base = -1;
262 252
263 combiner_base = of_iomap(np, 0); 253 combiner_base = of_iomap(np, 0);
264 if (!combiner_base) { 254 if (!combiner_base) {
@@ -266,7 +256,20 @@ static int __init combiner_of_init(struct device_node *np,
266 return -ENXIO; 256 return -ENXIO;
267 } 257 }
268 258
269 combiner_init(combiner_base, np); 259 if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
260 pr_info("%s: number of combiners not specified, "
261 "setting default as %d.\n",
262 __func__, max_nr);
263 }
264
265 /*
266 * FIXME: This is a hardwired COMBINER_IRQ(0,0). Once all devices
267 * get their IRQ from DT, remove this in order to get dynamic
268 * allocation.
269 */
270 irq_base = 160;
271
272 combiner_init(combiner_base, np, max_nr, irq_base);
270 273
271 return 0; 274 return 0;
272} 275}