beat_interrupt.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*
  2. * Celleb/Beat Interrupt controller
  3. *
  4. * (C) Copyright 2006-2007 TOSHIBA CORPORATION
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. */
  20. #include <linux/init.h>
  21. #include <linux/interrupt.h>
  22. #include <linux/irq.h>
  23. #include <linux/percpu.h>
  24. #include <linux/types.h>
  25. #include <asm/machdep.h>
  26. #include "beat_interrupt.h"
  27. #include "beat_wrapper.h"
  28. #define MAX_IRQS NR_IRQS
  29. static DEFINE_SPINLOCK(beatic_irq_mask_lock);
  30. static uint64_t beatic_irq_mask_enable[(MAX_IRQS+255)/64];
  31. static uint64_t beatic_irq_mask_ack[(MAX_IRQS+255)/64];
  32. static struct irq_host *beatic_host;
  33. /*
  34. * In this implementation, "virq" == "IRQ plug number",
  35. * "(irq_hw_number_t)hwirq" == "IRQ outlet number".
  36. */
  37. /* assumption: locked */
  38. static inline void beatic_update_irq_mask(unsigned int irq_plug)
  39. {
  40. int off;
  41. unsigned long masks[4];
  42. off = (irq_plug / 256) * 4;
  43. masks[0] = beatic_irq_mask_enable[off + 0]
  44. & beatic_irq_mask_ack[off + 0];
  45. masks[1] = beatic_irq_mask_enable[off + 1]
  46. & beatic_irq_mask_ack[off + 1];
  47. masks[2] = beatic_irq_mask_enable[off + 2]
  48. & beatic_irq_mask_ack[off + 2];
  49. masks[3] = beatic_irq_mask_enable[off + 3]
  50. & beatic_irq_mask_ack[off + 3];
  51. if (beat_set_interrupt_mask(irq_plug&~255UL,
  52. masks[0], masks[1], masks[2], masks[3]) != 0)
  53. panic("Failed to set mask IRQ!");
  54. }
  55. static void beatic_mask_irq(unsigned int irq_plug)
  56. {
  57. unsigned long flags;
  58. spin_lock_irqsave(&beatic_irq_mask_lock, flags);
  59. beatic_irq_mask_enable[irq_plug/64] &= ~(1UL << (63 - (irq_plug%64)));
  60. beatic_update_irq_mask(irq_plug);
  61. spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);
  62. }
  63. static void beatic_unmask_irq(unsigned int irq_plug)
  64. {
  65. unsigned long flags;
  66. spin_lock_irqsave(&beatic_irq_mask_lock, flags);
  67. beatic_irq_mask_enable[irq_plug/64] |= 1UL << (63 - (irq_plug%64));
  68. beatic_update_irq_mask(irq_plug);
  69. spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);
  70. }
  71. static void beatic_ack_irq(unsigned int irq_plug)
  72. {
  73. unsigned long flags;
  74. spin_lock_irqsave(&beatic_irq_mask_lock, flags);
  75. beatic_irq_mask_ack[irq_plug/64] &= ~(1UL << (63 - (irq_plug%64)));
  76. beatic_update_irq_mask(irq_plug);
  77. spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);
  78. }
  79. static void beatic_end_irq(unsigned int irq_plug)
  80. {
  81. s64 err;
  82. unsigned long flags;
  83. err = beat_downcount_of_interrupt(irq_plug);
  84. if (err != 0) {
  85. if ((err & 0xFFFFFFFF) != 0xFFFFFFF5) /* -11: wrong state */
  86. panic("Failed to downcount IRQ! Error = %16llx", err);
  87. printk(KERN_ERR "IRQ over-downcounted, plug %d\n", irq_plug);
  88. }
  89. spin_lock_irqsave(&beatic_irq_mask_lock, flags);
  90. beatic_irq_mask_ack[irq_plug/64] |= 1UL << (63 - (irq_plug%64));
  91. beatic_update_irq_mask(irq_plug);
  92. spin_unlock_irqrestore(&beatic_irq_mask_lock, flags);
  93. }
  94. static struct irq_chip beatic_pic = {
  95. .typename = " CELL-BEAT ",
  96. .unmask = beatic_unmask_irq,
  97. .mask = beatic_mask_irq,
  98. .eoi = beatic_end_irq,
  99. };
  100. /*
  101. * Dispose binding hardware IRQ number (hw) and Virtuql IRQ number (virq),
  102. * update flags.
  103. *
  104. * Note that the number (virq) is already assigned at upper layer.
  105. */
  106. static void beatic_pic_host_unmap(struct irq_host *h, unsigned int virq)
  107. {
  108. beat_destruct_irq_plug(virq);
  109. }
  110. /*
  111. * Create or update binding hardware IRQ number (hw) and Virtuql
  112. * IRQ number (virq). This is called only once for a given mapping.
  113. *
  114. * Note that the number (virq) is already assigned at upper layer.
  115. */
  116. static int beatic_pic_host_map(struct irq_host *h, unsigned int virq,
  117. irq_hw_number_t hw)
  118. {
  119. struct irq_desc *desc = get_irq_desc(virq);
  120. int64_t err;
  121. err = beat_construct_and_connect_irq_plug(virq, hw);
  122. if (err < 0)
  123. return -EIO;
  124. desc->status |= IRQ_LEVEL;
  125. set_irq_chip_and_handler(virq, &beatic_pic, handle_fasteoi_irq);
  126. return 0;
  127. }
  128. /*
  129. * Update binding hardware IRQ number (hw) and Virtuql
  130. * IRQ number (virq). This is called only once for a given mapping.
  131. */
  132. static void beatic_pic_host_remap(struct irq_host *h, unsigned int virq,
  133. irq_hw_number_t hw)
  134. {
  135. beat_construct_and_connect_irq_plug(virq, hw);
  136. }
  137. /*
  138. * Translate device-tree interrupt spec to irq_hw_number_t style (ulong),
  139. * to pass away to irq_create_mapping().
  140. *
  141. * Called from irq_create_of_mapping() only.
  142. * Note: We have only 1 entry to translate.
  143. */
  144. static int beatic_pic_host_xlate(struct irq_host *h, struct device_node *ct,
  145. u32 *intspec, unsigned int intsize,
  146. irq_hw_number_t *out_hwirq,
  147. unsigned int *out_flags)
  148. {
  149. u64 *intspec2 = (u64 *)intspec;
  150. *out_hwirq = *intspec2;
  151. *out_flags |= IRQ_TYPE_LEVEL_LOW;
  152. return 0;
  153. }
  154. static int beatic_pic_host_match(struct irq_host *h, struct device_node *np)
  155. {
  156. /* Match all */
  157. return 1;
  158. }
  159. static struct irq_host_ops beatic_pic_host_ops = {
  160. .map = beatic_pic_host_map,
  161. .remap = beatic_pic_host_remap,
  162. .unmap = beatic_pic_host_unmap,
  163. .xlate = beatic_pic_host_xlate,
  164. .match = beatic_pic_host_match,
  165. };
  166. /*
  167. * Get an IRQ number
  168. * Note: returns VIRQ
  169. */
  170. static inline unsigned int beatic_get_irq_plug(void)
  171. {
  172. int i;
  173. uint64_t pending[4], ub;
  174. for (i = 0; i < MAX_IRQS; i += 256) {
  175. beat_detect_pending_interrupts(i, pending);
  176. __asm__ ("cntlzd %0,%1":"=r"(ub):
  177. "r"(pending[0] & beatic_irq_mask_enable[i/64+0]
  178. & beatic_irq_mask_ack[i/64+0]));
  179. if (ub != 64)
  180. return i + ub + 0;
  181. __asm__ ("cntlzd %0,%1":"=r"(ub):
  182. "r"(pending[1] & beatic_irq_mask_enable[i/64+1]
  183. & beatic_irq_mask_ack[i/64+1]));
  184. if (ub != 64)
  185. return i + ub + 64;
  186. __asm__ ("cntlzd %0,%1":"=r"(ub):
  187. "r"(pending[2] & beatic_irq_mask_enable[i/64+2]
  188. & beatic_irq_mask_ack[i/64+2]));
  189. if (ub != 64)
  190. return i + ub + 128;
  191. __asm__ ("cntlzd %0,%1":"=r"(ub):
  192. "r"(pending[3] & beatic_irq_mask_enable[i/64+3]
  193. & beatic_irq_mask_ack[i/64+3]));
  194. if (ub != 64)
  195. return i + ub + 192;
  196. }
  197. return NO_IRQ;
  198. }
  199. unsigned int beatic_get_irq(void)
  200. {
  201. unsigned int ret;
  202. ret = beatic_get_irq_plug();
  203. if (ret != NO_IRQ)
  204. beatic_ack_irq(ret);
  205. return ret;
  206. }
  207. /*
  208. */
  209. void __init beatic_init_IRQ(void)
  210. {
  211. int i;
  212. memset(beatic_irq_mask_enable, 0, sizeof(beatic_irq_mask_enable));
  213. memset(beatic_irq_mask_ack, 255, sizeof(beatic_irq_mask_ack));
  214. for (i = 0; i < MAX_IRQS; i += 256)
  215. beat_set_interrupt_mask(i, 0L, 0L, 0L, 0L);
  216. /* Set out get_irq function */
  217. ppc_md.get_irq = beatic_get_irq;
  218. /* Allocate an irq host */
  219. beatic_host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0,
  220. &beatic_pic_host_ops,
  221. 0);
  222. BUG_ON(beatic_host == NULL);
  223. irq_set_default_host(beatic_host);
  224. }
  225. #ifdef CONFIG_SMP
  226. /* Nullified to compile with SMP mode */
  227. void beatic_setup_cpu(int cpu)
  228. {
  229. }
  230. void beatic_cause_IPI(int cpu, int mesg)
  231. {
  232. }
  233. void beatic_request_IPIs(void)
  234. {
  235. }
  236. #endif /* CONFIG_SMP */
  237. void beatic_deinit_IRQ(void)
  238. {
  239. int i;
  240. for (i = 1; i < NR_IRQS; i++)
  241. beat_destruct_irq_plug(i);
  242. }