123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- /*
- * 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;
- }
|