diff options
Diffstat (limited to 'drivers/irqchip/irq-vic.c')
-rw-r--r-- | drivers/irqchip/irq-vic.c | 59 |
1 files changed, 52 insertions, 7 deletions
diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index 8e21ae0bab46..6002942a231c 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c | |||
@@ -57,6 +57,7 @@ | |||
57 | 57 | ||
58 | /** | 58 | /** |
59 | * struct vic_device - VIC PM device | 59 | * struct vic_device - VIC PM device |
60 | * @parent_irq: The parent IRQ number of the VIC if cascaded, or 0. | ||
60 | * @irq: The IRQ number for the base of the VIC. | 61 | * @irq: The IRQ number for the base of the VIC. |
61 | * @base: The register base for the VIC. | 62 | * @base: The register base for the VIC. |
62 | * @valid_sources: A bitmask of valid interrupts | 63 | * @valid_sources: A bitmask of valid interrupts |
@@ -224,6 +225,17 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs) | |||
224 | return handled; | 225 | return handled; |
225 | } | 226 | } |
226 | 227 | ||
228 | static void vic_handle_irq_cascaded(unsigned int irq, struct irq_desc *desc) | ||
229 | { | ||
230 | u32 stat, hwirq; | ||
231 | struct vic_device *vic = irq_desc_get_handler_data(desc); | ||
232 | |||
233 | while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) { | ||
234 | hwirq = ffs(stat) - 1; | ||
235 | generic_handle_irq(irq_find_mapping(vic->domain, hwirq)); | ||
236 | } | ||
237 | } | ||
238 | |||
227 | /* | 239 | /* |
228 | * Keep iterating over all registered VIC's until there are no pending | 240 | * Keep iterating over all registered VIC's until there are no pending |
229 | * interrupts. | 241 | * interrupts. |
@@ -246,6 +258,7 @@ static struct irq_domain_ops vic_irqdomain_ops = { | |||
246 | /** | 258 | /** |
247 | * vic_register() - Register a VIC. | 259 | * vic_register() - Register a VIC. |
248 | * @base: The base address of the VIC. | 260 | * @base: The base address of the VIC. |
261 | * @parent_irq: The parent IRQ if cascaded, else 0. | ||
249 | * @irq: The base IRQ for the VIC. | 262 | * @irq: The base IRQ for the VIC. |
250 | * @valid_sources: bitmask of valid interrupts | 263 | * @valid_sources: bitmask of valid interrupts |
251 | * @resume_sources: bitmask of interrupts allowed for resume sources. | 264 | * @resume_sources: bitmask of interrupts allowed for resume sources. |
@@ -257,7 +270,8 @@ static struct irq_domain_ops vic_irqdomain_ops = { | |||
257 | * | 270 | * |
258 | * This also configures the IRQ domain for the VIC. | 271 | * This also configures the IRQ domain for the VIC. |
259 | */ | 272 | */ |
260 | static void __init vic_register(void __iomem *base, unsigned int irq, | 273 | static void __init vic_register(void __iomem *base, unsigned int parent_irq, |
274 | unsigned int irq, | ||
261 | u32 valid_sources, u32 resume_sources, | 275 | u32 valid_sources, u32 resume_sources, |
262 | struct device_node *node) | 276 | struct device_node *node) |
263 | { | 277 | { |
@@ -273,15 +287,25 @@ static void __init vic_register(void __iomem *base, unsigned int irq, | |||
273 | v->base = base; | 287 | v->base = base; |
274 | v->valid_sources = valid_sources; | 288 | v->valid_sources = valid_sources; |
275 | v->resume_sources = resume_sources; | 289 | v->resume_sources = resume_sources; |
276 | v->irq = irq; | ||
277 | set_handle_irq(vic_handle_irq); | 290 | set_handle_irq(vic_handle_irq); |
278 | vic_id++; | 291 | vic_id++; |
292 | |||
293 | if (parent_irq) { | ||
294 | irq_set_handler_data(parent_irq, v); | ||
295 | irq_set_chained_handler(parent_irq, vic_handle_irq_cascaded); | ||
296 | } | ||
297 | |||
279 | v->domain = irq_domain_add_simple(node, fls(valid_sources), irq, | 298 | v->domain = irq_domain_add_simple(node, fls(valid_sources), irq, |
280 | &vic_irqdomain_ops, v); | 299 | &vic_irqdomain_ops, v); |
281 | /* create an IRQ mapping for each valid IRQ */ | 300 | /* create an IRQ mapping for each valid IRQ */ |
282 | for (i = 0; i < fls(valid_sources); i++) | 301 | for (i = 0; i < fls(valid_sources); i++) |
283 | if (valid_sources & (1 << i)) | 302 | if (valid_sources & (1 << i)) |
284 | irq_create_mapping(v->domain, i); | 303 | irq_create_mapping(v->domain, i); |
304 | /* If no base IRQ was passed, figure out our allocated base */ | ||
305 | if (irq) | ||
306 | v->irq = irq; | ||
307 | else | ||
308 | v->irq = irq_find_mapping(v->domain, 0); | ||
285 | } | 309 | } |
286 | 310 | ||
287 | static void vic_ack_irq(struct irq_data *d) | 311 | static void vic_ack_irq(struct irq_data *d) |
@@ -409,10 +433,10 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start, | |||
409 | writel(32, base + VIC_PL190_DEF_VECT_ADDR); | 433 | writel(32, base + VIC_PL190_DEF_VECT_ADDR); |
410 | } | 434 | } |
411 | 435 | ||
412 | vic_register(base, irq_start, vic_sources, 0, node); | 436 | vic_register(base, 0, irq_start, vic_sources, 0, node); |
413 | } | 437 | } |
414 | 438 | ||
415 | void __init __vic_init(void __iomem *base, int irq_start, | 439 | void __init __vic_init(void __iomem *base, int parent_irq, int irq_start, |
416 | u32 vic_sources, u32 resume_sources, | 440 | u32 vic_sources, u32 resume_sources, |
417 | struct device_node *node) | 441 | struct device_node *node) |
418 | { | 442 | { |
@@ -449,7 +473,7 @@ void __init __vic_init(void __iomem *base, int irq_start, | |||
449 | 473 | ||
450 | vic_init2(base); | 474 | vic_init2(base); |
451 | 475 | ||
452 | vic_register(base, irq_start, vic_sources, resume_sources, node); | 476 | vic_register(base, parent_irq, irq_start, vic_sources, resume_sources, node); |
453 | } | 477 | } |
454 | 478 | ||
455 | /** | 479 | /** |
@@ -462,7 +486,28 @@ void __init __vic_init(void __iomem *base, int irq_start, | |||
462 | void __init vic_init(void __iomem *base, unsigned int irq_start, | 486 | void __init vic_init(void __iomem *base, unsigned int irq_start, |
463 | u32 vic_sources, u32 resume_sources) | 487 | u32 vic_sources, u32 resume_sources) |
464 | { | 488 | { |
465 | __vic_init(base, irq_start, vic_sources, resume_sources, NULL); | 489 | __vic_init(base, 0, irq_start, vic_sources, resume_sources, NULL); |
490 | } | ||
491 | |||
492 | /** | ||
493 | * vic_init_cascaded() - initialise a cascaded vectored interrupt controller | ||
494 | * @base: iomem base address | ||
495 | * @parent_irq: the parent IRQ we're cascaded off | ||
496 | * @irq_start: starting interrupt number, must be muliple of 32 | ||
497 | * @vic_sources: bitmask of interrupt sources to allow | ||
498 | * @resume_sources: bitmask of interrupt sources to allow for resume | ||
499 | * | ||
500 | * This returns the base for the new interrupts or negative on error. | ||
501 | */ | ||
502 | int __init vic_init_cascaded(void __iomem *base, unsigned int parent_irq, | ||
503 | u32 vic_sources, u32 resume_sources) | ||
504 | { | ||
505 | struct vic_device *v; | ||
506 | |||
507 | v = &vic_devices[vic_id]; | ||
508 | __vic_init(base, parent_irq, 0, vic_sources, resume_sources, NULL); | ||
509 | /* Return out acquired base */ | ||
510 | return v->irq; | ||
466 | } | 511 | } |
467 | 512 | ||
468 | #ifdef CONFIG_OF | 513 | #ifdef CONFIG_OF |
@@ -485,7 +530,7 @@ int __init vic_of_init(struct device_node *node, struct device_node *parent) | |||
485 | /* | 530 | /* |
486 | * Passing 0 as first IRQ makes the simple domain allocate descriptors | 531 | * Passing 0 as first IRQ makes the simple domain allocate descriptors |
487 | */ | 532 | */ |
488 | __vic_init(regs, 0, interrupt_mask, wakeup_mask, node); | 533 | __vic_init(regs, 0, 0, interrupt_mask, wakeup_mask, node); |
489 | 534 | ||
490 | return 0; | 535 | return 0; |
491 | } | 536 | } |