plic.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. * Copyright (c) 2006-2025 RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Support for RISC-V Platform-Level Interrupt Controller(PLIC)
  7. * Specification Version 1.0.0, which is currently(3/12/2023) the
  8. * lastest draft.
  9. *
  10. * Change Logs:
  11. * Date Author Notes
  12. * 2021-05-20 bigmagic first version
  13. * 2022-09-16 WangXiaoyao Porting to rv64
  14. * 2025-01-26 ZhangJing Porting to ultrarisc cp100
  15. */
  16. #include <rthw.h>
  17. #include <rtthread.h>
  18. #include <stdint.h>
  19. #include "plic.h"
  20. #include <io.h>
  21. #include "encoding.h"
  22. #include <interrupt.h>
  23. #include <riscv_io.h>
  24. #include <riscv.h>
  25. #include <string.h>
  26. #include <stdlib.h>
  27. #ifdef RT_USING_SMART
  28. #include <ioremap.h>
  29. #else
  30. #define rt_ioremap(addr, ...) (addr)
  31. #endif
  32. /* plic_base should be initialized in the bsp*/
  33. size_t plic_base = 0 ;
  34. /*
  35. * Each PLIC interrupt source can be assigned a priority by writing
  36. * to its 32-bit memory-mapped priority register.
  37. * Maximum priority is 7.
  38. * A priority value of 0 is reserved to mean "never interrupt" and
  39. * effectively disables the interrupt.
  40. * Ties between global interrupts of the same priority are broken by
  41. * the Interrupt ID; interrupts with the lowest ID have the highest
  42. * effective priority.
  43. */
  44. void plic_set_priority(int irq, int priority)
  45. {
  46. writel(priority, (void *)PLIC_PRIORITY(irq));
  47. }
  48. /*
  49. * Each global interrupt can be enabled by setting the corresponding
  50. * bit in the enables registers.
  51. */
  52. void plic_irq_enable(int irq)
  53. {
  54. int hart = __raw_hartid();
  55. void *enable = (void *)PLIC_ENABLE(hart) + (irq / 32) * sizeof(uint32_t);
  56. rt_uint32_t val = readl(enable);
  57. val |= (1 << (irq % 32));
  58. writel(val, enable);
  59. return;
  60. }
  61. /*
  62. * Each global interrupt can be disabled by clearing the corresponding
  63. * bit in the enables registers.
  64. */
  65. void plic_irq_disable(int irq)
  66. {
  67. int hart = __raw_hartid();
  68. void *enable = (void *)PLIC_ENABLE(hart) + (irq / 32) * sizeof(uint32_t);
  69. rt_uint32_t val = readl(enable);
  70. val &= ~(1 << (irq % 32));
  71. writel(val, enable);
  72. return;
  73. }
  74. /*
  75. * PLIC will mask all interrupts of a priority less than or equal to threshold.
  76. * Maximum threshold is 7.
  77. * For example, a threshold value of zero permits all interrupts with
  78. * non-zero priority, whereas a value of 7 masks all interrupts.
  79. * Notice, the threshold is global for PLIC, not for each interrupt source.
  80. */
  81. void plic_set_threshold(int threshold)
  82. {
  83. int hart = __raw_hartid();
  84. writel(threshold, (void *)PLIC_THRESHOLD(hart));
  85. return;
  86. }
  87. /*
  88. * DESCRIPTION:
  89. * Query the PLIC what interrupt we should serve.
  90. * Perform an interrupt claim by reading the claim register, which
  91. * returns the ID of the highest-priority pending interrupt or zero if there
  92. * is no pending interrupt.
  93. * A successful claim also atomically clears the corresponding pending bit
  94. * on the interrupt source.
  95. * RETURN VALUE:
  96. * the ID of the highest-priority pending interrupt or zero if there
  97. * is no pending interrupt.
  98. */
  99. rt_uint32_t plic_claim(void)
  100. {
  101. int hart = __raw_hartid();
  102. void *claim = (void *)PLIC_CLAIM(hart);
  103. return readl(claim);
  104. }
  105. /*
  106. * DESCRIPTION:
  107. * Writing the interrupt ID it received from the claim (irq) to the
  108. * complete register would signal the PLIC we've served this IRQ.
  109. * The PLIC does not check whether the completion ID is the same as the
  110. * last claim ID for that target. If the completion ID does not match an
  111. * interrupt source that is currently enabled for the target, the completion
  112. * is silently ignored.
  113. * RETURN VALUE: none
  114. */
  115. void plic_complete(int irq)
  116. {
  117. int hart = __raw_hartid();
  118. void *complete = (void *)PLIC_COMPLETE(hart);
  119. writel(irq, complete);
  120. return;
  121. }
  122. void plic_init()
  123. {
  124. if (!plic_base)
  125. {
  126. return;
  127. }
  128. /* PLIC takes up 64 MB space */
  129. plic_base = (size_t)rt_ioremap((void *)plic_base, 64 * 1024 * 1024);
  130. plic_set_threshold(0);
  131. /*set the same priority for all the irqs*/
  132. for (int i = 1; i < CONFIG_IRQ_NR; i++)
  133. {
  134. plic_set_priority(i, 1);
  135. }
  136. /*disable all interrupts*/
  137. for (int i = 1; i < CONFIG_IRQ_NR; i++)
  138. {
  139. plic_irq_disable(i);
  140. }
  141. set_csr(sie, read_csr(sie) | MIP_SEIP);
  142. return;
  143. }
  144. extern struct rt_irq_desc irq_desc[MAX_HANDLERS];
  145. #ifdef BOARD_UR_DP1000
  146. int find_first_bit(rt_uint32_t *addr, int size)
  147. {
  148. int i;
  149. for (i = 0; i < size; i++)
  150. {
  151. if (*addr & (1 << i))
  152. return i;
  153. }
  154. return -1;
  155. }
  156. static rt_bool_t is_irqs_pending(rt_uint32_t ie[])
  157. {
  158. int hartid = __raw_hartid();
  159. int nr_irqs = CONFIG_IRQ_NR;
  160. int nr_irq_groups = (nr_irqs + 31) / 32;
  161. rt_bool_t is_pending = RT_FALSE;
  162. void *pending_base = (void *)PLIC_PENDING(0);
  163. void *enable_base = (void *)PLIC_ENABLE(hartid);
  164. int i, j;
  165. for (i = 0; i < nr_irq_groups; i++)
  166. ie[i] = readl(enable_base + i * sizeof(rt_uint32_t));
  167. for (i = 0; i < nr_irq_groups; i++)
  168. {
  169. rt_uint32_t pending_irqs = readl(pending_base + i * sizeof(rt_uint32_t)) & ie[i];
  170. if (pending_irqs)
  171. {
  172. int nbit = find_first_bit(&pending_irqs, 32);
  173. for (j = 0; j < nr_irq_groups; j++)
  174. writel((i == j) ? (1 << nbit) : 0, enable_base + j * sizeof(rt_uint32_t));
  175. is_pending = RT_TRUE;
  176. break;
  177. }
  178. }
  179. return is_pending;
  180. }
  181. static void restore_irqs_enable(rt_uint32_t ie[])
  182. {
  183. int hartid = __raw_hartid();
  184. int nr_irqs = CONFIG_IRQ_NR;
  185. int nr_irq_groups = (nr_irqs + 31) / 32;
  186. void *enable_base = (void *)PLIC_ENABLE(hartid);
  187. int i;
  188. for (i = 0; i < nr_irq_groups; i++)
  189. writel(ie[i], enable_base + i * sizeof(rt_uint32_t));
  190. return;
  191. }
  192. #endif
  193. /*
  194. * Handling an interrupt is a two-step process: first you claim the interrupt
  195. * by reading the claim register, then you complete the interrupt by writing
  196. * that source ID back to the same claim register. This automatically enables
  197. * and disables the interrupt, so there's nothing else to do.
  198. */
  199. void plic_handle_irq(void)
  200. {
  201. rt_uint32_t plic_irq;
  202. #ifdef BOARD_UR_DP1000
  203. /* TODO: if not only one interrupt, we need to continue to check the interrupt source */
  204. unsigned int ie[32] = {0};
  205. plic_irq = is_irqs_pending(ie) ? plic_claim() : 0;
  206. restore_irqs_enable(ie);
  207. if (plic_irq > CONFIG_IRQ_NR)
  208. {
  209. /*spurious interrupt, return directly*/
  210. rt_kprintf("spurious interrupt, irq = %d\n", plic_irq);
  211. return;
  212. }
  213. plic_complete(plic_irq);
  214. irq_desc[plic_irq].handler(plic_irq, irq_desc[plic_irq].param);
  215. #else
  216. plic_irq = plic_claim();
  217. plic_complete(plic_irq);
  218. if (plic_irq > CONFIG_IRQ_NR)
  219. {
  220. /*spurious interrupt, return directly*/
  221. rt_kprintf("spurious interrupt, irq = %d\n", plic_irq);
  222. return;
  223. }
  224. irq_desc[plic_irq].handler(plic_irq, irq_desc[plic_irq].param);
  225. #endif
  226. return;
  227. }