Skip to content

[riscv]统一plic和中断控制的接口实现 #10147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 12 additions & 0 deletions libcpu/risc-v/plic/SConscript
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# 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