summaryrefslogtreecommitdiff
path: root/bsp/ultrarisc/arch/ur-cp100/plic.c
diff options
context:
space:
mode:
authorZhangJing <[email protected]>2025-05-30 13:32:58 +0800
committerRbb666 <[email protected]>2025-05-30 14:06:33 +0800
commit97893c004c65760c638fd7eb571a08fc987a55e5 (patch)
tree71c7c743e10a1281a104bfac56307e3d338a9818 /bsp/ultrarisc/arch/ur-cp100/plic.c
parent93cc80e461d1e6d81e84c16caee4358f229e80f3 (diff)
[bsp]增加超睿DP1000 bsp支持v5.2.1
Diffstat (limited to 'bsp/ultrarisc/arch/ur-cp100/plic.c')
-rw-r--r--bsp/ultrarisc/arch/ur-cp100/plic.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/bsp/ultrarisc/arch/ur-cp100/plic.c b/bsp/ultrarisc/arch/ur-cp100/plic.c
new file mode 100644
index 0000000000..7317b5b85e
--- /dev/null
+++ b/bsp/ultrarisc/arch/ur-cp100/plic.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2006-2025 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Support for RISC-V Platform-Level Interrupt Controller(PLIC)
+ * Specification Version 1.0.0, which is currently(3/12/2023) the
+ * lastest draft.
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-05-20 bigmagic first version
+ * 2022-09-16 WangXiaoyao Porting to rv64
+ * 2025-01-26 ZhangJing Porting to ultrarisc cp100
+ */
+#include <rthw.h>
+#include <rtthread.h>
+#include <stdint.h>
+#include "plic.h"
+#include <io.h>
+#include "encoding.h"
+#include <interrupt.h>
+#include <riscv_io.h>
+#include <riscv.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef RT_USING_SMART
+ #include <ioremap.h>
+#else
+ #define rt_ioremap(addr, ...) (addr)
+#endif
+
+/* plic_base should be initialized in the bsp*/
+size_t plic_base = 0 ;
+
+/*
+ * Each PLIC interrupt source can be assigned a priority by writing
+ * to its 32-bit memory-mapped priority register.
+ * Maximum priority is 7.
+ * A priority value of 0 is reserved to mean "never interrupt" and
+ * effectively disables the interrupt.
+ * Ties between global interrupts of the same priority are broken by
+ * the Interrupt ID; interrupts with the lowest ID have the highest
+ * effective priority.
+ */
+void plic_set_priority(int irq, int priority)
+{
+ writel(priority, (void *)PLIC_PRIORITY(irq));
+}
+
+/*
+ * Each global interrupt can be enabled by setting the corresponding
+ * bit in the enables registers.
+ */
+void plic_irq_enable(int irq)
+{
+ int hart = __raw_hartid();
+ void *enable = (void *)PLIC_ENABLE(hart) + (irq / 32) * sizeof(uint32_t);
+ rt_uint32_t val = readl(enable);
+ val |= (1 << (irq % 32));
+ writel(val, enable);
+ return;
+}
+
+/*
+ * Each global interrupt can be disabled by clearing the corresponding
+ * bit in the enables registers.
+ */
+void plic_irq_disable(int irq)
+{
+ int hart = __raw_hartid();
+ void *enable = (void *)PLIC_ENABLE(hart) + (irq / 32) * sizeof(uint32_t);
+ rt_uint32_t val = readl(enable);
+ val &= ~(1 << (irq % 32));
+ writel(val, enable);
+
+ return;
+}
+
+/*
+ * PLIC will mask all interrupts of a priority less than or equal to threshold.
+ * Maximum threshold is 7.
+ * For example, a threshold value of zero permits all interrupts with
+ * non-zero priority, whereas a value of 7 masks all interrupts.
+ * Notice, the threshold is global for PLIC, not for each interrupt source.
+ */
+void plic_set_threshold(int threshold)
+{
+ int hart = __raw_hartid();
+ writel(threshold, (void *)PLIC_THRESHOLD(hart));
+ return;
+}
+
+/*
+ * DESCRIPTION:
+ * Query the PLIC what interrupt we should serve.
+ * Perform an interrupt claim by reading the claim register, which
+ * returns the ID of the highest-priority pending interrupt or zero if there
+ * is no pending interrupt.
+ * A successful claim also atomically clears the corresponding pending bit
+ * on the interrupt source.
+ * RETURN VALUE:
+ * the ID of the highest-priority pending interrupt or zero if there
+ * is no pending interrupt.
+ */
+rt_uint32_t plic_claim(void)
+{
+ int hart = __raw_hartid();
+ void *claim = (void *)PLIC_CLAIM(hart);
+ return readl(claim);
+}
+
+/*
+ * DESCRIPTION:
+ * Writing the interrupt ID it received from the claim (irq) to the
+ * complete register would signal the PLIC we've served this IRQ.
+ * The PLIC does not check whether the completion ID is the same as the
+ * last claim ID for that target. If the completion ID does not match an
+ * interrupt source that is currently enabled for the target, the completion
+ * is silently ignored.
+ * RETURN VALUE: none
+ */
+void plic_complete(int irq)
+{
+ int hart = __raw_hartid();
+ void *complete = (void *)PLIC_COMPLETE(hart);
+ writel(irq, complete);
+ return;
+}
+
+void plic_init()
+{
+ if (!plic_base)
+ {
+ return;
+ }
+ /* PLIC takes up 64 MB space */
+ plic_base = (size_t)rt_ioremap((void *)plic_base, 64 * 1024 * 1024);
+
+ plic_set_threshold(0);
+
+ /*set the same priority for all the irqs*/
+ for (int i = 1; i < CONFIG_IRQ_NR; i++)
+ {
+ plic_set_priority(i, 1);
+ }
+
+ /*disable all interrupts*/
+ for (int i = 1; i < CONFIG_IRQ_NR; i++)
+ {
+ plic_irq_disable(i);
+ }
+
+ set_csr(sie, read_csr(sie) | MIP_SEIP);
+
+ return;
+}
+
+extern struct rt_irq_desc irq_desc[MAX_HANDLERS];
+#ifdef BOARD_UR_DP1000
+int find_first_bit(rt_uint32_t *addr, int size)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ if (*addr & (1 << i))
+ return i;
+ }
+ return -1;
+}
+
+static rt_bool_t is_irqs_pending(rt_uint32_t ie[])
+{
+
+ int hartid = __raw_hartid();
+ int nr_irqs = CONFIG_IRQ_NR;
+ int nr_irq_groups = (nr_irqs + 31) / 32;
+ rt_bool_t is_pending = RT_FALSE;
+ void *pending_base = (void *)PLIC_PENDING(0);
+ void *enable_base = (void *)PLIC_ENABLE(hartid);
+ int i, j;
+
+ for (i = 0; i < nr_irq_groups; i++)
+ ie[i] = readl(enable_base + i * sizeof(rt_uint32_t));
+
+ for (i = 0; i < nr_irq_groups; i++)
+ {
+ rt_uint32_t pending_irqs = readl(pending_base + i * sizeof(rt_uint32_t)) & ie[i];
+ if (pending_irqs)
+ {
+ int nbit = find_first_bit(&pending_irqs, 32);
+ for (j = 0; j < nr_irq_groups; j++)
+ writel((i == j) ? (1 << nbit) : 0, enable_base + j * sizeof(rt_uint32_t));
+
+ is_pending = RT_TRUE;
+ break;
+ }
+ }
+
+ return is_pending;
+}
+
+static void restore_irqs_enable(rt_uint32_t ie[])
+{
+ int hartid = __raw_hartid();
+ int nr_irqs = CONFIG_IRQ_NR;
+ int nr_irq_groups = (nr_irqs + 31) / 32;
+ void *enable_base = (void *)PLIC_ENABLE(hartid);
+ int i;
+
+ for (i = 0; i < nr_irq_groups; i++)
+ writel(ie[i], enable_base + i * sizeof(rt_uint32_t));
+
+ return;
+}
+#endif
+/*
+ * 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)
+{
+ rt_uint32_t plic_irq;
+#ifdef BOARD_UR_DP1000
+ /* TODO: if not only one interrupt, we need to continue to check the interrupt source */
+ unsigned int ie[32] = {0};
+ plic_irq = is_irqs_pending(ie) ? plic_claim() : 0;
+ restore_irqs_enable(ie);
+ if (plic_irq > CONFIG_IRQ_NR)
+ {
+ /*spurious interrupt, return directly*/
+ rt_kprintf("spurious interrupt, irq = %d\n", plic_irq);
+ return;
+ }
+ plic_complete(plic_irq);
+
+ irq_desc[plic_irq].handler(plic_irq, irq_desc[plic_irq].param);
+
+#else
+ plic_irq = plic_claim();
+ plic_complete(plic_irq);
+ if (plic_irq > CONFIG_IRQ_NR)
+ {
+ /*spurious interrupt, return directly*/
+ rt_kprintf("spurious interrupt, irq = %d\n", plic_irq);
+ return;
+ }
+
+ irq_desc[plic_irq].handler(plic_irq, irq_desc[plic_irq].param);
+#endif
+ return;
+} \ No newline at end of file