plic.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. * Copyright (c) 2006-2021, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2021-10-19 JasonHu first version
  9. * 2021-11-12 JasonHu fix bug that not intr on f133
  10. */
  11. #include <rtthread.h>
  12. #include <rtdbg.h>
  13. #include "plic.h"
  14. #include "rt_interrupt.h"
  15. #include "io.h"
  16. #include "encoding.h"
  17. static void *c906_plic_regs = RT_NULL;
  18. extern struct rt_irq_desc isr_table[];
  19. struct plic_handler
  20. {
  21. rt_bool_t present;
  22. void *hart_base;
  23. void *enable_base;
  24. };
  25. rt_inline void plic_toggle(struct plic_handler *handler, int hwirq, int enable);
  26. struct plic_handler c906_plic_handlers[C906_NR_CPUS];
  27. rt_inline void plic_irq_toggle(int hwirq, int enable)
  28. {
  29. int cpu = 0;
  30. /* set priority of interrupt, interrupt 0 is zero. */
  31. writel(enable, c906_plic_regs + PRIORITY_BASE + hwirq * PRIORITY_PER_ID);
  32. struct plic_handler *handler = &c906_plic_handlers[cpu];
  33. if (handler->present)
  34. {
  35. plic_toggle(handler, hwirq, enable);
  36. }
  37. }
  38. static void generic_handle_irq(int irq)
  39. {
  40. rt_isr_handler_t isr;
  41. void *param;
  42. if (irq < 0 || irq >= IRQ_MAX_NR)
  43. {
  44. LOG_E("bad irq number %d!\n", irq);
  45. return;
  46. }
  47. if (!irq) // irq = 0 => no irq
  48. {
  49. LOG_W("no irq!\n");
  50. return;
  51. }
  52. isr = isr_table[IRQ_OFFSET + irq].handler;
  53. param = isr_table[IRQ_OFFSET + irq].param;
  54. if (isr != RT_NULL)
  55. {
  56. isr(irq, param);
  57. }
  58. /* complete irq. */
  59. plic_complete(irq);
  60. }
  61. void plic_complete(int irqno)
  62. {
  63. int cpu = 0;
  64. struct plic_handler *handler = &c906_plic_handlers[cpu];
  65. writel(irqno, handler->hart_base + CONTEXT_CLAIM);
  66. }
  67. void plic_disable_irq(int irqno)
  68. {
  69. plic_irq_toggle(irqno, 0);
  70. }
  71. void plic_enable_irq(int irqno)
  72. {
  73. plic_irq_toggle(irqno, 1);
  74. }
  75. /*
  76. * Handling an interrupt is a two-step process: first you claim the interrupt
  77. * by reading the claim register, then you complete the interrupt by writing
  78. * that source ID back to the same claim register. This automatically enables
  79. * and disables the interrupt, so there's nothing else to do.
  80. */
  81. void plic_handle_irq(void)
  82. {
  83. int cpu = 0;
  84. unsigned int irq;
  85. struct plic_handler *handler = &c906_plic_handlers[cpu];
  86. void *claim = handler->hart_base + CONTEXT_CLAIM;
  87. if (c906_plic_regs == RT_NULL || !handler->present)
  88. {
  89. LOG_E("plic state not initialized.");
  90. return;
  91. }
  92. clear_csr(sie, SIE_SEIE);
  93. while ((irq = readl(claim)))
  94. {
  95. /* ID0 is diabled permantually from spec. */
  96. if (irq == 0)
  97. {
  98. LOG_E("irq no is zero.");
  99. }
  100. else
  101. {
  102. generic_handle_irq(irq);
  103. }
  104. }
  105. set_csr(sie, SIE_SEIE);
  106. }
  107. rt_inline void plic_toggle(struct plic_handler *handler, int hwirq, int enable)
  108. {
  109. uint32_t *reg = handler->enable_base + (hwirq / 32) * sizeof(uint32_t);
  110. uint32_t hwirq_mask = 1 << (hwirq % 32);
  111. if (enable)
  112. {
  113. writel(readl(reg) | hwirq_mask, reg);
  114. }
  115. else
  116. {
  117. writel(readl(reg) & ~hwirq_mask, reg);
  118. }
  119. }
  120. void plic_init(void)
  121. {
  122. int nr_irqs;
  123. int nr_context;
  124. int i;
  125. unsigned long hwirq;
  126. int cpu = 0;
  127. if (c906_plic_regs)
  128. {
  129. LOG_E("plic already initialized!");
  130. return;
  131. }
  132. nr_context = C906_NR_CONTEXT;
  133. c906_plic_regs = (void *)C906_PLIC_PHY_ADDR;
  134. if (!c906_plic_regs)
  135. {
  136. LOG_E("fatal error, plic is reg space is null.");
  137. return;
  138. }
  139. nr_irqs = C906_PLIC_NR_EXT_IRQS;
  140. for (i = 0; i < nr_context; i ++)
  141. {
  142. struct plic_handler *handler;
  143. uint32_t threshold = 0;
  144. cpu = 0;
  145. /* skip contexts other than supervisor external interrupt */
  146. if (i == 0)
  147. {
  148. continue;
  149. }
  150. // we always use CPU0 M-mode target register.
  151. handler = &c906_plic_handlers[cpu];
  152. if (handler->present)
  153. {
  154. threshold = 0xffffffff;
  155. goto done;
  156. }
  157. handler->present = RT_TRUE;
  158. handler->hart_base = c906_plic_regs + CONTEXT_BASE + i * CONTEXT_PER_HART;
  159. handler->enable_base = c906_plic_regs + ENABLE_BASE + i * ENABLE_PER_HART;
  160. done:
  161. /* priority must be > threshold to trigger an interrupt */
  162. writel(threshold, handler->hart_base + CONTEXT_THRESHOLD);
  163. for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
  164. {
  165. plic_toggle(handler, hwirq, 0);
  166. }
  167. }
  168. /* Enable supervisor external interrupts. */
  169. set_csr(sie, SIE_SEIE);
  170. }