1 /* Copyright 2018 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
4 #include <metal/machine/platform.h>
6 #ifdef METAL_RISCV_PLIC0
9 #include <metal/shutdown.h>
10 #include <metal/drivers/riscv_plic0.h>
11 #include <metal/machine.h>
13 unsigned int __metal_plic0_claim_interrupt (struct __metal_driver_riscv_plic0 *plic)
15 unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic);
16 return __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
17 METAL_RISCV_PLIC0_CLAIM));
20 void __metal_plic0_complete_interrupt(struct __metal_driver_riscv_plic0 *plic,
23 unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic);
24 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
25 METAL_RISCV_PLIC0_CLAIM)) = id;
28 void __metal_plic0_set_threshold(struct __metal_driver_riscv_plic0 *plic,
29 unsigned int threshold)
31 unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic);
32 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
33 METAL_RISCV_PLIC0_THRESHOLD)) = threshold;
36 void __metal_plic0_set_priority(struct __metal_driver_riscv_plic0 *plic,
37 int id, unsigned int priority)
39 unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic);
40 int max_priority = __metal_driver_sifive_plic0_max_priority((struct metal_interrupt *)plic);
41 if ( (max_priority) && (priority < max_priority) ) {
42 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
43 METAL_RISCV_PLIC0_PRIORITY_BASE +
44 (id << METAL_PLIC_SOURCE_PRIORITY_SHIFT))) = priority;
48 void __metal_plic0_enable(struct __metal_driver_riscv_plic0 *plic, int id, int enable)
51 unsigned long hartid = __metal_myhart_id();
52 unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic);
54 current = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
55 METAL_RISCV_PLIC0_ENABLE_BASE +
56 (id >> METAL_PLIC_SOURCE_SHIFT) * 4));
57 __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
58 METAL_RISCV_PLIC0_ENABLE_BASE +
59 ((id >> METAL_PLIC_SOURCE_SHIFT) * 4))) =
60 enable ? (current | (1 << (id & METAL_PLIC_SOURCE_MASK)))
61 : (current & ~(1 << (id & METAL_PLIC_SOURCE_MASK)));
64 void __metal_plic0_default_handler (int id, void *priv) {
68 void __metal_plic0_handler (int id, void *priv)
70 struct __metal_driver_riscv_plic0 *plic = priv;
71 unsigned int idx = __metal_plic0_claim_interrupt(plic);
72 int num_interrupts = __metal_driver_sifive_plic0_num_interrupts((struct metal_interrupt *)plic);
74 if ( (idx < num_interrupts) && (plic->metal_exint_table[idx]) ) {
75 plic->metal_exint_table[idx](idx,
76 plic->metal_exdata_table[idx].exint_data);
79 __metal_plic0_complete_interrupt(plic, idx);
82 void __metal_driver_riscv_plic0_init (struct metal_interrupt *controller)
84 struct __metal_driver_riscv_plic0 *plic = (void *)(controller);
86 if ( !plic->init_done ) {
87 int num_interrupts, line;
88 struct metal_interrupt *intc;
90 for(int parent = 0; parent < __METAL_PLIC_NUM_PARENTS; parent++) {
91 num_interrupts = __metal_driver_sifive_plic0_num_interrupts(controller);
92 intc = __metal_driver_sifive_plic0_interrupt_parents(controller, parent);
93 line = __metal_driver_sifive_plic0_interrupt_lines(controller, parent);
95 /* Initialize ist parent controller, aka cpu_intc. */
96 intc->vtable->interrupt_init(intc);
98 for (int i = 0; i < num_interrupts; i++) {
99 __metal_plic0_enable(plic, i, METAL_DISABLE);
100 __metal_plic0_set_priority(plic, i, 0);
101 plic->metal_exint_table[i] = NULL;
102 plic->metal_exdata_table[i].sub_int = NULL;
103 plic->metal_exdata_table[i].exint_data = NULL;
106 __metal_plic0_set_threshold(plic, 0);
108 /* Register plic (ext) interrupt with with parent controller */
109 intc->vtable->interrupt_register(intc, line, NULL, plic);
110 /* Register plic handler for dispatching its device interrupts */
111 intc->vtable->interrupt_register(intc, line, __metal_plic0_handler, plic);
112 /* Enable plic (ext) interrupt with with parent controller */
113 intc->vtable->interrupt_enable(intc, line);
119 int __metal_driver_riscv_plic0_register (struct metal_interrupt *controller,
120 int id, metal_interrupt_handler_t isr,
123 struct __metal_driver_riscv_plic0 *plic = (void *)(controller);
125 if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) {
130 __metal_plic0_set_priority(plic ,id, 2);
131 plic->metal_exint_table[id] = isr;
132 plic->metal_exdata_table[id].exint_data = priv;
134 __metal_plic0_set_priority(plic, id, 1);
135 plic->metal_exint_table[id] = __metal_plic0_default_handler;
136 plic->metal_exdata_table[id].sub_int = priv;
142 int __metal_driver_riscv_plic0_enable (struct metal_interrupt *controller, int id)
144 struct __metal_driver_riscv_plic0 *plic = (void *)(controller);
146 if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) {
150 __metal_plic0_enable(plic, id, METAL_ENABLE);
154 int __metal_driver_riscv_plic0_disable (struct metal_interrupt *controller, int id)
156 struct __metal_driver_riscv_plic0 *plic = (void *)(controller);
158 if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) {
161 __metal_plic0_enable(plic, id, METAL_DISABLE);
165 __METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_plic0) = {
166 .plic_vtable.interrupt_init = __metal_driver_riscv_plic0_init,
167 .plic_vtable.interrupt_register = __metal_driver_riscv_plic0_register,
168 .plic_vtable.interrupt_enable = __metal_driver_riscv_plic0_enable,
169 .plic_vtable.interrupt_disable = __metal_driver_riscv_plic0_disable,
172 #endif /* METAL_RISCV_PLIC0 */