1 /*
2  * Copyright (c) 2018 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #include <dev/interrupt/riscv_plic.h>
9 
10 #include <assert.h>
11 #include <lk/err.h>
12 #include <lk/debug.h>
13 #include <lk/reg.h>
14 #include <lk/trace.h>
15 #include <kernel/debug.h>
16 #include <kernel/thread.h>
17 #include <platform/interrupts.h>
18 
19 // Driver for simplic PLIC implementations for various RISC-V machines.
20 
21 #define LOCAL_TRACE 0
22 
23 // Preallocate space for up to 256 vectors.
24 // If more are needed will need to bump this up or switch to a dynamic scheme.
25 #define MAX_IRQS 256
26 static struct int_handlers {
27     int_handler handler;
28     void *arg;
29 } handlers[MAX_IRQS];
30 
31 static uintptr_t plic_base_virt = 0;
32 static size_t num_irqs = 0;
33 static bool hart0_m_only = false;
34 
35 #define PLIC_PRIORITY(irq)          (plic_base_virt + 4 * (irq))
36 #define PLIC_PENDING(irq)           (plic_base_virt + 0x1000 + (4 * ((irq) / 32)))
37 #define PLIC_ENABLE(irq, hart)      (plic_base_virt + 0x2000 + (0x80 * plic_hart_index(hart)) + (4 * ((irq) / 32)))
38 #define PLIC_THRESHOLD(hart)        (plic_base_virt + 0x200000 + (0x1000 * plic_hart_index(hart)))
39 #define PLIC_COMPLETE(hart)         (plic_base_virt + 0x200004 + (0x1000 * plic_hart_index(hart)))
40 #define PLIC_CLAIM(hart)            PLIC_COMPLETE(hart)
41 
42 // Mapping of HART to interrupt target is annoyingly complex. Switch between two modes
43 // based on the hart0_m_only bool:
44 //
45 // On the JH7110 (like other sifive socs) the first HART only has one mode, machine
46 // and the subsequent harts have both machine and supervisor. The interrupt targets
47 // are thus indexed:
48 // HART 0 machine mode = 0
49 // HART 1 machine mode = 1
50 // HART 1 supervisor mode = 2
51 // HART 2 machine mode = 3
52 // HART 2 supervisor mode = 4
53 // ...
54 //
55 // On flatter designs, such as qemu's 'virt' machine, all harts are equal and 0 indexed,
56 // so the mapping is simpler:
57 // HART 0 machine mode = 0
58 // HART 0 supervisor mode = 1
59 // HART 1 machine mode = 2
60 // HART 1 supervisor mode = 3
61 // HART 2 machine mode = 4
62 // HART 2 supervisor mode = 5
63 // ...
64 //
65 // This routine maps harts to the current mode's interrupt target
plic_hart_index(unsigned int hart)66 static unsigned int plic_hart_index(unsigned int hart) {
67     unsigned int index;
68     if (hart0_m_only) {
69 #if RISCV_M_MODE
70         index = (hart == 0) ? 0 : (2 * hart - 1);
71 #elif RISCV_S_MODE
72         DEBUG_ASSERT(hart != 0);
73         index = 2 * hart;
74 #else
75 #error undefined
76 #endif
77     } else {
78 #if RISCV_M_MODE
79         index = 2 * hart;
80 #elif RISCV_S_MODE
81         index = 2 * hart + 1;
82 #else
83 #error undefined
84 #endif
85     }
86     return index;
87 }
88 
plic_early_init(uintptr_t base,size_t num_irqs_,bool hart0_m_only_)89 void plic_early_init(uintptr_t base, size_t num_irqs_, bool hart0_m_only_) {
90     plic_base_virt = base;
91     DEBUG_ASSERT(num_irqs_ <= MAX_IRQS);
92     num_irqs = num_irqs_;
93     hart0_m_only = hart0_m_only_;
94 
95     // mask all irqs and set their priority to 1
96     // TODO: mask on all the other cpus too
97     for (size_t i = 1; i < num_irqs; i++) {
98         *REG32(PLIC_ENABLE(i, riscv_current_hart())) &= ~(1 << (i % 32));
99         *REG32(PLIC_PRIORITY(i)) = 1;
100     }
101 
102     // set global priority threshold to 0
103     *REG32(PLIC_THRESHOLD(riscv_current_hart())) = 0;
104 }
105 
plic_init(void)106 void plic_init(void) {}
107 
mask_interrupt(unsigned int vector)108 status_t mask_interrupt(unsigned int vector) {
109     LTRACEF("vector %u, current hart %u\n", vector, riscv_current_hart());
110     *REG32(PLIC_ENABLE(vector, riscv_current_hart())) &= ~(1 << (vector % 32));
111     return NO_ERROR;
112 }
113 
unmask_interrupt(unsigned int vector)114 status_t unmask_interrupt(unsigned int vector) {
115     LTRACEF("vector %u, current hart %u\n", vector, riscv_current_hart());
116     *REG32(PLIC_ENABLE(vector, riscv_current_hart())) |= (1 << (vector % 32));
117 
118     return NO_ERROR;
119 }
120 
register_int_handler(unsigned int vector,int_handler handler,void * arg)121 void register_int_handler(unsigned int vector, int_handler handler, void *arg) {
122     LTRACEF("vector %u handler %p arg %p, hart %u\n", vector, handler, arg, riscv_current_hart());
123 
124     DEBUG_ASSERT(vector < num_irqs);
125 
126     handlers[vector].handler = handler;
127     handlers[vector].arg = arg;
128 }
129 
register_int_handler_msi(unsigned int vector,int_handler handler,void * arg,bool edge)130 void register_int_handler_msi(unsigned int vector, int_handler handler, void *arg, bool edge) {
131     PANIC_UNIMPLEMENTED;
132 }
133 
riscv_platform_irq(void)134 enum handler_return riscv_platform_irq(void) {
135     // see what irq triggered it
136     uint32_t vector = *REG32(PLIC_CLAIM(riscv_current_hart()));
137     LTRACEF("vector %u\n", vector);
138 
139     if (unlikely(vector == 0)) {
140         // nothing pending
141         return INT_NO_RESCHEDULE;
142     }
143 
144     THREAD_STATS_INC(interrupts);
145     KEVLOG_IRQ_ENTER(vector);
146 
147     enum handler_return ret = INT_NO_RESCHEDULE;
148     if (handlers[vector].handler) {
149         ret = handlers[vector].handler(handlers[vector].arg);
150     }
151 
152     // ack the interrupt
153     *REG32(PLIC_COMPLETE(riscv_current_hart())) = vector;
154 
155     KEVLOG_IRQ_EXIT(vector);
156 
157     return ret;
158 }
159 
160