irq-armada-370-xp.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Marvell Armada 370 and Armada XP SoC IRQ handling
  3. *
  4. * Copyright (C) 2012 Marvell
  5. *
  6. * Lior Amsalem <alior@marvell.com>
  7. * Gregory CLEMENT <gregory.clement@free-electrons.com>
  8. * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
  9. * Ben Dooks <ben.dooks@codethink.co.uk>
  10. *
  11. * This file is licensed under the terms of the GNU General Public
  12. * License version 2. This program is licensed "as is" without any
  13. * warranty of any kind, whether express or implied.
  14. */
  15. #include <linux/kernel.h>
  16. #include <linux/module.h>
  17. #include <linux/init.h>
  18. #include <linux/irq.h>
  19. #include <linux/interrupt.h>
  20. #include <linux/io.h>
  21. #include <linux/of_address.h>
  22. #include <linux/of_irq.h>
  23. #include <linux/irqdomain.h>
  24. #include <asm/mach/arch.h>
  25. #include <asm/exception.h>
  26. #include <asm/smp_plat.h>
  27. /* Interrupt Controller Registers Map */
  28. #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
  29. #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
  30. #define ARMADA_370_XP_INT_CONTROL (0x00)
  31. #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30)
  32. #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34)
  33. #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
  34. #define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4)
  35. #define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc)
  36. #define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8)
  37. #define ACTIVE_DOORBELLS (8)
  38. static void __iomem *per_cpu_int_base;
  39. static void __iomem *main_int_base;
  40. static struct irq_domain *armada_370_xp_mpic_domain;
  41. static void armada_370_xp_irq_mask(struct irq_data *d)
  42. {
  43. writel(irqd_to_hwirq(d),
  44. per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS);
  45. }
  46. static void armada_370_xp_irq_unmask(struct irq_data *d)
  47. {
  48. writel(irqd_to_hwirq(d),
  49. per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
  50. }
  51. #ifdef CONFIG_SMP
  52. static int armada_xp_set_affinity(struct irq_data *d,
  53. const struct cpumask *mask_val, bool force)
  54. {
  55. return 0;
  56. }
  57. #endif
  58. static struct irq_chip armada_370_xp_irq_chip = {
  59. .name = "armada_370_xp_irq",
  60. .irq_mask = armada_370_xp_irq_mask,
  61. .irq_mask_ack = armada_370_xp_irq_mask,
  62. .irq_unmask = armada_370_xp_irq_unmask,
  63. #ifdef CONFIG_SMP
  64. .irq_set_affinity = armada_xp_set_affinity,
  65. #endif
  66. };
  67. static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
  68. unsigned int virq, irq_hw_number_t hw)
  69. {
  70. armada_370_xp_irq_mask(irq_get_irq_data(virq));
  71. writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
  72. irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
  73. handle_level_irq);
  74. irq_set_status_flags(virq, IRQ_LEVEL);
  75. set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
  76. return 0;
  77. }
  78. #ifdef CONFIG_SMP
  79. void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq)
  80. {
  81. int cpu;
  82. unsigned long map = 0;
  83. /* Convert our logical CPU mask into a physical one. */
  84. for_each_cpu(cpu, mask)
  85. map |= 1 << cpu_logical_map(cpu);
  86. /*
  87. * Ensure that stores to Normal memory are visible to the
  88. * other CPUs before issuing the IPI.
  89. */
  90. dsb();
  91. /* submit softirq */
  92. writel((map << 8) | irq, main_int_base +
  93. ARMADA_370_XP_SW_TRIG_INT_OFFS);
  94. }
  95. void armada_xp_mpic_smp_cpu_init(void)
  96. {
  97. /* Clear pending IPIs */
  98. writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
  99. /* Enable first 8 IPIs */
  100. writel((1 << ACTIVE_DOORBELLS) - 1, per_cpu_int_base +
  101. ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
  102. /* Unmask IPI interrupt */
  103. writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
  104. }
  105. #endif /* CONFIG_SMP */
  106. static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
  107. .map = armada_370_xp_mpic_irq_map,
  108. .xlate = irq_domain_xlate_onecell,
  109. };
  110. static int __init armada_370_xp_mpic_of_init(struct device_node *node,
  111. struct device_node *parent)
  112. {
  113. u32 control;
  114. main_int_base = of_iomap(node, 0);
  115. per_cpu_int_base = of_iomap(node, 1);
  116. BUG_ON(!main_int_base);
  117. BUG_ON(!per_cpu_int_base);
  118. control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
  119. armada_370_xp_mpic_domain =
  120. irq_domain_add_linear(node, (control >> 2) & 0x3ff,
  121. &armada_370_xp_mpic_irq_ops, NULL);
  122. if (!armada_370_xp_mpic_domain)
  123. panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n");
  124. irq_set_default_host(armada_370_xp_mpic_domain);
  125. #ifdef CONFIG_SMP
  126. armada_xp_mpic_smp_cpu_init();
  127. #endif
  128. return 0;
  129. }
  130. asmlinkage void __exception_irq_entry armada_370_xp_handle_irq(struct pt_regs
  131. *regs)
  132. {
  133. u32 irqstat, irqnr;
  134. do {
  135. irqstat = readl_relaxed(per_cpu_int_base +
  136. ARMADA_370_XP_CPU_INTACK_OFFS);
  137. irqnr = irqstat & 0x3FF;
  138. if (irqnr > 1022)
  139. break;
  140. if (irqnr >= 8) {
  141. irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
  142. irqnr);
  143. handle_IRQ(irqnr, regs);
  144. continue;
  145. }
  146. #ifdef CONFIG_SMP
  147. /* IPI Handling */
  148. if (irqnr == 0) {
  149. u32 ipimask, ipinr;
  150. ipimask = readl_relaxed(per_cpu_int_base +
  151. ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
  152. & 0xFF;
  153. writel(0x0, per_cpu_int_base +
  154. ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
  155. /* Handle all pending doorbells */
  156. for (ipinr = 0; ipinr < ACTIVE_DOORBELLS; ipinr++) {
  157. if (ipimask & (0x1 << ipinr))
  158. handle_IPI(ipinr, regs);
  159. }
  160. continue;
  161. }
  162. #endif
  163. } while (1);
  164. }
  165. static const struct of_device_id mpic_of_match[] __initconst = {
  166. {.compatible = "marvell,mpic", .data = armada_370_xp_mpic_of_init},
  167. {},
  168. };
  169. void __init armada_370_xp_init_irq(void)
  170. {
  171. of_irq_init(mpic_of_match);
  172. }