Просмотр исходного кода

!369 修复D1的PLIC不能产生中断的BUG
Merge pull request !369 from 胡自成/smart-d1

bernard 4 лет назад
Родитель
Сommit
f89ff0d029

+ 4 - 0
libcpu/risc-v/t-head/c906/encoding.h

@@ -103,6 +103,10 @@
 #define SIP_STIP    MIP_STIP /* timer interrupt */
 #define SIP_SEIP    MIP_SEIP /* ext interrupt */
 
+#define SIE_SSIE            (1 << IRQ_S_SOFT)
+#define SIE_STIE            (1 << IRQ_S_TIMER)
+#define SIE_SEIE            (1 << IRQ_S_EXT)
+
 #define RISCV_XLEN    64
 
 #define SCAUSE_INTERRUPT    (1UL << (RISCV_XLEN - 1))

+ 3 - 3
libcpu/risc-v/t-head/c906/interrupt.c

@@ -31,6 +31,9 @@ static void rt_hw_interrupt_handler(int vector, void *param)
  */
 void rt_hw_interrupt_init(void)
 {
+    /* init interrupt controller */
+    plic_init();
+
     rt_int32_t idx;
 
     rt_memset(isr_table, 0x00, sizeof(isr_table));
@@ -44,9 +47,6 @@ void rt_hw_interrupt_init(void)
     rt_interrupt_from_thread        = 0;
     rt_interrupt_to_thread          = 0;
     rt_thread_switch_interrupt_flag = 0;
-
-    /* Enable machine external interrupts. */
-    set_csr(sie, SIP_SEIP);
 }
 
 /**

+ 140 - 37
libcpu/risc-v/t-head/c906/plic.c

@@ -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);
 }

+ 36 - 13
libcpu/risc-v/t-head/c906/plic.h

@@ -11,24 +11,47 @@
 #ifndef __RISCV64_PLIC_H__
 #define __RISCV64_PLIC_H__
 
-// Platform level interrupt controller
-#define PLIC                    0x10000000L
-
-#define PLIC_PRIORITY           (PLIC + 0x0)
-#define PLIC_PENDING            (PLIC + 0x1000)
-#define PLIC_MENABLE(hart)      (PLIC + 0x2000 + (hart) * 0x100)      // machine interrupt enable
-#define PLIC_SENABLE(hart)      (PLIC + 0x2080 + (hart) * 0x100)      // supervisor interrupt enable
-#define PLIC_MPRIORITY(hart)    (PLIC + 0x200000 + (hart) * 0x2000)
-#define PLIC_SPRIORITY(hart)    (PLIC + 0x201000 + (hart) * 0x2000)
-#define PLIC_MCLAIM(hart)       (PLIC + 0x200004 + (hart) * 0x2000)
-#define PLIC_SCLAIM(hart)       (PLIC + 0x201004 + (hart) * 0x2000)
+#include <rt_interrupt.h>
+
+#define C906_PLIC_PHY_ADDR              (0x10000000)
+#define C906_PLIC_NR_EXT_IRQS           (IRQ_MAX_NR)
+#define C906_NR_CPUS                    (NR_CPUS)
+
+/* M and S mode context. */
+#define C906_NR_CONTEXT                 (2)
+
+#define MAX_DEVICES                     1024
+#define MAX_CONTEXTS                    15872
+
+/*
+ *  Each interrupt source has a priority register associated with it.
+ *  We always hardwire it to one in Linux.
+ */
+#define PRIORITY_BASE                   0
+#define PRIORITY_PER_ID                 4
+
+/*
+ *  Each hart context has a vector of interrupt enable bits associated with it.
+ *  There's one bit for each interrupt source.
+ */
+#define ENABLE_BASE                     0x2000
+#define ENABLE_PER_HART                 0x80
+
+/*
+ *  Each hart context has a set of control registers associated with it.  Right
+ *  now there's only two: a source priority threshold over which the hart will
+ *  take an interrupt, and a register to claim interrupts.
+ */
+#define CONTEXT_BASE                    0x200000
+#define CONTEXT_PER_HART                0x1000
+#define CONTEXT_THRESHOLD               0x00
+#define CONTEXT_CLAIM                   0x04
 
 void plic_init(void);
 void plic_enable_irq(int irqno);
 void plic_disable_irq(int irqno);
-// ask PLIC what interrupt we should serve
-int plic_claim(void);
 // tell PLIC that we've served this IRQ
 void plic_complete(int irq);
+void plic_handle_irq(void);
 
 #endif

+ 5 - 1
libcpu/risc-v/t-head/c906/rt_interrupt.h

@@ -13,8 +13,10 @@
 
 #include <rthw.h>
 
+#define NR_CPUS       1
+
 #define IRQ_OFFSET          16
-#define IRQ_MAX_NR          234
+#define IRQ_MAX_NR          207
 #define INTERRUPTS_MAX      (IRQ_OFFSET + IRQ_MAX_NR)
 
 enum {
@@ -41,4 +43,6 @@ void rt_hw_interrupt_mask(int vector);
 void rt_hw_interrupt_umask(int vector);
 rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, void *param, const char *name);
 
+void generic_handle_irq(int irq);
+
 #endif

+ 28 - 30
libcpu/risc-v/t-head/c906/trap.c

@@ -207,11 +207,35 @@ static const char *Interrupt_Name[] =
                                     "Reserved-11",
                                 };
 
-static int sys_ticks = 0;
-
 extern struct rt_irq_desc isr_table[];
 
-//Trap处理入口
+void generic_handle_irq(int irq)
+{
+    rt_isr_handler_t isr;
+    void *param;
+
+    if (irq < 0 || irq >= IRQ_MAX_NR)
+    {
+        LOG_E("bad irq number %d!\n", irq);
+        return;
+    }
+
+    if (!irq)   // irq = 0 => no irq
+    {
+        LOG_W("no irq!\n");
+        return;
+    }
+    isr = isr_table[IRQ_OFFSET + irq].handler;
+    param = isr_table[IRQ_OFFSET + irq].param;
+    if (isr != RT_NULL)
+    {
+        isr(irq, param);
+    }
+    /* complete irq. */
+    plic_complete(irq);
+}
+
+/* Trap entry */
 void handle_trap(rt_size_t scause,rt_size_t stval,rt_size_t sepc,struct rt_hw_stack_frame *sp)
 {
     rt_size_t id = __MASKVALUE(scause,__MASK(63UL));
@@ -221,39 +245,13 @@ void handle_trap(rt_size_t scause,rt_size_t stval,rt_size_t sepc,struct rt_hw_st
     if ((SCAUSE_INTERRUPT & scause) && SCAUSE_S_EXTERNAL_INTR == (scause & 0xff))
     {
         rt_interrupt_enter();
-        int irq = plic_claim();
-        rt_isr_handler_t isr;
-        void *param;
-
-        if (irq < 0 || irq >= IRQ_MAX_NR)
-        {
-            LOG_E("bad irq number %d!\n", irq);
-            return;
-        }
-
-        if (!irq)   // irq = 0 => no irq
-        {
-            LOG_W("no irq!\n");
-            return;
-        }
-        isr = isr_table[IRQ_OFFSET + irq].handler;
-        param = isr_table[IRQ_OFFSET + irq].param;
-        if (isr != RT_NULL)
-        {
-            isr(irq, param);
-        }
-        plic_complete(irq);
+        plic_handle_irq();
         rt_interrupt_leave();
         return;
     }
     else if ((SCAUSE_INTERRUPT | SCAUSE_S_TIMER_INTR) == scause)
     {
         /* supervisor timer */
-        sys_ticks++;
-        if (sys_ticks % RT_TICK_PER_SECOND == 0)
-        {
-            // rt_kprintf(".");
-        }
         rt_interrupt_enter();
         tick_isr();
         rt_interrupt_leave();