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