1 /* Copyright 2018 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
4 #include <metal/machine/platform.h>
6 #ifdef METAL_RISCV_CLINT0
9 #include <metal/drivers/riscv_clint0.h>
11 #include <metal/machine.h>
14 __metal_clint0_mtime_get(struct __metal_driver_riscv_clint0 *clint) {
15 __metal_io_u32 lo, hi;
16 unsigned long control_base =
17 __metal_driver_sifive_clint0_control_base(&clint->controller);
19 /* Guard against rollover when reading */
21 hi = __METAL_ACCESS_ONCE(
22 (__metal_io_u32 *)(control_base + METAL_RISCV_CLINT0_MTIME + 4));
23 lo = __METAL_ACCESS_ONCE(
24 (__metal_io_u32 *)(control_base + METAL_RISCV_CLINT0_MTIME));
25 } while (__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
26 METAL_RISCV_CLINT0_MTIME +
29 return (((unsigned long long)hi) << 32) | lo;
32 int __metal_driver_riscv_clint0_mtimecmp_set(struct metal_interrupt *controller,
34 unsigned long long time) {
35 struct __metal_driver_riscv_clint0 *clint =
36 (struct __metal_driver_riscv_clint0 *)(controller);
37 unsigned long control_base =
38 __metal_driver_sifive_clint0_control_base(&clint->controller);
39 /* Per spec, the RISC-V MTIME/MTIMECMP registers are 64 bit,
40 * and are NOT internally latched for multiword transfers.
41 * Need to be careful about sequencing to avoid triggering
42 * spurious interrupts: For that set the high word to a max
45 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) +
46 METAL_RISCV_CLINT0_MTIMECMP_BASE +
48 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) +
49 METAL_RISCV_CLINT0_MTIMECMP_BASE)) =
51 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) +
52 METAL_RISCV_CLINT0_MTIMECMP_BASE +
53 4)) = (__metal_io_u32)(time >> 32);
57 static struct metal_interrupt *_get_cpu_intc() {
59 __asm__ volatile("csrr %[hartid], mhartid"
60 : [hartid] "=r"(hartid)::"memory");
62 struct metal_cpu *cpu = metal_cpu_get(hartid);
64 return metal_cpu_interrupt_controller(cpu);
67 void __metal_driver_riscv_clint0_init(struct metal_interrupt *controller) {
69 __metal_driver_sifive_clint0_num_interrupts(controller);
70 struct __metal_driver_riscv_clint0 *clint =
71 (struct __metal_driver_riscv_clint0 *)(controller);
73 if (!clint->init_done) {
74 /* Register its interrupts with with parent controller, aka sw and
75 * timerto its default isr */
76 for (int i = 0; i < num_interrupts; i++) {
77 struct metal_interrupt *intc =
78 __metal_driver_sifive_clint0_interrupt_parents(controller, i);
80 __metal_driver_sifive_clint0_interrupt_lines(controller, i);
81 intc->vtable->interrupt_register(intc, line, NULL, controller);
87 int __metal_driver_riscv_clint0_register(struct metal_interrupt *controller,
88 int id, metal_interrupt_handler_t isr,
91 metal_vector_mode mode = __metal_controller_interrupt_vector_mode();
92 struct metal_interrupt *intc = NULL;
93 struct metal_interrupt *cpu_intc = _get_cpu_intc();
95 __metal_driver_sifive_clint0_num_interrupts(controller);
97 if ((mode != METAL_VECTOR_MODE) && (mode != METAL_DIRECT_MODE)) {
101 for (int i = 0; i < num_interrupts; i++) {
102 int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
103 intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
104 if (cpu_intc == intc && id == line) {
110 /* Register its interrupts with parent controller */
112 rc = intc->vtable->interrupt_register(intc, id, isr, priv);
117 int __metal_driver_riscv_clint0_vector_register(
118 struct metal_interrupt *controller, int id,
119 metal_interrupt_vector_handler_t isr, void *priv) {
120 /* Not supported. User can override the 'weak' handler with their own */
125 metal_vector_mode __metal_driver_riscv_clint0_get_vector_mode(
126 struct metal_interrupt *controller) {
127 return __metal_controller_interrupt_vector_mode();
130 int __metal_driver_riscv_clint0_set_vector_mode(
131 struct metal_interrupt *controller, metal_vector_mode mode) {
133 struct metal_interrupt *intc = _get_cpu_intc();
136 /* Valid vector modes are VECTOR and DIRECT, anything else is invalid
139 case METAL_VECTOR_MODE:
140 case METAL_DIRECT_MODE:
141 rc = intc->vtable->interrupt_set_vector_mode(intc, mode);
150 int __metal_driver_riscv_clint0_enable(struct metal_interrupt *controller,
155 struct metal_interrupt *intc = NULL;
156 struct metal_interrupt *cpu_intc = _get_cpu_intc();
158 __metal_driver_sifive_clint0_num_interrupts(controller);
160 for (int i = 0; i < num_interrupts; i++) {
162 __metal_driver_sifive_clint0_interrupt_lines(controller, i);
164 __metal_driver_sifive_clint0_interrupt_parents(controller, i);
165 if (cpu_intc == intc && id == line) {
171 /* Enable its interrupts with parent controller */
173 rc = intc->vtable->interrupt_enable(intc, id);
180 int __metal_driver_riscv_clint0_disable(struct metal_interrupt *controller,
185 struct metal_interrupt *intc = NULL;
186 struct metal_interrupt *cpu_intc = _get_cpu_intc();
188 __metal_driver_sifive_clint0_num_interrupts(controller);
190 for (int i = 0; i < num_interrupts; i++) {
192 __metal_driver_sifive_clint0_interrupt_lines(controller, i);
194 __metal_driver_sifive_clint0_interrupt_parents(controller, i);
195 if (cpu_intc == intc && id == line) {
201 /* Disable its interrupts with parent controller */
203 rc = intc->vtable->interrupt_disable(intc, id);
210 int __metal_driver_riscv_clint0_command_request(
211 struct metal_interrupt *controller, int command, void *data) {
214 struct __metal_driver_riscv_clint0 *clint =
215 (struct __metal_driver_riscv_clint0 *)(controller);
216 unsigned long control_base =
217 __metal_driver_sifive_clint0_control_base(controller);
220 case METAL_TIMER_MTIME_GET:
222 *(unsigned long long *)data = __metal_clint0_mtime_get(clint);
226 case METAL_SOFTWARE_IPI_CLEAR:
228 hartid = *(int *)data;
229 __METAL_ACCESS_ONCE((
230 __metal_io_u32 *)(control_base + (hartid * 4))) = METAL_DISABLE;
234 case METAL_SOFTWARE_IPI_SET:
236 hartid = *(int *)data;
238 (__metal_io_u32 *)(control_base + (hartid * 4))) = METAL_ENABLE;
239 /* Callers of this function assume it's blocking, in the sense that
240 * the IPI is guarnteed to have been delivered before the function
241 * returns. We can't really guarnteed it's delivered, but we can
242 * read back the control register after writing it in at least an
243 * attempt to provide some semblence of ordering here. The fence
244 * ensures the read is order after the write -- it wouldn't be
245 * necessary under RVWMO because this is the same address, but we
246 * don't have an IO memory model so I'm being a bit overkill here.
248 __METAL_IO_FENCE(o, i);
249 rc = __METAL_ACCESS_ONCE(
250 (__metal_io_u32 *)(control_base + (hartid * 4)));
254 case METAL_SOFTWARE_MSIP_GET:
257 hartid = *(int *)data;
258 rc = __METAL_ACCESS_ONCE(
259 (__metal_io_u32 *)(control_base + (hartid * 4)));
269 int __metal_driver_riscv_clint0_clear_interrupt(
270 struct metal_interrupt *controller, int id) {
271 int hartid = metal_cpu_get_current_hartid();
272 return __metal_driver_riscv_clint0_command_request(
273 controller, METAL_SOFTWARE_IPI_CLEAR, &hartid);
276 int __metal_driver_riscv_clint0_set_interrupt(
277 struct metal_interrupt *controller, int id) {
278 int hartid = metal_cpu_get_current_hartid();
279 return __metal_driver_riscv_clint0_command_request(
280 controller, METAL_SOFTWARE_IPI_SET, &hartid);
283 __METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_clint0) = {
284 .clint_vtable.interrupt_init = __metal_driver_riscv_clint0_init,
285 .clint_vtable.interrupt_register = __metal_driver_riscv_clint0_register,
286 .clint_vtable.interrupt_vector_register =
287 __metal_driver_riscv_clint0_vector_register,
288 .clint_vtable.interrupt_enable = __metal_driver_riscv_clint0_enable,
289 .clint_vtable.interrupt_disable = __metal_driver_riscv_clint0_disable,
290 .clint_vtable.interrupt_get_vector_mode =
291 __metal_driver_riscv_clint0_get_vector_mode,
292 .clint_vtable.interrupt_set_vector_mode =
293 __metal_driver_riscv_clint0_set_vector_mode,
294 .clint_vtable.interrupt_clear = __metal_driver_riscv_clint0_clear_interrupt,
295 .clint_vtable.interrupt_set = __metal_driver_riscv_clint0_set_interrupt,
296 .clint_vtable.command_request = __metal_driver_riscv_clint0_command_request,
297 .clint_vtable.mtimecmp_set = __metal_driver_riscv_clint0_mtimecmp_set,
300 #endif /* METAL_RISCV_CLINT0 */
302 typedef int no_empty_translation_units;