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,
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 for(int i = 0; i < num_interrupts; i++) {
85 int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
86 intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
87 if (cpu_intc == intc && id == line) {
93 /* Register its interrupts with parent controller */
95 rc = intc->vtable->interrupt_register(intc, id, isr, priv);
100 int __metal_driver_riscv_clint0_enable (struct metal_interrupt *controller, int id)
105 struct metal_interrupt *intc = NULL;
106 struct metal_interrupt *cpu_intc = _get_cpu_intc();
107 int num_interrupts = __metal_driver_sifive_clint0_num_interrupts(controller);
109 for(int i = 0; i < num_interrupts; i++) {
110 int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
111 intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
112 if(cpu_intc == intc && id == line) {
118 /* Enable its interrupts with parent controller */
120 rc = intc->vtable->interrupt_enable(intc, id);
125 int __metal_driver_riscv_clint0_disable (struct metal_interrupt *controller, int id)
130 struct metal_interrupt *intc = NULL;
131 struct metal_interrupt *cpu_intc = _get_cpu_intc();
132 int num_interrupts = __metal_driver_sifive_clint0_num_interrupts(controller);
134 for(int i = 0; i < num_interrupts; i++) {
135 int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
136 intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
137 if(cpu_intc == intc && id == line) {
143 /* Disable its interrupts with parent controller */
145 rc = intc->vtable->interrupt_disable(intc, id);
150 int __metal_driver_riscv_clint0_command_request (struct metal_interrupt *controller,
151 int command, void *data)
155 struct __metal_driver_riscv_clint0 *clint =
156 (struct __metal_driver_riscv_clint0 *)(controller);
157 unsigned long control_base = __metal_driver_sifive_clint0_control_base(controller);
160 case METAL_TIMER_MTIME_GET:
162 *(unsigned long long *)data = __metal_clint0_mtime_get(clint);
166 case METAL_SOFTWARE_IPI_CLEAR:
168 hartid = *(int *)data;
169 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
170 (hartid * 4))) = METAL_DISABLE;
174 case METAL_SOFTWARE_IPI_SET:
176 hartid = *(int *)data;
177 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
178 (hartid * 4))) = METAL_ENABLE;
179 /* Callers of this function assume it's blocking, in the sense that
180 * the IPI is guarnteed to have been delivered before the function
181 * returns. We can't really guarnteed it's delivered, but we can
182 * read back the control register after writing it in at least an
183 * attempt to provide some semblence of ordering here. The fence
184 * ensures the read is order after the write -- it wouldn't be
185 * necessary under RVWMO because this is the same address, but we
186 * don't have an IO memory model so I'm being a bit overkill here.
188 __METAL_IO_FENCE(o,i);
189 rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
194 case METAL_SOFTWARE_MSIP_GET:
197 hartid = *(int *)data;
198 rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
209 __METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_clint0) = {
210 .clint_vtable.interrupt_init = __metal_driver_riscv_clint0_init,
211 .clint_vtable.interrupt_register = __metal_driver_riscv_clint0_register,
212 .clint_vtable.interrupt_enable = __metal_driver_riscv_clint0_enable,
213 .clint_vtable.interrupt_disable = __metal_driver_riscv_clint0_disable,
214 .clint_vtable.command_request = __metal_driver_riscv_clint0_command_request,
215 .clint_vtable.mtimecmp_set = __metal_driver_riscv_clint0_mtimecmp_set,
218 #endif /* METAL_RISCV_CLINT0 */