|
|
@@ -6,69 +6,172 @@
|
|
|
* Change Logs:
|
|
|
* Date Author Notes
|
|
|
* 2021-10-19 JasonHu first version
|
|
|
+ * 2021-11-12 JasonHu fix bug that not intr on f133
|
|
|
*/
|
|
|
|
|
|
#include <rtthread.h>
|
|
|
-#include <cpuport.h>
|
|
|
|
|
|
#include <rtdbg.h>
|
|
|
|
|
|
#include "plic.h"
|
|
|
#include "rt_interrupt.h"
|
|
|
#include "io.h"
|
|
|
+#include "encoding.h"
|
|
|
|
|
|
-void plic_enable_irq(int irqno)
|
|
|
+static void *c906_plic_regs = RT_NULL;
|
|
|
+
|
|
|
+struct plic_handler
|
|
|
{
|
|
|
- if (irqno < 0 || irqno >= IRQ_MAX_NR) {
|
|
|
- LOG_E("[IRQ] plic enable bad irq %d!", irqno);
|
|
|
- return;
|
|
|
- }
|
|
|
- int hart = rt_hw_cpu_id();
|
|
|
+ rt_bool_t present;
|
|
|
+ void *hart_base;
|
|
|
+ void *enable_base;
|
|
|
+};
|
|
|
|
|
|
- uint32_t *enable_addr = (uint32_t *)PLIC_SENABLE(hart);
|
|
|
+rt_inline void plic_toggle(struct plic_handler *handler, int hwirq, int enable);
|
|
|
+struct plic_handler c906_plic_handlers[C906_NR_CPUS];
|
|
|
|
|
|
- enable_addr += irqno / 32;
|
|
|
- uint8_t irqno_off = irqno % 32;
|
|
|
- // set uart's enable bit for this hart's S-mode.
|
|
|
- writel(readl((void *)enable_addr) | (1 << irqno_off), enable_addr);
|
|
|
+rt_inline void plic_irq_toggle(int hwirq, int enable)
|
|
|
+{
|
|
|
+ int cpu = 0;
|
|
|
+
|
|
|
+ /* set priority of interrupt, interrupt 0 is zero. */
|
|
|
+ writel(enable, c906_plic_regs + PRIORITY_BASE + hwirq * PRIORITY_PER_ID);
|
|
|
+ struct plic_handler *handler = &c906_plic_handlers[cpu];
|
|
|
|
|
|
- uint32_t *priority_addr = (uint32_t *) PLIC_PRIORITY;
|
|
|
- priority_addr += irqno;
|
|
|
- writel(1, priority_addr);
|
|
|
+ if (handler->present)
|
|
|
+ {
|
|
|
+ plic_toggle(handler, hwirq, enable);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void plic_complete(int irqno)
|
|
|
+{
|
|
|
+ int cpu = 0;
|
|
|
+ struct plic_handler *handler = &c906_plic_handlers[cpu];
|
|
|
+
|
|
|
+ writel(irqno, handler->hart_base + CONTEXT_CLAIM);
|
|
|
}
|
|
|
|
|
|
void plic_disable_irq(int irqno)
|
|
|
{
|
|
|
- if (irqno < 0 || irqno >= IRQ_MAX_NR)
|
|
|
- {
|
|
|
- LOG_E("[IRQ] plic disable bad irq %d!", irqno);
|
|
|
- }
|
|
|
+ plic_irq_toggle(irqno, 0);
|
|
|
+}
|
|
|
|
|
|
- int hart = rt_hw_cpu_id();
|
|
|
+void plic_enable_irq(int irqno)
|
|
|
+{
|
|
|
+ plic_irq_toggle(irqno, 1);
|
|
|
+}
|
|
|
|
|
|
- uint32_t *enable_addr = (uint32_t *)PLIC_SENABLE(hart);
|
|
|
+/*
|
|
|
+ * Handling an interrupt is a two-step process: first you claim the interrupt
|
|
|
+ * by reading the claim register, then you complete the interrupt by writing
|
|
|
+ * that source ID back to the same claim register. This automatically enables
|
|
|
+ * and disables the interrupt, so there's nothing else to do.
|
|
|
+ */
|
|
|
+void plic_handle_irq(void)
|
|
|
+{
|
|
|
+ int cpu = 0;
|
|
|
+ unsigned int irq;
|
|
|
+
|
|
|
+ struct plic_handler *handler = &c906_plic_handlers[cpu];
|
|
|
+ void *claim = handler->hart_base + CONTEXT_CLAIM;
|
|
|
+
|
|
|
+ if (c906_plic_regs == RT_NULL || !handler->present)
|
|
|
+ {
|
|
|
+ LOG_E("plic state not initialized.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- enable_addr += irqno / 32;
|
|
|
- uint8_t irqno_off = irqno % 32;
|
|
|
- // set uart's enable bit for this hart's S-mode.
|
|
|
- writel(readl((void *)enable_addr) & ~(1 << irqno_off), enable_addr);
|
|
|
+ clear_csr(sie, SIE_SEIE);
|
|
|
|
|
|
- uint32_t *priority_addr = (uint32_t *) PLIC_PRIORITY;
|
|
|
- priority_addr += irqno;
|
|
|
- writel(0, priority_addr);
|
|
|
+ while ((irq = readl(claim)))
|
|
|
+ {
|
|
|
+ /* ID0 is diabled permantually from spec. */
|
|
|
+ if (irq == 0)
|
|
|
+ {
|
|
|
+ LOG_E("irq no is zero.");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ generic_handle_irq(irq);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ set_csr(sie, SIE_SEIE);
|
|
|
}
|
|
|
|
|
|
-// ask the PLIC what interrupt we should serve.
|
|
|
-int plic_claim(void)
|
|
|
+rt_inline void plic_toggle(struct plic_handler *handler, int hwirq, int enable)
|
|
|
{
|
|
|
- int hart = rt_hw_cpu_id();
|
|
|
- int irq = readl((void *)PLIC_SCLAIM(hart));
|
|
|
- return irq;
|
|
|
+ uint32_t *reg = handler->enable_base + (hwirq / 32) * sizeof(uint32_t);
|
|
|
+ uint32_t hwirq_mask = 1 << (hwirq % 32);
|
|
|
+
|
|
|
+ if (enable)
|
|
|
+ {
|
|
|
+ writel(readl(reg) | hwirq_mask, reg);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ writel(readl(reg) & ~hwirq_mask, reg);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-// tell the PLIC we've served this IRQ.
|
|
|
-void plic_complete(int irq)
|
|
|
+void plic_init(void)
|
|
|
{
|
|
|
- int hart = rt_hw_cpu_id();
|
|
|
- writel(irq, (void *)PLIC_SCLAIM(hart));
|
|
|
+ int nr_irqs;
|
|
|
+ int nr_context;
|
|
|
+ int i;
|
|
|
+ unsigned long hwirq;
|
|
|
+ int cpu = 0;
|
|
|
+
|
|
|
+ if (c906_plic_regs)
|
|
|
+ {
|
|
|
+ LOG_E("plic already initialized!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ nr_context = C906_NR_CONTEXT;
|
|
|
+
|
|
|
+ c906_plic_regs = (void *)C906_PLIC_PHY_ADDR;
|
|
|
+ if (!c906_plic_regs)
|
|
|
+ {
|
|
|
+ LOG_E("fatal error, plic is reg space is null.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ nr_irqs = C906_PLIC_NR_EXT_IRQS;
|
|
|
+
|
|
|
+ for (i = 0; i < nr_context; i ++)
|
|
|
+ {
|
|
|
+ struct plic_handler *handler;
|
|
|
+ uint32_t threshold = 0;
|
|
|
+
|
|
|
+ cpu = 0;
|
|
|
+
|
|
|
+ /* skip contexts other than supervisor external interrupt */
|
|
|
+ if (i == 0)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // we always use CPU0 M-mode target register.
|
|
|
+ handler = &c906_plic_handlers[cpu];
|
|
|
+ if (handler->present)
|
|
|
+ {
|
|
|
+ threshold = 0xffffffff;
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ handler->present = RT_TRUE;
|
|
|
+ handler->hart_base = c906_plic_regs + CONTEXT_BASE + i * CONTEXT_PER_HART;
|
|
|
+ handler->enable_base = c906_plic_regs + ENABLE_BASE + i * ENABLE_PER_HART;
|
|
|
+done:
|
|
|
+ /* priority must be > threshold to trigger an interrupt */
|
|
|
+ writel(threshold, handler->hart_base + CONTEXT_THRESHOLD);
|
|
|
+ for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
|
|
|
+ {
|
|
|
+ plic_toggle(handler, hwirq, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Enable supervisor external interrupts. */
|
|
|
+ set_csr(sie, SIE_SEIE);
|
|
|
}
|