diff options
Diffstat (limited to 'arch/arm/mach-omap2/omap-wakeupgen.c')
-rw-r--r-- | arch/arm/mach-omap2/omap-wakeupgen.c | 128 |
1 files changed, 98 insertions, 30 deletions
diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c index f961c46453b9..3b56722dfd8a 100644 --- a/arch/arm/mach-omap2/omap-wakeupgen.c +++ b/arch/arm/mach-omap2/omap-wakeupgen.c | |||
@@ -20,11 +20,12 @@ | |||
20 | #include <linux/init.h> | 20 | #include <linux/init.h> |
21 | #include <linux/io.h> | 21 | #include <linux/io.h> |
22 | #include <linux/irq.h> | 22 | #include <linux/irq.h> |
23 | #include <linux/irqdomain.h> | ||
24 | #include <linux/of_address.h> | ||
23 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
24 | #include <linux/cpu.h> | 26 | #include <linux/cpu.h> |
25 | #include <linux/notifier.h> | 27 | #include <linux/notifier.h> |
26 | #include <linux/cpu_pm.h> | 28 | #include <linux/cpu_pm.h> |
27 | #include <linux/irqchip/arm-gic.h> | ||
28 | 29 | ||
29 | #include "omap-wakeupgen.h" | 30 | #include "omap-wakeupgen.h" |
30 | #include "omap-secure.h" | 31 | #include "omap-secure.h" |
@@ -78,29 +79,12 @@ static inline void sar_writel(u32 val, u32 offset, u8 idx) | |||
78 | 79 | ||
79 | static inline int _wakeupgen_get_irq_info(u32 irq, u32 *bit_posn, u8 *reg_index) | 80 | static inline int _wakeupgen_get_irq_info(u32 irq, u32 *bit_posn, u8 *reg_index) |
80 | { | 81 | { |
81 | unsigned int spi_irq; | ||
82 | |||
83 | /* | ||
84 | * PPIs and SGIs are not supported. | ||
85 | */ | ||
86 | if (irq < OMAP44XX_IRQ_GIC_START) | ||
87 | return -EINVAL; | ||
88 | |||
89 | /* | ||
90 | * Subtract the GIC offset. | ||
91 | */ | ||
92 | spi_irq = irq - OMAP44XX_IRQ_GIC_START; | ||
93 | if (spi_irq > MAX_IRQS) { | ||
94 | pr_err("omap wakeupGen: Invalid IRQ%d\n", irq); | ||
95 | return -EINVAL; | ||
96 | } | ||
97 | |||
98 | /* | 82 | /* |
99 | * Each WakeupGen register controls 32 interrupt. | 83 | * Each WakeupGen register controls 32 interrupt. |
100 | * i.e. 1 bit per SPI IRQ | 84 | * i.e. 1 bit per SPI IRQ |
101 | */ | 85 | */ |
102 | *reg_index = spi_irq >> 5; | 86 | *reg_index = irq >> 5; |
103 | *bit_posn = spi_irq %= 32; | 87 | *bit_posn = irq %= 32; |
104 | 88 | ||
105 | return 0; | 89 | return 0; |
106 | } | 90 | } |
@@ -141,6 +125,7 @@ static void wakeupgen_mask(struct irq_data *d) | |||
141 | raw_spin_lock_irqsave(&wakeupgen_lock, flags); | 125 | raw_spin_lock_irqsave(&wakeupgen_lock, flags); |
142 | _wakeupgen_clear(d->hwirq, irq_target_cpu[d->hwirq]); | 126 | _wakeupgen_clear(d->hwirq, irq_target_cpu[d->hwirq]); |
143 | raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); | 127 | raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); |
128 | irq_chip_mask_parent(d); | ||
144 | } | 129 | } |
145 | 130 | ||
146 | /* | 131 | /* |
@@ -153,6 +138,7 @@ static void wakeupgen_unmask(struct irq_data *d) | |||
153 | raw_spin_lock_irqsave(&wakeupgen_lock, flags); | 138 | raw_spin_lock_irqsave(&wakeupgen_lock, flags); |
154 | _wakeupgen_set(d->hwirq, irq_target_cpu[d->hwirq]); | 139 | _wakeupgen_set(d->hwirq, irq_target_cpu[d->hwirq]); |
155 | raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); | 140 | raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); |
141 | irq_chip_unmask_parent(d); | ||
156 | } | 142 | } |
157 | 143 | ||
158 | #ifdef CONFIG_HOTPLUG_CPU | 144 | #ifdef CONFIG_HOTPLUG_CPU |
@@ -400,15 +386,91 @@ int omap_secure_apis_support(void) | |||
400 | return omap_secure_apis; | 386 | return omap_secure_apis; |
401 | } | 387 | } |
402 | 388 | ||
389 | static struct irq_chip wakeupgen_chip = { | ||
390 | .name = "WUGEN", | ||
391 | .irq_eoi = irq_chip_eoi_parent, | ||
392 | .irq_mask = wakeupgen_mask, | ||
393 | .irq_unmask = wakeupgen_unmask, | ||
394 | .irq_retrigger = irq_chip_retrigger_hierarchy, | ||
395 | .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, | ||
396 | #ifdef CONFIG_SMP | ||
397 | .irq_set_affinity = irq_chip_set_affinity_parent, | ||
398 | #endif | ||
399 | }; | ||
400 | |||
401 | static int wakeupgen_domain_xlate(struct irq_domain *domain, | ||
402 | struct device_node *controller, | ||
403 | const u32 *intspec, | ||
404 | unsigned int intsize, | ||
405 | unsigned long *out_hwirq, | ||
406 | unsigned int *out_type) | ||
407 | { | ||
408 | if (domain->of_node != controller) | ||
409 | return -EINVAL; /* Shouldn't happen, really... */ | ||
410 | if (intsize != 3) | ||
411 | return -EINVAL; /* Not GIC compliant */ | ||
412 | if (intspec[0] != 0) | ||
413 | return -EINVAL; /* No PPI should point to this domain */ | ||
414 | |||
415 | *out_hwirq = intspec[1]; | ||
416 | *out_type = intspec[2]; | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static int wakeupgen_domain_alloc(struct irq_domain *domain, | ||
421 | unsigned int virq, | ||
422 | unsigned int nr_irqs, void *data) | ||
423 | { | ||
424 | struct of_phandle_args *args = data; | ||
425 | struct of_phandle_args parent_args; | ||
426 | irq_hw_number_t hwirq; | ||
427 | int i; | ||
428 | |||
429 | if (args->args_count != 3) | ||
430 | return -EINVAL; /* Not GIC compliant */ | ||
431 | if (args->args[0] != 0) | ||
432 | return -EINVAL; /* No PPI should point to this domain */ | ||
433 | |||
434 | hwirq = args->args[1]; | ||
435 | if (hwirq >= MAX_IRQS) | ||
436 | return -EINVAL; /* Can't deal with this */ | ||
437 | |||
438 | for (i = 0; i < nr_irqs; i++) | ||
439 | irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, | ||
440 | &wakeupgen_chip, NULL); | ||
441 | |||
442 | parent_args = *args; | ||
443 | parent_args.np = domain->parent->of_node; | ||
444 | return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args); | ||
445 | } | ||
446 | |||
447 | static struct irq_domain_ops wakeupgen_domain_ops = { | ||
448 | .xlate = wakeupgen_domain_xlate, | ||
449 | .alloc = wakeupgen_domain_alloc, | ||
450 | .free = irq_domain_free_irqs_common, | ||
451 | }; | ||
452 | |||
403 | /* | 453 | /* |
404 | * Initialise the wakeupgen module. | 454 | * Initialise the wakeupgen module. |
405 | */ | 455 | */ |
406 | int __init omap_wakeupgen_init(void) | 456 | static int __init wakeupgen_init(struct device_node *node, |
457 | struct device_node *parent) | ||
407 | { | 458 | { |
459 | struct irq_domain *parent_domain, *domain; | ||
408 | int i; | 460 | int i; |
409 | unsigned int boot_cpu = smp_processor_id(); | 461 | unsigned int boot_cpu = smp_processor_id(); |
410 | u32 val; | 462 | u32 val; |
411 | 463 | ||
464 | if (!parent) { | ||
465 | pr_err("%s: no parent, giving up\n", node->full_name); | ||
466 | return -ENODEV; | ||
467 | } | ||
468 | |||
469 | parent_domain = irq_find_host(parent); | ||
470 | if (!parent_domain) { | ||
471 | pr_err("%s: unable to obtain parent domain\n", node->full_name); | ||
472 | return -ENXIO; | ||
473 | } | ||
412 | /* Not supported on OMAP4 ES1.0 silicon */ | 474 | /* Not supported on OMAP4 ES1.0 silicon */ |
413 | if (omap_rev() == OMAP4430_REV_ES1_0) { | 475 | if (omap_rev() == OMAP4430_REV_ES1_0) { |
414 | WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n"); | 476 | WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n"); |
@@ -416,7 +478,7 @@ int __init omap_wakeupgen_init(void) | |||
416 | } | 478 | } |
417 | 479 | ||
418 | /* Static mapping, never released */ | 480 | /* Static mapping, never released */ |
419 | wakeupgen_base = ioremap(OMAP_WKUPGEN_BASE, SZ_4K); | 481 | wakeupgen_base = of_iomap(node, 0); |
420 | if (WARN_ON(!wakeupgen_base)) | 482 | if (WARN_ON(!wakeupgen_base)) |
421 | return -ENOMEM; | 483 | return -ENOMEM; |
422 | 484 | ||
@@ -429,6 +491,14 @@ int __init omap_wakeupgen_init(void) | |||
429 | max_irqs = AM43XX_IRQS; | 491 | max_irqs = AM43XX_IRQS; |
430 | } | 492 | } |
431 | 493 | ||
494 | domain = irq_domain_add_hierarchy(parent_domain, 0, max_irqs, | ||
495 | node, &wakeupgen_domain_ops, | ||
496 | NULL); | ||
497 | if (!domain) { | ||
498 | iounmap(wakeupgen_base); | ||
499 | return -ENOMEM; | ||
500 | } | ||
501 | |||
432 | /* Clear all IRQ bitmasks at wakeupGen level */ | 502 | /* Clear all IRQ bitmasks at wakeupGen level */ |
433 | for (i = 0; i < irq_banks; i++) { | 503 | for (i = 0; i < irq_banks; i++) { |
434 | wakeupgen_writel(0, i, CPU0_ID); | 504 | wakeupgen_writel(0, i, CPU0_ID); |
@@ -437,14 +507,6 @@ int __init omap_wakeupgen_init(void) | |||
437 | } | 507 | } |
438 | 508 | ||
439 | /* | 509 | /* |
440 | * Override GIC architecture specific functions to add | ||
441 | * OMAP WakeupGen interrupt controller along with GIC | ||
442 | */ | ||
443 | gic_arch_extn.irq_mask = wakeupgen_mask; | ||
444 | gic_arch_extn.irq_unmask = wakeupgen_unmask; | ||
445 | gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE; | ||
446 | |||
447 | /* | ||
448 | * FIXME: Add support to set_smp_affinity() once the core | 510 | * FIXME: Add support to set_smp_affinity() once the core |
449 | * GIC code has necessary hooks in place. | 511 | * GIC code has necessary hooks in place. |
450 | */ | 512 | */ |
@@ -474,3 +536,9 @@ int __init omap_wakeupgen_init(void) | |||
474 | 536 | ||
475 | return 0; | 537 | return 0; |
476 | } | 538 | } |
539 | |||
540 | /* | ||
541 | * We cannot use the IRQCHIP_DECLARE macro that lives in | ||
542 | * drivers/irqchip, so we're forced to roll our own. Not very nice. | ||
543 | */ | ||
544 | OF_DECLARE_2(irqchip, ti_wakeupgen, "ti,omap4-wugen-mpu", wakeupgen_init); | ||