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 }