]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_GCC/freedom-metal/src/drivers/riscv_clint0.c
f1814d3653ee2cc60bf861104c06218ce51a99fe
[freertos] / FreeRTOS / Demo / RISC-V_RV32_SiFive_HiFive1_GCC / 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
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     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) {
88             break;
89         }
90         intc = NULL;
91     }
92
93     /* Register its interrupts with parent controller */
94     if (intc) {
95         rc = intc->vtable->interrupt_register(intc, id, isr, priv);
96     }
97     return rc;
98 }
99
100 int __metal_driver_riscv_clint0_enable (struct metal_interrupt *controller, int id)
101 {
102     int rc = -1;
103
104     if ( 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);
108
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) {
113                 break;
114             }
115             intc = NULL;
116         }
117         
118         /* Enable its interrupts with parent controller */
119         if (intc) {
120             rc = intc->vtable->interrupt_enable(intc, id);
121         }
122     }
123 }
124
125 int __metal_driver_riscv_clint0_disable (struct metal_interrupt *controller, int id)
126 {
127     int rc = -1;
128
129     if ( 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);
133
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) {
138                 break;
139             }
140             intc = NULL;
141         }
142         
143         /* Disable its interrupts with parent controller */
144         if (intc) {
145             rc = intc->vtable->interrupt_disable(intc, id);
146         }
147     }
148 }
149
150 int __metal_driver_riscv_clint0_command_request (struct metal_interrupt *controller,
151                                                int command, void *data)
152 {
153     int hartid;
154     int rc = -1;
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);
158
159     switch (command) {
160     case METAL_TIMER_MTIME_GET:
161         if (data) {
162             *(unsigned long long *)data = __metal_clint0_mtime_get(clint);
163             rc = 0;
164         }
165         break;
166     case METAL_SOFTWARE_IPI_CLEAR:
167         if (data) {
168             hartid = *(int *)data;
169             __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
170                                                (hartid * 4))) = METAL_DISABLE;
171             rc = 0;
172         }
173         break;
174     case METAL_SOFTWARE_IPI_SET:
175         if (data) {
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.
187              */
188             __METAL_IO_FENCE(o,i);
189             rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
190                                                (hartid * 4)));
191             rc = 0;
192         }
193         break;
194     case METAL_SOFTWARE_MSIP_GET:
195         rc = 0;
196         if (data) {
197             hartid = *(int *)data;
198             rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
199                                                     (hartid * 4)));
200         }
201         break;
202     default:
203         break;
204     }
205
206     return rc;
207 }
208
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,
216 };
217
218 #endif /* METAL_RISCV_CLINT0 */