Skip to content
Snippets Groups Projects
Commit c73d5178 authored by Kent McLeod's avatar Kent McLeod
Browse files

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>
parent 4b240066
No related branches found
No related tags found
No related merge requests found
......@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment