1 /*
2 * Copyright (c) 2006-2023, 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 * 2023-04-22 flyingcys add plic register ioremap
11 */
12
13 #include <rtthread.h>
14
15 #include <rtdbg.h>
16
17 #include "plic.h"
18 #include "interrupt.h"
19 #include "io.h"
20 #include "encoding.h"
21 #include "ioremap.h"
22
23 static void *c906_plic_regs = RT_NULL;
24 extern struct rt_irq_desc isr_table[];
25
26 struct plic_handler
27 {
28 rt_bool_t present;
29 void *hart_base;
30 void *enable_base;
31 };
32
33 rt_inline void plic_toggle(struct plic_handler *handler, int hwirq, int enable);
34 struct plic_handler c906_plic_handlers[C906_NR_CPUS];
35 static void *c906_irq_priority[INTERRUPTS_MAX] = {RT_NULL};
36
plic_irq_toggle(int hwirq,int enable)37 rt_inline void plic_irq_toggle(int hwirq, int enable)
38 {
39 int cpu = 0;
40 void *priority_addr;
41
42 /* set priority of interrupt, interrupt 0 is zero. */
43 priority_addr = (void *)((rt_size_t)c906_plic_regs + PRIORITY_BASE + hwirq * PRIORITY_PER_ID);
44 #ifdef RT_USING_SMART
45 if (c906_irq_priority[hwirq] == RT_NULL)
46 {
47 c906_irq_priority[hwirq] = (void *)rt_ioremap(priority_addr, 0x1000);
48 }
49 priority_addr = c906_irq_priority[hwirq];
50 #endif
51 writel(enable, priority_addr);
52 struct plic_handler *handler = &c906_plic_handlers[cpu];
53
54 if (handler->present)
55 {
56 plic_toggle(handler, hwirq, enable);
57 }
58 }
59
generic_handle_irq(int irq)60 static void generic_handle_irq(int irq)
61 {
62 rt_isr_handler_t isr;
63 void *param;
64
65 if (irq < 0 || irq >= IRQ_MAX_NR)
66 {
67 LOG_E("bad irq number %d!\n", irq);
68 return;
69 }
70
71 if (!irq) // irq = 0 => no irq
72 {
73 LOG_W("no irq!\n");
74 return;
75 }
76 isr = isr_table[IRQ_OFFSET + irq].handler;
77 param = isr_table[IRQ_OFFSET + irq].param;
78 if (isr != RT_NULL)
79 {
80 isr(irq, param);
81 }
82 /* complete irq. */
83 plic_complete(irq);
84 }
85
plic_complete(int irqno)86 void plic_complete(int irqno)
87 {
88 int cpu = 0;
89 struct plic_handler *handler = &c906_plic_handlers[cpu];
90
91 writel(irqno, (void *)((rt_size_t)handler->hart_base + CONTEXT_CLAIM));
92 }
93
plic_disable_irq(int irqno)94 void plic_disable_irq(int irqno)
95 {
96 plic_irq_toggle(irqno, 0);
97 }
98
plic_enable_irq(int irqno)99 void plic_enable_irq(int irqno)
100 {
101 plic_irq_toggle(irqno, 1);
102 }
103
104 /*
105 * Handling an interrupt is a two-step process: first you claim the interrupt
106 * by reading the claim register, then you complete the interrupt by writing
107 * that source ID back to the same claim register. This automatically enables
108 * and disables the interrupt, so there's nothing else to do.
109 */
plic_handle_irq(void)110 void plic_handle_irq(void)
111 {
112 int cpu = 0;
113 unsigned int irq;
114
115 struct plic_handler *handler = &c906_plic_handlers[cpu];
116 void *claim = (void *)((rt_size_t)handler->hart_base + CONTEXT_CLAIM);
117
118 if (c906_plic_regs == RT_NULL || !handler->present)
119 {
120 LOG_E("plic state not initialized.");
121 return;
122 }
123
124 clear_csr(sie, SIE_SEIE);
125
126 while ((irq = readl(claim)))
127 {
128 /* ID0 is diabled permantually from spec. */
129 if (irq == 0)
130 {
131 LOG_E("irq no is zero.");
132 }
133 else
134 {
135 generic_handle_irq(irq);
136 }
137 }
138 set_csr(sie, SIE_SEIE);
139 }
140
plic_toggle(struct plic_handler * handler,int hwirq,int enable)141 rt_inline void plic_toggle(struct plic_handler *handler, int hwirq, int enable)
142 {
143 uint32_t *reg = (uint32_t *)((rt_size_t)handler->enable_base + (hwirq / 32) * sizeof(uint32_t));
144 uint32_t hwirq_mask = 1 << (hwirq % 32);
145
146 if (enable)
147 {
148 writel(readl(reg) | hwirq_mask, reg);
149 }
150 else
151 {
152 writel(readl(reg) & ~hwirq_mask, reg);
153 }
154 }
155
plic_init(void)156 void plic_init(void)
157 {
158 int nr_irqs;
159 int nr_context;
160 int i;
161 unsigned long hwirq;
162 int cpu = 0;
163
164 if (c906_plic_regs)
165 {
166 LOG_E("plic already initialized!");
167 return;
168 }
169
170 nr_context = C906_NR_CONTEXT;
171
172 c906_plic_regs = (void *)C906_PLIC_PHY_ADDR;
173 if (!c906_plic_regs)
174 {
175 LOG_E("fatal error, plic is reg space is null.");
176 return;
177 }
178
179 nr_irqs = C906_PLIC_NR_EXT_IRQS;
180
181 for (i = 0; i < nr_context; i ++)
182 {
183 struct plic_handler *handler;
184 uint32_t threshold = 0;
185
186 cpu = 0;
187
188 /* skip contexts other than supervisor external interrupt */
189 if (i == 0)
190 {
191 continue;
192 }
193
194 // we always use CPU0 M-mode target register.
195 handler = &c906_plic_handlers[cpu];
196 if (handler->present)
197 {
198 threshold = 0xffffffff;
199 goto done;
200 }
201
202 handler->present = RT_TRUE;
203 handler->hart_base = (void *)((rt_size_t)c906_plic_regs + CONTEXT_BASE + i * CONTEXT_PER_HART);
204 handler->enable_base = (void *)((rt_size_t)c906_plic_regs + ENABLE_BASE + i * ENABLE_PER_HART);
205 #ifdef RT_USING_SMART
206 handler->hart_base = (void *)rt_ioremap(handler->hart_base, 0x1000);
207 handler->enable_base = (void *)rt_ioremap(handler->enable_base, 0x1000);
208 #endif
209 done:
210 /* priority must be > threshold to trigger an interrupt */
211 writel(threshold, (void *)((rt_size_t)handler->hart_base + CONTEXT_THRESHOLD));
212 for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
213 {
214 plic_toggle(handler, hwirq, 0);
215 }
216 }
217
218 /* Enable supervisor external interrupts. */
219 set_csr(sie, SIE_SEIE);
220 }
221