interrupts.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * (C) Copyright 2000-2002
  4. * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
  5. */
  6. #include <common.h>
  7. #include <mpc8xx.h>
  8. #include <mpc8xx_irq.h>
  9. #include <asm/cpm_8xx.h>
  10. #include <asm/processor.h>
  11. #include <asm/io.h>
  12. /************************************************************************/
  13. /*
  14. * CPM interrupt vector functions.
  15. */
  16. struct interrupt_action {
  17. interrupt_handler_t *handler;
  18. void *arg;
  19. };
  20. static struct interrupt_action cpm_vecs[CPMVEC_NR];
  21. static struct interrupt_action irq_vecs[NR_IRQS];
  22. static void cpm_interrupt_init(void);
  23. static void cpm_interrupt(void *regs);
  24. /************************************************************************/
  25. void interrupt_init_cpu(unsigned *decrementer_count)
  26. {
  27. immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
  28. *decrementer_count = get_tbclk() / CONFIG_SYS_HZ;
  29. /* disable all interrupts */
  30. out_be32(&immr->im_siu_conf.sc_simask, 0);
  31. /* Configure CPM interrupts */
  32. cpm_interrupt_init();
  33. }
  34. /************************************************************************/
  35. /*
  36. * Handle external interrupts
  37. */
  38. void external_interrupt(struct pt_regs *regs)
  39. {
  40. immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
  41. int irq;
  42. ulong simask;
  43. ulong vec, v_bit;
  44. /*
  45. * read the SIVEC register and shift the bits down
  46. * to get the irq number
  47. */
  48. vec = in_be32(&immr->im_siu_conf.sc_sivec);
  49. irq = vec >> 26;
  50. v_bit = 0x80000000UL >> irq;
  51. /*
  52. * Read Interrupt Mask Register and Mask Interrupts
  53. */
  54. simask = in_be32(&immr->im_siu_conf.sc_simask);
  55. clrbits_be32(&immr->im_siu_conf.sc_simask, 0xFFFF0000 >> irq);
  56. if (!(irq & 0x1)) { /* External Interrupt ? */
  57. ulong siel;
  58. /*
  59. * Read Interrupt Edge/Level Register
  60. */
  61. siel = in_be32(&immr->im_siu_conf.sc_siel);
  62. if (siel & v_bit) { /* edge triggered interrupt ? */
  63. /*
  64. * Rewrite SIPEND Register to clear interrupt
  65. */
  66. out_be32(&immr->im_siu_conf.sc_sipend, v_bit);
  67. }
  68. }
  69. if (irq_vecs[irq].handler != NULL) {
  70. irq_vecs[irq].handler(irq_vecs[irq].arg);
  71. } else {
  72. printf("\nBogus External Interrupt IRQ %d Vector %ld\n",
  73. irq, vec);
  74. /* turn off the bogus interrupt to avoid it from now */
  75. simask &= ~v_bit;
  76. }
  77. /*
  78. * Re-Enable old Interrupt Mask
  79. */
  80. out_be32(&immr->im_siu_conf.sc_simask, simask);
  81. }
  82. /************************************************************************/
  83. /*
  84. * CPM interrupt handler
  85. */
  86. static void cpm_interrupt(void *regs)
  87. {
  88. immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
  89. uint vec;
  90. /*
  91. * Get the vector by setting the ACK bit
  92. * and then reading the register.
  93. */
  94. out_be16(&immr->im_cpic.cpic_civr, 1);
  95. vec = in_be16(&immr->im_cpic.cpic_civr);
  96. vec >>= 11;
  97. if (cpm_vecs[vec].handler != NULL) {
  98. (*cpm_vecs[vec].handler) (cpm_vecs[vec].arg);
  99. } else {
  100. clrbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec);
  101. printf("Masking bogus CPM interrupt vector 0x%x\n", vec);
  102. }
  103. /*
  104. * After servicing the interrupt,
  105. * we have to remove the status indicator.
  106. */
  107. setbits_be32(&immr->im_cpic.cpic_cisr, 1 << vec);
  108. }
  109. /*
  110. * The CPM can generate the error interrupt when there is a race
  111. * condition between generating and masking interrupts. All we have
  112. * to do is ACK it and return. This is a no-op function so we don't
  113. * need any special tests in the interrupt handler.
  114. */
  115. static void cpm_error_interrupt(void *dummy)
  116. {
  117. }
  118. /************************************************************************/
  119. /*
  120. * Install and free an interrupt handler
  121. */
  122. void irq_install_handler(int vec, interrupt_handler_t *handler, void *arg)
  123. {
  124. immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
  125. if ((vec & CPMVEC_OFFSET) != 0) {
  126. /* CPM interrupt */
  127. vec &= 0xffff;
  128. if (cpm_vecs[vec].handler != NULL)
  129. printf("CPM interrupt 0x%x replacing 0x%x\n",
  130. (uint)handler, (uint)cpm_vecs[vec].handler);
  131. cpm_vecs[vec].handler = handler;
  132. cpm_vecs[vec].arg = arg;
  133. setbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec);
  134. } else {
  135. /* SIU interrupt */
  136. if (irq_vecs[vec].handler != NULL)
  137. printf("SIU interrupt %d 0x%x replacing 0x%x\n",
  138. vec, (uint)handler, (uint)cpm_vecs[vec].handler);
  139. irq_vecs[vec].handler = handler;
  140. irq_vecs[vec].arg = arg;
  141. setbits_be32(&immr->im_siu_conf.sc_simask, 1 << (31 - vec));
  142. }
  143. }
  144. void irq_free_handler(int vec)
  145. {
  146. immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
  147. if ((vec & CPMVEC_OFFSET) != 0) {
  148. /* CPM interrupt */
  149. vec &= 0xffff;
  150. clrbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec);
  151. cpm_vecs[vec].handler = NULL;
  152. cpm_vecs[vec].arg = NULL;
  153. } else {
  154. /* SIU interrupt */
  155. clrbits_be32(&immr->im_siu_conf.sc_simask, 1 << (31 - vec));
  156. irq_vecs[vec].handler = NULL;
  157. irq_vecs[vec].arg = NULL;
  158. }
  159. }
  160. /************************************************************************/
  161. static void cpm_interrupt_init(void)
  162. {
  163. immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
  164. uint cicr;
  165. /*
  166. * Initialize the CPM interrupt controller.
  167. */
  168. cicr = CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1 |
  169. ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK;
  170. out_be32(&immr->im_cpic.cpic_cicr, cicr);
  171. out_be32(&immr->im_cpic.cpic_cimr, 0);
  172. /*
  173. * Install the error handler.
  174. */
  175. irq_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL);
  176. setbits_be32(&immr->im_cpic.cpic_cicr, CICR_IEN);
  177. /*
  178. * Install the cpm interrupt handler
  179. */
  180. irq_install_handler(CPM_INTERRUPT, cpm_interrupt, NULL);
  181. }
  182. /************************************************************************/
  183. /*
  184. * timer_interrupt - gets called when the decrementer overflows,
  185. * with interrupts disabled.
  186. * Trivial implementation - no need to be really accurate.
  187. */
  188. void timer_interrupt_cpu(struct pt_regs *regs)
  189. {
  190. immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
  191. /* Reset Timer Expired and Timers Interrupt Status */
  192. out_be32(&immr->im_clkrstk.cark_plprcrk, KAPWR_KEY);
  193. __asm__ ("nop");
  194. /*
  195. Clear TEXPS (and TMIST on older chips). SPLSS (on older
  196. chips) is cleared too.
  197. Bitwise OR is a read-modify-write operation so ALL bits
  198. which are cleared by writing `1' would be cleared by
  199. operations like
  200. immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS;
  201. The same can be achieved by simple writing of the PLPRCR
  202. to itself. If a bit value should be preserved, read the
  203. register, ZERO the bit and write, not OR, the result back.
  204. */
  205. setbits_be32(&immr->im_clkrst.car_plprcr, 0);
  206. }
  207. /************************************************************************/