Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions bsp/qemu-virt64-riscv/driver/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ void rt_hw_board_init(void)
rt_system_heap_init(RT_HW_HEAP_BEGIN, RT_HW_HEAP_END);
#endif

plic_init();

rt_hw_interrupt_init();

rt_hw_uart_init();
Expand Down
1 change: 1 addition & 0 deletions libcpu/risc-v/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ list = os.listdir(cwd)
# add common code files
if rtconfig.CPU in common64_arch :
group += SConscript(os.path.join('common64', 'SConscript'))
group += SConscript(os.path.join('plic', 'SConscript'))
else :
group += SConscript(os.path.join('common', 'SConscript'))

Expand Down
153 changes: 153 additions & 0 deletions libcpu/risc-v/common64/interrupt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018/10/01 Bernard The first version
* 2018/12/27 Jesven Change irq enable/disable to cpu0
*/
#include <rtthread.h>
#include <plic.h>
#include <rthw.h>
#include "encoding.h"
#include "riscv.h"
#include "interrupt.h"

#ifdef RT_USING_SMART
#include <ioremap.h>
#endif

/* TODO define PLIC_PHY_ADDR in BSP and remove me */
#ifdef C908_PLIC_PHY_ADDR
#define PLIC_PHY_ADDR C908_PLIC_PHY_ADDR
#elif defined(C906_PLIC_PHY_ADDR)
#define PLIC_PHY_ADDR C906_PLIC_PHY_ADDR
#elif !defined(PLIC_PHY_ADDR)
#define PLIC_PHY_ADDR 0x0c000000L
#endif

static struct rt_irq_desc isr_table[INTERRUPTS_MAX];
static struct plic_handler plic_handlers[1];

rt_inline struct plic_handler *plic_handler_get(void)
{
return &plic_handlers[0];
}

static void plic_init(void)
{
void *plic_base = (void *)PLIC_PHY_ADDR;

#ifdef RT_USING_SMART
// PLIC takes up 64 MB space
plic_base = rt_ioremap(plic_base, 64 * 1024 * 1024);
#endif
/* skip contexts other than supervisor external interrupt */
plic_handler_init(plic_handler_get(), plic_base, 1);

plic_set_threshold(plic_handler_get(), 0);
}

static void rt_hw_interrupt_handle(int vector, void *param)
{
rt_kprintf("UN-handled interrupt %d occurred!!!\n", vector);
}

/**
* This function will mask a interrupt.
* @param vector the interrupt number
*/
void rt_hw_interrupt_mask(int vector)
{
if ((vector < 0) || (vector > IRQ_MAX_NR))
{
return;
}

plic_irq_disable(plic_handler_get(), vector);
}

/**
* This function will un-mask a interrupt.
* @param vector the interrupt number
*/
void rt_hw_interrupt_umask(int vector)
{
if ((vector < 0) || (vector > IRQ_MAX_NR))
{
return;
}

plic_set_priority(plic_handler_get(), vector, 1);

plic_irq_enable(plic_handler_get(), vector);
}

/**
* This function will install a interrupt service routine to a interrupt.
* @param vector the interrupt number
* @param new_handler the interrupt service routine to be installed
* @param old_handler the old interrupt service routine
*/
rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler,
void *param, const char *name)
{
rt_isr_handler_t old_handler = RT_NULL;

if ((vector < 0) || (vector > IRQ_MAX_NR))
{
return old_handler;
}

old_handler = isr_table[IRQ_OFFSET + vector].handler;

isr_table[IRQ_OFFSET + vector].handler = handler;
isr_table[IRQ_OFFSET + vector].param = param;

return old_handler;
}

void rt_hw_interrupt_init(void)
{
/* init exceptions table */
for (int idx = 0; idx < INTERRUPTS_MAX; idx++)
{
isr_table[idx].handler = rt_hw_interrupt_handle;
isr_table[idx].param = RT_NULL;
}

plic_init();

/* Enable supervisor external interrupts. */
set_csr(sie, SIE_SEIE);
}

/*
* 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)
{
struct plic_handler *plic;
rt_isr_handler_t isr;
void *param;
int irq;

plic = plic_handler_get();

while ((irq = plic_claim(plic)))
{
isr = isr_table[IRQ_OFFSET + irq].handler;
param = isr_table[IRQ_OFFSET + irq].param;
if (isr)
{
isr(irq, param);
}

plic_complete(plic, irq);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
#ifndef INTERRUPT_H__
#define INTERRUPT_H__

#define MAX_HANDLERS 128

#include <rthw.h>
#include "stack.h"
#define IRQ_OFFSET 16
#ifndef IRQ_MAX_NR
#define IRQ_MAX_NR 200
#endif
#define INTERRUPTS_MAX (IRQ_OFFSET + IRQ_MAX_NR)

enum
{
Expand All @@ -33,14 +34,14 @@ enum
EP_INSTRUCTION_PAGE_FAULT, /* page attr */
EP_LOAD_PAGE_FAULT, /* read data */
EP_RESERVED14,
EP_STORE_PAGE_FAULT, /* write data */
EP_STORE_PAGE_FAULT, /* write data */
};

int rt_hw_plic_irq_enable(int irq_number);
int rt_hw_plic_irq_disable(int irq_number);
void rt_hw_interrupt_init(void);
void rt_hw_interrupt_mask(int vector);
void rt_hw_interrupt_umask(int vector);
rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, void *param, const char *name);
void handle_trap(rt_ubase_t xcause, rt_ubase_t xtval, rt_ubase_t xepc, struct rt_hw_stack_frame *sp);

void plic_handle_irq(void);

#endif
13 changes: 13 additions & 0 deletions libcpu/risc-v/plic/SConscript
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# RT-Thread building script for component

from building import *
cwd = GetCurrentDir()
src = []
CPPPATH = []

src += Glob('*.c')
CPPPATH += [cwd]

group = DefineGroup('libcpu', src, depend = [''], CPPPATH = CPPPATH)

Return('group')
133 changes: 133 additions & 0 deletions libcpu/risc-v/plic/plic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-05-20 bigmagic first version
* 2022-09-16 WangXiaoyao Porting to rv64
*/
#include <rtdef.h>
#include "plic.h"
#include <riscv_io.h>

/*
* Each interrupt source has a priority register associated with it.
* We always hardwire it to one in Linux.
*/
#define PRIORITY_BASE 0
#define PRIORITY_PER_ID 4

/*
* Each hart context has a vector of interrupt enable bits associated with it.
* There's one bit for each interrupt source.
*/
#define CONTEXT_ENABLE_BASE 0x2000
#define CONTEXT_ENABLE_SIZE 0x80

/*
* Each hart context has a set of control registers associated with it. Right
* now there's only two: a source priority threshold over which the hart will
* take an interrupt, and a register to claim interrupts.
*/
#define CONTEXT_BASE 0x200000
#define CONTEXT_SIZE 0x1000
#define CONTEXT_THRESHOLD 0x00
#define CONTEXT_CLAIM 0x04

static void plic_toggle(struct plic_handler *handler, unsigned int irq, int enable)
{
void *reg = handler->enable_base + (irq / 32) * sizeof(unsigned int);
unsigned int hwirq_mask = 1 << (irq % 32);

if (enable)
writel(readl(reg) | hwirq_mask, reg);
else
writel(readl(reg) & ~hwirq_mask, reg);
}

/*
* Each PLIC interrupt source can be assigned a priority by writing
* to its 32-bit memory-mapped priority register.
* The QEMU-virt (the same as FU540-C000) supports 7 levels of priority.
* A priority value of 0 is reserved to mean "never interrupt" and
* effectively disables the interrupt.
* Priority 1 is the lowest active priority, and priority 7 is the highest.
* 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(struct plic_handler *handler, int irq, int priority)
{
writel(priority, handler->base + PRIORITY_BASE + irq * PRIORITY_PER_ID);
}

/*
* Each global interrupt can be enabled by setting the corresponding
* bit in the enables registers.
*/
void plic_irq_enable(struct plic_handler *handler, int irq)
{
plic_toggle(handler, irq, 1);
}

void plic_irq_disable(struct plic_handler *handler, int irq)
{
plic_toggle(handler, irq, 1);
}

/*
* 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(struct plic_handler *handler, int threshold)
{
writel(threshold, handler->hart_base + CONTEXT_THRESHOLD);
}

/*
* 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.
*/
int plic_claim(struct plic_handler *handler)
{
void *claim = handler->hart_base + CONTEXT_CLAIM;

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(struct plic_handler *handler, int irq)
{
void *claim = handler->hart_base + CONTEXT_CLAIM;

writel(irq, claim);
}

void plic_handler_init(struct plic_handler *handler, void *base, unsigned int context_id)
{
handler->base = base;
handler->hart_base = base + CONTEXT_BASE + context_id * CONTEXT_SIZE;
handler->enable_base = base + CONTEXT_ENABLE_BASE + context_id * CONTEXT_ENABLE_SIZE;
}
30 changes: 30 additions & 0 deletions libcpu/risc-v/plic/plic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-05-20 bigmagic first version
* 2021-10-20 bernard fix s-mode issue
*/

#ifndef __PLIC_H__
#define __PLIC_H__

struct plic_handler
{
void *base;
void *hart_base;
void *enable_base;
};

void plic_set_priority(struct plic_handler *handler, int irq, int priority);
void plic_irq_enable(struct plic_handler *handler, int irq);
void plic_irq_disable(struct plic_handler *handler, int irq);
void plic_set_threshold(struct plic_handler *handler, int mthreshold);
int plic_claim(struct plic_handler *handler);
void plic_complete(struct plic_handler *handler, int irq);
void plic_handler_init(struct plic_handler *handler, void *base, unsigned int context_id);

#endif
Loading