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