1 /* Copyright 2018 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
4 #include <metal/machine/platform.h>
6 #ifdef METAL_RISCV_CLINT0
10 #include <metal/drivers/riscv_clint0.h>
11 #include <metal/machine.h>
13 unsigned long long __metal_clint0_mtime_get (struct __metal_driver_riscv_clint0 *clint)
15 __metal_io_u32 lo, hi;
16 unsigned long control_base = __metal_driver_sifive_clint0_control_base(&clint->controller);
18 /* Guard against rollover when reading */
20 hi = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_RISCV_CLINT0_MTIME + 4));
21 lo = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_RISCV_CLINT0_MTIME));
22 } while (__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_RISCV_CLINT0_MTIME + 4)) != hi);
24 return (((unsigned long long)hi) << 32) | lo;
27 int __metal_driver_riscv_clint0_mtimecmp_set(struct metal_interrupt *controller,
29 unsigned long long time)
31 struct __metal_driver_riscv_clint0 *clint =
32 (struct __metal_driver_riscv_clint0 *)(controller);
33 unsigned long control_base = __metal_driver_sifive_clint0_control_base(&clint->controller);
34 /* Per spec, the RISC-V MTIME/MTIMECMP registers are 64 bit,
35 * and are NOT internally latched for multiword transfers.
36 * Need to be careful about sequencing to avoid triggering
37 * spurious interrupts: For that set the high word to a max
40 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + METAL_RISCV_CLINT0_MTIMECMP_BASE + 4)) = 0xFFFFFFFF;
41 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + METAL_RISCV_CLINT0_MTIMECMP_BASE)) = (__metal_io_u32)time;
42 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + METAL_RISCV_CLINT0_MTIMECMP_BASE + 4)) = (__metal_io_u32)(time >> 32);
46 static struct metal_interrupt *_get_cpu_intc()
49 __asm__ volatile("csrr %[hartid], mhartid"
50 : [hartid] "=r" (hartid) :: "memory");
52 struct metal_cpu *cpu = metal_cpu_get(hartid);
54 return metal_cpu_interrupt_controller(cpu);
57 void __metal_driver_riscv_clint0_init (struct metal_interrupt *controller)
59 int num_interrupts = __metal_driver_sifive_clint0_num_interrupts(controller);
60 struct __metal_driver_riscv_clint0 *clint =
61 (struct __metal_driver_riscv_clint0 *)(controller);
63 if ( !clint->init_done ) {
64 /* Register its interrupts with with parent controller, aka sw and timerto its default isr */
65 for (int i = 0; i < num_interrupts; i++) {
66 struct metal_interrupt *intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
67 int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
68 intc->vtable->interrupt_register(intc, line, NULL, controller);
74 int __metal_driver_riscv_clint0_register (struct metal_interrupt *controller,
75 int id, metal_interrupt_handler_t isr,
79 metal_vector_mode mode = __metal_controller_interrupt_vector_mode();
80 struct metal_interrupt *intc = NULL;
81 struct metal_interrupt *cpu_intc = _get_cpu_intc();
82 int num_interrupts = __metal_driver_sifive_clint0_num_interrupts(controller);
84 if ( (mode != METAL_VECTOR_MODE) && (mode != METAL_DIRECT_MODE) ) {
88 for(int i = 0; i < num_interrupts; i++) {
89 int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
90 intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
91 if (cpu_intc == intc && id == line) {
97 /* Register its interrupts with parent controller */
99 rc = intc->vtable->interrupt_register(intc, id, isr, priv);
104 int __metal_driver_riscv_clint0_vector_register (struct metal_interrupt *controller,
105 int id, metal_interrupt_vector_handler_t isr,
108 /* Not supported. User can override the 'weak' handler with their own */
113 metal_vector_mode __metal_driver_riscv_clint0_get_vector_mode (struct metal_interrupt *controller)
115 return __metal_controller_interrupt_vector_mode();
118 int __metal_driver_riscv_clint0_set_vector_mode (struct metal_interrupt *controller, metal_vector_mode mode)
121 struct metal_interrupt *intc = _get_cpu_intc();
124 /* Valid vector modes are VECTOR and DIRECT, anything else is invalid (-1) */
126 case METAL_VECTOR_MODE:
127 case METAL_DIRECT_MODE:
128 rc = intc->vtable->interrupt_set_vector_mode(intc, mode);
130 case METAL_HARDWARE_VECTOR_MODE:
131 case METAL_SELECTIVE_NONVECTOR_MODE:
132 case METAL_SELECTIVE_VECTOR_MODE:
139 int __metal_driver_riscv_clint0_enable (struct metal_interrupt *controller, int id)
144 struct metal_interrupt *intc = NULL;
145 struct metal_interrupt *cpu_intc = _get_cpu_intc();
146 int num_interrupts = __metal_driver_sifive_clint0_num_interrupts(controller);
148 for(int i = 0; i < num_interrupts; i++) {
149 int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
150 intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
151 if(cpu_intc == intc && id == line) {
157 /* Enable its interrupts with parent controller */
159 rc = intc->vtable->interrupt_enable(intc, id);
166 int __metal_driver_riscv_clint0_disable (struct metal_interrupt *controller, int id)
171 struct metal_interrupt *intc = NULL;
172 struct metal_interrupt *cpu_intc = _get_cpu_intc();
173 int num_interrupts = __metal_driver_sifive_clint0_num_interrupts(controller);
175 for(int i = 0; i < num_interrupts; i++) {
176 int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
177 intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
178 if(cpu_intc == intc && id == line) {
184 /* Disable its interrupts with parent controller */
186 rc = intc->vtable->interrupt_disable(intc, id);
193 int __metal_driver_riscv_clint0_command_request (struct metal_interrupt *controller,
194 int command, void *data)
198 struct __metal_driver_riscv_clint0 *clint =
199 (struct __metal_driver_riscv_clint0 *)(controller);
200 unsigned long control_base = __metal_driver_sifive_clint0_control_base(controller);
203 case METAL_TIMER_MTIME_GET:
205 *(unsigned long long *)data = __metal_clint0_mtime_get(clint);
209 case METAL_SOFTWARE_IPI_CLEAR:
211 hartid = *(int *)data;
212 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
213 (hartid * 4))) = METAL_DISABLE;
217 case METAL_SOFTWARE_IPI_SET:
219 hartid = *(int *)data;
220 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
221 (hartid * 4))) = METAL_ENABLE;
222 /* Callers of this function assume it's blocking, in the sense that
223 * the IPI is guarnteed to have been delivered before the function
224 * returns. We can't really guarnteed it's delivered, but we can
225 * read back the control register after writing it in at least an
226 * attempt to provide some semblence of ordering here. The fence
227 * ensures the read is order after the write -- it wouldn't be
228 * necessary under RVWMO because this is the same address, but we
229 * don't have an IO memory model so I'm being a bit overkill here.
231 __METAL_IO_FENCE(o,i);
232 rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
237 case METAL_SOFTWARE_MSIP_GET:
240 hartid = *(int *)data;
241 rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
252 int __metal_driver_riscv_clint0_clear_interrupt (struct metal_interrupt *controller, int id)
254 int hartid = metal_cpu_get_current_hartid();
255 return __metal_driver_riscv_clint0_command_request(controller,
256 METAL_SOFTWARE_IPI_CLEAR, &hartid);
259 int __metal_driver_riscv_clint0_set_interrupt (struct metal_interrupt *controller, int id)
261 int hartid = metal_cpu_get_current_hartid();
262 return __metal_driver_riscv_clint0_command_request(controller,
263 METAL_SOFTWARE_IPI_SET, &hartid);
267 __METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_clint0) = {
268 .clint_vtable.interrupt_init = __metal_driver_riscv_clint0_init,
269 .clint_vtable.interrupt_register = __metal_driver_riscv_clint0_register,
270 .clint_vtable.interrupt_vector_register = __metal_driver_riscv_clint0_vector_register,
271 .clint_vtable.interrupt_enable = __metal_driver_riscv_clint0_enable,
272 .clint_vtable.interrupt_disable = __metal_driver_riscv_clint0_disable,
273 .clint_vtable.interrupt_get_vector_mode = __metal_driver_riscv_clint0_get_vector_mode,
274 .clint_vtable.interrupt_set_vector_mode = __metal_driver_riscv_clint0_set_vector_mode,
275 .clint_vtable.interrupt_clear = __metal_driver_riscv_clint0_clear_interrupt,
276 .clint_vtable.interrupt_set = __metal_driver_riscv_clint0_set_interrupt,
277 .clint_vtable.command_request = __metal_driver_riscv_clint0_command_request,
278 .clint_vtable.mtimecmp_set = __metal_driver_riscv_clint0_mtimecmp_set,
281 #endif /* METAL_RISCV_CLINT0 */
283 typedef int no_empty_translation_units;