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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018/10/01 Bernard The first version
* 2018/12/27 Jesven Change irq enable/disable to cpu0
*/
#include <plic.h>
#include <mmu.h>
#include "tick.h"
#include "encoding.h"
#include "riscv.h"
#include "interrupt.h"
struct rt_irq_desc irq_desc[MAX_HANDLERS];
#ifdef RT_USING_SMP
#include "sbi.h"
struct rt_irq_desc ipi_desc[RT_MAX_IPI];
uint8_t ipi_vectors[RT_CPUS_NR] = { 0 };
#endif /* RT_USING_SMP */
static rt_isr_handler_t rt_hw_interrupt_handle(rt_uint32_t vector, void *param)
{
rt_kprintf("UN-handled interrupt %d occurred!!!\n", vector);
return RT_NULL;
}
int rt_hw_plic_irq_enable(int irq_number)
{
plic_irq_enable(irq_number);
return 0;
}
int rt_hw_plic_irq_disable(int irq_number)
{
plic_irq_disable(irq_number);
return 0;
}
/**
* This function will un-mask a interrupt.
* @param vector the interrupt number
*/
void rt_hw_interrupt_umask(int vector)
{
plic_set_priority(vector, 1);
rt_hw_plic_irq_enable(vector);
}
/**
* This function will install a interrupt service routine to a interrupt.
* @param vector the interrupt number
* @param new_handler the interrupt service routine to be installed
* @param old_handler the old interrupt service routine
*/
rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler,
void *param, const char *name)
{
rt_isr_handler_t old_handler = RT_NULL;
if (vector < MAX_HANDLERS)
{
old_handler = irq_desc[vector].handler;
if (handler != RT_NULL)
{
irq_desc[vector].handler = (rt_isr_handler_t)handler;
irq_desc[vector].param = param;
#ifdef RT_USING_INTERRUPT_INFO
rt_snprintf(irq_desc[vector].name, RT_NAME_MAX - 1, "%s", name);
irq_desc[vector].counter = 0;
#endif
}
}
return old_handler;
}
void rt_hw_interrupt_init()
{
/* Enable machine external interrupts. */
// set_csr(sie, SIP_SEIP);
int idx = 0;
/* init exceptions table */
for (idx = 0; idx < MAX_HANDLERS; idx++)
{
irq_desc[idx].handler = (rt_isr_handler_t)rt_hw_interrupt_handle;
irq_desc[idx].param = RT_NULL;
#ifdef RT_USING_INTERRUPT_INFO
rt_snprintf(irq_desc[idx].name, RT_NAME_MAX - 1, "default");
irq_desc[idx].counter = 0;
#endif
}
plic_set_threshold(0);
}
#ifdef RT_USING_SMP
void rt_hw_interrupt_set_priority(int vector, unsigned int priority)
{
plic_set_priority(vector, priority);
}
unsigned int rt_hw_interrupt_get_priority(int vector)
{
return (*(uint32_t *)PLIC_PRIORITY(vector));
}
rt_bool_t rt_hw_interrupt_is_disabled(void)
{
/* Determine the interrupt enable state */
rt_ubase_t sstatus;
__asm__ volatile("csrr %0, sstatus" : "=r"(sstatus));
return (sstatus & SSTATUS_SIE) == 0;
}
void rt_hw_spin_lock_init(rt_hw_spinlock_t *_lock)
{
_lock->slock = 0;
}
void rt_hw_spin_lock(rt_hw_spinlock_t *lock)
{
/* Use ticket lock implemented on top of the 32/64-bit atomic AMO ops.
* The combined word layout (slock) maps two uint16_t fields:
* low 16 bits: owner
* high 16 bits: next (ticket allocator)
* We atomically increment the "next" field by (1 << 16) and use the
* returned old value to compute our ticket. Then wait until owner == ticket.
*/
rt_atomic_t prev;
rt_atomic_t ticket;
rt_atomic_t owner;
/* Allocate a ticket by adding (1 << 16) to slock, prev holds previous value */
prev = rt_hw_atomic_add((volatile rt_atomic_t *)&lock->slock, (rt_atomic_t)(1UL << 16));
ticket = (prev >> 16) & 0xffffUL;
/* Wait until owner equals our ticket */
for (;;)
{
owner = rt_hw_atomic_load((volatile rt_atomic_t *)&lock->slock) & 0xffffUL;
if (owner == ticket)
break;
/* TODO: low-power wait for interrupt while spinning */
}
/* Ensure all following memory accesses are ordered after acquiring the lock */
__asm__ volatile("fence rw, rw" ::: "memory");
}
void rt_hw_spin_unlock(rt_hw_spinlock_t *lock)
{
/* Ensure memory operations before unlock are visible before owner increment */
__asm__ volatile("fence rw, rw" ::: "memory");
/* Increment owner (low 16 bits) to hand over lock to next ticket.
* Use an atomic load of the combined slock word and compare the low
* 16-bit owner field.If owner would overflow (0xffff), clear the owner field
* atomically by ANDing with 0xffff0000; otherwise increment owner by 1.
*/
if ((rt_hw_atomic_load((volatile rt_atomic_t *)&lock->slock) & (rt_atomic_t)0xffffUL) == (rt_atomic_t)0xffffUL)
{
/* Atomic clear owner (low 16 bits) when it overflows. Keep next ticket field. */
rt_hw_atomic_and((volatile rt_atomic_t *)&lock->slock, (rt_atomic_t)0xffff0000UL);
}
else
{
rt_hw_atomic_add((volatile rt_atomic_t *)&lock->slock, (rt_atomic_t)1);
}
// TODO: IPI interrupt to wake up other harts waiting for the lock
/* Make the increment visible to other harts */
__asm__ volatile("fence rw, rw" ::: "memory");
}
void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask)
{
int cpuid = __builtin_ctz(cpu_mask); // get the bit position of the lowest set bit
ipi_vectors[cpuid] |= (uint8_t)ipi_vector;
sbi_send_ipi((const unsigned long *)&cpu_mask);
}
void rt_hw_ipi_init(void)
{
int idx = 0, cpuid = rt_cpu_get_id();
ipi_vectors[cpuid] = 0;
/* init exceptions table */
for (idx = 0; idx < RT_MAX_IPI; idx++)
{
ipi_desc[idx].handler = RT_NULL;
ipi_desc[idx].param = RT_NULL;
#ifdef RT_USING_INTERRUPT_INFO
rt_snprintf(ipi_desc[idx].name, RT_NAME_MAX - 1, "default");
ipi_desc[idx].counter = 0;
#endif
}
set_csr(sie, SIP_SSIP);
}
void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler)
{
if (ipi_vector < RT_MAX_IPI)
{
if (ipi_isr_handler != RT_NULL)
{
ipi_desc[ipi_vector].handler = (rt_isr_handler_t)ipi_isr_handler;
ipi_desc[ipi_vector].param = RT_NULL;
}
}
}
void rt_hw_ipi_handler(void)
{
rt_uint32_t ipi_vector;
ipi_vector = ipi_vectors[rt_cpu_get_id()];
while (ipi_vector)
{
int bitpos = __builtin_ctz(ipi_vector);
ipi_vector &= ~(1 << bitpos);
if (bitpos < RT_MAX_IPI && ipi_desc[bitpos].handler != RT_NULL)
{
rt_hw_atomic_and((volatile rt_atomic_t *)&ipi_vectors[rt_cpu_get_id()], ~((rt_atomic_t)(1 << bitpos)));
/* call the irq service routine */
ipi_desc[bitpos].handler(bitpos, ipi_desc[bitpos].param);
}
}
// TODO: Clear the software interrupt pending bit in CLINT
clear_csr(sip, SIP_SSIP);
}
#endif /* RT_USING_SMP */
|