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