RISC-V: Add PLIC interface and HiFive driver
We assume the PLIC is currently the only global interrupt controller that RISC-V platforms are going to use. Each platform may have a different programmers model for interracting with the hardware controller. We provide a common interface for PLIC drivers to implement that the kernel will use to manage IRQs. Also authored by: Siwei Zhuang <siwei.zhuang@data61.csiro.au>
This commit is contained in:
parent
4b24006678
commit
c73d5178d9
4 changed files with 185 additions and 7 deletions
|
|
@ -13,6 +13,114 @@
|
|||
#ifndef __PLAT_INSTANCE_HARDWARE_H
|
||||
#define __PLAT_INSTANCE_HARDWARE_H
|
||||
|
||||
#define PLIC_MAX_NUM_INT 53
|
||||
|
||||
#define PLIC_BASE 0x0C000000
|
||||
#define PLIC_PPTR_BASE 0xFFFFFFFFCC000000
|
||||
|
||||
#define PLIC_HARTID 2
|
||||
|
||||
#define PLIC_PRIO 0x0
|
||||
#define PLIC_PRIO_PER_ID 0x4
|
||||
|
||||
#define PLIC_EN 0x2000
|
||||
#define PLIC_EN_PER_HART 0x80
|
||||
|
||||
#define PLIC_THRES 0x200000
|
||||
#define PLIC_THRES_PER_HART 0x1000
|
||||
#define PLIC_THRES_CLAIM 0x4
|
||||
|
||||
static inline uint32_t readl(const volatile uint64_t addr)
|
||||
{
|
||||
uint32_t val;
|
||||
asm volatile("lw %0, 0(%1)" : "=r"(val) : "r"(addr));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void writel(uint32_t val, volatile uint64_t addr)
|
||||
{
|
||||
asm volatile("sw %0, 0(%1)" : : "r"(val), "r"(addr));
|
||||
}
|
||||
|
||||
|
||||
static inline interrupt_t plic_get_claim(void)
|
||||
{
|
||||
/* Read the claim register for our HART interrupt context */
|
||||
return readl(PLIC_PPTR_BASE + PLIC_THRES + PLIC_THRES_PER_HART * PLIC_HARTID +
|
||||
PLIC_THRES_CLAIM);
|
||||
}
|
||||
|
||||
static inline void plic_complete_claim(interrupt_t irq)
|
||||
{
|
||||
/* Complete the IRQ claim by writing back to the claim register. */
|
||||
writel(irq, PLIC_PPTR_BASE + PLIC_THRES + PLIC_THRES_PER_HART * PLIC_HARTID +
|
||||
PLIC_THRES_CLAIM);
|
||||
}
|
||||
|
||||
static inline void plic_mask_irq(bool_t disable, interrupt_t irq)
|
||||
{
|
||||
uint64_t addr = 0;
|
||||
uint32_t val = 0;
|
||||
|
||||
if (irq >= 32) {
|
||||
irq -= 32;
|
||||
addr = 0x4;
|
||||
}
|
||||
|
||||
addr += PLIC_PPTR_BASE + PLIC_EN;
|
||||
val = readl(addr + PLIC_EN_PER_HART * PLIC_HARTID);
|
||||
if (disable) {
|
||||
val &= ~BIT(irq);
|
||||
} else {
|
||||
val |= BIT(irq);
|
||||
}
|
||||
|
||||
writel(val, addr + PLIC_EN_PER_HART * PLIC_HARTID);
|
||||
|
||||
if (!disable) {
|
||||
/* Clear any pending claims if we are enabling otherwise they may not
|
||||
* be raised again */
|
||||
plic_complete_claim(irq);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void plic_init_controller(void)
|
||||
{
|
||||
uint32_t pending;
|
||||
|
||||
/* Clear all pending bits */
|
||||
pending = readl(PLIC_PPTR_BASE + 0x1000);
|
||||
for (int i = 0; i < 32 ; i++) {
|
||||
if (pending & (1 << i)) {
|
||||
readl(PLIC_PPTR_BASE + PLIC_THRES +
|
||||
PLIC_THRES_PER_HART * PLIC_HARTID +
|
||||
PLIC_THRES_CLAIM);
|
||||
}
|
||||
}
|
||||
pending = readl(PLIC_PPTR_BASE + 0x1004);
|
||||
for (int i = 0; i < 22 ; i++) {
|
||||
if (pending & (1 << i)) {
|
||||
readl(PLIC_PPTR_BASE + PLIC_THRES +
|
||||
PLIC_THRES_PER_HART * PLIC_HARTID +
|
||||
PLIC_THRES_CLAIM);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable interrupts */
|
||||
writel(0, PLIC_PPTR_BASE + PLIC_EN + PLIC_EN_PER_HART * PLIC_HARTID);
|
||||
writel(0, PLIC_PPTR_BASE + PLIC_EN + PLIC_EN_PER_HART * PLIC_HARTID + 0x4);
|
||||
|
||||
/* Set threshold to zero */
|
||||
writel(1, (PLIC_PPTR_BASE + PLIC_THRES + PLIC_THRES_PER_HART * PLIC_HARTID));
|
||||
|
||||
/* Set the priorities of all interrupts to 1 */
|
||||
for (int i = 1; i <= PLIC_MAX_NUM_INT + 1; i++) {
|
||||
writel(2, PLIC_PPTR_BASE + PLIC_PRIO + PLIC_PRIO_PER_ID * i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Available physical memory regions on platform (RAM minus kernel image). */
|
||||
/* NOTE: Regions are not allowed to be adjacent! */
|
||||
static p_region_t BOOT_DATA avail_p_regs[] = {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,25 @@
|
|||
#ifndef __PLAT_INSTANCE_HARDWARE_H
|
||||
#define __PLAT_INSTANCE_HARDWARE_H
|
||||
|
||||
#define PLIC_MAX_NUM_INT 0
|
||||
|
||||
static inline interrupt_t plic_get_claim(void)
|
||||
{
|
||||
return irqInvalid;
|
||||
}
|
||||
|
||||
static inline void plic_complete_claim(interrupt_t irq)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void plic_mask_irq(bool_t disable, interrupt_t irq)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void plic_init_controller(void)
|
||||
{
|
||||
}
|
||||
|
||||
/* Available physical memory regions on platform (RAM minus kernel image). */
|
||||
/* NOTE: Regions are not allowed to be adjacent! */
|
||||
static p_region_t BOOT_DATA avail_p_regs[] = {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,26 @@
|
|||
#ifndef __PLAT_INSTANCE_HARDWARE_H
|
||||
#define __PLAT_INSTANCE_HARDWARE_H
|
||||
|
||||
#define PLIC_MAX_NUM_INT 0
|
||||
|
||||
static inline interrupt_t plic_get_claim(void)
|
||||
{
|
||||
return irqInvalid;
|
||||
}
|
||||
|
||||
static inline void plic_complete_claim(interrupt_t irq)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void plic_mask_irq(bool_t disable, interrupt_t irq)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void plic_init_controller(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* Available physical memory regions on platform (RAM minus kernel image). */
|
||||
/* NOTE: Regions are not allowed to be adjacent! */
|
||||
static p_region_t BOOT_DATA avail_p_regs[] = {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,44 @@
|
|||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
|
||||
/*
|
||||
* RISC-V defines a Platform-level interrupt controller (PLIC) (priv-1.10).
|
||||
* It is responsible for managing global interrupts in a RISC-V system.
|
||||
*
|
||||
* A PLIC takes multiple interrupt sources from external devices and delivers
|
||||
* them to different HART contexts depending on per IRQ configuration registers.
|
||||
* A HART context is a given privilege level on a given HART. If an IRQ is pending
|
||||
* for a particular HART context, the PLIC will raise an interrupt on that HART context's
|
||||
* External interrupt pending(EIP) pin and trigger an interrupt. The HART can then claim
|
||||
* the IRQ message by communicating with the PLIC where it will receive the highest
|
||||
* priority pending interrupt. The PLIC will deassert the EIP pin when there are
|
||||
* no more pending interrupts for that HART. When the HART has finished processing
|
||||
* the IRQ it completes the claim by notifying the PLIC. Until an IRQ claim has
|
||||
* completed, the PLIC won't generate futher interrupts for that IRQ. In multicore
|
||||
* systems, if an IRQ is routed to multiple HARTs, the first HART to claim the IRQ
|
||||
* gets to process the IRQ and subsequent HARTs won't receive a claim for the same IRQ.
|
||||
*
|
||||
* We require each platform to provide the following functions:
|
||||
* interrupt_t plic_get_claim(void): If called when an IRQ is pending, returns
|
||||
* the pending priority and starts a claim process. Will return irqInvalid
|
||||
* if no IRQs are pending.
|
||||
* void plic_complete_claim(interrupt_t irq): Complete a claim process for an
|
||||
* interrupt.
|
||||
* void plic_mask_irq(bool_t disable, interrupt_t irq): Disables or enables an
|
||||
* IRQ at the PLIC.
|
||||
* void plic_irq_set_trigger(interrupt_t irq, bool_t edge_triggered): Configure
|
||||
* an IRQ source on the PLIC to be edge or level triggered. This function does
|
||||
* not need to be implemented if the PLIC doesn't support configuring this.
|
||||
* void plic_init_controller(void): Perform PLIC initialisation during boot.
|
||||
*/
|
||||
typedef uint32_t interrupt_t;
|
||||
typedef uint32_t irq_t;
|
||||
|
||||
enum irqNumbers {
|
||||
irqInvalid = 6
|
||||
};
|
||||
|
||||
#if defined(CONFIG_BUILD_SPIKE_QEMU)
|
||||
#include <plat/instance/qemu/hardware.h>
|
||||
#elif defined(CONFIG_BUILD_ROCKET_CHIP_ZEDBOARD)
|
||||
|
|
@ -41,12 +79,5 @@ enum IRQConstants {
|
|||
#define KERNEL_TIMER_IRQ INTERRUPT_TIMER
|
||||
#define IRQ_CNODE_SLOT_BITS 3
|
||||
|
||||
enum irqNumbers {
|
||||
irqInvalid = 6
|
||||
};
|
||||
|
||||
typedef uint32_t interrupt_t;
|
||||
typedef uint32_t irq_t;
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue