]> begriffs open source - cmsis-freertos/blob - Demo/RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio/freedom-metal/src/drivers/riscv_clint0.c
Updated pack to FreeRTOS 10.4.4
[cmsis-freertos] / Demo / RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio / freedom-metal / src / drivers / riscv_clint0.c
1 /* Copyright 2018 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
3
4 #include <metal/machine/platform.h>
5
6 #ifdef METAL_RISCV_CLINT0
7
8 #include <metal/cpu.h>
9 #include <metal/drivers/riscv_clint0.h>
10 #include <metal/io.h>
11 #include <metal/machine.h>
12
13 unsigned long long
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);
18
19     /* Guard against rollover when reading */
20     do {
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 +
27                                                     4)) != hi);
28
29     return (((unsigned long long)hi) << 32) | lo;
30 }
31
32 int __metal_driver_riscv_clint0_mtimecmp_set(struct metal_interrupt *controller,
33                                              int hartid,
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
43      * value first.
44      */
45     __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) +
46                                            METAL_RISCV_CLINT0_MTIMECMP_BASE +
47                                            4)) = 0xFFFFFFFF;
48     __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) +
49                                            METAL_RISCV_CLINT0_MTIMECMP_BASE)) =
50         (__metal_io_u32)time;
51     __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) +
52                                            METAL_RISCV_CLINT0_MTIMECMP_BASE +
53                                            4)) = (__metal_io_u32)(time >> 32);
54     return 0;
55 }
56
57 static struct metal_interrupt *_get_cpu_intc() {
58     int hartid = 0;
59     __asm__ volatile("csrr %[hartid], mhartid"
60                      : [hartid] "=r"(hartid)::"memory");
61
62     struct metal_cpu *cpu = metal_cpu_get(hartid);
63
64     return metal_cpu_interrupt_controller(cpu);
65 }
66
67 void __metal_driver_riscv_clint0_init(struct metal_interrupt *controller) {
68     int num_interrupts =
69         __metal_driver_sifive_clint0_num_interrupts(controller);
70     struct __metal_driver_riscv_clint0 *clint =
71         (struct __metal_driver_riscv_clint0 *)(controller);
72
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);
79             int line =
80                 __metal_driver_sifive_clint0_interrupt_lines(controller, i);
81             intc->vtable->interrupt_register(intc, line, NULL, controller);
82         }
83         clint->init_done = 1;
84     }
85 }
86
87 int __metal_driver_riscv_clint0_register(struct metal_interrupt *controller,
88                                          int id, metal_interrupt_handler_t isr,
89                                          void *priv) {
90     int rc = -1;
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();
94     int num_interrupts =
95         __metal_driver_sifive_clint0_num_interrupts(controller);
96
97     if ((mode != METAL_VECTOR_MODE) && (mode != METAL_DIRECT_MODE)) {
98         return rc;
99     }
100
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) {
105             break;
106         }
107         intc = NULL;
108     }
109
110     /* Register its interrupts with parent controller */
111     if (intc) {
112         rc = intc->vtable->interrupt_register(intc, id, isr, priv);
113     }
114     return rc;
115 }
116
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 */
121     int rc = -1;
122     return rc;
123 }
124
125 metal_vector_mode __metal_driver_riscv_clint0_get_vector_mode(
126     struct metal_interrupt *controller) {
127     return __metal_controller_interrupt_vector_mode();
128 }
129
130 int __metal_driver_riscv_clint0_set_vector_mode(
131     struct metal_interrupt *controller, metal_vector_mode mode) {
132     int rc = -1;
133     struct metal_interrupt *intc = _get_cpu_intc();
134
135     if (intc) {
136         /* Valid vector modes are VECTOR and DIRECT, anything else is invalid
137          * (-1) */
138         switch (mode) {
139         case METAL_VECTOR_MODE:
140         case METAL_DIRECT_MODE:
141             rc = intc->vtable->interrupt_set_vector_mode(intc, mode);
142             break;
143         default:
144             break;
145         }
146     }
147     return rc;
148 }
149
150 int __metal_driver_riscv_clint0_enable(struct metal_interrupt *controller,
151                                        int id) {
152     int rc = -1;
153
154     if (id) {
155         struct metal_interrupt *intc = NULL;
156         struct metal_interrupt *cpu_intc = _get_cpu_intc();
157         int num_interrupts =
158             __metal_driver_sifive_clint0_num_interrupts(controller);
159
160         for (int i = 0; i < num_interrupts; i++) {
161             int line =
162                 __metal_driver_sifive_clint0_interrupt_lines(controller, i);
163             intc =
164                 __metal_driver_sifive_clint0_interrupt_parents(controller, i);
165             if (cpu_intc == intc && id == line) {
166                 break;
167             }
168             intc = NULL;
169         }
170
171         /* Enable its interrupts with parent controller */
172         if (intc) {
173             rc = intc->vtable->interrupt_enable(intc, id);
174         }
175     }
176
177     return rc;
178 }
179
180 int __metal_driver_riscv_clint0_disable(struct metal_interrupt *controller,
181                                         int id) {
182     int rc = -1;
183
184     if (id) {
185         struct metal_interrupt *intc = NULL;
186         struct metal_interrupt *cpu_intc = _get_cpu_intc();
187         int num_interrupts =
188             __metal_driver_sifive_clint0_num_interrupts(controller);
189
190         for (int i = 0; i < num_interrupts; i++) {
191             int line =
192                 __metal_driver_sifive_clint0_interrupt_lines(controller, i);
193             intc =
194                 __metal_driver_sifive_clint0_interrupt_parents(controller, i);
195             if (cpu_intc == intc && id == line) {
196                 break;
197             }
198             intc = NULL;
199         }
200
201         /* Disable its interrupts with parent controller */
202         if (intc) {
203             rc = intc->vtable->interrupt_disable(intc, id);
204         }
205     }
206
207     return rc;
208 }
209
210 int __metal_driver_riscv_clint0_command_request(
211     struct metal_interrupt *controller, int command, void *data) {
212     int hartid;
213     int rc = -1;
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);
218
219     switch (command) {
220     case METAL_TIMER_MTIME_GET:
221         if (data) {
222             *(unsigned long long *)data = __metal_clint0_mtime_get(clint);
223             rc = 0;
224         }
225         break;
226     case METAL_SOFTWARE_IPI_CLEAR:
227         if (data) {
228             hartid = *(int *)data;
229             __METAL_ACCESS_ONCE((
230                 __metal_io_u32 *)(control_base + (hartid * 4))) = METAL_DISABLE;
231             rc = 0;
232         }
233         break;
234     case METAL_SOFTWARE_IPI_SET:
235         if (data) {
236             hartid = *(int *)data;
237             __METAL_ACCESS_ONCE(
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.
247              */
248             __METAL_IO_FENCE(o, i);
249             rc = __METAL_ACCESS_ONCE(
250                 (__metal_io_u32 *)(control_base + (hartid * 4)));
251             rc = 0;
252         }
253         break;
254     case METAL_SOFTWARE_MSIP_GET:
255         rc = 0;
256         if (data) {
257             hartid = *(int *)data;
258             rc = __METAL_ACCESS_ONCE(
259                 (__metal_io_u32 *)(control_base + (hartid * 4)));
260         }
261         break;
262     default:
263         break;
264     }
265
266     return rc;
267 }
268
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);
274 }
275
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);
281 }
282
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,
298 };
299
300 #endif /* METAL_RISCV_CLINT0 */
301
302 typedef int no_empty_translation_units;