plic.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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-05-20 bigmagic first version
  9. * 2022-09-16 WangXiaoyao Porting to rv64
  10. */
  11. #include <rthw.h>
  12. #include <rtthread.h>
  13. #include <stdint.h>
  14. #include "plic.h"
  15. #include <riscv_io.h>
  16. #include "encoding.h"
  17. #include <interrupt.h>
  18. #include <riscv.h>
  19. #include <string.h>
  20. #include <stdlib.h>
  21. #ifdef RT_USING_SMART
  22. #include <ioremap.h>
  23. #else
  24. #define rt_ioremap(addr, ...) (addr)
  25. #endif
  26. size_t plic_base = 0x0c000000L;
  27. /*
  28. * Each PLIC interrupt source can be assigned a priority by writing
  29. * to its 32-bit memory-mapped priority register.
  30. * The QEMU-virt (the same as FU540-C000) supports 7 levels of priority.
  31. * A priority value of 0 is reserved to mean "never interrupt" and
  32. * effectively disables the interrupt.
  33. * Priority 1 is the lowest active priority, and priority 7 is the highest.
  34. * Ties between global interrupts of the same priority are broken by
  35. * the Interrupt ID; interrupts with the lowest ID have the highest
  36. * effective priority.
  37. */
  38. void plic_set_priority(int irq, int priority)
  39. {
  40. *(uint32_t *)PLIC_PRIORITY(irq) = priority;
  41. }
  42. /*
  43. * Each global interrupt can be enabled by setting the corresponding
  44. * bit in the enables registers.
  45. */
  46. void plic_irq_enable(int irq)
  47. {
  48. int hart = __raw_hartid();
  49. *(uint32_t *)PLIC_ENABLE(hart) = ((*(uint32_t *)PLIC_ENABLE(hart)) | (1 << irq));
  50. #ifdef RISCV_VIRT64_S_MODE
  51. set_csr(sie, read_csr(sie) | MIP_SEIP);
  52. #else
  53. set_csr(mie, read_csr(mie) | MIP_MEIP);
  54. #endif
  55. }
  56. void plic_irq_disable(int irq)
  57. {
  58. int hart = __raw_hartid();
  59. *(uint32_t *)PLIC_ENABLE(hart) = (((*(uint32_t *)PLIC_ENABLE(hart)) & (~(1 << irq))));
  60. }
  61. /*
  62. * PLIC will mask all interrupts of a priority less than or equal to threshold.
  63. * Maximum threshold is 7.
  64. * For example, a threshold value of zero permits all interrupts with
  65. * non-zero priority, whereas a value of 7 masks all interrupts.
  66. * Notice, the threshold is global for PLIC, not for each interrupt source.
  67. */
  68. void plic_set_threshold(int threshold)
  69. {
  70. int hart = __raw_hartid();
  71. *(uint32_t *)PLIC_THRESHOLD(hart) = threshold;
  72. }
  73. /*
  74. * DESCRIPTION:
  75. * Query the PLIC what interrupt we should serve.
  76. * Perform an interrupt claim by reading the claim register, which
  77. * returns the ID of the highest-priority pending interrupt or zero if there
  78. * is no pending interrupt.
  79. * A successful claim also atomically clears the corresponding pending bit
  80. * on the interrupt source.
  81. * RETURN VALUE:
  82. * the ID of the highest-priority pending interrupt or zero if there
  83. * is no pending interrupt.
  84. */
  85. int plic_claim(void)
  86. {
  87. int hart = __raw_hartid();
  88. int irq = *(uint32_t *)PLIC_CLAIM(hart);
  89. return irq;
  90. }
  91. /*
  92. * DESCRIPTION:
  93. * Writing the interrupt ID it received from the claim (irq) to the
  94. * complete register would signal the PLIC we've served this IRQ.
  95. * The PLIC does not check whether the completion ID is the same as the
  96. * last claim ID for that target. If the completion ID does not match an
  97. * interrupt source that is currently enabled for the target, the completion
  98. * is silently ignored.
  99. * RETURN VALUE: none
  100. */
  101. void plic_complete(int irq)
  102. {
  103. int hart = __raw_hartid();
  104. *(uint32_t *)PLIC_COMPLETE(hart) = irq;
  105. }
  106. void plic_set_ie(rt_uint32_t word_index, rt_uint32_t val)
  107. {
  108. volatile void *plic_ie = (void *)(rt_ubase_t)(plic_base + PLIC_ENABLE_BASE + 0x80 + word_index * 4);
  109. writel(val, plic_ie);
  110. }
  111. static void _set_sie(int hartid)
  112. {
  113. for (size_t i = hartid * WORD_CNT_BYTE; i < 32; i++)
  114. plic_set_ie(i, 0xffffffff);
  115. }
  116. void plic_init()
  117. {
  118. // PLIC takes up 64 MB space
  119. plic_base = (size_t)rt_ioremap((void *)plic_base, 64 * 1024 * 1024);
  120. plic_set_threshold(0);
  121. for (int i = 0; i < CONFIG_IRQ_NR; i++)
  122. {
  123. plic_set_priority(i, 1);
  124. }
  125. // in a single core system, only current context was set
  126. _set_sie(__raw_hartid());
  127. }
  128. extern struct rt_irq_desc irq_desc[MAX_HANDLERS];
  129. /*
  130. * Handling an interrupt is a two-step process: first you claim the interrupt
  131. * by reading the claim register, then you complete the interrupt by writing
  132. * that source ID back to the same claim register. This automatically enables
  133. * and disables the interrupt, so there's nothing else to do.
  134. */
  135. void plic_handle_irq(void)
  136. {
  137. int plic_irq = plic_claim();
  138. plic_complete(plic_irq);
  139. irq_desc[plic_irq].handler(plic_irq, irq_desc[plic_irq].param);
  140. }