diff options
| author | ZhangJing <[email protected]> | 2025-05-30 13:32:58 +0800 |
|---|---|---|
| committer | Rbb666 <[email protected]> | 2025-05-30 14:06:33 +0800 |
| commit | 97893c004c65760c638fd7eb571a08fc987a55e5 (patch) | |
| tree | 71c7c743e10a1281a104bfac56307e3d338a9818 /bsp/ultrarisc/arch/ur-cp100/plic.c | |
| parent | 93cc80e461d1e6d81e84c16caee4358f229e80f3 (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.c | 255 |
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 |
