1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2021-10-19     JasonHu      first version
9  * 2021-11-12     JasonHu      fix bug that not intr on f133
10  * 2023-04-22     flyingcys    add plic register ioremap
11  */
12 
13 #include <rtthread.h>
14 
15 #include <rtdbg.h>
16 
17 #include "plic.h"
18 #include "interrupt.h"
19 #include "io.h"
20 #include "encoding.h"
21 #include "ioremap.h"
22 
23 static void *c906_plic_regs = RT_NULL;
24 extern struct rt_irq_desc isr_table[];
25 
26 struct plic_handler
27 {
28     rt_bool_t present;
29     void *hart_base;
30     void *enable_base;
31 };
32 
33 rt_inline void plic_toggle(struct plic_handler *handler, int hwirq, int enable);
34 struct plic_handler c906_plic_handlers[C906_NR_CPUS];
35 static void *c906_irq_priority[INTERRUPTS_MAX] = {RT_NULL};
36 
plic_irq_toggle(int hwirq,int enable)37 rt_inline void plic_irq_toggle(int hwirq, int enable)
38 {
39     int cpu = 0;
40     void *priority_addr;
41 
42     /* set priority of interrupt, interrupt 0 is zero. */
43     priority_addr = (void *)((rt_size_t)c906_plic_regs + PRIORITY_BASE + hwirq * PRIORITY_PER_ID);
44 #ifdef RT_USING_SMART
45     if (c906_irq_priority[hwirq] == RT_NULL)
46     {
47         c906_irq_priority[hwirq] = (void *)rt_ioremap(priority_addr, 0x1000);
48     }
49     priority_addr = c906_irq_priority[hwirq];
50 #endif
51     writel(enable, priority_addr);
52     struct plic_handler *handler = &c906_plic_handlers[cpu];
53 
54     if (handler->present)
55     {
56         plic_toggle(handler, hwirq, enable);
57     }
58 }
59 
generic_handle_irq(int irq)60 static void generic_handle_irq(int irq)
61 {
62     rt_isr_handler_t isr;
63     void *param;
64 
65     if (irq < 0 || irq >= IRQ_MAX_NR)
66     {
67         LOG_E("bad irq number %d!\n", irq);
68         return;
69     }
70 
71     if (!irq)   // irq = 0 => no irq
72     {
73         LOG_W("no irq!\n");
74         return;
75     }
76     isr = isr_table[IRQ_OFFSET + irq].handler;
77     param = isr_table[IRQ_OFFSET + irq].param;
78     if (isr != RT_NULL)
79     {
80         isr(irq, param);
81     }
82     /* complete irq. */
83     plic_complete(irq);
84 }
85 
plic_complete(int irqno)86 void plic_complete(int irqno)
87 {
88     int cpu = 0;
89     struct plic_handler *handler = &c906_plic_handlers[cpu];
90 
91     writel(irqno, (void *)((rt_size_t)handler->hart_base + CONTEXT_CLAIM));
92 }
93 
plic_disable_irq(int irqno)94 void plic_disable_irq(int irqno)
95 {
96     plic_irq_toggle(irqno, 0);
97 }
98 
plic_enable_irq(int irqno)99 void plic_enable_irq(int irqno)
100 {
101     plic_irq_toggle(irqno, 1);
102 }
103 
104 /*
105  * Handling an interrupt is a two-step process: first you claim the interrupt
106  * by reading the claim register, then you complete the interrupt by writing
107  * that source ID back to the same claim register.  This automatically enables
108  * and disables the interrupt, so there's nothing else to do.
109  */
plic_handle_irq(void)110 void plic_handle_irq(void)
111 {
112     int cpu = 0;
113     unsigned int irq;
114 
115     struct plic_handler *handler = &c906_plic_handlers[cpu];
116     void *claim = (void *)((rt_size_t)handler->hart_base + CONTEXT_CLAIM);
117 
118     if (c906_plic_regs == RT_NULL || !handler->present)
119     {
120         LOG_E("plic state not initialized.");
121         return;
122     }
123 
124     clear_csr(sie, SIE_SEIE);
125 
126     while ((irq = readl(claim)))
127     {
128         /* ID0 is diabled permantually from spec. */
129         if (irq == 0)
130         {
131             LOG_E("irq no is zero.");
132         }
133         else
134         {
135             generic_handle_irq(irq);
136         }
137     }
138     set_csr(sie, SIE_SEIE);
139 }
140 
plic_toggle(struct plic_handler * handler,int hwirq,int enable)141 rt_inline void plic_toggle(struct plic_handler *handler, int hwirq, int enable)
142 {
143     uint32_t  *reg = (uint32_t *)((rt_size_t)handler->enable_base + (hwirq / 32) * sizeof(uint32_t));
144     uint32_t hwirq_mask = 1 << (hwirq % 32);
145 
146     if (enable)
147     {
148         writel(readl(reg) | hwirq_mask, reg);
149     }
150     else
151     {
152         writel(readl(reg) & ~hwirq_mask, reg);
153     }
154 }
155 
plic_init(void)156 void plic_init(void)
157 {
158     int nr_irqs;
159     int nr_context;
160     int i;
161     unsigned long hwirq;
162     int cpu = 0;
163 
164     if (c906_plic_regs)
165     {
166         LOG_E("plic already initialized!");
167         return;
168     }
169 
170     nr_context = C906_NR_CONTEXT;
171 
172     c906_plic_regs = (void *)C906_PLIC_PHY_ADDR;
173     if (!c906_plic_regs)
174     {
175         LOG_E("fatal error, plic is reg space is null.");
176         return;
177     }
178 
179     nr_irqs = C906_PLIC_NR_EXT_IRQS;
180 
181     for (i = 0; i < nr_context; i ++)
182     {
183         struct plic_handler *handler;
184         uint32_t threshold = 0;
185 
186         cpu = 0;
187 
188         /* skip contexts other than supervisor external interrupt */
189         if (i == 0)
190         {
191             continue;
192         }
193 
194         // we always use CPU0 M-mode target register.
195         handler = &c906_plic_handlers[cpu];
196         if (handler->present)
197         {
198             threshold  = 0xffffffff;
199             goto done;
200         }
201 
202         handler->present = RT_TRUE;
203         handler->hart_base = (void *)((rt_size_t)c906_plic_regs + CONTEXT_BASE + i * CONTEXT_PER_HART);
204         handler->enable_base = (void *)((rt_size_t)c906_plic_regs + ENABLE_BASE + i * ENABLE_PER_HART);
205 #ifdef RT_USING_SMART
206         handler->hart_base = (void *)rt_ioremap(handler->hart_base, 0x1000);
207         handler->enable_base = (void *)rt_ioremap(handler->enable_base, 0x1000);
208 #endif
209 done:
210         /* priority must be > threshold to trigger an interrupt */
211         writel(threshold, (void *)((rt_size_t)handler->hart_base + CONTEXT_THRESHOLD));
212         for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
213         {
214             plic_toggle(handler, hwirq, 0);
215         }
216     }
217 
218     /* Enable supervisor external interrupts. */
219     set_csr(sie, SIE_SEIE);
220 }
221