]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_clint0.c
Update RISCC-V-RV32-SiFive_HiFive1_FreedomStudio project to latest tools and metal...
[freertos] / FreeRTOS / Demo / RISC-V_RV32_SiFive_HiFive1_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/io.h>
9 #include <metal/cpu.h>
10 #include <metal/drivers/riscv_clint0.h>
11 #include <metal/machine.h>
12
13 unsigned long long __metal_clint0_mtime_get (struct __metal_driver_riscv_clint0 *clint)
14 {
15     __metal_io_u32 lo, hi;
16     unsigned long control_base = __metal_driver_sifive_clint0_control_base(&clint->controller);
17
18     /* Guard against rollover when reading */
19     do {
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);
23
24     return (((unsigned long long)hi) << 32) | lo;
25 }
26
27 int __metal_driver_riscv_clint0_mtimecmp_set(struct metal_interrupt *controller,
28                                              int hartid,
29                                              unsigned long long time)
30 {   
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
38      * value first.
39      */
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);
43     return 0;
44 }
45
46 static struct metal_interrupt *_get_cpu_intc()
47 {
48     int hartid = 0;
49     __asm__ volatile("csrr %[hartid], mhartid"
50                      : [hartid] "=r" (hartid) :: "memory");
51
52     struct metal_cpu *cpu = metal_cpu_get(hartid);
53
54     return metal_cpu_interrupt_controller(cpu);
55 }
56
57 void __metal_driver_riscv_clint0_init (struct metal_interrupt *controller)
58 {
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);
62
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);
69         }
70         clint->init_done = 1;
71     }   
72 }
73
74 int __metal_driver_riscv_clint0_register (struct metal_interrupt *controller,
75                                         int id, metal_interrupt_handler_t isr,
76                                         void *priv)
77 {
78     int rc = -1;
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);
83
84     if ( (mode != METAL_VECTOR_MODE) && (mode != METAL_DIRECT_MODE) ) {
85         return rc;
86     }
87
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) {
92             break;
93         }
94         intc = NULL;
95     }
96
97     /* Register its interrupts with parent controller */
98     if (intc) {
99         rc = intc->vtable->interrupt_register(intc, id, isr, priv);
100     }
101     return rc;
102 }
103
104 int __metal_driver_riscv_clint0_vector_register (struct metal_interrupt *controller,
105                                                  int id, metal_interrupt_vector_handler_t isr,
106                                                  void *priv)
107 {   
108     /* Not supported. User can override the 'weak' handler with their own */
109     int rc = -1;
110     return rc;
111 }
112
113 metal_vector_mode __metal_driver_riscv_clint0_get_vector_mode (struct metal_interrupt *controller)
114 {
115     return __metal_controller_interrupt_vector_mode();
116 }
117
118 int __metal_driver_riscv_clint0_set_vector_mode (struct metal_interrupt *controller, metal_vector_mode mode)
119 {
120     int rc = -1;
121     struct metal_interrupt *intc = _get_cpu_intc();
122
123     if (intc) {
124         /* Valid vector modes are VECTOR and DIRECT, anything else is invalid (-1) */
125         switch (mode) {
126         case METAL_VECTOR_MODE:
127         case METAL_DIRECT_MODE:
128             rc = intc->vtable->interrupt_set_vector_mode(intc, mode);
129             break;
130         case METAL_HARDWARE_VECTOR_MODE:
131         case METAL_SELECTIVE_NONVECTOR_MODE:
132         case METAL_SELECTIVE_VECTOR_MODE:
133         break;
134         }
135     }
136     return rc;
137 }
138
139 int __metal_driver_riscv_clint0_enable (struct metal_interrupt *controller, int id)
140 {
141     int rc = -1;
142
143     if ( 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);
147
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) {
152                 break;
153             }
154             intc = NULL;
155         }
156         
157         /* Enable its interrupts with parent controller */
158         if (intc) {
159             rc = intc->vtable->interrupt_enable(intc, id);
160         }
161     }
162
163     return rc;
164 }
165
166 int __metal_driver_riscv_clint0_disable (struct metal_interrupt *controller, int id)
167 {
168     int rc = -1;
169
170     if ( 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);
174
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) {
179                 break;
180             }
181             intc = NULL;
182         }
183         
184         /* Disable its interrupts with parent controller */
185         if (intc) {
186             rc = intc->vtable->interrupt_disable(intc, id);
187         }
188     }
189
190     return rc;
191 }
192
193 int __metal_driver_riscv_clint0_command_request (struct metal_interrupt *controller,
194                                                int command, void *data)
195 {
196     int hartid;
197     int rc = -1;
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);
201
202     switch (command) {
203     case METAL_TIMER_MTIME_GET:
204         if (data) {
205             *(unsigned long long *)data = __metal_clint0_mtime_get(clint);
206             rc = 0;
207         }
208         break;
209     case METAL_SOFTWARE_IPI_CLEAR:
210         if (data) {
211             hartid = *(int *)data;
212             __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
213                                                (hartid * 4))) = METAL_DISABLE;
214             rc = 0;
215         }
216         break;
217     case METAL_SOFTWARE_IPI_SET:
218         if (data) {
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.
230              */
231             __METAL_IO_FENCE(o,i);
232             rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
233                                                (hartid * 4)));
234             rc = 0;
235         }
236         break;
237     case METAL_SOFTWARE_MSIP_GET:
238         rc = 0;
239         if (data) {
240             hartid = *(int *)data;
241             rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
242                                                     (hartid * 4)));
243         }
244         break;
245     default:
246         break;
247     }
248
249     return rc;
250 }
251
252 int __metal_driver_riscv_clint0_clear_interrupt (struct metal_interrupt *controller, int id)
253 {
254     int hartid = metal_cpu_get_current_hartid();
255     return __metal_driver_riscv_clint0_command_request(controller,
256                                                 METAL_SOFTWARE_IPI_CLEAR, &hartid);
257 }
258
259 int __metal_driver_riscv_clint0_set_interrupt (struct metal_interrupt *controller, int id)
260 {
261     int hartid = metal_cpu_get_current_hartid();
262     return __metal_driver_riscv_clint0_command_request(controller,
263                                                 METAL_SOFTWARE_IPI_SET, &hartid);
264 }
265
266
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,
279 };
280
281 #endif /* METAL_RISCV_CLINT0 */
282
283 typedef int no_empty_translation_units;