1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2021-05-20 bigmagic first version
9 * 2022-09-16 WangXiaoyao Porting to rv64
10 */
11 #include <rthw.h>
12 #include <rtthread.h>
13 #include <stdint.h>
14 #include "plic.h"
15 #include <riscv_io.h>
16 #include "encoding.h"
17 #include <interrupt.h>
18
19 #include <riscv.h>
20 #include <string.h>
21 #include <stdlib.h>
22
23 #ifdef RT_USING_SMART
24 #include <ioremap.h>
25 #else
26 #define rt_ioremap(addr, ...) (addr)
27 #endif
28
29 size_t plic_base = 0x0c000000L;
30
31 /*
32 * Each PLIC interrupt source can be assigned a priority by writing
33 * to its 32-bit memory-mapped priority register.
34 * The QEMU-virt (the same as FU540-C000) supports 7 levels of priority.
35 * A priority value of 0 is reserved to mean "never interrupt" and
36 * effectively disables the interrupt.
37 * Priority 1 is the lowest active priority, and priority 7 is the highest.
38 * Ties between global interrupts of the same priority are broken by
39 * the Interrupt ID; interrupts with the lowest ID have the highest
40 * effective priority.
41 */
plic_set_priority(int irq,int priority)42 void plic_set_priority(int irq, int priority)
43 {
44 *(uint32_t *)PLIC_PRIORITY(irq) = priority;
45 }
46
47 /*
48 * Each global interrupt can be enabled by setting the corresponding
49 * bit in the enables registers.
50 */
plic_irq_enable(int irq)51 void plic_irq_enable(int irq)
52 {
53 int hart = __raw_hartid();
54 *(uint32_t *)PLIC_ENABLE(hart) = ((*(uint32_t *)PLIC_ENABLE(hart)) | (1 << irq));
55 #ifdef RISCV_VIRT64_S_MODE
56 set_csr(sie, read_csr(sie) | MIP_SEIP);
57 #else
58 set_csr(mie, read_csr(mie) | MIP_MEIP);
59 #endif
60 }
61
plic_irq_disable(int irq)62 void plic_irq_disable(int irq)
63 {
64 int hart = __raw_hartid();
65 *(uint32_t *)PLIC_ENABLE(hart) = (((*(uint32_t *)PLIC_ENABLE(hart)) & (~(1 << irq))));
66 }
67
68 /*
69 * PLIC will mask all interrupts of a priority less than or equal to threshold.
70 * Maximum threshold is 7.
71 * For example, a threshold value of zero permits all interrupts with
72 * non-zero priority, whereas a value of 7 masks all interrupts.
73 * Notice, the threshold is global for PLIC, not for each interrupt source.
74 */
plic_set_threshold(int threshold)75 void plic_set_threshold(int threshold)
76 {
77 int hart = __raw_hartid();
78 *(uint32_t *)PLIC_THRESHOLD(hart) = threshold;
79 }
80
81 /*
82 * DESCRIPTION:
83 * Query the PLIC what interrupt we should serve.
84 * Perform an interrupt claim by reading the claim register, which
85 * returns the ID of the highest-priority pending interrupt or zero if there
86 * is no pending interrupt.
87 * A successful claim also atomically clears the corresponding pending bit
88 * on the interrupt source.
89 * RETURN VALUE:
90 * the ID of the highest-priority pending interrupt or zero if there
91 * is no pending interrupt.
92 */
plic_claim(void)93 int plic_claim(void)
94 {
95 int hart = __raw_hartid();
96 int irq = *(uint32_t *)PLIC_CLAIM(hart);
97 return irq;
98 }
99
100 /*
101 * DESCRIPTION:
102 * Writing the interrupt ID it received from the claim (irq) to the
103 * complete register would signal the PLIC we've served this IRQ.
104 * The PLIC does not check whether the completion ID is the same as the
105 * last claim ID for that target. If the completion ID does not match an
106 * interrupt source that is currently enabled for the target, the completion
107 * is silently ignored.
108 * RETURN VALUE: none
109 */
plic_complete(int irq)110 void plic_complete(int irq)
111 {
112 int hart = __raw_hartid();
113 *(uint32_t *)PLIC_COMPLETE(hart) = irq;
114 }
115
plic_set_ie(rt_uint32_t word_index,rt_uint32_t val)116 void plic_set_ie(rt_uint32_t word_index, rt_uint32_t val)
117 {
118 volatile void *plic_ie = (void *)(rt_ubase_t)(plic_base + PLIC_ENABLE_BASE + 0x80 + word_index * 4);
119 writel(val, plic_ie);
120 }
121
_set_sie(int hartid)122 static void _set_sie(int hartid)
123 {
124 for (size_t i = hartid * WORD_CNT_BYTE; i < 32; i++)
125 plic_set_ie(i, 0xffffffff);
126 }
127
plic_init()128 void plic_init()
129 {
130 // PLIC takes up 64 MB space
131 plic_base = (size_t)rt_ioremap((void *)plic_base, 64 * 1024 * 1024);
132
133 plic_set_threshold(0);
134
135 for (int i = 0; i < CONFIG_IRQ_NR; i++)
136 {
137 plic_set_priority(i, 1);
138 }
139
140 // in a single core system, only current context was set
141 _set_sie(__raw_hartid());
142 }
143
144 extern struct rt_irq_desc irq_desc[MAX_HANDLERS];
145 /*
146 * Handling an interrupt is a two-step process: first you claim the interrupt
147 * by reading the claim register, then you complete the interrupt by writing
148 * that source ID back to the same claim register. This automatically enables
149 * and disables the interrupt, so there's nothing else to do.
150 */
plic_handle_irq(void)151 void plic_handle_irq(void)
152 {
153 int plic_irq = plic_claim();
154 plic_complete(plic_irq);
155 irq_desc[plic_irq].handler(plic_irq, irq_desc[plic_irq].param);
156 }
157