mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-09-02 04:13:54 -04:00
Rename RISC-V_RV32_SiFive_HiFive1-FreedomStudio directory to RISC-V_RV32_SiFive_HiFive1-RevB-FreedomStudio as it targets Rev B of the hardware.
This commit is contained in:
parent
4b943b35e0
commit
dfc1bf8ec3
189 changed files with 0 additions and 0 deletions
|
@ -0,0 +1,5 @@
|
|||
[{000214A0-0000-0000-C000-000000000046}]
|
||||
Prop3=19,11
|
||||
[InternetShortcut]
|
||||
IDList=
|
||||
URL=https://github.com/sifive/freedom-metal-docs
|
|
@ -0,0 +1,246 @@
|
|||
/* Copyright (c) 2017-2018 SiFive Inc. All rights reserved.
|
||||
|
||||
This copyrighted material is made available to anyone wishing to use,
|
||||
modify, copy, or redistribute it subject to the terms and conditions
|
||||
of the FreeBSD License. This program is distributed in the hope that
|
||||
it will be useful, but WITHOUT ANY WARRANTY expressed or implied,
|
||||
including the implied warranties of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. A copy of this license is available at
|
||||
http://www.opensource.org/licenses.
|
||||
*/
|
||||
|
||||
/* crt0.S: Entry point for RISC-V METAL programs. */
|
||||
|
||||
.section .text.libgloss.start
|
||||
.global _start
|
||||
.type _start, @function
|
||||
|
||||
/* _start is defined by the METAL to have been called with the following
|
||||
* arguments:
|
||||
* a0: the hart ID of the currently executing hart. Harts can start at
|
||||
* any arbitrary point, it's the C library's job to ensure the code is
|
||||
* safe.
|
||||
* a1: a pointer to a description of the machine on which this code is
|
||||
* currently executing. This is probably 0 on an embedded system
|
||||
* because they tend to not be dynamically portable. As such, newlib
|
||||
* ignores this argument.
|
||||
* a2: a pointer to a function that must be run after the envirnoment has
|
||||
* been initialized, but before user code can be expected to be run.
|
||||
* If this is 0 then there is no function to be run. */
|
||||
_start:
|
||||
.cfi_startproc
|
||||
.cfi_undefined ra
|
||||
|
||||
/* This is a bit funky: it's not usually sane for _start to return, but in
|
||||
* this case we actually want to in order to signal an error to the METAL. */
|
||||
mv s0, ra
|
||||
|
||||
/* Before doing anything we must initialize the global pointer, as we cannot
|
||||
* safely perform any access that may be relaxed without GP being set. This
|
||||
* is done with relaxation disabled to avoid relaxing the address calculation
|
||||
* to just "addi gp, gp, 0". */
|
||||
.option push
|
||||
.option norelax
|
||||
la gp, __global_pointer$
|
||||
.option pop
|
||||
|
||||
/* The METAL is designed for a bare-metal environment and therefor is expected
|
||||
* to define its own stack pointer. We also align the stack pointer here
|
||||
* because the only RISC-V ABI that's currently defined mandates 16-byte
|
||||
* stack alignment. */
|
||||
la sp, _sp
|
||||
|
||||
/* Increment by hartid number of stack sizes */
|
||||
li t0, 0
|
||||
la t1, __stack_size
|
||||
1:
|
||||
beq t0, a0, 1f
|
||||
add sp, sp, t1
|
||||
addi t0, t0, 1
|
||||
j 1b
|
||||
1:
|
||||
andi sp, sp, -16
|
||||
|
||||
/* If we're not hart 0, skip the initialization work */
|
||||
la t0, __metal_boot_hart
|
||||
bne a0, t0, _skip_init
|
||||
|
||||
/* Embedded systems frequently require relocating the data segment before C
|
||||
* code can be run -- for example, the data segment may exist in flash upon
|
||||
* boot and then need to get relocated into a non-persistant writable memory
|
||||
* before C code can execute. If this is the case we do so here. This step
|
||||
* is optional: if the METAL provides an environment in which this relocation
|
||||
* is not necessary then it must simply set metal_segment_data_source_start to
|
||||
* be equal to metal_segment_data_target_start. */
|
||||
la t0, metal_segment_data_source_start
|
||||
la t1, metal_segment_data_target_start
|
||||
la t2, metal_segment_data_target_end
|
||||
|
||||
beq t0, t1, 2f
|
||||
bge t1, t2, 2f
|
||||
|
||||
1:
|
||||
#if __riscv_xlen == 32
|
||||
lw a0, 0(t0)
|
||||
addi t0, t0, 4
|
||||
sw a0, 0(t1)
|
||||
addi t1, t1, 4
|
||||
blt t1, t2, 1b
|
||||
#else
|
||||
ld a0, 0(t0)
|
||||
addi t0, t0, 8
|
||||
sd a0, 0(t1)
|
||||
addi t1, t1, 8
|
||||
blt t1, t2, 1b
|
||||
#endif
|
||||
2:
|
||||
|
||||
/* Copy the ITIM section */
|
||||
la t0, metal_segment_itim_source_start
|
||||
la t1, metal_segment_itim_target_start
|
||||
la t2, metal_segment_itim_target_end
|
||||
|
||||
beq t0, t1, 2f
|
||||
bge t1, t2, 2f
|
||||
|
||||
1:
|
||||
#if __riscv_xlen == 32
|
||||
lw a0, 0(t0)
|
||||
addi t0, t0, 4
|
||||
sw a0, 0(t1)
|
||||
addi t1, t1, 4
|
||||
blt t1, t2, 1b
|
||||
#else
|
||||
ld a0, 0(t0)
|
||||
addi t0, t0, 8
|
||||
sd a0, 0(t1)
|
||||
addi t1, t1, 8
|
||||
blt t1, t2, 1b
|
||||
#endif
|
||||
2:
|
||||
|
||||
/* Fence all subsequent instruction fetches until after the ITIM writes
|
||||
complete */
|
||||
fence.i
|
||||
|
||||
/* Zero the BSS segment. */
|
||||
la t1, metal_segment_bss_target_start
|
||||
la t2, metal_segment_bss_target_end
|
||||
|
||||
bge t1, t2, 2f
|
||||
|
||||
1:
|
||||
#if __riscv_xlen == 32
|
||||
sw x0, 0(t1)
|
||||
addi t1, t1, 4
|
||||
blt t1, t2, 1b
|
||||
#else
|
||||
sd x0, 0(t1)
|
||||
addi t1, t1, 8
|
||||
blt t1, t2, 1b
|
||||
#endif
|
||||
2:
|
||||
|
||||
/* At this point we're in an environment that can execute C code. The first
|
||||
* thing to do is to make the callback to the parent environment if it's been
|
||||
* requested to do so. */
|
||||
beqz a2, 1f
|
||||
jalr a2
|
||||
1:
|
||||
|
||||
/* The RISC-V port only uses new-style constructors and destructors. */
|
||||
la a0, __libc_fini_array
|
||||
call atexit
|
||||
call __libc_init_array
|
||||
|
||||
_skip_init:
|
||||
|
||||
/* Synchronize harts so that secondary harts wait until hart 0 finishes
|
||||
initializing */
|
||||
call __metal_synchronize_harts
|
||||
|
||||
/* Check RISC-V isa and enable FS bits if Floating Point architecture. */
|
||||
csrr a5, misa
|
||||
li a4, 0x10028
|
||||
and a5, a5, a4
|
||||
beqz a5, 1f
|
||||
csrr a5, mstatus
|
||||
lui a4, 0x2
|
||||
or a5, a5, a4
|
||||
csrw mstatus, a5
|
||||
csrwi fcsr, 0
|
||||
1:
|
||||
|
||||
/* This is a C runtime, so main() is defined to have some arguments. Since
|
||||
* there's nothing sane the METAL can pass we don't bother with that but
|
||||
* instead just setup as close to a NOP as we can. */
|
||||
li a0, 1 /* argc=1 */
|
||||
la a1, argv /* argv = {"libgloss", NULL} */
|
||||
la a2, envp /* envp = {NULL} */
|
||||
call secondary_main
|
||||
|
||||
/* Call exit to handle libc's cleanup routines. Under normal contains this
|
||||
* shouldn't even get called, but I'm still not using a tail call here
|
||||
* because returning to the METAL is the right thing to do in pathological
|
||||
* situations. */
|
||||
call exit
|
||||
|
||||
/* And here's where we return. Again, it's a bit odd but the METAL defines
|
||||
* this as a bad idea (ie, as opposed to leaving it undefined) and at this
|
||||
* point it's really the only thing left to do. */
|
||||
mv ra, s0
|
||||
ret
|
||||
|
||||
.cfi_endproc
|
||||
|
||||
/* RISC-V systems always use __libc_{init,fini}_array, but for compatibility we
|
||||
* define _{init,fini} to do nothing. */
|
||||
.global _init
|
||||
.type _init, @function
|
||||
.global _fini
|
||||
.type _fini, @function
|
||||
_init:
|
||||
_fini:
|
||||
ret
|
||||
.size _init, .-_init
|
||||
.size _fini, .-_fini
|
||||
|
||||
/* By default, secondary_main will cause secondary harts to spin forever.
|
||||
* Users can redefine secondary_main themselves to run code on secondary harts */
|
||||
.weak secondary_main
|
||||
.global secondary_main
|
||||
.type secondary_main, @function
|
||||
|
||||
secondary_main:
|
||||
addi sp, sp, -16
|
||||
#if __riscv_xlen == 32
|
||||
sw ra, 4(sp)
|
||||
#else
|
||||
sd ra, 8(sp)
|
||||
#endif
|
||||
csrr t0, mhartid
|
||||
la t1, __metal_boot_hart
|
||||
beq t0, t1, 2f
|
||||
1:
|
||||
wfi
|
||||
j 1b
|
||||
2:
|
||||
call main
|
||||
#if __riscv_xlen == 32
|
||||
lw ra, 4(sp)
|
||||
#else
|
||||
ld ra, 8(sp)
|
||||
#endif
|
||||
addi sp, sp, 16
|
||||
ret
|
||||
|
||||
/* This shim allows main() to be passed a set of arguments that can satisfy the
|
||||
* requirements of the C API. */
|
||||
.section .rodata.libgloss.start
|
||||
argv:
|
||||
.dc.a name
|
||||
envp:
|
||||
.dc.a 0
|
||||
name:
|
||||
.asciz "libgloss"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
int
|
||||
nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine.h>
|
||||
#include <metal/machine/platform.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/cpu.h>
|
||||
|
||||
#define METAL_REG(base, offset) (((unsigned long)(base) + (offset)))
|
||||
#define METAL_REGW(base, offset) (__METAL_ACCESS_ONCE((__metal_io_u32 *)METAL_REG((base), (offset))))
|
||||
#define METAL_MSIP(base, hart) (METAL_REGW((base),4*(hart)))
|
||||
|
||||
/*
|
||||
* _synchronize_harts() is called by crt0.S to cause harts > 0 to wait for
|
||||
* hart 0 to finish copying the datat section, zeroing the BSS, and running
|
||||
* the libc contstructors.
|
||||
*/
|
||||
void _synchronize_harts() {
|
||||
#if __METAL_DT_MAX_HARTS > 1
|
||||
|
||||
int hart = metal_cpu_get_current_hartid();
|
||||
uintptr_t msip_base = 0;
|
||||
|
||||
/* Get the base address of the MSIP registers */
|
||||
#ifdef __METAL_DT_RISCV_CLINT0_HANDLE
|
||||
msip_base = __metal_driver_sifive_clint0_control_base(__METAL_DT_RISCV_CLINT0_HANDLE);
|
||||
msip_base += METAL_RISCV_CLINT0_MSIP_BASE;
|
||||
#elif __METAL_DT_RISCV_CLIC0_HANDLE
|
||||
msip_base = __metal_driver_sifive_clic0_control_base(__METAL_DT_RISCV_CLIC0_HANDLE);
|
||||
msip_base += METAL_RISCV_CLIC0_MSIP_BASE;
|
||||
#else
|
||||
#warning No handle for CLINT or CLIC found, harts may be unsynchronized after init!
|
||||
#endif
|
||||
|
||||
/* Disable machine interrupts as a precaution */
|
||||
__asm__ volatile("csrc mstatus, %0" :: "r" (METAL_MSTATUS_MIE));
|
||||
|
||||
if (hart == 0) {
|
||||
/* Hart 0 waits for all harts to set their MSIP bit */
|
||||
for (int i = 1 ; i < __METAL_DT_MAX_HARTS; i++) {
|
||||
while (METAL_MSIP(msip_base, i) == 0) ;
|
||||
}
|
||||
|
||||
/* Hart 0 clears everyone's MSIP bit */
|
||||
for (int i = 1 ; i < __METAL_DT_MAX_HARTS; i++) {
|
||||
METAL_MSIP(msip_base, i) = 0;
|
||||
}
|
||||
} else {
|
||||
/* Other harts set their MSIP bit to indicate they're ready */
|
||||
METAL_MSIP(msip_base, hart) = 1;
|
||||
__asm__ volatile ("fence w,rw");
|
||||
|
||||
/* Wait for hart 0 to clear the MSIP bit */
|
||||
while (METAL_MSIP(msip_base, hart) == 1) ;
|
||||
}
|
||||
|
||||
#endif /* __METAL_DT_MAX_HARTS > 1 */
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_access(const char *file, int mode)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_chdir(const char *path)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
int
|
||||
_chmod(const char *path, mode_t mode)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
int
|
||||
_chown(const char *path, uid_t owner, gid_t group)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_close(int file)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_execve(const char *name, char *const argv[], char *const env[])
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <metal/shutdown.h>
|
||||
|
||||
void
|
||||
_exit(int exit_status)
|
||||
{
|
||||
metal_shutdown(exit_status);
|
||||
while (1);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_faccessat(int dirfd, const char *file, int mode, int flags)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_fork()
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int
|
||||
_fstat(int file, struct stat *st)
|
||||
{
|
||||
errno = -ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int
|
||||
_fstatat(int dirfd, const char *file, struct stat *st, int flags)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <errno.h>
|
||||
#include <sys/timeb.h>
|
||||
|
||||
int
|
||||
_ftime(struct timeb *tp)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
char *
|
||||
_getcwd(char *buf, size_t size)
|
||||
{
|
||||
errno = -ENOSYS;
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_getpid()
|
||||
{
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#include <errno.h>
|
||||
#include <metal/timer.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
int
|
||||
_gettimeofday(struct timeval *tp, void *tzp)
|
||||
{
|
||||
int rv;
|
||||
unsigned long long mcc, timebase;
|
||||
rv = metal_timer_get_cyclecount(0, &mcc);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
rv = metal_timer_get_timebase_frequency(0, &timebase);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
tp->tv_sec = mcc / timebase;
|
||||
tp->tv_usec = mcc % timebase * 1000000 / timebase;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
int
|
||||
_isatty(int file)
|
||||
{
|
||||
return (file == STDOUT_FILENO);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_kill(int pid, int sig)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include <errno.h>
|
||||
|
||||
int _link(const char *old_name, const char *new_name)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
off_t
|
||||
_lseek(int file, off_t ptr, int dir)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int _lstat(const char *file, struct stat *st)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_open(const char *name, int flags, int mode)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_openat(int dirfd, const char *name, int flags, int mode)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
ssize_t
|
||||
_read(int file, void *ptr, size_t len)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
/* brk is handled entirely within the C library. This limits METAL programs that
|
||||
* use the C library to be disallowed from dynamically allocating memory
|
||||
* without talking to the C library, but that sounds like a sane way to go
|
||||
* about it. Note that there is no error checking anywhere in this file, users
|
||||
* will simply get the relevant error when actually trying to use the memory
|
||||
* that's been allocated. */
|
||||
extern char metal_segment_heap_target_start;
|
||||
extern char metal_segment_heap_target_end;
|
||||
static char *brk = &metal_segment_heap_target_start;
|
||||
|
||||
int
|
||||
_brk(void *addr)
|
||||
{
|
||||
brk = addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
_sbrk(ptrdiff_t incr)
|
||||
{
|
||||
char *old = brk;
|
||||
|
||||
/* If __heap_size == 0, we can't allocate memory on the heap */
|
||||
if(&metal_segment_heap_target_start == &metal_segment_heap_target_end) {
|
||||
return (void *)-1;
|
||||
}
|
||||
|
||||
/* Don't move the break past the end of the heap */
|
||||
if ((brk + incr) < &metal_segment_heap_target_end) {
|
||||
brk += incr;
|
||||
} else {
|
||||
brk = &metal_segment_heap_target_end;
|
||||
return (void *)-1;
|
||||
}
|
||||
|
||||
return old;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int
|
||||
_stat(const char *file, struct stat *st)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Get configurable system variables. */
|
||||
|
||||
long
|
||||
_sysconf(int name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case _SC_CLK_TCK:
|
||||
return CLOCKS_PER_SEC;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#include <sys/times.h>
|
||||
#include <sys/time.h>
|
||||
#include <metal/timer.h>
|
||||
#include <errno.h>
|
||||
|
||||
extern int _gettimeofday(struct timeval *, void *);
|
||||
|
||||
/* Timing information for current process. From
|
||||
newlib/libc/include/sys/times.h the tms struct fields are as follows:
|
||||
|
||||
- clock_t tms_utime : user clock ticks
|
||||
- clock_t tms_stime : system clock ticks
|
||||
- clock_t tms_cutime : children's user clock ticks
|
||||
- clock_t tms_cstime : children's system clock ticks
|
||||
|
||||
Since maven does not currently support processes we set both of the
|
||||
children's times to zero. Eventually we might want to separately
|
||||
account for user vs system time, but for now we just return the total
|
||||
number of cycles since starting the program. */
|
||||
clock_t
|
||||
_times(struct tms *buf)
|
||||
{
|
||||
int rv;
|
||||
// when called for the first time, initialize t0
|
||||
static struct timeval t0;
|
||||
if (t0.tv_sec == 0 && t0.tv_usec == 0)
|
||||
_gettimeofday (&t0, 0);
|
||||
|
||||
struct timeval t;
|
||||
_gettimeofday (&t, 0);
|
||||
|
||||
unsigned long long timebase;
|
||||
rv = metal_timer_get_timebase_frequency(0, &timebase);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
long long utime = (t.tv_sec - t0.tv_sec) * 1000000 + (t.tv_usec - t0.tv_usec);
|
||||
buf->tms_utime = utime * timebase / 1000000;
|
||||
buf->tms_stime = buf->tms_cstime = buf->tms_cutime = 0;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
int
|
||||
_unlink(const char *name)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <errno.h>
|
||||
struct utimbuf;
|
||||
|
||||
int
|
||||
_utime(const char *path, const struct utimbuf *times)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include <errno.h>
|
||||
|
||||
int _wait(int *status)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#include <metal/tty.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Write to a file. */
|
||||
ssize_t
|
||||
_write(int file, const void *ptr, size_t len)
|
||||
{
|
||||
if (file != STDOUT_FILENO) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *bptr = ptr;
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
metal_tty_putc(bptr[i]);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__BUTTON_H
|
||||
#define METAL__BUTTON_H
|
||||
|
||||
/*!
|
||||
* @file button.h
|
||||
* API for interfacing with physical buttons
|
||||
*/
|
||||
|
||||
#include <metal/interrupt.h>
|
||||
|
||||
struct metal_button;
|
||||
|
||||
struct metal_button_vtable {
|
||||
int (*button_exist)(struct metal_button *button, char *label);
|
||||
struct metal_interrupt* (*interrupt_controller)(struct metal_button *button);
|
||||
int (*get_interrupt_id)(struct metal_button *button);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief A button device handle
|
||||
*
|
||||
* A `struct metal_button` is an implementation-defined object which represents
|
||||
* a button on a development board.
|
||||
*/
|
||||
struct metal_button {
|
||||
const struct metal_button_vtable *vtable;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Get a reference to a button
|
||||
*
|
||||
* @param label The DeviceTree label for the button
|
||||
* @return A handle for the button
|
||||
*/
|
||||
struct metal_button* metal_button_get(char *label);
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt controller for a button
|
||||
*
|
||||
* @param button The handle for the button
|
||||
* @return A pointer to the interrupt controller responsible for handling
|
||||
* button interrupts.
|
||||
*/
|
||||
__inline__ struct metal_interrupt*
|
||||
metal_button_interrupt_controller(struct metal_button *button) { return button->vtable->interrupt_controller(button); }
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt id for a button
|
||||
*
|
||||
* @param button The handle for the button
|
||||
* @return The interrupt id corresponding to a button.
|
||||
*/
|
||||
__inline__ int metal_button_get_interrupt_id(struct metal_button *button) { return button->vtable->get_interrupt_id(button); }
|
||||
|
||||
#endif
|
|
@ -0,0 +1,96 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__CACHE_H
|
||||
#define METAL__CACHE_H
|
||||
|
||||
/*!
|
||||
* @file cache.h
|
||||
*
|
||||
* @brief API for configuring caches
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
struct metal_cache;
|
||||
|
||||
struct __metal_cache_vtable {
|
||||
void (*init)(struct metal_cache *cache, int ways);
|
||||
int (*get_enabled_ways)(struct metal_cache *cache);
|
||||
int (*set_enabled_ways)(struct metal_cache *cache, int ways);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief a handle for a cache
|
||||
*/
|
||||
struct metal_cache {
|
||||
const struct __metal_cache_vtable *vtable;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Initialize a cache
|
||||
* @param cache The handle for the cache to initialize
|
||||
* @param ways The number of ways to enable
|
||||
*
|
||||
* Initializes a cache with the requested number of ways enabled.
|
||||
*/
|
||||
__inline__ void metal_cache_init(struct metal_cache *cache, int ways) {
|
||||
cache->vtable->init(cache, ways);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the current number of enabled cache ways
|
||||
* @param cache The handle for the cache
|
||||
* @return The current number of enabled cache ways
|
||||
*/
|
||||
__inline__ int metal_cache_get_enabled_ways(struct metal_cache *cache) {
|
||||
return cache->vtable->get_enabled_ways(cache);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Enable the requested number of cache ways
|
||||
* @param cache The handle for the cache
|
||||
* @param ways The number of ways to enabled
|
||||
* @return 0 if the ways are successfully enabled
|
||||
*/
|
||||
__inline__ int metal_cache_set_enabled_ways(struct metal_cache *cache, int ways) {
|
||||
return cache->vtable->set_enabled_ways(cache, ways);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Check if dcache is supported on the core
|
||||
* @param hartid The core to check
|
||||
* @return 1 if dcache is present
|
||||
*/
|
||||
int metal_dcache_l1_available(int hartid);
|
||||
|
||||
/*!
|
||||
* @brief Flush dcache for L1 on the requested core with write back
|
||||
* @param hartid The core to flush
|
||||
* @param address The virtual address of cacheline to invalidate
|
||||
* @return None
|
||||
*/
|
||||
void metal_dcache_l1_flush(int hartid, uintptr_t address);
|
||||
|
||||
/*!
|
||||
* @brief Discard dcache for L1 on the requested core with no write back
|
||||
* @param hartid The core to discard
|
||||
* @param address The virtual address of cacheline to invalidate
|
||||
* @return None
|
||||
*/
|
||||
void metal_dcache_l1_discard(int hartid, uintptr_t address);
|
||||
|
||||
/*!
|
||||
* @brief Check if icache is supported on the core
|
||||
* @param hartid The core to check
|
||||
* @return 1 if icache is present
|
||||
*/
|
||||
int metal_icache_l1_available(int hartid);
|
||||
|
||||
/*!
|
||||
* @brief Flush icache for L1 on the requested core
|
||||
* @param hartid The core to flush
|
||||
* @return None
|
||||
*/
|
||||
void metal_icache_l1_flush(int hartid);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,154 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__CLOCK_H
|
||||
#define METAL__CLOCK_H
|
||||
|
||||
/*!
|
||||
* @file clock.h
|
||||
* @brief API for manipulating clock sources
|
||||
*
|
||||
* The clock interface allows for controlling the rate of various clocks in the system.
|
||||
*/
|
||||
|
||||
struct metal_clock;
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* The generic interface to all clocks. */
|
||||
struct __metal_clock_vtable {
|
||||
long (*get_rate_hz)(const struct metal_clock *clk);
|
||||
long (*set_rate_hz)(struct metal_clock *clk, long hz);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Function signature of clock rate change callbacks
|
||||
*/
|
||||
typedef void (*metal_clock_rate_change_callback)(void *priv);
|
||||
|
||||
struct _metal_clock_callback_t;
|
||||
struct _metal_clock_callback_t {
|
||||
/* The callback function */
|
||||
metal_clock_rate_change_callback callback;
|
||||
|
||||
/* Private data for the callback function */
|
||||
void *priv;
|
||||
|
||||
struct _metal_clock_callback_t *_next;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Type for the linked list of callbacks for clock rate changes
|
||||
*/
|
||||
typedef struct _metal_clock_callback_t metal_clock_callback;
|
||||
|
||||
/*!
|
||||
* @brief Call all callbacks in the linked list, if any are registered
|
||||
*/
|
||||
__inline__ void _metal_clock_call_all_callbacks(const metal_clock_callback *const list) {
|
||||
const metal_clock_callback *current = list;
|
||||
while (current) {
|
||||
current->callback(current->priv);
|
||||
current = current->_next;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Append a callback to the linked list and return the head of the list
|
||||
*/
|
||||
__inline__ metal_clock_callback *_metal_clock_append_to_callbacks(metal_clock_callback *list, metal_clock_callback *const cb) {
|
||||
cb->_next = NULL;
|
||||
|
||||
if (!list) {
|
||||
return cb;
|
||||
}
|
||||
|
||||
metal_clock_callback *current = list;
|
||||
|
||||
while ((current->_next) != NULL) {
|
||||
current = current->_next;
|
||||
}
|
||||
|
||||
current->_next = cb;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @struct metal_clock
|
||||
* @brief The handle for a clock
|
||||
*
|
||||
* Clocks are defined as a pointer to a `struct metal_clock`, the contents of which
|
||||
* are implementation defined. Users of the clock interface must call functions
|
||||
* which accept a `struct metal_clock *` as an argument to interract with the clock.
|
||||
*
|
||||
* Note that no mechanism for obtaining a pointer to a `struct metal_clock` has been
|
||||
* defined, making it impossible to call any of these functions without invoking
|
||||
* implementation-defined behavior.
|
||||
*/
|
||||
struct metal_clock {
|
||||
const struct __metal_clock_vtable *vtable;
|
||||
|
||||
/* Pre-rate change callback linked list */
|
||||
metal_clock_callback *_pre_rate_change_callback;
|
||||
|
||||
/* Post-rate change callback linked list */
|
||||
metal_clock_callback *_post_rate_change_callback;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Returns the current rate of the given clock
|
||||
*
|
||||
* @param clk The handle for the clock
|
||||
* @return The current rate of the clock in Hz
|
||||
*/
|
||||
__inline__ long metal_clock_get_rate_hz(const struct metal_clock *clk) { return clk->vtable->get_rate_hz(clk); }
|
||||
|
||||
/*!
|
||||
* @brief Set the current rate of a clock
|
||||
*
|
||||
* @param clk The handle for the clock
|
||||
* @param hz The desired rate in Hz
|
||||
* @return The new rate of the clock in Hz.
|
||||
*
|
||||
* Attempts to set the current rate of the given clock to as close as possible
|
||||
* to the given rate in Hz. Returns the actual value that's been selected, which
|
||||
* could be anything!
|
||||
*
|
||||
* Prior to and after the rate change of the clock, this will call the registered
|
||||
* pre- and post-rate change callbacks.
|
||||
*/
|
||||
__inline__ long metal_clock_set_rate_hz(struct metal_clock *clk, long hz)
|
||||
{
|
||||
_metal_clock_call_all_callbacks(clk->_pre_rate_change_callback);
|
||||
|
||||
long out = clk->vtable->set_rate_hz(clk, hz);
|
||||
|
||||
_metal_clock_call_all_callbacks(clk->_post_rate_change_callback);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Register a callback that must be called before a rate change
|
||||
*
|
||||
* @param clk The handle for the clock
|
||||
* @param cb The callback to be registered
|
||||
*/
|
||||
__inline__ void metal_clock_register_pre_rate_change_callback(struct metal_clock *clk, metal_clock_callback *cb)
|
||||
{
|
||||
clk->_pre_rate_change_callback = _metal_clock_append_to_callbacks(clk->_pre_rate_change_callback, cb);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Registers a callback that must be called after a rate change
|
||||
*
|
||||
* @param clk The handle for the clock
|
||||
* @param cb The callback to be registered
|
||||
*/
|
||||
__inline__ void metal_clock_register_post_rate_change_callback(struct metal_clock *clk, metal_clock_callback *cb)
|
||||
{
|
||||
clk->_post_rate_change_callback = _metal_clock_append_to_callbacks(clk->_post_rate_change_callback, cb);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__COMPILER_H
|
||||
#define METAL__COMPILER_H
|
||||
|
||||
#define __METAL_DECLARE_VTABLE(type) \
|
||||
extern const struct type type;
|
||||
|
||||
#define __METAL_DEFINE_VTABLE(type) \
|
||||
const struct type type
|
||||
|
||||
#define __METAL_GET_FIELD(reg, mask) \
|
||||
(((reg) & (mask)) / ((mask) & ~((mask) << 1)))
|
||||
|
||||
/* Set field with mask for a given value */
|
||||
#define __METAL_SET_FIELD(reg, mask, val) \
|
||||
(((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
|
||||
|
||||
void _metal_trap(int ecode);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,271 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/*! @file cpu.h
|
||||
* @brief API for accessing CPU capabilities.
|
||||
*/
|
||||
|
||||
#ifndef METAL__CPU_H
|
||||
#define METAL__CPU_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <metal/interrupt.h>
|
||||
|
||||
struct metal_cpu;
|
||||
|
||||
/*!
|
||||
* @brief Function signature for exception handlers
|
||||
*/
|
||||
typedef void (*metal_exception_handler_t) (struct metal_cpu *cpu, int ecode);
|
||||
|
||||
struct metal_cpu_vtable {
|
||||
unsigned long long (*mcycle_get)(struct metal_cpu *cpu);
|
||||
unsigned long long (*timebase_get)(struct metal_cpu *cpu);
|
||||
unsigned long long (*mtime_get)(struct metal_cpu *cpu);
|
||||
int (*mtimecmp_set)(struct metal_cpu *cpu, unsigned long long time);
|
||||
struct metal_interrupt* (*tmr_controller_interrupt)(struct metal_cpu *cpu);
|
||||
int (*get_tmr_interrupt_id)(struct metal_cpu *cpu);
|
||||
struct metal_interrupt* (*sw_controller_interrupt)(struct metal_cpu *cpu);
|
||||
int (*get_sw_interrupt_id)(struct metal_cpu *cpu);
|
||||
int (*set_sw_ipi)(struct metal_cpu *cpu, int hartid);
|
||||
int (*clear_sw_ipi)(struct metal_cpu *cpu, int hartid);
|
||||
int (*get_msip)(struct metal_cpu *cpu, int hartid);
|
||||
struct metal_interrupt* (*controller_interrupt)(struct metal_cpu *cpu);
|
||||
int (*exception_register)(struct metal_cpu *cpu, int ecode, metal_exception_handler_t handler);
|
||||
int (*get_ilen)(struct metal_cpu *cpu, uintptr_t epc);
|
||||
uintptr_t (*get_epc)(struct metal_cpu *cpu);
|
||||
int (*set_epc)(struct metal_cpu *cpu, uintptr_t epc);
|
||||
};
|
||||
|
||||
/*! @brief A device handle for a CPU hart
|
||||
*/
|
||||
struct metal_cpu {
|
||||
const struct metal_cpu_vtable *vtable;
|
||||
};
|
||||
|
||||
/*! @brief Get a reference to a CPU hart
|
||||
*
|
||||
* @param hartid The ID of the desired CPU hart
|
||||
* @return A pointer to the CPU device handle
|
||||
*/
|
||||
struct metal_cpu* metal_cpu_get(unsigned int hartid);
|
||||
|
||||
/*! @brief Get the hartid of the CPU hart executing this function
|
||||
*
|
||||
* @return The hartid of the current CPU hart */
|
||||
int metal_cpu_get_current_hartid(void);
|
||||
|
||||
/*! @brief Get the number of CPU harts
|
||||
*
|
||||
* @return The number of CPU harts */
|
||||
int metal_cpu_get_num_harts(void);
|
||||
|
||||
/*! @brief Get the CPU cycle count timer value
|
||||
*
|
||||
* Get the value of the cycle count timer for a given CPU
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @return The value of the CPU cycle count timer
|
||||
*/
|
||||
__inline__ unsigned long long metal_cpu_get_timer(struct metal_cpu *cpu)
|
||||
{ return cpu->vtable->mcycle_get(cpu); }
|
||||
|
||||
/*! @brief Get the timebase of the CPU
|
||||
*
|
||||
* Get the value of the timebase of the cycle count timer
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @return The value of the cycle count timer timebase
|
||||
*/
|
||||
__inline__ unsigned long long metal_cpu_get_timebase(struct metal_cpu *cpu)
|
||||
{ return cpu->vtable->timebase_get(cpu); }
|
||||
|
||||
/*! @brief Get the value of the mtime RTC
|
||||
*
|
||||
* Get the value of the mtime real-time clock. The CPU interrupt controller
|
||||
* must be initialized before this function is called or the return value
|
||||
* will be 0.
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @return The value of mtime, or 0 if failure
|
||||
*/
|
||||
__inline__ unsigned long long metal_cpu_get_mtime(struct metal_cpu *cpu)
|
||||
{ return cpu->vtable->mtime_get(cpu); }
|
||||
|
||||
/*! @brief Set the value of the RTC mtimecmp RTC
|
||||
*
|
||||
* Set the value of the mtime real-time clock compare register. The CPU
|
||||
* interrupt controller must be initialized before this function is called
|
||||
* or the return value will be -1;
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @param time The value to set the compare register to
|
||||
* @return The value of mtimecmp or -1 if error
|
||||
*/
|
||||
__inline__ int metal_cpu_set_mtimecmp(struct metal_cpu *cpu, unsigned long long time)
|
||||
{ return cpu->vtable->mtimecmp_set(cpu, time); }
|
||||
|
||||
/*! @brief Get a reference to RTC timer interrupt controller
|
||||
*
|
||||
* Get a reference to the interrupt controller for the real-time clock interrupt.
|
||||
* The controller returned by this function must be initialized before any interrupts
|
||||
* are registered or enabled with it.
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @return A pointer to the timer interrupt handle
|
||||
*/
|
||||
__inline__ struct metal_interrupt* metal_cpu_timer_interrupt_controller(struct metal_cpu *cpu)
|
||||
{ return cpu->vtable->tmr_controller_interrupt(cpu); }
|
||||
|
||||
/*! @brief Get the RTC timer interrupt id
|
||||
*
|
||||
* Get the interrupt ID of the real-time clock interrupt
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @return The timer interrupt ID
|
||||
*/
|
||||
__inline__ int metal_cpu_timer_get_interrupt_id(struct metal_cpu *cpu)
|
||||
{ return cpu->vtable->get_tmr_interrupt_id(cpu); }
|
||||
|
||||
/*! @brief Get a reference to the software interrupt controller
|
||||
*
|
||||
* Get a reference to the interrupt controller for the software/inter-process
|
||||
* interrupt. The controller returned by this function must be initialized before
|
||||
* any interrupts are registered or enabled with it.
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @return A pointer to the software interrupt handle
|
||||
*/
|
||||
__inline__ struct metal_interrupt* metal_cpu_software_interrupt_controller(struct metal_cpu *cpu)
|
||||
{ return cpu->vtable->sw_controller_interrupt(cpu); }
|
||||
|
||||
/*! @brief Get the software interrupt id
|
||||
*
|
||||
* Get the interrupt ID for the software/inter-process interrupt
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @return the software interrupt ID
|
||||
*/
|
||||
__inline__ int metal_cpu_software_get_interrupt_id(struct metal_cpu *cpu)
|
||||
{ return cpu->vtable->get_sw_interrupt_id(cpu); }
|
||||
|
||||
/*!
|
||||
* @brief Set the inter-process interrupt for a hart
|
||||
*
|
||||
* Trigger a software/inter-process interrupt for a hart. The CPU interrupt
|
||||
* controller for the CPU handle passed to this function must be initialized
|
||||
* before this function is called.
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @param hartid The CPU hart ID to be interrupted
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_cpu_software_set_ipi(struct metal_cpu *cpu, int hartid)
|
||||
{ return cpu->vtable->set_sw_ipi(cpu, hartid); }
|
||||
|
||||
/*!
|
||||
* @brief Clear the inter-process interrupt for a hart
|
||||
*
|
||||
* Clear the software/inter-process interrupt for a hart. The CPU interrupt
|
||||
* controller for the CPU handle passed to this function must be initialized
|
||||
* before this function is called.
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @param hartid The CPU hart ID to clear
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_cpu_software_clear_ipi(struct metal_cpu *cpu, int hartid)
|
||||
{ return cpu->vtable->clear_sw_ipi(cpu, hartid); }
|
||||
|
||||
/*!
|
||||
* @brief Get the value of MSIP for the given hart
|
||||
*
|
||||
* Get the value of the machine software interrupt pending bit for
|
||||
* the given hart. The CPU interrupt controller for the CPU handle passed
|
||||
* as argument to this function must be initialized before this function
|
||||
* is called.
|
||||
*
|
||||
* @param cpu the CPU device handle
|
||||
* @param hartid The CPU hart to read
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_cpu_get_msip(struct metal_cpu *cpu, int hartid)
|
||||
{ return cpu->vtable->get_msip(cpu, hartid); }
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt controller for the CPU
|
||||
*
|
||||
* Get the CPU interrupt controller. The controller returned by this
|
||||
* function must be initialized before any interrupts are registered
|
||||
* or enabled and before any exception handlers are registered with
|
||||
* this CPU.
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @return The handle for the CPU interrupt controller
|
||||
*/
|
||||
__inline__ struct metal_interrupt* metal_cpu_interrupt_controller(struct metal_cpu *cpu)
|
||||
{ return cpu->vtable->controller_interrupt(cpu); }
|
||||
|
||||
/*!
|
||||
* @brief Register an exception handler
|
||||
*
|
||||
* Register an exception handler for the CPU. The CPU interrupt controller must be initialized
|
||||
* before this function is called.
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @param ecode The exception code to register a handler for
|
||||
* @param handler Callback function for the exception handler
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_cpu_exception_register(struct metal_cpu *cpu, int ecode, metal_exception_handler_t handler)
|
||||
{ return cpu->vtable->exception_register(cpu, ecode, handler); }
|
||||
|
||||
/*!
|
||||
* @brief Get the length of an instruction in bytes
|
||||
*
|
||||
* Get the length of an instruction in bytes.
|
||||
*
|
||||
* On RISC-V platforms, this is useful for detecting whether an instruction is
|
||||
* compressed (2 bytes long) or uncompressed (4 bytes long).
|
||||
*
|
||||
* This function is useful in conjuction with `metal_cpu_get_exception_pc()`
|
||||
* and `metal_cpu_set_exception_pc()` in order to cause the exception handler to
|
||||
* return execution after the faulting instruction.
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @param epc The address of the instruction to measure
|
||||
* @return the length of the instruction in bytes
|
||||
*/
|
||||
__inline__ int metal_cpu_get_instruction_length(struct metal_cpu *cpu, uintptr_t epc)
|
||||
{ return cpu->vtable->get_ilen(cpu, epc); }
|
||||
|
||||
/*!
|
||||
* @brief Get the program counter of the current exception.
|
||||
*
|
||||
* This function must be called within an exception handler. The behavior is
|
||||
* undefined outside of an exception handler.
|
||||
*
|
||||
* @param cpu The CPU device handle
|
||||
* @return The value of the program counter at the time of the exception
|
||||
*/
|
||||
__inline__ uintptr_t metal_cpu_get_exception_pc(struct metal_cpu *cpu)
|
||||
{ return cpu->vtable->get_epc(cpu); }
|
||||
|
||||
/*!
|
||||
* @brief Set the exception program counter
|
||||
*
|
||||
* This function must be called within an exception handler. The behavior
|
||||
* is undefined outside of an exception handler.
|
||||
*
|
||||
* This function can be used to cause an exception handler to return execution
|
||||
* to an address other than the one that caused the exception.
|
||||
*
|
||||
* @param cpu the CPU device handle
|
||||
* @param epc The address to set the exception program counter to
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_cpu_set_exception_pc(struct metal_cpu *cpu, uintptr_t epc)
|
||||
{ return cpu->vtable->set_epc(cpu, epc); }
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__FIXED_CLOCK_H
|
||||
#define METAL__DRIVERS__FIXED_CLOCK_H
|
||||
|
||||
struct __metal_driver_fixed_clock;
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/clock.h>
|
||||
|
||||
struct __metal_driver_vtable_fixed_clock {
|
||||
struct __metal_clock_vtable clock;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_fixed_clock)
|
||||
|
||||
struct __metal_driver_fixed_clock {
|
||||
struct metal_clock clock;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__FIXED_FACTOR_CLOCK_H
|
||||
#define METAL__DRIVERS__FIXED_FACTOR_CLOCK_H
|
||||
|
||||
struct __metal_driver_fixed_factor_clock;
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/clock.h>
|
||||
|
||||
struct __metal_driver_vtable_fixed_factor_clock {
|
||||
struct __metal_clock_vtable clock;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_fixed_factor_clock)
|
||||
|
||||
struct __metal_driver_fixed_factor_clock {
|
||||
struct metal_clock clock;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__RISCV_CLINT0_H
|
||||
#define METAL__DRIVERS__RISCV_CLINT0_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/drivers/riscv_cpu.h>
|
||||
|
||||
struct __metal_driver_vtable_riscv_clint0 {
|
||||
struct metal_interrupt_vtable clint_vtable;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_riscv_clint0)
|
||||
|
||||
#define __METAL_MACHINE_MACROS
|
||||
#include <metal/machine.h>
|
||||
struct __metal_driver_riscv_clint0 {
|
||||
struct metal_interrupt controller;
|
||||
int init_done;
|
||||
};
|
||||
#undef __METAL_MACHINE_MACROS
|
||||
|
||||
#endif
|
|
@ -0,0 +1,192 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__RISCV_CPU_H
|
||||
#define METAL__DRIVERS__RISCV_CPU_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <metal/cpu.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
#define METAL_MAX_CORES 8
|
||||
#define METAL_MAX_MI 32 /* Per ISA MCause interrupts 32+ are Reserved */
|
||||
#define METAL_MAX_ME 12 /* Per ISA Exception codes 12+ are Reserved */
|
||||
#define METAL_DEFAULT_RTC_FREQ 32768
|
||||
|
||||
#define METAL_DISABLE 0
|
||||
#define METAL_ENABLE 1
|
||||
|
||||
#define METAL_ISA_A_EXTENSIONS 0x0001
|
||||
#define METAL_ISA_C_EXTENSIONS 0x0004
|
||||
#define METAL_ISA_D_EXTENSIONS 0x0008
|
||||
#define METAL_ISA_E_EXTENSIONS 0x0010
|
||||
#define METAL_ISA_F_EXTENSIONS 0x0020
|
||||
#define METAL_ISA_G_EXTENSIONS 0x0040
|
||||
#define METAL_ISA_I_EXTENSIONS 0x0100
|
||||
#define METAL_ISA_M_EXTENSIONS 0x1000
|
||||
#define METAL_ISA_N_EXTENSIONS 0x2000
|
||||
#define METAL_ISA_Q_EXTENSIONS 0x10000
|
||||
#define METAL_ISA_S_EXTENSIONS 0x40000
|
||||
#define METAL_ISA_U_EXTENSIONS 0x100000
|
||||
#define METAL_ISA_V_EXTENSIONS 0x200000
|
||||
#define METAL_ISA_XL32_EXTENSIONS 0x40000000UL
|
||||
#define METAL_ISA_XL64_EXTENSIONS 0x8000000000000000UL
|
||||
#define METAL_ISA_XL128_EXTENSIONS 0xC000000000000000UL
|
||||
|
||||
#define METAL_MTVEC_DIRECT 0x00
|
||||
#define METAL_MTVEC_VECTORED 0x01
|
||||
#define METAL_MTVEC_CLIC 0x02
|
||||
#define METAL_MTVEC_CLIC_VECTORED 0x03
|
||||
#define METAL_MTVEC_CLIC_RESERVED 0x3C
|
||||
#define METAL_MTVEC_MASK 0x3F
|
||||
#if __riscv_xlen == 32
|
||||
#define METAL_MCAUSE_INTR 0x80000000UL
|
||||
#define METAL_MCAUSE_CAUSE 0x000003FFUL
|
||||
#else
|
||||
#define METAL_MCAUSE_INTR 0x8000000000000000UL
|
||||
#define METAL_MCAUSE_CAUSE 0x00000000000003FFUL
|
||||
#endif
|
||||
#define METAL_MCAUSE_MINHV 0x40000000UL
|
||||
#define METAL_MCAUSE_MPP 0x30000000UL
|
||||
#define METAL_MCAUSE_MPIE 0x08000000UL
|
||||
#define METAL_MCAUSE_MPIL 0x00FF0000UL
|
||||
#define METAL_MSTATUS_MIE 0x00000008UL
|
||||
#define METAL_MSTATUS_MPIE 0x00000080UL
|
||||
#define METAL_MSTATUS_MPP 0x00001800UL
|
||||
#define METAL_MSTATUS_FS_INIT 0x00002000UL
|
||||
#define METAL_MSTATUS_FS_CLEAN 0x00004000UL
|
||||
#define METAL_MSTATUS_FS_DIRTY 0x00006000UL
|
||||
#define METAL_MSTATUS_MPRV 0x00020000UL
|
||||
#define METAL_MSTATUS_MXR 0x00080000UL
|
||||
#define METAL_MINTSTATUS_MIL 0xFF000000UL
|
||||
#define METAL_MINTSTATUS_SIL 0x0000FF00UL
|
||||
#define METAL_MINTSTATUS_UIL 0x000000FFUL
|
||||
|
||||
#define METAL_LOCAL_INTR(X) (16 + X)
|
||||
#define METAL_MCAUSE_EVAL(cause) (cause & METAL_MCAUSE_INTR)
|
||||
#define METAL_INTERRUPT(cause) (METAL_MCAUSE_EVAL(cause) ? 1 : 0)
|
||||
#define METAL_EXCEPTION(cause) (METAL_MCAUSE_EVAL(cause) ? 0 : 1)
|
||||
#define METAL_SW_INTR_EXCEPTION (METAL_MCAUSE_INTR + 3)
|
||||
#define METAL_TMR_INTR_EXCEPTION (METAL_MCAUSE_INTR + 7)
|
||||
#define METAL_EXT_INTR_EXCEPTION (METAL_MCAUSE_INTR + 11)
|
||||
#define METAL_LOCAL_INTR_EXCEPTION(X) (METAL_MCAUSE_INTR + METAL_LOCAL_INTR(X))
|
||||
#define METAL_LOCAL_INTR_RESERVE0 1
|
||||
#define METAL_LOCAL_INTR_RESERVE1 2
|
||||
#define METAL_LOCAL_INTR_RESERVE2 4
|
||||
#define METAL_LOCAL_INTERRUPT_SW 8 /* Bit3 0x008 */
|
||||
#define METAL_LOCAL_INTR_RESERVE4 16
|
||||
#define METAL_LOCAL_INTR_RESERVE5 32
|
||||
#define METAL_LOCAL_INTR_RESERVE6 64
|
||||
#define METAL_LOCAL_INTERRUPT_TMR 128 /* Bit7 0x080 */
|
||||
#define METAL_LOCAL_INTR_RESERVE8 256
|
||||
#define METAL_LOCAL_INTR_RESERVE9 512
|
||||
#define METAL_LOCAL_INTR_RESERVE10 1024
|
||||
#define METAL_LOCAL_INTERRUPT_EXT 2048 /* Bit11 0x800 */
|
||||
/* Bit12 to Bit15 are Reserved */
|
||||
#define METAL_LOCAL_INTERRUPT(X) (0x10000 << X) /* Bit16+ Start of Custom Local Interrupt */
|
||||
#define METAL_MIE_INTERRUPT METAL_MSTATUS_MIE
|
||||
|
||||
#define METAL_INSN_LENGTH_MASK 3
|
||||
#define METAL_INSN_NOT_COMPRESSED 3
|
||||
|
||||
typedef enum {
|
||||
METAL_MACHINE_PRIVILEGE_MODE,
|
||||
METAL_SUPERVISOR_PRIVILEGE_MODE,
|
||||
METAL_USER_PRIVILEGE_MODE,
|
||||
} metal_privilege_mode_e;
|
||||
|
||||
typedef enum {
|
||||
METAL_INTERRUPT_ID_BASE,
|
||||
METAL_INTERRUPT_ID_SW = (METAL_INTERRUPT_ID_BASE + 3),
|
||||
METAL_INTERRUPT_ID_TMR = (METAL_INTERRUPT_ID_BASE + 7),
|
||||
METAL_INTERRUPT_ID_EXT = (METAL_INTERRUPT_ID_BASE + 11),
|
||||
METAL_INTERRUPT_ID_CSW = (METAL_INTERRUPT_ID_BASE + 12),
|
||||
METAL_INTERRUPT_ID_LC0 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(0)),
|
||||
METAL_INTERRUPT_ID_LC1 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(1)),
|
||||
METAL_INTERRUPT_ID_LC2 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(2)),
|
||||
METAL_INTERRUPT_ID_LC3 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(3)),
|
||||
METAL_INTERRUPT_ID_LC4 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(4)),
|
||||
METAL_INTERRUPT_ID_LC5 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(5)),
|
||||
METAL_INTERRUPT_ID_LC6 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(6)),
|
||||
METAL_INTERRUPT_ID_LC7 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(7)),
|
||||
METAL_INTERRUPT_ID_LC8 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(8)),
|
||||
METAL_INTERRUPT_ID_LC9 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(9)),
|
||||
METAL_INTERRUPT_ID_LC10 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(10)),
|
||||
METAL_INTERRUPT_ID_LC11 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(11)),
|
||||
METAL_INTERRUPT_ID_LC12 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(12)),
|
||||
METAL_INTERRUPT_ID_LC13 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(13)),
|
||||
METAL_INTERRUPT_ID_LC14 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(14)),
|
||||
METAL_INTERRUPT_ID_LC15 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(15)),
|
||||
METAL_INTERRUPT_ID_LCMX,
|
||||
METAL_INTERRUPT_ID_GL0 = METAL_INTERRUPT_ID_LCMX,
|
||||
METAL_INTERRUPT_ID_GLMX = (METAL_MCAUSE_CAUSE + 1),
|
||||
} metal_interrupt_id_e;
|
||||
|
||||
typedef enum {
|
||||
METAL_IAM_EXCEPTION_CODE, /* Instruction address misaligned */
|
||||
METAL_IAF_EXCEPTION_CODE, /* Instruction access faultd */
|
||||
METAL_II_EXCEPTION_CODE, /* Illegal instruction */
|
||||
METAL_BREAK_EXCEPTION_CODE, /* Breakpoint */
|
||||
METAL_LAM_EXCEPTION_CODE, /* Load address misaligned */
|
||||
METAL_LAF_EXCEPTION_CODE, /* Load access fault */
|
||||
METAL_SAMOAM_EXCEPTION_CODE, /* Store/AMO address misaligned */
|
||||
METAL_SAMOAF_EXCEPTION_CODE, /* Store/AMO access fault */
|
||||
METAL_ECALL_U_EXCEPTION_CODE, /* Environment call from U-mode */
|
||||
METAL_R9_EXCEPTION_CODE, /* Reserved */
|
||||
METAL_R10_EXCEPTION_CODE, /* Reserved */
|
||||
METAL_ECALL_M_EXCEPTION_CODE, /* Environment call from M-mode */
|
||||
METAL_MAX_EXCEPTION_CODE,
|
||||
} metal_exception_code_e;
|
||||
|
||||
typedef enum {
|
||||
METAL_TIMER_MTIME_GET = 1,
|
||||
METAL_SOFTWARE_IPI_CLEAR,
|
||||
METAL_SOFTWARE_IPI_SET,
|
||||
METAL_SOFTWARE_MSIP_GET,
|
||||
METAL_MAX_INTERRUPT_GET,
|
||||
METAL_INDEX_INTERRUPT_GET,
|
||||
} metal_interrup_cmd_e;
|
||||
|
||||
typedef struct __metal_interrupt_data {
|
||||
long long pad : 64;
|
||||
metal_interrupt_handler_t handler;
|
||||
void *sub_int;
|
||||
void *exint_data;
|
||||
} __metal_interrupt_data;
|
||||
|
||||
/* CPU interrupt controller */
|
||||
|
||||
uintptr_t __metal_myhart_id(void);
|
||||
|
||||
struct __metal_driver_vtable_riscv_cpu_intc {
|
||||
struct metal_interrupt_vtable controller_vtable;
|
||||
};
|
||||
|
||||
|
||||
void __metal_interrupt_global_enable(void);
|
||||
void __metal_interrupt_global_disable(void);
|
||||
metal_vector_mode __metal_controller_interrupt_vector_mode(void);
|
||||
void __metal_controller_interrupt_vector(metal_vector_mode mode, void *vec_table);
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_riscv_cpu_intc)
|
||||
|
||||
struct __metal_driver_riscv_cpu_intc {
|
||||
struct metal_interrupt controller;
|
||||
int init_done;
|
||||
uintptr_t metal_mtvec_table[METAL_MAX_MI];
|
||||
__metal_interrupt_data metal_int_table[METAL_MAX_MI];
|
||||
metal_exception_handler_t metal_exception_table[METAL_MAX_ME];
|
||||
};
|
||||
|
||||
/* CPU driver*/
|
||||
struct __metal_driver_vtable_cpu {
|
||||
struct metal_cpu_vtable cpu_vtable;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_cpu)
|
||||
|
||||
struct __metal_driver_cpu {
|
||||
struct metal_cpu cpu;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__RISCV_PLIC0_H
|
||||
#define METAL__DRIVERS__RISCV_PLIC0_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/drivers/riscv_cpu.h>
|
||||
|
||||
#define METAL_PLIC_SOURCE_MASK 0x1F
|
||||
#define METAL_PLIC_SOURCE_SHIFT 5
|
||||
#define METAL_PLIC_SOURCE_PRIORITY_SHIFT 2
|
||||
#define METAL_PLIC_SOURCE_PENDING_SHIFT 0
|
||||
|
||||
struct __metal_driver_vtable_riscv_plic0 {
|
||||
struct metal_interrupt_vtable plic_vtable;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_riscv_plic0)
|
||||
|
||||
#define __METAL_MACHINE_MACROS
|
||||
#include <metal/machine.h>
|
||||
struct __metal_driver_riscv_plic0 {
|
||||
struct metal_interrupt controller;
|
||||
int init_done;
|
||||
metal_interrupt_handler_t metal_exint_table[__METAL_PLIC_SUBINTERRUPTS];
|
||||
__metal_interrupt_data metal_exdata_table[__METAL_PLIC_SUBINTERRUPTS];
|
||||
};
|
||||
#undef __METAL_MACHINE_MACROS
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_CCACHE0_H
|
||||
#define METAL__DRIVERS__SIFIVE_CCACHE0_H
|
||||
|
||||
#include <metal/cache.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_ccache0 {
|
||||
struct __metal_cache_vtable cache;
|
||||
};
|
||||
|
||||
struct __metal_driver_sifive_ccache0;
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_ccache0)
|
||||
|
||||
struct __metal_driver_sifive_ccache0 {
|
||||
struct metal_cache cache;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_CLIC0_H
|
||||
#define METAL__DRIVERS__SIFIVE_CLIC0_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/drivers/riscv_cpu.h>
|
||||
|
||||
#define METAL_CLIC_MAX_NMBITS 2
|
||||
#define METAL_CLIC_MAX_NLBITS 8
|
||||
#define METAL_CLIC_MAX_NVBITS 1
|
||||
|
||||
#define METAL_SIFIVE_CLIC0_CLICCFG_NMBITS_MMODE 0x00
|
||||
#define METAL_SIFIVE_CLIC0_CLICCFG_NMBITS_SMODE1 0x20
|
||||
#define METAL_SIFIVE_CLIC0_CLICCFG_NMBITS_SMODE2 0x40
|
||||
#define METAL_SIFIVE_CLIC0_CLICCFG_NMBITS_MASK 0x60
|
||||
#define METAL_SIFIVE_CLIC0_CLICCFG_NLBITS_MASK 0x1E
|
||||
#define METAL_SIFIVE_CLIC0_CLICCFG_NVBIT_MASK 0x01
|
||||
|
||||
#define METAL_CLIC_ICTRL_SMODE1_MASK 0x7F /* b8 set imply M-mode */
|
||||
#define METAL_CLIC_ICTRL_SMODE2_MASK 0x3F /* b8 set M-mode, b7 clear U-mode */
|
||||
|
||||
#define METAL_MAX_INTERRUPT_LEVEL ((1 << METAL_CLIC_MAX_NLBITS) - 1)
|
||||
|
||||
struct __metal_driver_vtable_sifive_clic0 {
|
||||
struct metal_interrupt_vtable clic_vtable;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_clic0)
|
||||
|
||||
#define __METAL_MACHINE_MACROS
|
||||
#include <metal/machine.h>
|
||||
struct __metal_driver_sifive_clic0 {
|
||||
struct metal_interrupt controller;
|
||||
int init_done;
|
||||
int pad[14];
|
||||
metal_interrupt_vector_handler_t metal_mtvt_table[__METAL_CLIC_SUBINTERRUPTS];
|
||||
__metal_interrupt_data metal_exint_table[__METAL_CLIC_SUBINTERRUPTS];
|
||||
};
|
||||
#undef __METAL_MACHINE_MACROS
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_FE310_G000_HFROSC_H
|
||||
#define METAL__DRIVERS__SIFIVE_FE310_G000_HFROSC_H
|
||||
|
||||
#include <metal/drivers/sifive_fe310-g000_prci.h>
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/clock.h>
|
||||
#include <metal/io.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_fe310_g000_hfrosc {
|
||||
struct __metal_clock_vtable clock;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_hfrosc)
|
||||
|
||||
struct __metal_driver_sifive_fe310_g000_hfrosc {
|
||||
struct metal_clock clock;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_FE310_G000_HFXOSC_H
|
||||
#define METAL__DRIVERS__SIFIVE_FE310_G000_HFXOSC_H
|
||||
|
||||
#include <metal/clock.h>
|
||||
#include <metal/drivers/sifive_fe310-g000_prci.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_fe310_g000_hfxosc {
|
||||
struct __metal_clock_vtable clock;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_hfxosc)
|
||||
|
||||
struct __metal_driver_sifive_fe310_g000_hfxosc {
|
||||
struct metal_clock clock;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_FE310_G000_LFROSC_H
|
||||
#define METAL__DRIVERS__SIFIVE_FE310_G000_LFROSC_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/clock.h>
|
||||
#include <metal/io.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_fe310_g000_lfrosc {
|
||||
struct __metal_clock_vtable clock;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_lfrosc)
|
||||
|
||||
struct __metal_driver_sifive_fe310_g000_lfrosc {
|
||||
struct metal_clock clock;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,26 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_FE310_G000_PLL_H
|
||||
#define METAL__DRIVERS__SIFIVE_FE310_G000_PLL_H
|
||||
|
||||
struct __metal_driver_sifive_fe310_g000_pll;
|
||||
|
||||
#include <metal/clock.h>
|
||||
#include <metal/drivers/sifive_fe310-g000_prci.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_fe310_g000_pll {
|
||||
void (*init)(struct __metal_driver_sifive_fe310_g000_pll *pll);
|
||||
struct __metal_clock_vtable clock;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_pll)
|
||||
|
||||
struct __metal_driver_sifive_fe310_g000_pll {
|
||||
struct metal_clock clock;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_FE310_G000_PRCI_H
|
||||
#define METAL__DRIVERS__SIFIVE_FE310_G000_PRCI_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/io.h>
|
||||
|
||||
struct __metal_driver_sifive_fe310_g000_prci;
|
||||
|
||||
struct __metal_driver_vtable_sifive_fe310_g000_prci {
|
||||
long (*get_reg)(const struct __metal_driver_sifive_fe310_g000_prci *, long offset);
|
||||
long (*set_reg)(const struct __metal_driver_sifive_fe310_g000_prci *, long offset, long value);
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_prci)
|
||||
|
||||
struct __metal_driver_sifive_fe310_g000_prci {
|
||||
const struct __metal_driver_vtable_sifive_fe310_g000_prci *vtable;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_FU540_C000_L2_H
|
||||
#define METAL__DRIVERS__SIFIVE_FU540_C000_L2_H
|
||||
|
||||
#include <metal/cache.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_fu540_c000_l2 {
|
||||
struct __metal_cache_vtable cache;
|
||||
};
|
||||
|
||||
struct __metal_driver_sifive_fu540_c000_l2;
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_fu540_c000_l2)
|
||||
|
||||
struct __metal_driver_sifive_fu540_c000_l2 {
|
||||
struct metal_cache cache;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_GLOBAL_EXTERNAL_INTERRUPTS0_H
|
||||
#define METAL__DRIVERS__SIFIVE_GLOBAL_EXTERNAL_INTERRUPTS0_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/drivers/riscv_cpu.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_global_external_interrupts0 {
|
||||
struct metal_interrupt_vtable global0_vtable;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_global_external_interrupts0)
|
||||
|
||||
struct __metal_driver_sifive_global_external_interrupts0 {
|
||||
struct metal_interrupt irc;
|
||||
int init_done;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_GPIO_BUTTONS_H
|
||||
#define METAL__DRIVERS__SIFIVE_GPIO_BUTTONS_H
|
||||
|
||||
#include <string.h>
|
||||
#include <metal/button.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_button {
|
||||
struct metal_button_vtable button_vtable;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_button)
|
||||
|
||||
struct __metal_driver_sifive_gpio_button {
|
||||
struct metal_button button;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_GPIO_LEDS_H
|
||||
#define METAL__DRIVERS__SIFIVE_GPIO_LEDS_H
|
||||
|
||||
#include <metal/drivers/sifive_gpio0.h>
|
||||
#include <metal/led.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_led {
|
||||
struct metal_led_vtable led_vtable;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_led)
|
||||
|
||||
struct __metal_driver_sifive_gpio_led {
|
||||
struct metal_led led;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_GPIO_SWITCHES_H
|
||||
#define METAL__DRIVERS__SIFIVE_GPIO_SWITCHES_H
|
||||
|
||||
#include <metal/drivers/sifive_gpio0.h>
|
||||
#include <metal/switch.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_switch {
|
||||
struct metal_switch_vtable switch_vtable;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_switch)
|
||||
|
||||
struct __metal_driver_sifive_gpio_switch {
|
||||
struct metal_switch flip;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_GPIO0_H
|
||||
#define METAL__DRIVERS__SIFIVE_GPIO0_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/gpio.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_gpio0 {
|
||||
const struct __metal_gpio_vtable gpio;
|
||||
};
|
||||
|
||||
//struct __metal_driver_sifive_gpio0;
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_gpio0)
|
||||
|
||||
struct __metal_driver_sifive_gpio0 {
|
||||
struct metal_gpio gpio;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_EXTERNAL_INTERRUPTS0_H
|
||||
#define METAL__DRIVERS__SIFIVE_EXTERNAL_INTERRUPTS0_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/drivers/riscv_cpu.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_local_external_interrupts0 {
|
||||
struct metal_interrupt_vtable local0_vtable;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_local_external_interrupts0)
|
||||
|
||||
struct __metal_driver_sifive_local_external_interrupts0 {
|
||||
struct metal_interrupt irc;
|
||||
int init_done;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_RTC0_H
|
||||
#define METAL__DRIVERS__SIFIVE_RTC0_H
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
#include <metal/clock.h>
|
||||
#include <metal/interrupt.h>
|
||||
#include <metal/rtc.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_rtc0 {
|
||||
const struct metal_rtc_vtable rtc;
|
||||
};
|
||||
|
||||
struct __metal_driver_sifive_rtc0;
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_rtc0)
|
||||
|
||||
struct __metal_driver_sifive_rtc0 {
|
||||
const struct metal_rtc rtc;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_SPI0_H
|
||||
#define METAL__DRIVERS__SIFIVE_SPI0_H
|
||||
|
||||
#include <metal/drivers/sifive_gpio0.h>
|
||||
#include <metal/clock.h>
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/spi.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_spi0 {
|
||||
const struct metal_spi_vtable spi;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_spi0)
|
||||
|
||||
struct __metal_driver_sifive_spi0 {
|
||||
struct metal_spi spi;
|
||||
unsigned long baud_rate;
|
||||
metal_clock_callback pre_rate_change_callback;
|
||||
metal_clock_callback post_rate_change_callback;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_TEST0_H
|
||||
#define METAL__DRIVERS__SIFIVE_TEST0_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/shutdown.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_test0 {
|
||||
const struct __metal_shutdown_vtable shutdown;
|
||||
};
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_test0)
|
||||
|
||||
struct __metal_driver_sifive_test0 {
|
||||
struct __metal_shutdown shutdown;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_TRACE_H
|
||||
#define METAL__DRIVERS__SIFIVE_TRACE_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/uart.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_trace {
|
||||
const struct metal_uart_vtable uart;
|
||||
};
|
||||
|
||||
struct __metal_driver_sifive_trace;
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_trace)
|
||||
|
||||
struct __metal_driver_sifive_trace {
|
||||
struct metal_uart uart;
|
||||
};
|
||||
|
||||
#endif /* METAL__DRIVERS__SIFIVE_TRACE_H */
|
|
@ -0,0 +1,30 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_UART0_H
|
||||
#define METAL__DRIVERS__SIFIVE_UART0_H
|
||||
|
||||
#include <metal/drivers/sifive_gpio0.h>
|
||||
#include <metal/drivers/riscv_plic0.h>
|
||||
#include <metal/clock.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/uart.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_uart0 {
|
||||
const struct metal_uart_vtable uart;
|
||||
};
|
||||
|
||||
struct __metal_driver_sifive_uart0;
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_uart0)
|
||||
|
||||
struct __metal_driver_sifive_uart0 {
|
||||
struct metal_uart uart;
|
||||
unsigned long baud_rate;
|
||||
metal_clock_callback pre_rate_change_callback;
|
||||
metal_clock_callback post_rate_change_callback;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,26 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__DRIVERS__SIFIVE_WDOG0_H
|
||||
#define METAL__DRIVERS__SIFIVE_WDOG0_H
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
#include <metal/watchdog.h>
|
||||
#include <metal/clock.h>
|
||||
#include <metal/interrupt.h>
|
||||
|
||||
struct __metal_driver_vtable_sifive_wdog0 {
|
||||
const struct metal_watchdog_vtable watchdog;
|
||||
};
|
||||
|
||||
struct __metal_driver_sifive_wdog0;
|
||||
|
||||
__METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_wdog0)
|
||||
|
||||
struct __metal_driver_sifive_wdog0 {
|
||||
const struct metal_watchdog watchdog;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,284 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__GPIO_H
|
||||
#define METAL__GPIO_H
|
||||
|
||||
#include <metal/compiler.h>
|
||||
#include <metal/interrupt.h>
|
||||
|
||||
/*!
|
||||
* @file gpio.h
|
||||
* @brief API for manipulating general-purpose input/output
|
||||
*/
|
||||
|
||||
struct metal_gpio;
|
||||
|
||||
struct __metal_gpio_vtable {
|
||||
int (*disable_input)(struct metal_gpio *, long pins);
|
||||
int (*enable_input)(struct metal_gpio *, long pins);
|
||||
long (*input)(struct metal_gpio *);
|
||||
long (*output)(struct metal_gpio *);
|
||||
int (*disable_output)(struct metal_gpio *, long pins);
|
||||
int (*enable_output)(struct metal_gpio *, long pins);
|
||||
int (*output_set)(struct metal_gpio *, long value);
|
||||
int (*output_clear)(struct metal_gpio *, long value);
|
||||
int (*output_toggle)(struct metal_gpio *, long value);
|
||||
int (*enable_io)(struct metal_gpio *, long pins, long dest);
|
||||
int (*disable_io)(struct metal_gpio *, long pins);
|
||||
int (*config_int)(struct metal_gpio *, long pins, int intr_type);
|
||||
int (*clear_int)(struct metal_gpio *, long pins, int intr_type);
|
||||
struct metal_interrupt* (*interrupt_controller)(struct metal_gpio *gpio);
|
||||
int (*get_interrupt_id)(struct metal_gpio *gpio, int pin);
|
||||
};
|
||||
|
||||
#define METAL_GPIO_INT_DISABLE 0
|
||||
#define METAL_GPIO_INT_RISING 1
|
||||
#define METAL_GPIO_INT_FALLING 2
|
||||
#define METAL_GPIO_INT_BOTH_EDGE 3
|
||||
#define METAL_GPIO_INT_LOW 4
|
||||
#define METAL_GPIO_INT_HIGH 5
|
||||
#define METAL_GPIO_INT_BOTH_LEVEL 6
|
||||
#define METAL_GPIO_INT_MAX 7
|
||||
|
||||
/*!
|
||||
* @struct metal_gpio
|
||||
* @brief The handle for a GPIO interface
|
||||
*/
|
||||
struct metal_gpio {
|
||||
const struct __metal_gpio_vtable *vtable;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Get a GPIO device handle
|
||||
* @param device_num The GPIO device index
|
||||
* @return The GPIO device handle, or NULL if there is no device at that index
|
||||
*/
|
||||
struct metal_gpio *metal_gpio_get_device(unsigned int device_num);
|
||||
|
||||
/*!
|
||||
* @brief enable input on a pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The pin number indexed from 0
|
||||
* @return 0 if the input is successfully enabled
|
||||
*/
|
||||
__inline__ int metal_gpio_enable_input(struct metal_gpio *gpio, int pin) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return gpio->vtable->enable_input(gpio, (1 << pin));
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Disable input on a pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The pin number indexed from 0
|
||||
* @return 0 if the input is successfully disabled
|
||||
*/
|
||||
__inline__ int metal_gpio_disable_input(struct metal_gpio *gpio, int pin) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return gpio->vtable->disable_input(gpio, (1 << pin));
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Enable output on a pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The pin number indexed from 0
|
||||
* @return 0 if the output is successfully enabled
|
||||
*/
|
||||
__inline__ int metal_gpio_enable_output(struct metal_gpio *gpio, int pin) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return gpio->vtable->enable_output(gpio, (1 << pin));
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Disable output on a pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The pin number indexed from 0
|
||||
* @return 0 if the output is successfully disabled
|
||||
*/
|
||||
__inline__ int metal_gpio_disable_output(struct metal_gpio *gpio, int pin) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return gpio->vtable->disable_output(gpio, (1 << pin));
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set the output value of a GPIO pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The pin number indexed from 0
|
||||
* @param value The value to set the pin to
|
||||
* @return 0 if the output is successfully set
|
||||
*/
|
||||
__inline__ int metal_gpio_set_pin(struct metal_gpio *gpio, int pin, int value) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(value == 0) {
|
||||
return gpio->vtable->output_clear(gpio, (1 << pin));
|
||||
} else {
|
||||
return gpio->vtable->output_set(gpio, (1 << pin));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the value of the GPIO pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The pin number indexed from 0
|
||||
* @return The value of the GPIO pin
|
||||
*/
|
||||
__inline__ int metal_gpio_get_input_pin(struct metal_gpio *gpio, int pin) {
|
||||
if(!gpio) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long value = gpio->vtable->input(gpio);
|
||||
|
||||
if(value & (1 << pin)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the value of the GPIO pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The pin number indexed from 0
|
||||
* @return The value of the GPIO pin
|
||||
*/
|
||||
__inline__ int metal_gpio_get_output_pin(struct metal_gpio *gpio, int pin) {
|
||||
if(!gpio) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long value = gpio->vtable->output(gpio);
|
||||
|
||||
if(value & (1 << pin)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Clears the value of the GPIO pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The pin number indexed from 0
|
||||
* @return 0 if the pin is successfully cleared
|
||||
*/
|
||||
__inline__ int metal_gpio_clear_pin(struct metal_gpio *gpio, int pin) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return gpio->vtable->output_clear(gpio, (1 << pin));
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Toggles the value of the GPIO pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The pin number indexed from 0
|
||||
* @return 0 if the pin is successfully toggled
|
||||
*/
|
||||
__inline__ int metal_gpio_toggle_pin(struct metal_gpio *gpio, int pin) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return gpio->vtable->output_toggle(gpio, (1 << pin));
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Enables and sets the pinmux for a GPIO pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The bitmask for the pin to enable pinmux on
|
||||
* @param io_function The IO function to set
|
||||
* @return 0 if the pinmux is successfully set
|
||||
*/
|
||||
__inline__ int metal_gpio_enable_pinmux(struct metal_gpio *gpio, int pin, int io_function) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return gpio->vtable->enable_io(gpio, (1 << pin), (io_function << pin));
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Disables the pinmux for a GPIO pin
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The bitmask for the pin to disable pinmux on
|
||||
* @return 0 if the pinmux is successfully set
|
||||
*/
|
||||
__inline__ int metal_gpio_disable_pinmux(struct metal_gpio *gpio, int pin) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return gpio->vtable->disable_io(gpio, (1 << pin));
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Config gpio interrupt type
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The bitmask for the pin to enable gpio interrupt
|
||||
* @param intr_type The interrupt type
|
||||
* @return 0 if the interrupt mode is setup properly
|
||||
*/
|
||||
__inline__ int metal_gpio_config_interrupt(struct metal_gpio *gpio, int pin, int intr_type) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return gpio->vtable->config_int(gpio, (1 << pin), intr_type);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Clear gpio interrupt status
|
||||
* @param gpio The handle for the GPIO interface
|
||||
* @param pin The bitmask for the pin to clear gpio interrupt
|
||||
* @param intr_type The interrupt type to be clear
|
||||
* @return 0 if the interrupt is cleared
|
||||
*/
|
||||
__inline__ int metal_gpio_clear_interrupt(struct metal_gpio *gpio, int pin, int intr_type) {
|
||||
if(!gpio) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return gpio->vtable->clear_int(gpio, (1 << pin), intr_type);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt controller for a gpio
|
||||
*
|
||||
* @param gpio The handle for the gpio
|
||||
* @return A pointer to the interrupt controller responsible for handling
|
||||
* gpio interrupts.
|
||||
*/
|
||||
__inline__ struct metal_interrupt*
|
||||
metal_gpio_interrupt_controller(struct metal_gpio *gpio) {
|
||||
return gpio->vtable->interrupt_controller(gpio);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt id for a gpio
|
||||
*
|
||||
* @param gpio The handle for the gpio
|
||||
* @param pin The bitmask for the pin to get gpio interrupt id
|
||||
* @return The interrupt id corresponding to a gpio.
|
||||
*/
|
||||
__inline__ int metal_gpio_get_interrupt_id(struct metal_gpio *gpio, int pin) {
|
||||
return gpio->vtable->get_interrupt_id(gpio, pin);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,461 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__INTERRUPT_H
|
||||
#define METAL__INTERRUPT_H
|
||||
|
||||
/*! @file interrupt.h
|
||||
* @brief API for registering and manipulating interrupts
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*!
|
||||
* @brief Possible interrupt controllers
|
||||
*/
|
||||
typedef enum metal_interrupt_controller_ {
|
||||
METAL_CPU_CONTROLLER = 0,
|
||||
METAL_CLINT_CONTROLLER = 1,
|
||||
METAL_CLIC_CONTROLLER = 2,
|
||||
METAL_PLIC_CONTROLLER = 3
|
||||
} metal_intr_cntrl_type;
|
||||
|
||||
/*!
|
||||
* @brief Possible mode of interrupts to operate
|
||||
*/
|
||||
typedef enum metal_vector_mode_ {
|
||||
METAL_DIRECT_MODE = 0,
|
||||
METAL_VECTOR_MODE = 1,
|
||||
METAL_SELECTIVE_NONVECTOR_MODE = 2,
|
||||
METAL_SELECTIVE_VECTOR_MODE = 3,
|
||||
METAL_HARDWARE_VECTOR_MODE = 4
|
||||
} metal_vector_mode;
|
||||
|
||||
/*!
|
||||
* @brief Possible mode of privilege interrupts to operate
|
||||
*/
|
||||
typedef enum metal_intr_priv_mode_ {
|
||||
METAL_INTR_PRIV_M_MODE = 0,
|
||||
METAL_INTR_PRIV_MU_MODE = 1,
|
||||
METAL_INTR_PRIV_MSU_MODE = 2
|
||||
} metal_intr_priv_mode;
|
||||
|
||||
/*!
|
||||
* @brief Function signature for interrupt callback handlers
|
||||
*/
|
||||
typedef void (*metal_interrupt_handler_t) (int, void *);
|
||||
typedef void (*metal_interrupt_vector_handler_t) (void);
|
||||
|
||||
struct metal_interrupt;
|
||||
|
||||
struct metal_interrupt_vtable {
|
||||
void (*interrupt_init)(struct metal_interrupt *controller);
|
||||
int (*interrupt_set_vector_mode)(struct metal_interrupt *controller, metal_vector_mode mode);
|
||||
metal_vector_mode (*interrupt_get_vector_mode)(struct metal_interrupt *controller);
|
||||
int (*interrupt_set_privilege)(struct metal_interrupt *controller, metal_intr_priv_mode priv);
|
||||
metal_intr_priv_mode (*interrupt_get_privilege)(struct metal_interrupt *controller);
|
||||
int (*interrupt_clear)(struct metal_interrupt *controller, int id);
|
||||
int (*interrupt_set)(struct metal_interrupt *controller, int id);
|
||||
int (*interrupt_register)(struct metal_interrupt *controller, int id,
|
||||
metal_interrupt_handler_t isr, void *priv_data);
|
||||
int (*interrupt_vector_register)(struct metal_interrupt *controller, int id,
|
||||
metal_interrupt_vector_handler_t isr, void *priv_data);
|
||||
int (*interrupt_enable)(struct metal_interrupt *controller, int id);
|
||||
int (*interrupt_disable)(struct metal_interrupt *controller, int id);
|
||||
int (*interrupt_vector_enable)(struct metal_interrupt *controller, int id);
|
||||
int (*interrupt_vector_disable)(struct metal_interrupt *controller, int id);
|
||||
unsigned int (*interrupt_get_threshold)(struct metal_interrupt *controller);
|
||||
int (*interrupt_set_threshold)(struct metal_interrupt *controller, unsigned int threshold);
|
||||
unsigned int (*interrupt_get_priority)(struct metal_interrupt *controller, int id);
|
||||
int (*interrupt_set_priority)(struct metal_interrupt *controller, int id, unsigned int priority);
|
||||
int (*command_request)(struct metal_interrupt *controller, int cmd, void *data);
|
||||
int (*mtimecmp_set)(struct metal_interrupt *controller, int hartid, unsigned long long time);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief A handle for an interrupt
|
||||
*/
|
||||
struct metal_interrupt {
|
||||
const struct metal_interrupt_vtable *vtable;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Initialize a given interrupt controller
|
||||
*
|
||||
* Initialize a given interrupt controller. This function must be called
|
||||
* before any interrupts are registered or enabled with the handler. It
|
||||
* is invalid to initialize an interrupt controller more than once.
|
||||
*
|
||||
* @param controller The handle for the interrupt controller
|
||||
*/
|
||||
__inline__ void metal_interrupt_init(struct metal_interrupt *controller)
|
||||
{
|
||||
controller->vtable->interrupt_init(controller);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the handle for an given interrupt controller type
|
||||
* @param cntrl The type ofinterrupt controller
|
||||
* @param id The instance of the interrupt controller
|
||||
* @return A handle to the interrupt controller (CLINT, CLIC, PLIC), or
|
||||
* NULL if none is found for the requested label
|
||||
*/
|
||||
struct metal_interrupt* metal_interrupt_get_controller(metal_intr_cntrl_type cntrl,
|
||||
int id);
|
||||
|
||||
/*!
|
||||
* @brief Configure vector mode for an interrupt controller
|
||||
*
|
||||
* Configure vector mode for an interrupt controller.
|
||||
* This function must be called after initialization and before
|
||||
* configuring individual interrupts, registering ISR.
|
||||
*
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param mode The vector mode of the interrupt controller.
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_interrupt_set_vector_mode(struct metal_interrupt *controller,
|
||||
metal_vector_mode mode)
|
||||
{
|
||||
return controller->vtable->interrupt_set_vector_mode(controller, mode);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get vector mode of a given an interrupt controller
|
||||
*
|
||||
* Configure vector mode for an interrupt controller.
|
||||
* This function must be called after initialization and before
|
||||
* configuring individual interrupts, registering ISR.
|
||||
*
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param mode The vector mode of the interrupt controller.
|
||||
* @return The interrupt vector mode
|
||||
*/
|
||||
__inline__ metal_vector_mode metal_interrupt_get_vector_mode(struct metal_interrupt *controller)
|
||||
{
|
||||
return controller->vtable->interrupt_get_vector_mode(controller);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Configure privilege mode a of given interrupt controller
|
||||
*
|
||||
* Configure privilege mode for a given interrupt controller.
|
||||
* This function must be called after initialization and before
|
||||
* configuring individual interrupts, registering ISR.
|
||||
*
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param privilege The privilege mode of the interrupt controller.
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_interrupt_set_privilege(struct metal_interrupt *controller,
|
||||
metal_intr_priv_mode privilege)
|
||||
{
|
||||
return controller->vtable->interrupt_set_privilege(controller, privilege);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get privilege mode a of given interrupt controller
|
||||
*
|
||||
* Get privilege mode for a given interrupt controller.
|
||||
* This function must be called after initialization and before
|
||||
* configuring individual interrupts, registering ISR.
|
||||
*
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @return The interrupt privilege mode
|
||||
*/
|
||||
__inline__ metal_intr_priv_mode metal_interrupt_get_privilege(struct metal_interrupt *controller)
|
||||
{
|
||||
return controller->vtable->interrupt_get_privilege(controller);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief clear an interrupt
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param id The interrupt ID to trigger
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_interrupt_clear(struct metal_interrupt *controller, int id)
|
||||
{
|
||||
return controller->vtable->interrupt_clear(controller, id);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set an interrupt
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param id The interrupt ID to trigger
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_interrupt_set(struct metal_interrupt *controller, int id)
|
||||
{
|
||||
return controller->vtable->interrupt_set(controller, id);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Register an interrupt handler
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param id The interrupt ID to register
|
||||
* @param handler The interrupt handler callback
|
||||
* @param priv_data Private data for the interrupt handler
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_interrupt_register_handler(struct metal_interrupt *controller,
|
||||
int id,
|
||||
metal_interrupt_handler_t handler,
|
||||
void *priv_data)
|
||||
{
|
||||
return controller->vtable->interrupt_register(controller, id, handler, priv_data);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Register an interrupt vector handler
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param id The interrupt ID to register
|
||||
* @param handler The interrupt vector handler callback
|
||||
* @param priv_data Private data for the interrupt handler
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_interrupt_register_vector_handler(struct metal_interrupt *controller,
|
||||
int id,
|
||||
metal_interrupt_vector_handler_t handler,
|
||||
void *priv_data)
|
||||
{
|
||||
return controller->vtable->interrupt_vector_register(controller, id, handler, priv_data);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Enable an interrupt
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param id The interrupt ID to enable
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_interrupt_enable(struct metal_interrupt *controller, int id)
|
||||
{
|
||||
return controller->vtable->interrupt_enable(controller, id);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Disable an interrupt
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param id The interrupt ID to disable
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_interrupt_disable(struct metal_interrupt *controller, int id)
|
||||
{
|
||||
return controller->vtable->interrupt_disable(controller, id);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set interrupt threshold level
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param threshold The interrupt threshold level
|
||||
* @return 0 upon success
|
||||
*/
|
||||
inline int metal_interrupt_set_threshold(struct metal_interrupt *controller, unsigned int level)
|
||||
{
|
||||
return controller->vtable->interrupt_set_threshold(controller, level);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get an interrupt threshold level
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @return The interrupt threshold level
|
||||
*/
|
||||
inline unsigned int metal_interrupt_get_threshold(struct metal_interrupt *controller)
|
||||
{
|
||||
return controller->vtable->interrupt_get_threshold(controller);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set an interrupt priority level
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param id The interrupt ID to enable
|
||||
* @param priority The interrupt priority level
|
||||
* @return 0 upon success
|
||||
*/
|
||||
inline int metal_interrupt_set_priority(struct metal_interrupt *controller,
|
||||
int id, unsigned int priority)
|
||||
{
|
||||
return controller->vtable->interrupt_set_priority(controller, id, priority);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get an interrupt priority level
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param id The interrupt ID to enable
|
||||
* @return The interrupt priority level
|
||||
*/
|
||||
inline unsigned int metal_interrupt_get_priority(struct metal_interrupt *controller, int id)
|
||||
{
|
||||
return controller->vtable->interrupt_get_priority(controller, id);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Enable an interrupt vector
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param id The interrupt ID to enable
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_interrupt_vector_enable(struct metal_interrupt *controller, int id)
|
||||
{
|
||||
return controller->vtable->interrupt_vector_enable(controller, id);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Disable an interrupt vector
|
||||
* @param controller The handle for the interrupt controller
|
||||
* @param id The interrupt ID to disable
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_interrupt_vector_disable(struct metal_interrupt *controller, int id)
|
||||
{
|
||||
return controller->vtable->interrupt_vector_disable(controller, id);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Default interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Software interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_software_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Timer interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_timer_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal External interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_external_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 0 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc0_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 1 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc1_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 2 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc2_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 3 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc3_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 4 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc4_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 5 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc5_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 6 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc6_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 7 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc7_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 8 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc8_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 9 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc9_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 10 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc10_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 11 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc11_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 12 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc12_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 13 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc13_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 14 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc14_interrupt_vector_handler(void);
|
||||
|
||||
/*!
|
||||
* @brief Metal Local 15 interrupt vector handler, that can be overriden by user
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void __attribute__((weak, interrupt)) metal_lc15_interrupt_vector_handler(void);
|
||||
|
||||
/* Utilities function to controll, manages devices via a given interrupt controller */
|
||||
__inline__ int _metal_interrupt_command_request(struct metal_interrupt *controller,
|
||||
int cmd, void *data)
|
||||
{
|
||||
return controller->vtable->command_request(controller, cmd, data);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__IO_H
|
||||
#define METAL__IO_H
|
||||
|
||||
/* This macro enforces that the compiler will not elide the given access. */
|
||||
#define __METAL_ACCESS_ONCE(x) (*(__typeof__(*x) volatile *)(x))
|
||||
|
||||
/* Allows users to specify arbitrary fences. */
|
||||
#define __METAL_IO_FENCE(pred, succ) __asm__ volatile ("fence " #pred "," #succ ::: "memory");
|
||||
|
||||
/* Types that explicitly describe an address as being used for memory-mapped
|
||||
* IO. These should only be accessed via __METAL_ACCESS_ONCE. */
|
||||
typedef unsigned char __metal_io_u8;
|
||||
typedef unsigned short __metal_io_u16;
|
||||
typedef unsigned int __metal_io_u32;
|
||||
#if __riscv_xlen >= 64
|
||||
typedef unsigned long __metal_io_u64;
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__ITIM_H
|
||||
#define METAL__ITIM_H
|
||||
|
||||
/*! @file itim.h
|
||||
*
|
||||
* API for manipulating ITIM allocation
|
||||
*/
|
||||
|
||||
|
||||
/*! @def METAL_PLACE_IN_ITIM
|
||||
* @brief Link a function into the ITIM
|
||||
*
|
||||
* Link a function into the ITIM (Instruction Tightly Integrated
|
||||
* Memory) if the ITIM is present on the target device.
|
||||
*/
|
||||
#define METAL_PLACE_IN_ITIM __attribute__((section(".itim")))
|
||||
|
||||
#endif
|
|
@ -0,0 +1,68 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__LED_H
|
||||
#define METAL__LED_H
|
||||
|
||||
/*!
|
||||
* @file led.h
|
||||
* @brief API for manipulating LEDs
|
||||
*/
|
||||
|
||||
struct metal_led;
|
||||
|
||||
struct metal_led_vtable {
|
||||
int (*led_exist)(struct metal_led *led, char *label);
|
||||
void (*led_enable)(struct metal_led *led);
|
||||
void (*led_on)(struct metal_led *led);
|
||||
void (*led_off)(struct metal_led *led);
|
||||
void (*led_toggle)(struct metal_led *led);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief A handle for an LED
|
||||
*/
|
||||
struct metal_led {
|
||||
const struct metal_led_vtable *vtable;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Get a handle for an LED
|
||||
* @param label The DeviceTree label for the desired LED
|
||||
* @return A handle to the LED, or NULL if none is found for the requested label
|
||||
*/
|
||||
struct metal_led* metal_led_get(char *label);
|
||||
|
||||
/*!
|
||||
* @brief Get a handle for a channel of an RGB LED
|
||||
* @param label The DeviceTree label for the desired LED
|
||||
* @param color The color for the LED in the DeviceTree
|
||||
* @return A handle to the LED, or NULL if none is found for the requested label and color
|
||||
*/
|
||||
struct metal_led* metal_led_get_rgb(char *label, char *color);
|
||||
|
||||
/*!
|
||||
* @brief Enable an LED
|
||||
* @param led The handle for the LED
|
||||
*/
|
||||
__inline__ void metal_led_enable(struct metal_led *led) { led->vtable->led_enable(led); }
|
||||
|
||||
/*!
|
||||
* @brief Turn an LED on
|
||||
* @param led The handle for the LED
|
||||
*/
|
||||
__inline__ void metal_led_on(struct metal_led *led) { led->vtable->led_on(led); }
|
||||
|
||||
/*!
|
||||
* @brief Turn an LED off
|
||||
* @param led The handle for the LED
|
||||
*/
|
||||
__inline__ void metal_led_off(struct metal_led *led) { led->vtable->led_off(led); }
|
||||
|
||||
/*!
|
||||
* @brief Toggle the on/off state of an LED
|
||||
* @param led The handle for the LED
|
||||
*/
|
||||
__inline__ void metal_led_toggle(struct metal_led *led) { led->vtable->led_toggle(led); }
|
||||
|
||||
#endif
|
|
@ -0,0 +1,146 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__LOCK_H
|
||||
#define METAL__LOCK_H
|
||||
|
||||
#include <metal/machine.h>
|
||||
#include <metal/memory.h>
|
||||
#include <metal/compiler.h>
|
||||
|
||||
/*!
|
||||
* @file lock.h
|
||||
* @brief An API for creating and using a software lock/mutex
|
||||
*/
|
||||
|
||||
/* TODO: How can we make the exception code platform-independant? */
|
||||
#define _METAL_STORE_AMO_ACCESS_FAULT 7
|
||||
|
||||
#define METAL_LOCK_BACKOFF_CYCLES 32
|
||||
#define METAL_LOCK_BACKOFF_EXPONENT 2
|
||||
|
||||
/*!
|
||||
* @def METAL_LOCK_DECLARE
|
||||
* @brief Declare a lock
|
||||
*
|
||||
* Locks must be declared with METAL_LOCK_DECLARE to ensure that the lock
|
||||
* is linked into a memory region which supports atomic memory operations.
|
||||
*/
|
||||
#define METAL_LOCK_DECLARE(name) \
|
||||
__attribute__((section(".data.locks"))) \
|
||||
struct metal_lock name
|
||||
|
||||
/*!
|
||||
* @brief A handle for a lock
|
||||
*/
|
||||
struct metal_lock {
|
||||
int _state;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Initialize a lock
|
||||
* @param lock The handle for a lock
|
||||
* @return 0 if the lock is successfully initialized. A non-zero code indicates failure.
|
||||
*
|
||||
* If the lock cannot be initialized, attempts to take or give the lock
|
||||
* will result in a Store/AMO access fault.
|
||||
*/
|
||||
__inline__ int metal_lock_init(struct metal_lock *lock) {
|
||||
#ifdef __riscv_atomic
|
||||
/* Get a handle for the memory which holds the lock state */
|
||||
struct metal_memory *lock_mem = metal_get_memory_from_address((uintptr_t) &(lock->_state));
|
||||
if(!lock_mem) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If the memory doesn't support atomics, report an error */
|
||||
if(!metal_memory_supports_atomics(lock_mem)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
lock->_state = 0;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return 3;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Take a lock
|
||||
* @param lock The handle for a lock
|
||||
* @return 0 if the lock is successfully taken
|
||||
*
|
||||
* If the lock initialization failed, attempts to take a lock will result in
|
||||
* a Store/AMO access fault.
|
||||
*/
|
||||
__inline__ int metal_lock_take(struct metal_lock *lock) {
|
||||
#ifdef __riscv_atomic
|
||||
int old = 1;
|
||||
int new = 1;
|
||||
|
||||
int backoff = 1;
|
||||
const int max_backoff = METAL_LOCK_BACKOFF_CYCLES * METAL_MAX_CORES;
|
||||
|
||||
while(1) {
|
||||
__asm__ volatile("amoswap.w.aq %[old], %[new], (%[state])"
|
||||
: [old] "=r" (old)
|
||||
: [new] "r" (new), [state] "r" (&(lock->_state))
|
||||
: "memory");
|
||||
|
||||
if (old == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < backoff; i++) {
|
||||
__asm__ volatile("");
|
||||
}
|
||||
|
||||
if (backoff < max_backoff) {
|
||||
backoff *= METAL_LOCK_BACKOFF_EXPONENT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
/* Store the memory address in mtval like a normal store/amo access fault */
|
||||
__asm__ ("csrw mtval, %[state]"
|
||||
:: [state] "r" (&(lock->_state)));
|
||||
|
||||
/* Trigger a Store/AMO access fault */
|
||||
_metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
|
||||
|
||||
/* If execution returns, indicate failure */
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Give back a held lock
|
||||
* @param lock The handle for a lock
|
||||
* @return 0 if the lock is successfully given
|
||||
*
|
||||
* If the lock initialization failed, attempts to give a lock will result in
|
||||
* a Store/AMO access fault.
|
||||
*/
|
||||
__inline__ int metal_lock_give(struct metal_lock *lock) {
|
||||
#ifdef __riscv_atomic
|
||||
__asm__ volatile("amoswap.w.rl x0, x0, (%[state])"
|
||||
:: [state] "r" (&(lock->_state))
|
||||
: "memory");
|
||||
|
||||
return 0;
|
||||
#else
|
||||
/* Store the memory address in mtval like a normal store/amo access fault */
|
||||
__asm__ ("csrw mtval, %[state]"
|
||||
:: [state] "r" (&(lock->_state)));
|
||||
|
||||
/* Trigger a Store/AMO access fault */
|
||||
_metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
|
||||
|
||||
/* If execution returns, indicate failure */
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* METAL__LOCK_H */
|
|
@ -0,0 +1,81 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__MEMORY_H
|
||||
#define METAL__MEMORY_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/*!
|
||||
* @file memory.h
|
||||
*
|
||||
* @brief API for enumerating memory blocks
|
||||
*/
|
||||
|
||||
struct _metal_memory_attributes {
|
||||
unsigned int R : 1;
|
||||
unsigned int W : 1;
|
||||
unsigned int X : 1;
|
||||
unsigned int C : 1;
|
||||
unsigned int A : 1;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief A handle for a memory block
|
||||
*/
|
||||
struct metal_memory {
|
||||
const uintptr_t _base_address;
|
||||
const size_t _size;
|
||||
const struct _metal_memory_attributes _attrs;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Get the memory block which services the given address
|
||||
*
|
||||
* Given a physical memory address, get a handle for the memory block to which
|
||||
* that address is mapped.
|
||||
*
|
||||
* @param address The address to query
|
||||
* @return The memory block handle, or NULL if the address is not mapped to a memory block
|
||||
*/
|
||||
struct metal_memory *metal_get_memory_from_address(const uintptr_t address);
|
||||
|
||||
/*!
|
||||
* @brief Get the base address for a memory block
|
||||
* @param memory The handle for the memory block
|
||||
* @return The base address of the memory block
|
||||
*/
|
||||
__inline__ uintptr_t metal_memory_get_base_address(const struct metal_memory *memory) {
|
||||
return memory->_base_address;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the size of a memory block
|
||||
* @param memory The handle for the memory block
|
||||
* @return The size of the memory block
|
||||
*/
|
||||
__inline__ size_t metal_memory_get_size(const struct metal_memory *memory) {
|
||||
return memory->_size;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Query if a memory block supports atomic operations
|
||||
* @param memory The handle for the memory block
|
||||
* @return nonzero if the memory block supports atomic operations
|
||||
*/
|
||||
__inline__ int metal_memory_supports_atomics(const struct metal_memory *memory) {
|
||||
return memory->_attrs.A;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Query if a memory block is cacheable
|
||||
* @param memory The handle for the memory block
|
||||
* @return nonzero if the memory block is cachable
|
||||
*/
|
||||
__inline__ int metal_memory_is_cachable(const struct metal_memory *memory) {
|
||||
return memory->_attrs.C;
|
||||
}
|
||||
|
||||
#endif /* METAL__MEMORY_H */
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__PMP_H
|
||||
#define METAL__PMP_H
|
||||
|
||||
/*!
|
||||
* @file metal/pmp.h
|
||||
*
|
||||
* @brief API for Configuring Physical Memory Protection on RISC-V Cores
|
||||
*
|
||||
* The Physical Memory Protection (PMP) interface on RISC-V cores
|
||||
* is a form of memory protection unit which allows for a finite number
|
||||
* of physical memory regions to be configured with certain access
|
||||
* permissions.
|
||||
*
|
||||
* Additional information about the use and configuration rules for PMPs
|
||||
* can be found by reading the RISC-V Privileged Architecture Specification.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
struct metal_pmp;
|
||||
|
||||
/*!
|
||||
* @brief Set of available PMP addressing modes
|
||||
*/
|
||||
enum metal_pmp_address_mode {
|
||||
/*! @brief Disable the PMP region */
|
||||
METAL_PMP_OFF = 0,
|
||||
/*! @brief Use Top-of-Range mode */
|
||||
METAL_PMP_TOR = 1,
|
||||
/*! @brief Use naturally-aligned 4-byte region mode */
|
||||
METAL_PMP_NA4 = 2,
|
||||
/*! @brief Use naturally-aligned power-of-two mode */
|
||||
METAL_PMP_NAPOT = 3
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Configuration for a PMP region
|
||||
*/
|
||||
struct metal_pmp_config {
|
||||
/*! @brief Sets whether reads to the PMP region succeed */
|
||||
unsigned int R : 1;
|
||||
/*! @brief Sets whether writes to the PMP region succeed */
|
||||
unsigned int W : 1;
|
||||
/*! @brief Sets whether the PMP region is executable */
|
||||
unsigned int X : 1;
|
||||
|
||||
/*! @brief Sets the addressing mode of the PMP region */
|
||||
enum metal_pmp_address_mode A : 2;
|
||||
|
||||
int _pad : 2;
|
||||
|
||||
/*! @brief Sets whether the PMP region is locked */
|
||||
enum metal_pmp_locked {
|
||||
METAL_PMP_UNLOCKED = 0,
|
||||
METAL_PMP_LOCKED = 1
|
||||
} L : 1;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief A handle for the PMP device
|
||||
*/
|
||||
struct metal_pmp {
|
||||
/* The minimum granularity of the PMP region. Set by metal_pmp_init */
|
||||
uintptr_t _granularity[METAL_MAX_CORES];
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Get the PMP device handle
|
||||
*/
|
||||
struct metal_pmp *metal_pmp_get_device(void);
|
||||
|
||||
/*!
|
||||
* @brief Get the number of pmp regions for the hartid
|
||||
*/
|
||||
int metal_pmp_num_regions(int hartid);
|
||||
|
||||
/*!
|
||||
* @brief Initialize the PMP
|
||||
* @param pmp The PMP device handle to be initialized
|
||||
*
|
||||
* The PMP initialization routine is optional and may be called as many times
|
||||
* as is desired. The effect of the initialization routine is to attempt to set
|
||||
* all regions to unlocked and disabled, as well as to clear the X, W, and R
|
||||
* bits. Only the pmp configuration of the hart which executes the routine will
|
||||
* be affected.
|
||||
*
|
||||
* If any regions are fused to preset values by the implementation or locked,
|
||||
* those PMP regions will silently remain uninitialized.
|
||||
*/
|
||||
void metal_pmp_init(struct metal_pmp *pmp);
|
||||
|
||||
/*!
|
||||
* @brief Configure a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to configure
|
||||
* @param config The desired configuration of the PMP region
|
||||
* @param address The desired address of the PMP region
|
||||
* @return 0 upon success
|
||||
*/
|
||||
int metal_pmp_set_region(struct metal_pmp *pmp, unsigned int region, struct metal_pmp_config config, size_t address);
|
||||
|
||||
/*!
|
||||
* @brief Get the configuration for a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to read
|
||||
* @param config Variable to store the PMP region configuration
|
||||
* @param address Variable to store the PMP region address
|
||||
* @return 0 if the region is read successfully
|
||||
*/
|
||||
int metal_pmp_get_region(struct metal_pmp *pmp, unsigned int region, struct metal_pmp_config *config, size_t *address);
|
||||
|
||||
/*!
|
||||
* @brief Lock a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to lock
|
||||
* @return 0 if the region is successfully locked
|
||||
*/
|
||||
int metal_pmp_lock(struct metal_pmp *pmp, unsigned int region);
|
||||
|
||||
/*!
|
||||
* @brief Set the address for a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to set
|
||||
* @param address The desired address of the PMP region
|
||||
* @return 0 if the address is successfully set
|
||||
*/
|
||||
int metal_pmp_set_address(struct metal_pmp *pmp, unsigned int region, size_t address);
|
||||
|
||||
/*!
|
||||
* @brief Get the address of a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to read
|
||||
* @return The address of the PMP region, or 0 if the region could not be read
|
||||
*/
|
||||
size_t metal_pmp_get_address(struct metal_pmp *pmp, unsigned int region);
|
||||
|
||||
/*!
|
||||
* @brief Set the addressing mode of a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to set
|
||||
* @param mode The PMP addressing mode to set
|
||||
* @return 0 if the addressing mode is successfully set
|
||||
*/
|
||||
int metal_pmp_set_address_mode(struct metal_pmp *pmp, unsigned int region, enum metal_pmp_address_mode mode);
|
||||
|
||||
/*!
|
||||
* @brief Get the addressing mode of a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to read
|
||||
* @return The address mode of the PMP region
|
||||
*/
|
||||
enum metal_pmp_address_mode metal_pmp_get_address_mode(struct metal_pmp *pmp, unsigned int region);
|
||||
|
||||
/*!
|
||||
* @brief Set the executable bit for a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to set
|
||||
* @param X The desired value of the executable bit
|
||||
* @return 0 if the executable bit is successfully set
|
||||
*/
|
||||
int metal_pmp_set_executable(struct metal_pmp *pmp, unsigned int region, int X);
|
||||
|
||||
/*!
|
||||
* @brief Get the executable bit for a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to read
|
||||
* @return the value of the executable bit
|
||||
*/
|
||||
int metal_pmp_get_executable(struct metal_pmp *pmp, unsigned int region);
|
||||
|
||||
/*!
|
||||
* @brief Set the writable bit for a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to set
|
||||
* @param W The desired value of the writable bit
|
||||
* @return 0 if the writable bit is successfully set
|
||||
*/
|
||||
int metal_pmp_set_writeable(struct metal_pmp *pmp, unsigned int region, int W);
|
||||
|
||||
/*!
|
||||
* @brief Get the writable bit for a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to read
|
||||
* @return the value of the writable bit
|
||||
*/
|
||||
int metal_pmp_get_writeable(struct metal_pmp *pmp, unsigned int region);
|
||||
|
||||
/*!
|
||||
* @brief Set the readable bit for a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to set
|
||||
* @param R The desired value of the readable bit
|
||||
* @return 0 if the readable bit is successfully set
|
||||
*/
|
||||
int metal_pmp_set_readable(struct metal_pmp *pmp, unsigned int region, int R);
|
||||
|
||||
/*!
|
||||
* @brief Set the readable bit for a PMP region
|
||||
* @param pmp The PMP device handle
|
||||
* @param region The PMP region to read
|
||||
* @return the value of the readable bit
|
||||
*/
|
||||
int metal_pmp_get_readable(struct metal_pmp *pmp, unsigned int region);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,122 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__PRIVILEGE_H
|
||||
#define METAL__PRIVILEGE_H
|
||||
|
||||
/*!
|
||||
* @file metal/privilege.h
|
||||
*
|
||||
* @brief API for manipulating the privilege mode of a RISC-V system
|
||||
*
|
||||
* Additional information about privilege modes on RISC-V systems can be found
|
||||
* by reading the RISC-V Privileged Architecture Specification v1.10.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum metal_privilege_mode {
|
||||
METAL_PRIVILEGE_USER = 0,
|
||||
METAL_PRIVILEGE_SUPERVISOR = 1,
|
||||
METAL_PRIVILEGE_MACHINE = 3,
|
||||
};
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
typedef uint32_t metal_xreg_t;
|
||||
#elif __riscv_xlen == 64
|
||||
typedef uint64_t metal_xreg_t;
|
||||
#endif
|
||||
|
||||
#if __riscv_flen == 32
|
||||
typedef uint32_t metal_freg_t;
|
||||
#elif __riscv_flen == 64
|
||||
typedef uint64_t metal_freg_t;
|
||||
#endif
|
||||
|
||||
struct metal_register_file {
|
||||
metal_xreg_t ra;
|
||||
metal_xreg_t sp;
|
||||
metal_xreg_t gp;
|
||||
metal_xreg_t tp;
|
||||
|
||||
metal_xreg_t t0;
|
||||
metal_xreg_t t1;
|
||||
metal_xreg_t t2;
|
||||
|
||||
metal_xreg_t s0;
|
||||
metal_xreg_t s1;
|
||||
|
||||
metal_xreg_t a0;
|
||||
metal_xreg_t a1;
|
||||
metal_xreg_t a2;
|
||||
metal_xreg_t a3;
|
||||
metal_xreg_t a4;
|
||||
metal_xreg_t a5;
|
||||
#ifndef __riscv_32e
|
||||
metal_xreg_t a6;
|
||||
metal_xreg_t a7;
|
||||
|
||||
metal_xreg_t s2;
|
||||
metal_xreg_t s3;
|
||||
metal_xreg_t s4;
|
||||
metal_xreg_t s5;
|
||||
metal_xreg_t s6;
|
||||
metal_xreg_t s7;
|
||||
metal_xreg_t s8;
|
||||
metal_xreg_t s9;
|
||||
metal_xreg_t s10;
|
||||
metal_xreg_t s11;
|
||||
|
||||
metal_xreg_t t3;
|
||||
metal_xreg_t t4;
|
||||
metal_xreg_t t5;
|
||||
metal_xreg_t t6;
|
||||
#endif /* __riscv_32e */
|
||||
|
||||
#ifdef __riscv_flen
|
||||
metal_freg_t ft0;
|
||||
metal_freg_t ft1;
|
||||
metal_freg_t ft2;
|
||||
metal_freg_t ft3;
|
||||
metal_freg_t ft4;
|
||||
metal_freg_t ft5;
|
||||
metal_freg_t ft6;
|
||||
metal_freg_t ft7;
|
||||
|
||||
metal_freg_t fs0;
|
||||
metal_freg_t fs1;
|
||||
|
||||
metal_freg_t fa0;
|
||||
metal_freg_t fa1;
|
||||
metal_freg_t fa2;
|
||||
metal_freg_t fa3;
|
||||
metal_freg_t fa4;
|
||||
metal_freg_t fa5;
|
||||
metal_freg_t fa6;
|
||||
metal_freg_t fa7;
|
||||
|
||||
metal_freg_t fs2;
|
||||
metal_freg_t fs3;
|
||||
metal_freg_t fs4;
|
||||
metal_freg_t fs5;
|
||||
metal_freg_t fs6;
|
||||
metal_freg_t fs7;
|
||||
metal_freg_t fs8;
|
||||
metal_freg_t fs9;
|
||||
metal_freg_t fs10;
|
||||
metal_freg_t fs11;
|
||||
|
||||
metal_freg_t ft8;
|
||||
metal_freg_t ft9;
|
||||
metal_freg_t ft10;
|
||||
metal_freg_t ft11;
|
||||
#endif /* __riscv_flen */
|
||||
};
|
||||
|
||||
typedef void (*metal_privilege_entry_point_t)(void);
|
||||
|
||||
void metal_privilege_drop_to_mode(enum metal_privilege_mode mode,
|
||||
struct metal_register_file regfile,
|
||||
metal_privilege_entry_point_t entry_point);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,127 @@
|
|||
/* Copyright 2019 SiFive, Inc. */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__RTC_H
|
||||
#define METAL__RTC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*!
|
||||
* @file rtc.h
|
||||
* @brief API for Real-Time Clocks
|
||||
*/
|
||||
|
||||
struct metal_rtc;
|
||||
|
||||
/*!
|
||||
* @brief List of RTC run behaviors
|
||||
*/
|
||||
enum metal_rtc_run_option {
|
||||
METAL_RTC_STOP = 0,
|
||||
METAL_RTC_RUN,
|
||||
};
|
||||
|
||||
struct metal_rtc_vtable {
|
||||
uint64_t (*get_rate)(const struct metal_rtc *const rtc);
|
||||
uint64_t (*set_rate)(const struct metal_rtc *const rtc, const uint64_t rate);
|
||||
uint64_t (*get_compare)(const struct metal_rtc *const rtc);
|
||||
uint64_t (*set_compare)(const struct metal_rtc *const rtc, const uint64_t compare);
|
||||
uint64_t (*get_count)(const struct metal_rtc *const rtc);
|
||||
uint64_t (*set_count)(const struct metal_rtc *const rtc, const uint64_t count);
|
||||
int (*run)(const struct metal_rtc *const rtc, const enum metal_rtc_run_option option);
|
||||
struct metal_interrupt *(*get_interrupt)(const struct metal_rtc *const rtc);
|
||||
int (*get_interrupt_id)(const struct metal_rtc *const rtc);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Handle for a Real-Time Clock
|
||||
*/
|
||||
struct metal_rtc {
|
||||
const struct metal_rtc_vtable *vtable;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Get the rate of the RTC
|
||||
* @return The rate in Hz
|
||||
*/
|
||||
inline uint64_t metal_rtc_get_rate(const struct metal_rtc *const rtc) {
|
||||
return rtc->vtable->get_rate(rtc);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set (if possible) the rate of the RTC
|
||||
* @return The new rate of the RTC (not guaranteed to be the same as requested)
|
||||
*/
|
||||
inline uint64_t metal_rtc_set_rate(const struct metal_rtc *const rtc, const uint64_t rate) {
|
||||
return rtc->vtable->set_rate(rtc, rate);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the compare value of the RTC
|
||||
* @return The compare value
|
||||
*/
|
||||
inline uint64_t metal_rtc_get_compare(const struct metal_rtc *const rtc) {
|
||||
return rtc->vtable->get_compare(rtc);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set the compare value of the RTC
|
||||
* @return The set compare value (not guaranteed to be exactly the requested value)
|
||||
*
|
||||
* The RTC device might impose limits on the maximum compare value or the granularity
|
||||
* of the compare value.
|
||||
*/
|
||||
inline uint64_t metal_rtc_set_compare(const struct metal_rtc *const rtc, const uint64_t compare) {
|
||||
return rtc->vtable->set_compare(rtc, compare);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the current count of the RTC
|
||||
* @return The count
|
||||
*/
|
||||
inline uint64_t metal_rtc_get_count(const struct metal_rtc *const rtc) {
|
||||
return rtc->vtable->get_count(rtc);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set the current count of the RTC
|
||||
* @return The set value of the count (not guaranteed to be exactly the requested value)
|
||||
*
|
||||
* The RTC device might impose limits on the maximum value of the count
|
||||
*/
|
||||
inline uint64_t metal_rtc_set_count(const struct metal_rtc *const rtc, const uint64_t count) {
|
||||
return rtc->vtable->set_count(rtc, count);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Start or stop the RTC
|
||||
* @return 0 if the RTC was successfully started/stopped
|
||||
*/
|
||||
inline int metal_rtc_run(const struct metal_rtc *const rtc, const enum metal_rtc_run_option option) {
|
||||
return rtc->vtable->run(rtc, option);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt handle for the RTC compare
|
||||
* @return The interrupt handle
|
||||
*/
|
||||
inline struct metal_interrupt *metal_rtc_get_interrupt(const struct metal_rtc *const rtc) {
|
||||
return rtc->vtable->get_interrupt(rtc);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt ID for the RTC compare
|
||||
* @return The interrupt ID
|
||||
*/
|
||||
inline int metal_rtc_get_interrupt_id(const struct metal_rtc *const rtc) {
|
||||
return rtc->vtable->get_interrupt_id(rtc);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the handle for an RTC by index
|
||||
* @return The RTC handle, or NULL if none is available at that index
|
||||
*/
|
||||
struct metal_rtc *metal_rtc_get_device(int index);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__SHUTDOWN_H
|
||||
#define METAL__SHUTDOWN_H
|
||||
|
||||
/*!
|
||||
* @file shutdown.h
|
||||
* @brief API for shutting down a machine
|
||||
*/
|
||||
|
||||
struct __metal_shutdown;
|
||||
|
||||
struct __metal_shutdown_vtable {
|
||||
void (*exit)(const struct __metal_shutdown *sd, int code) __attribute__((noreturn));
|
||||
};
|
||||
|
||||
struct __metal_shutdown {
|
||||
const struct __metal_shutdown_vtable *vtable;
|
||||
};
|
||||
|
||||
__inline__ void __metal_shutdown_exit(const struct __metal_shutdown *sd, int code) __attribute__((noreturn));
|
||||
__inline__ void __metal_shutdown_exit(const struct __metal_shutdown *sd, int code) { sd->vtable->exit(sd, code); }
|
||||
|
||||
/*!
|
||||
* @brief The public METAL shutdown interface
|
||||
*
|
||||
* Shuts down the machine, if the machine enables an interface for
|
||||
* shutting down. When no interface is provided, will cause the machine
|
||||
* to spin indefinitely.
|
||||
*
|
||||
* @param code The return code to set. 0 indicates program success.
|
||||
*/
|
||||
void metal_shutdown(int code) __attribute__((noreturn));
|
||||
|
||||
#endif
|
|
@ -0,0 +1,90 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__SPI_H
|
||||
#define METAL__SPI_H
|
||||
|
||||
struct metal_spi;
|
||||
|
||||
/*! @brief The configuration for a SPI transfer */
|
||||
struct metal_spi_config {
|
||||
/*! @brief The protocol for the SPI transfer */
|
||||
enum {
|
||||
METAL_SPI_SINGLE,
|
||||
METAL_SPI_DUAL,
|
||||
METAL_SPI_QUAD
|
||||
} protocol;
|
||||
|
||||
/*! @brief The polarity of the SPI transfer, equivalent to CPOL */
|
||||
unsigned int polarity : 1;
|
||||
/*! @brief The phase of the SPI transfer, equivalent to CPHA */
|
||||
unsigned int phase : 1;
|
||||
/*! @brief The endianness of the SPI transfer */
|
||||
unsigned int little_endian : 1;
|
||||
/*! @brief The active state of the chip select line */
|
||||
unsigned int cs_active_high : 1;
|
||||
/*! @brief The chip select ID to activate for the SPI transfer */
|
||||
unsigned int csid;
|
||||
/*! @brief The spi command frame number (cycles = num * frame_len) */
|
||||
unsigned int cmd_num;
|
||||
/*! @brief The spi address frame number */
|
||||
unsigned int addr_num;
|
||||
/*! @brief The spi dummy frame number */
|
||||
unsigned int dummy_num;
|
||||
/*! @brief The Dual/Quad spi mode selection.*/
|
||||
enum {
|
||||
MULTI_WIRE_ALL,
|
||||
MULTI_WIRE_DATA_ONLY,
|
||||
MULTI_WIRE_ADDR_DATA
|
||||
} multi_wire;
|
||||
};
|
||||
|
||||
struct metal_spi_vtable {
|
||||
void (*init)(struct metal_spi *spi, int baud_rate);
|
||||
int (*transfer)(struct metal_spi *spi, struct metal_spi_config *config, size_t len, char *tx_buf, char *rx_buf);
|
||||
int (*get_baud_rate)(struct metal_spi *spi);
|
||||
int (*set_baud_rate)(struct metal_spi *spi, int baud_rate);
|
||||
};
|
||||
|
||||
/*! @brief A handle for a SPI device */
|
||||
struct metal_spi {
|
||||
const struct metal_spi_vtable *vtable;
|
||||
};
|
||||
|
||||
/*! @brief Get a handle for a SPI device
|
||||
* @param device_num The index of the desired SPI device
|
||||
* @return A handle to the SPI device, or NULL if the device does not exist*/
|
||||
struct metal_spi *metal_spi_get_device(unsigned int device_num);
|
||||
|
||||
/*! @brief Initialize a SPI device with a certain baud rate
|
||||
* @param spi The handle for the SPI device to initialize
|
||||
* @param baud_rate The baud rate to set the SPI device to
|
||||
*/
|
||||
__inline__ void metal_spi_init(struct metal_spi *spi, int baud_rate) { spi->vtable->init(spi, baud_rate); }
|
||||
|
||||
/*! @brief Perform a SPI transfer
|
||||
* @param spi The handle for the SPI device to perform the transfer
|
||||
* @param config The configuration for the SPI transfer.
|
||||
* @param len The number of bytes to transfer
|
||||
* @param tx_buf The buffer to send over the SPI bus. Must be len bytes long. If NULL, the SPI will transfer the value 0.
|
||||
* @param rx_buf The buffer to receive data into. Must be len bytes long. If NULL, the SPI will ignore received bytes.
|
||||
* @return 0 if the transfer succeeds
|
||||
*/
|
||||
__inline__ int metal_spi_transfer(struct metal_spi *spi, struct metal_spi_config *config, size_t len, char *tx_buf, char *rx_buf) {
|
||||
return spi->vtable->transfer(spi, config, len, tx_buf, rx_buf);
|
||||
}
|
||||
|
||||
/*! @brief Get the current baud rate of the SPI device
|
||||
* @param spi The handle for the SPI device
|
||||
* @return The baud rate in Hz
|
||||
*/
|
||||
__inline__ int metal_spi_get_baud_rate(struct metal_spi *spi) { return spi->vtable->get_baud_rate(spi); }
|
||||
|
||||
/*! @brief Set the current baud rate of the SPI device
|
||||
* @param spi The handle for the SPI device
|
||||
* @param baud_rate The desired baud rate of the SPI device
|
||||
* @return 0 if the baud rate is successfully changed
|
||||
*/
|
||||
__inline__ int metal_spi_set_baud_rate(struct metal_spi *spi, int baud_rate) { return spi->vtable->set_baud_rate(spi, baud_rate); }
|
||||
|
||||
#endif
|
|
@ -0,0 +1,51 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__SWITCH_H
|
||||
#define METAL__SWITCH_H
|
||||
|
||||
/*!
|
||||
* @file switch.h
|
||||
* @brief API for reading toggle switches
|
||||
*/
|
||||
|
||||
#include <metal/interrupt.h>
|
||||
|
||||
struct metal_switch;
|
||||
|
||||
struct metal_switch_vtable {
|
||||
int (*switch_exist)(struct metal_switch *sw, char *label);
|
||||
struct metal_interrupt* (*interrupt_controller)(struct metal_switch *sw);
|
||||
int (*get_interrupt_id)(struct metal_switch *sw);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief A handle for a switch
|
||||
*/
|
||||
struct metal_switch {
|
||||
const struct metal_switch_vtable *vtable;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Get a handle for a switch
|
||||
* @param label The DeviceTree label for the desired switch
|
||||
* @return A handle to the switch, or NULL if none is found for the requested label
|
||||
*/
|
||||
struct metal_switch* metal_switch_get(char *label);
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt controller for a switch
|
||||
* @param sw The handle for the switch
|
||||
* @return The interrupt controller handle
|
||||
*/
|
||||
__inline__ struct metal_interrupt*
|
||||
metal_switch_interrupt_controller(struct metal_switch *sw) { return sw->vtable->interrupt_controller(sw); }
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt id for a switch
|
||||
* @param sw The handle for the switch
|
||||
* @return The interrupt ID for the switch
|
||||
*/
|
||||
__inline__ int metal_switch_get_interrupt_id(struct metal_switch *sw) { return sw->vtable->get_interrupt_id(sw); }
|
||||
|
||||
#endif
|
|
@ -0,0 +1,18 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__TIME_H
|
||||
#define METAL__TIME_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/*!
|
||||
* @file time.h
|
||||
* @brief API for dealing with time
|
||||
*/
|
||||
|
||||
int metal_gettimeofday(struct timeval *tp, void *tzp);
|
||||
|
||||
time_t metal_time(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,36 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__TIMER_H
|
||||
#define METAL__TIMER_H
|
||||
|
||||
/*!
|
||||
* @file timer.h
|
||||
* @brief API for reading and manipulating the machine timer
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @brief Read the machine cycle count
|
||||
* @param hartid The hart ID to read the cycle count of
|
||||
* @param cyclecount The variable to hold the value
|
||||
* @return 0 upon success
|
||||
*/
|
||||
int metal_timer_get_cyclecount(int hartid, unsigned long long *cyclecount);
|
||||
|
||||
/*!
|
||||
* @brief Get the machine timebase frequency
|
||||
* @param hartid The hart ID to read the timebase of
|
||||
* @param timebase The variable to hold the value
|
||||
* @return 0 upon success
|
||||
*/
|
||||
int metal_timer_get_timebase_frequency(int hartid, unsigned long long *timebase);
|
||||
|
||||
/*!
|
||||
* @brief Set the machine timer tick interval in seconds
|
||||
* @param hartid The hart ID to read the timebase of
|
||||
* @param second The number of seconds to set the tick interval to
|
||||
* @return 0 upon success
|
||||
*/
|
||||
int metal_timer_set_tick(int hartid, int second);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,52 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__TTY_H
|
||||
#define METAL__TTY_H
|
||||
|
||||
/*!
|
||||
* @file tty.h
|
||||
* @brief API for emulated serial teriminals
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @brief Write a character to the default output device
|
||||
*
|
||||
* Write a character to the default output device, which for most
|
||||
* targets is the UART serial port.
|
||||
*
|
||||
* putc() does CR/LF mapping.
|
||||
* putc_raw() does not.
|
||||
*
|
||||
* @param c The character to write to the terminal
|
||||
* @return 0 on success, or -1 on failure.
|
||||
*/
|
||||
int metal_tty_putc(int c);
|
||||
|
||||
/*!
|
||||
* @brief Write a raw character to the default output device
|
||||
*
|
||||
* Write a character to the default output device, which for most
|
||||
* targets is the UART serial port.
|
||||
*
|
||||
* putc() does CR/LF mapping.
|
||||
* putc_raw() does not.
|
||||
*
|
||||
* @param c The character to write to the terminal
|
||||
* @return 0 on success, or -1 on failure.
|
||||
*/
|
||||
int metal_tty_putc_raw(int c);
|
||||
|
||||
/*!
|
||||
* @brief Get a byte from the default output device
|
||||
*
|
||||
* The default output device, is typically the UART serial port.
|
||||
*
|
||||
* This call is non-blocking, if nothing is ready c==-1
|
||||
* if something is ready, then c=[0x00 to 0xff] byte value.
|
||||
*
|
||||
* @return 0 on success, or -1 on failure.
|
||||
*/
|
||||
int metal_tty_getc(int *c);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,106 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__UART_H
|
||||
#define METAL__UART_H
|
||||
|
||||
/*!
|
||||
* @file uart.h
|
||||
* @brief API for UART serial ports
|
||||
*/
|
||||
|
||||
#include <metal/interrupt.h>
|
||||
|
||||
struct metal_uart;
|
||||
#undef getc
|
||||
#undef putc
|
||||
struct metal_uart_vtable {
|
||||
void (*init)(struct metal_uart *uart, int baud_rate);
|
||||
int (*putc)(struct metal_uart *uart, int c);
|
||||
int (*txready)(struct metal_uart *uart);
|
||||
int (*getc)(struct metal_uart *uart, int *c);
|
||||
int (*get_baud_rate)(struct metal_uart *uart);
|
||||
int (*set_baud_rate)(struct metal_uart *uart, int baud_rate);
|
||||
struct metal_interrupt* (*controller_interrupt)(struct metal_uart *uart);
|
||||
int (*get_interrupt_id)(struct metal_uart *uart);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Handle for a UART serial device
|
||||
*/
|
||||
struct metal_uart {
|
||||
const struct metal_uart_vtable *vtable;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Initialize UART device
|
||||
|
||||
* Initialize the UART device described by the UART handle. This function must be called before any
|
||||
* other method on the UART can be invoked. It is invalid to initialize a UART more than once.
|
||||
*
|
||||
* @param uart The UART device handle
|
||||
* @param baud_rate the baud rate to set the UART to
|
||||
*/
|
||||
__inline__ void metal_uart_init(struct metal_uart *uart, int baud_rate) { uart->vtable->init(uart, baud_rate); }
|
||||
|
||||
/*!
|
||||
* @brief Output a character over the UART
|
||||
* @param uart The UART device handle
|
||||
* @param c The character to send over the UART
|
||||
* @return 0 upon success
|
||||
*/
|
||||
__inline__ int metal_uart_putc(struct metal_uart *uart, int c) { return uart->vtable->putc(uart, c); }
|
||||
|
||||
/*!
|
||||
* @brief Test, determine if tx output is blocked(full/busy)
|
||||
* @param uart The UART device handle
|
||||
* @return 0 not blocked
|
||||
*/
|
||||
__inline__ int metal_uart_txready(struct metal_uart *uart) { return uart->vtable->txready(uart); }
|
||||
|
||||
/*!
|
||||
* @brief Read a character sent over the UART
|
||||
* @param uart The UART device handle
|
||||
* @param c The varible to hold the read character
|
||||
* @return 0 upon success
|
||||
*
|
||||
* If "c == -1" no char was ready.
|
||||
* If "c != -1" then C == byte value (0x00 to 0xff)
|
||||
*/
|
||||
__inline__ int metal_uart_getc(struct metal_uart *uart, int *c) { return uart->vtable->getc(uart, c); }
|
||||
|
||||
/*!
|
||||
* @brief Get the baud rate of the UART peripheral
|
||||
* @param uart The UART device handle
|
||||
* @return The current baud rate of the UART
|
||||
*/
|
||||
__inline__ int metal_uart_get_baud_rate(struct metal_uart *uart) { return uart->vtable->get_baud_rate(uart); }
|
||||
|
||||
/*!
|
||||
* @brief Set the baud rate of the UART peripheral
|
||||
* @param uart The UART device handle
|
||||
* @param baud_rate The baud rate to configure
|
||||
* @return the new baud rate of the UART
|
||||
*/
|
||||
__inline__ int metal_uart_set_baud_rate(struct metal_uart *uart, int baud_rate) { return uart->vtable->set_baud_rate(uart, baud_rate); }
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt controller of the UART peripheral
|
||||
*
|
||||
* Get the interrupt controller for the UART peripheral. The interrupt
|
||||
* controller must be initialized before any interrupts can be registered
|
||||
* or enabled with it.
|
||||
*
|
||||
* @param uart The UART device handle
|
||||
* @return The handle for the UART interrupt controller
|
||||
*/
|
||||
__inline__ struct metal_interrupt* metal_uart_interrupt_controller(struct metal_uart *uart) { return uart->vtable->controller_interrupt(uart); }
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt ID of the UART controller
|
||||
* @param uart The UART device handle
|
||||
* @return The UART interrupt id
|
||||
*/
|
||||
__inline__ int metal_uart_get_interrupt_id(struct metal_uart *uart) { return uart->vtable->get_interrupt_id(uart); }
|
||||
|
||||
#endif
|
|
@ -0,0 +1,163 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef METAL__WATCHDOG_H
|
||||
#define METAL__WATCHDOG_H
|
||||
|
||||
/*!
|
||||
* @file watchdog.h
|
||||
*
|
||||
* @brief API for configuring watchdog timers
|
||||
*/
|
||||
|
||||
#include <metal/interrupt.h>
|
||||
|
||||
struct metal_watchdog;
|
||||
|
||||
/*!
|
||||
* @brief List of watchdog timer count behaviors
|
||||
*/
|
||||
enum metal_watchdog_run_option {
|
||||
METAL_WATCHDOG_STOP = 0, /*!< Stop the watchdog */
|
||||
METAL_WATCHDOG_RUN_ALWAYS, /*!< Run the watchdog continuously, even during sleep */
|
||||
METAL_WATCHDOG_RUN_AWAKE, /*!< Run the watchdog only while the CPU is awake */
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief List of behaviors when a watchdog triggers
|
||||
*/
|
||||
enum metal_watchdog_result {
|
||||
METAL_WATCHDOG_NO_RESULT = 0, /*!< When the watchdog triggers, do nothing */
|
||||
METAL_WATCHDOG_INTERRUPT, /*!< When the watchdog triggers, fire an interrupt */
|
||||
METAL_WATCHDOG_FULL_RESET, /*!< When the watchdog triggers, cause a full system reset */
|
||||
};
|
||||
|
||||
|
||||
struct metal_watchdog_vtable {
|
||||
int (*feed)(const struct metal_watchdog *const wdog);
|
||||
long int (*get_rate)(const struct metal_watchdog *const wdog);
|
||||
long int (*set_rate)(const struct metal_watchdog *const wdog, const long int rate);
|
||||
long int (*get_timeout)(const struct metal_watchdog *const wdog);
|
||||
long int (*set_timeout)(const struct metal_watchdog *const wdog, const long int timeout);
|
||||
int (*set_result)(const struct metal_watchdog *const wdog,
|
||||
const enum metal_watchdog_result result);
|
||||
int (*run)(const struct metal_watchdog *const wdog,
|
||||
const enum metal_watchdog_run_option option);
|
||||
struct metal_interrupt *(*get_interrupt)(const struct metal_watchdog *const wdog);
|
||||
int (*get_interrupt_id)(const struct metal_watchdog *const wdog);
|
||||
int (*clear_interrupt)(const struct metal_watchdog *const wdog);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Handle for a Watchdog Timer
|
||||
*/
|
||||
struct metal_watchdog {
|
||||
const struct metal_watchdog_vtable *vtable;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Feed the watchdog timer
|
||||
*/
|
||||
inline int metal_watchdog_feed(const struct metal_watchdog *const wdog)
|
||||
{
|
||||
return wdog->vtable->feed(wdog);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the rate of the watchdog timer in Hz
|
||||
*
|
||||
* @return the rate of the watchdog timer
|
||||
*/
|
||||
inline long int metal_watchdog_get_rate(const struct metal_watchdog *const wdog)
|
||||
{
|
||||
return wdog->vtable->get_rate(wdog);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set the rate of the watchdog timer in Hz
|
||||
*
|
||||
* There is no guarantee that the new rate will match the requested rate.
|
||||
*
|
||||
* @return the new rate of the watchdog timer
|
||||
*/
|
||||
inline long int metal_watchdog_set_rate(const struct metal_watchdog *const wdog, const long int rate)
|
||||
{
|
||||
return wdog->vtable->set_rate(wdog, rate);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the timeout of the watchdog timer
|
||||
*
|
||||
* @return the watchdog timeout value
|
||||
*/
|
||||
inline long int metal_watchdog_get_timeout(const struct metal_watchdog *const wdog)
|
||||
{
|
||||
return wdog->vtable->get_timeout(wdog);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set the timeout of the watchdog timer
|
||||
*
|
||||
* The set rate will be the minimimum of the requested and maximum supported rates.
|
||||
*
|
||||
* @return the new watchdog timeout value
|
||||
*/
|
||||
inline long int metal_watchdog_set_timeout(const struct metal_watchdog *const wdog, const long int timeout)
|
||||
{
|
||||
return wdog->vtable->set_timeout(wdog, timeout);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Sets the result behavior of a watchdog timer timeout
|
||||
*
|
||||
* @return 0 if the requested result behavior is supported
|
||||
*/
|
||||
inline int metal_watchdog_set_result(const struct metal_watchdog *const wdog,
|
||||
const enum metal_watchdog_result result)
|
||||
{
|
||||
return wdog->vtable->set_result(wdog, result);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Set the run behavior of the watchdog
|
||||
*
|
||||
* Used to enable/disable the watchdog timer
|
||||
*
|
||||
* @return 0 if the watchdog was successfully started/stopped
|
||||
*/
|
||||
inline int metal_watchdog_run(const struct metal_watchdog *const wdog,
|
||||
const enum metal_watchdog_run_option option)
|
||||
{
|
||||
return wdog->vtable->run(wdog, option);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the interrupt controller for the watchdog interrupt
|
||||
*/
|
||||
inline struct metal_interrupt *metal_watchdog_get_interrupt(const struct metal_watchdog *const wdog)
|
||||
{
|
||||
return wdog->vtable->get_interrupt(wdog);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @Brief Get the interrupt id for the watchdog interrupt
|
||||
*/
|
||||
inline int metal_watchdog_get_interrupt_id(const struct metal_watchdog *const wdog)
|
||||
{
|
||||
return wdog->vtable->get_interrupt_id(wdog);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Clear the watchdog interrupt
|
||||
*/
|
||||
inline int metal_watchdog_clear_interrupt(const struct metal_watchdog *const wdog)
|
||||
{
|
||||
return wdog->vtable->clear_interrupt(wdog);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get a watchdog handle
|
||||
*/
|
||||
struct metal_watchdog *metal_watchdog_get_device(const int index);
|
||||
|
||||
#endif /* METAL__WATCHDOG_H */
|
|
@ -0,0 +1,27 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/button.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
struct metal_button* metal_button_get (char *label)
|
||||
{
|
||||
int i;
|
||||
struct metal_button *button;
|
||||
|
||||
if ((__METAL_DT_MAX_BUTTONS == 0) || (label == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < __METAL_DT_MAX_BUTTONS; i++) {
|
||||
button = (struct metal_button*)__metal_button_table[i];
|
||||
if (button->vtable->button_exist(button, label)) {
|
||||
return button;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern __inline__ struct metal_interrupt*
|
||||
metal_button_interrupt_controller(struct metal_button *button);
|
||||
extern __inline__ int metal_button_get_interrupt_id(struct metal_button *button);
|
|
@ -0,0 +1,187 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/cache.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
extern __inline__ void metal_cache_init(struct metal_cache *cache, int ways);
|
||||
extern __inline__ int metal_cache_get_enabled_ways(struct metal_cache *cache);
|
||||
extern __inline__ int metal_cache_set_enabled_ways(struct metal_cache *cache, int ways);
|
||||
|
||||
int metal_dcache_l1_available(int hartid) {
|
||||
switch (hartid) {
|
||||
case 0:
|
||||
#ifdef __METAL_CPU_0_DCACHE_HANDLE
|
||||
return __METAL_CPU_0_DCACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 1:
|
||||
#ifdef __METAL_CPU_1_DCACHE_HANDLE
|
||||
return __METAL_CPU_1_DCACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 2:
|
||||
#ifdef __METAL_CPU_2_DCACHE_HANDLE
|
||||
return __METAL_CPU_2_DCACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 3:
|
||||
#ifdef __METAL_CPU_3_DCACHE_HANDLE
|
||||
return __METAL_CPU_3_DCACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 4:
|
||||
#ifdef __METAL_CPU_4_DCACHE_HANDLE
|
||||
return __METAL_CPU_4_DCACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 5:
|
||||
#ifdef __METAL_CPU_5_DCACHE_HANDLE
|
||||
return __METAL_CPU_5_DCACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 6:
|
||||
#ifdef __METAL_CPU_6_DCACHE_HANDLE
|
||||
return __METAL_CPU_6_DCACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 7:
|
||||
#ifdef __METAL_CPU_7_DCACHE_HANDLE
|
||||
return __METAL_CPU_7_DCACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 8:
|
||||
#ifdef __METAL_CPU_8_DCACHE_HANDLE
|
||||
return __METAL_CPU_8_DCACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metal_icache_l1_available(int hartid) {
|
||||
switch (hartid) {
|
||||
case 0:
|
||||
#ifdef __METAL_CPU_0_ICACHE_HANDLE
|
||||
return __METAL_CPU_0_ICACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 1:
|
||||
#ifdef __METAL_CPU_1_ICACHE_HANDLE
|
||||
return __METAL_CPU_1_ICACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 2:
|
||||
#ifdef __METAL_CPU_2_ICACHE_HANDLE
|
||||
return __METAL_CPU_2_ICACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 3:
|
||||
#ifdef __METAL_CPU_3_ICACHE_HANDLE
|
||||
return __METAL_CPU_3_ICACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 4:
|
||||
#ifdef __METAL_CPU_4_ICACHE_HANDLE
|
||||
return __METAL_CPU_4_ICACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 5:
|
||||
#ifdef __METAL_CPU_5_ICACHE_HANDLE
|
||||
return __METAL_CPU_5_ICACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 6:
|
||||
#ifdef __METAL_CPU_6_ICACHE_HANDLE
|
||||
return __METAL_CPU_6_ICACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 7:
|
||||
#ifdef __METAL_CPU_7_ICACHE_HANDLE
|
||||
return __METAL_CPU_7_ICACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
case 8:
|
||||
#ifdef __METAL_CPU_8_ICACHE_HANDLE
|
||||
return __METAL_CPU_8_ICACHE_HANDLE;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief CFlush.D.L1 instruction is a custom instruction implemented as a
|
||||
* state machine in L1 Data Cache (D$) with funct3=0, (for core with data caches)
|
||||
* It is an I type: .insn i opcode, func3, rd, rs1, simm12(signed immediate 12bs)
|
||||
* 31 28 27 24 23 20 19 16 15 12 11 8 7 4 3 0
|
||||
* |--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
* +-------------+------------+----------+------+--------+-----------------+
|
||||
* |sign immediate12b (simm12)| rs1 | func3| rd | opcode |
|
||||
* |-1-1-1-1 -1-1-0-0 -0-0-0-0|-x-x-x-x-x|0-0-0-|-0-0-0-0|-0-1-1-1 -0-0-1-1|
|
||||
* +--------------------------+----------+------+--------+-----------------+
|
||||
* 31 -0x40 20 15 0 12 x0 7 0x73 0
|
||||
* +--------+--------+--------+----------+------+--------+--------+--------+
|
||||
* where,
|
||||
* rs1 = 0x0, CFLUSH.D.L1 writes back and invalidates all lines in the L1 D$
|
||||
* rs1 != x0, CFLUSH.D.L1 writes back and invalidates the L1 D$ line containing
|
||||
* the virtual address in integer register rs1.
|
||||
*/
|
||||
void metal_dcache_l1_flush(int hartid, uintptr_t address)
|
||||
{
|
||||
if (metal_dcache_l1_available(hartid)) {
|
||||
// Using ‘.insn’ pseudo directive: '.insn i opcode, func3, rd, rs1, simm12'
|
||||
__asm__ __volatile__ (".insn i 0x73, 0, x0, %0, -0x40" : : "r" (address));
|
||||
__asm__ __volatile__ ("fence.i"); // FENCE
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief CDiscard.D.L1 instruction is a custom instruction implemented as a
|
||||
* state machine in L1 Data Cache (D$) with funct3=0, (for core with data caches)
|
||||
* It is an I type: .insn i opcode, func3, rd, rs1, simm12(signed immediate 12bs)
|
||||
* 31 28 27 24 23 20 19 16 15 12 11 8 7 4 3 0
|
||||
* |--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
* +-------------+------------+----------+------+--------+-----------------+
|
||||
* |sign immediate12b (simm12)| rs1 | func3| rd | opcode |
|
||||
* |-1-1-1-1 -1-1-0-0 -0-0-0-0|-x-x-x-x-x|0-0-0-|-0-0-0-0|-0-1-1-1 -0-0-1-1|
|
||||
* +--------------------------+----------+------+--------+-----------------+
|
||||
* 31 -0x3E 20 15 0 12 x0 7 0x73 0
|
||||
* +--------+--------+--------+----------+------+--------+--------+--------+
|
||||
* where,
|
||||
* rs1 = 0x0, CDISCARD.D.L1 invalidates all lines in the L1 D$ with no writes back.
|
||||
* rs1 != x0, CDISCARD.D.L1 invalidates the L1 D$ line containing the virtual address
|
||||
* in integer register rs1, with no writes back.
|
||||
*/
|
||||
void metal_dcache_l1_discard(int hartid, uintptr_t address)
|
||||
{
|
||||
if (metal_dcache_l1_available(hartid)) {
|
||||
// Using ‘.insn’ pseudo directive: '.insn i opcode, func3, rd, rs1, simm12'
|
||||
__asm__ __volatile__ (".insn i 0x73, 0, x0, %0, -0x3E" : : "r" (address));
|
||||
__asm__ __volatile__ ("fence.i"); // FENCE
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief CFlush.I.L1 instruction is a custom instruction implemented as a state
|
||||
* machine in L1 Instruction Cache (I$) with funct3=0, (for core with data caches)
|
||||
* It is an I type: .insn i opcode, func3, rd, rs1, simm12(signed immediate 12bs)
|
||||
* 31 28 27 24 23 20 19 16 15 12 11 8 7 4 3 0
|
||||
* |--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
* +-------------+------------+----------+------+--------+-----------------+
|
||||
* |sign immediate12b (simm12)| rs1 | func3| rd | opcode |
|
||||
* |-1-1-1-1 -1-1-0-0 -0-0-0-0|-0-0-0-0-0|0-0-0-|-0-0-0-0|-0-1-1-1 -0-0-1-1|
|
||||
* +--------------------------+----------+------+--------+-----------------+
|
||||
* 31 -0x3F 20 15 0 12 x0 7 0x73 0
|
||||
* +--------+--------+--------+----------+------+--------+--------+--------+
|
||||
* CFLUSH.I.L1 invalidates all lines in the L1 I$.
|
||||
*/
|
||||
void metal_icache_l1_flush(int hartid)
|
||||
{
|
||||
if (metal_icache_l1_available(hartid)) {
|
||||
// Using ‘.insn’ pseudo directive: '.insn i opcode, func3, rd, rs1, simm12'
|
||||
__asm__ __volatile__ (".insn i 0x73, 0, x0, x0, -0x3F" : : );
|
||||
__asm__ __volatile__ ("fence.i"); // FENCE
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/clock.h>
|
||||
|
||||
extern __inline__ void _metal_clock_call_all_callbacks(const metal_clock_callback *const list);
|
||||
extern __inline__ metal_clock_callback *_metal_clock_append_to_callbacks(metal_clock_callback *list, metal_clock_callback *const cb);
|
||||
|
||||
extern __inline__ long metal_clock_get_rate_hz(const struct metal_clock *clk);
|
||||
extern __inline__ long metal_clock_set_rate_hz(struct metal_clock *clk, long hz);
|
||||
extern __inline__ void metal_clock_register_post_rate_change_callback(struct metal_clock *clk, metal_clock_callback *cb);
|
||||
extern __inline__ void metal_clock_register_pre_rate_change_callback(struct metal_clock *clk, metal_clock_callback *cb);
|
|
@ -0,0 +1,59 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/cpu.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
struct metal_cpu* metal_cpu_get(unsigned int hartid)
|
||||
{
|
||||
if (hartid < __METAL_DT_MAX_HARTS) {
|
||||
return (struct metal_cpu *)__metal_cpu_table[hartid];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int metal_cpu_get_current_hartid()
|
||||
{
|
||||
#ifdef __riscv
|
||||
int mhartid;
|
||||
__asm__ volatile("csrr %0, mhartid" : "=r" (mhartid));
|
||||
return mhartid;
|
||||
#endif
|
||||
}
|
||||
|
||||
int metal_cpu_get_num_harts()
|
||||
{
|
||||
return __METAL_DT_MAX_HARTS;
|
||||
}
|
||||
|
||||
extern __inline__ unsigned long long metal_cpu_get_timer(struct metal_cpu *cpu);
|
||||
|
||||
extern __inline__ unsigned long long metal_cpu_get_timebase(struct metal_cpu *cpu);
|
||||
|
||||
extern __inline__ unsigned long long metal_cpu_get_mtime(struct metal_cpu *cpu);
|
||||
|
||||
extern __inline__ int metal_cpu_set_mtimecmp(struct metal_cpu *cpu, unsigned long long time);
|
||||
|
||||
extern __inline__ struct metal_interrupt* metal_cpu_timer_interrupt_controller(struct metal_cpu *cpu);
|
||||
|
||||
extern __inline__ int metal_cpu_timer_get_interrupt_id(struct metal_cpu *cpu);
|
||||
|
||||
extern __inline__ struct metal_interrupt* metal_cpu_software_interrupt_controller(struct metal_cpu *cpu);
|
||||
|
||||
extern __inline__ int metal_cpu_software_get_interrupt_id(struct metal_cpu *cpu);
|
||||
|
||||
extern __inline__ int metal_cpu_software_set_ipi(struct metal_cpu *cpu, int hartid);
|
||||
|
||||
extern __inline__ int metal_cpu_software_clear_ipi(struct metal_cpu *cpu, int hartid);
|
||||
|
||||
extern __inline__ int metal_cpu_get_msip(struct metal_cpu *cpu, int hartid);
|
||||
|
||||
extern __inline__ struct metal_interrupt* metal_cpu_interrupt_controller(struct metal_cpu *cpu);
|
||||
|
||||
extern __inline__ int metal_cpu_exception_register(struct metal_cpu *cpu, int ecode, metal_exception_handler_t handler);
|
||||
|
||||
extern __inline__ int metal_cpu_get_instruction_length(struct metal_cpu *cpu, uintptr_t epc);
|
||||
|
||||
extern __inline__ uintptr_t metal_cpu_get_exception_pc(struct metal_cpu *cpu);
|
||||
|
||||
extern __inline__ int metal_cpu_set_exception_pc(struct metal_cpu *cpu, uintptr_t epc);
|
|
@ -0,0 +1,29 @@
|
|||
/* Copyright 2018 SiFive, Inc. */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_FIXED_CLOCK
|
||||
|
||||
#include <metal/drivers/fixed-clock.h>
|
||||
#include <stddef.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
long __metal_driver_fixed_clock_get_rate_hz(const struct metal_clock *gclk)
|
||||
{
|
||||
return __metal_driver_fixed_clock_rate(gclk);
|
||||
}
|
||||
|
||||
long __metal_driver_fixed_clock_set_rate_hz(struct metal_clock *gclk, long target_hz)
|
||||
{
|
||||
return __metal_driver_fixed_clock_get_rate_hz(gclk);
|
||||
}
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_fixed_clock) = {
|
||||
.clock.get_rate_hz = __metal_driver_fixed_clock_get_rate_hz,
|
||||
.clock.set_rate_hz = __metal_driver_fixed_clock_set_rate_hz,
|
||||
};
|
||||
|
||||
#endif /* METAL_FIXED_CLOCK */
|
||||
|
||||
typedef int no_empty_translation_units;
|
|
@ -0,0 +1,34 @@
|
|||
/* Copyright 2018 SiFive, Inc. */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_FIXED_FACTOR_CLOCK
|
||||
|
||||
#include <metal/drivers/fixed-factor-clock.h>
|
||||
#include <stddef.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
long __metal_driver_fixed_factor_clock_get_rate_hz(const struct metal_clock *gclk)
|
||||
{
|
||||
struct metal_clock *parent = __metal_driver_fixed_factor_clock_parent(gclk);
|
||||
long parent_rate = 1;
|
||||
if(parent) {
|
||||
parent_rate = parent->vtable->get_rate_hz(parent);
|
||||
}
|
||||
|
||||
return __metal_driver_fixed_factor_clock_mult(gclk) * parent_rate / __metal_driver_fixed_factor_clock_div(gclk);
|
||||
}
|
||||
|
||||
long __metal_driver_fixed_factor_clock_set_rate_hz(struct metal_clock *gclk, long target_hz)
|
||||
{
|
||||
return __metal_driver_fixed_factor_clock_get_rate_hz(gclk);
|
||||
}
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_fixed_factor_clock) = {
|
||||
.clock.get_rate_hz = __metal_driver_fixed_factor_clock_get_rate_hz,
|
||||
.clock.set_rate_hz = __metal_driver_fixed_factor_clock_set_rate_hz,
|
||||
};
|
||||
#endif /* METAL_FIXED_FACTOR_CLOCK */
|
||||
|
||||
typedef int no_empty_translation_units;
|
|
@ -0,0 +1,5 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/inline.h>
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_RISCV_CLINT0
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/cpu.h>
|
||||
#include <metal/drivers/riscv_clint0.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
unsigned long long __metal_clint0_mtime_get (struct __metal_driver_riscv_clint0 *clint)
|
||||
{
|
||||
__metal_io_u32 lo, hi;
|
||||
unsigned long control_base = __metal_driver_sifive_clint0_control_base(&clint->controller);
|
||||
|
||||
/* Guard against rollover when reading */
|
||||
do {
|
||||
hi = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_RISCV_CLINT0_MTIME + 4));
|
||||
lo = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_RISCV_CLINT0_MTIME));
|
||||
} while (__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_RISCV_CLINT0_MTIME + 4)) != hi);
|
||||
|
||||
return (((unsigned long long)hi) << 32) | lo;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_clint0_mtimecmp_set(struct metal_interrupt *controller,
|
||||
int hartid,
|
||||
unsigned long long time)
|
||||
{
|
||||
struct __metal_driver_riscv_clint0 *clint =
|
||||
(struct __metal_driver_riscv_clint0 *)(controller);
|
||||
unsigned long control_base = __metal_driver_sifive_clint0_control_base(&clint->controller);
|
||||
/* Per spec, the RISC-V MTIME/MTIMECMP registers are 64 bit,
|
||||
* and are NOT internally latched for multiword transfers.
|
||||
* Need to be careful about sequencing to avoid triggering
|
||||
* spurious interrupts: For that set the high word to a max
|
||||
* value first.
|
||||
*/
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + METAL_RISCV_CLINT0_MTIMECMP_BASE + 4)) = 0xFFFFFFFF;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + METAL_RISCV_CLINT0_MTIMECMP_BASE)) = (__metal_io_u32)time;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + METAL_RISCV_CLINT0_MTIMECMP_BASE + 4)) = (__metal_io_u32)(time >> 32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct metal_interrupt *_get_cpu_intc()
|
||||
{
|
||||
int hartid = 0;
|
||||
__asm__ volatile("csrr %[hartid], mhartid"
|
||||
: [hartid] "=r" (hartid) :: "memory");
|
||||
|
||||
struct metal_cpu *cpu = metal_cpu_get(hartid);
|
||||
|
||||
return metal_cpu_interrupt_controller(cpu);
|
||||
}
|
||||
|
||||
void __metal_driver_riscv_clint0_init (struct metal_interrupt *controller)
|
||||
{
|
||||
int num_interrupts = __metal_driver_sifive_clint0_num_interrupts(controller);
|
||||
struct __metal_driver_riscv_clint0 *clint =
|
||||
(struct __metal_driver_riscv_clint0 *)(controller);
|
||||
|
||||
if ( !clint->init_done ) {
|
||||
/* Register its interrupts with with parent controller, aka sw and timerto its default isr */
|
||||
for (int i = 0; i < num_interrupts; i++) {
|
||||
struct metal_interrupt *intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
|
||||
int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
|
||||
intc->vtable->interrupt_register(intc, line, NULL, controller);
|
||||
}
|
||||
clint->init_done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_clint0_register (struct metal_interrupt *controller,
|
||||
int id, metal_interrupt_handler_t isr,
|
||||
void *priv)
|
||||
{
|
||||
int rc = -1;
|
||||
metal_vector_mode mode = __metal_controller_interrupt_vector_mode();
|
||||
struct metal_interrupt *intc = NULL;
|
||||
struct metal_interrupt *cpu_intc = _get_cpu_intc();
|
||||
int num_interrupts = __metal_driver_sifive_clint0_num_interrupts(controller);
|
||||
|
||||
if ( (mode != METAL_VECTOR_MODE) && (mode != METAL_DIRECT_MODE) ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
for(int i = 0; i < num_interrupts; i++) {
|
||||
int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
|
||||
intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
|
||||
if (cpu_intc == intc && id == line) {
|
||||
break;
|
||||
}
|
||||
intc = NULL;
|
||||
}
|
||||
|
||||
/* Register its interrupts with parent controller */
|
||||
if (intc) {
|
||||
rc = intc->vtable->interrupt_register(intc, id, isr, priv);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_clint0_vector_register (struct metal_interrupt *controller,
|
||||
int id, metal_interrupt_vector_handler_t isr,
|
||||
void *priv)
|
||||
{
|
||||
/* Not supported. User can override the 'weak' handler with their own */
|
||||
int rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
metal_vector_mode __metal_driver_riscv_clint0_get_vector_mode (struct metal_interrupt *controller)
|
||||
{
|
||||
return __metal_controller_interrupt_vector_mode();
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_clint0_set_vector_mode (struct metal_interrupt *controller, metal_vector_mode mode)
|
||||
{
|
||||
int rc = -1;
|
||||
struct metal_interrupt *intc = _get_cpu_intc();
|
||||
|
||||
if (intc) {
|
||||
/* Valid vector modes are VECTOR and DIRECT, anything else is invalid (-1) */
|
||||
switch (mode) {
|
||||
case METAL_VECTOR_MODE:
|
||||
case METAL_DIRECT_MODE:
|
||||
rc = intc->vtable->interrupt_set_vector_mode(intc, mode);
|
||||
break;
|
||||
case METAL_HARDWARE_VECTOR_MODE:
|
||||
case METAL_SELECTIVE_NONVECTOR_MODE:
|
||||
case METAL_SELECTIVE_VECTOR_MODE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_clint0_enable (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
if ( id ) {
|
||||
struct metal_interrupt *intc = NULL;
|
||||
struct metal_interrupt *cpu_intc = _get_cpu_intc();
|
||||
int num_interrupts = __metal_driver_sifive_clint0_num_interrupts(controller);
|
||||
|
||||
for(int i = 0; i < num_interrupts; i++) {
|
||||
int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
|
||||
intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
|
||||
if(cpu_intc == intc && id == line) {
|
||||
break;
|
||||
}
|
||||
intc = NULL;
|
||||
}
|
||||
|
||||
/* Enable its interrupts with parent controller */
|
||||
if (intc) {
|
||||
rc = intc->vtable->interrupt_enable(intc, id);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_clint0_disable (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
if ( id ) {
|
||||
struct metal_interrupt *intc = NULL;
|
||||
struct metal_interrupt *cpu_intc = _get_cpu_intc();
|
||||
int num_interrupts = __metal_driver_sifive_clint0_num_interrupts(controller);
|
||||
|
||||
for(int i = 0; i < num_interrupts; i++) {
|
||||
int line = __metal_driver_sifive_clint0_interrupt_lines(controller, i);
|
||||
intc = __metal_driver_sifive_clint0_interrupt_parents(controller, i);
|
||||
if(cpu_intc == intc && id == line) {
|
||||
break;
|
||||
}
|
||||
intc = NULL;
|
||||
}
|
||||
|
||||
/* Disable its interrupts with parent controller */
|
||||
if (intc) {
|
||||
rc = intc->vtable->interrupt_disable(intc, id);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_clint0_command_request (struct metal_interrupt *controller,
|
||||
int command, void *data)
|
||||
{
|
||||
int hartid;
|
||||
int rc = -1;
|
||||
struct __metal_driver_riscv_clint0 *clint =
|
||||
(struct __metal_driver_riscv_clint0 *)(controller);
|
||||
unsigned long control_base = __metal_driver_sifive_clint0_control_base(controller);
|
||||
|
||||
switch (command) {
|
||||
case METAL_TIMER_MTIME_GET:
|
||||
if (data) {
|
||||
*(unsigned long long *)data = __metal_clint0_mtime_get(clint);
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
case METAL_SOFTWARE_IPI_CLEAR:
|
||||
if (data) {
|
||||
hartid = *(int *)data;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
(hartid * 4))) = METAL_DISABLE;
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
case METAL_SOFTWARE_IPI_SET:
|
||||
if (data) {
|
||||
hartid = *(int *)data;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
(hartid * 4))) = METAL_ENABLE;
|
||||
/* Callers of this function assume it's blocking, in the sense that
|
||||
* the IPI is guarnteed to have been delivered before the function
|
||||
* returns. We can't really guarnteed it's delivered, but we can
|
||||
* read back the control register after writing it in at least an
|
||||
* attempt to provide some semblence of ordering here. The fence
|
||||
* ensures the read is order after the write -- it wouldn't be
|
||||
* necessary under RVWMO because this is the same address, but we
|
||||
* don't have an IO memory model so I'm being a bit overkill here.
|
||||
*/
|
||||
__METAL_IO_FENCE(o,i);
|
||||
rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
(hartid * 4)));
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
case METAL_SOFTWARE_MSIP_GET:
|
||||
rc = 0;
|
||||
if (data) {
|
||||
hartid = *(int *)data;
|
||||
rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
(hartid * 4)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_clint0_clear_interrupt (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
int hartid = metal_cpu_get_current_hartid();
|
||||
return __metal_driver_riscv_clint0_command_request(controller,
|
||||
METAL_SOFTWARE_IPI_CLEAR, &hartid);
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_clint0_set_interrupt (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
int hartid = metal_cpu_get_current_hartid();
|
||||
return __metal_driver_riscv_clint0_command_request(controller,
|
||||
METAL_SOFTWARE_IPI_SET, &hartid);
|
||||
}
|
||||
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_clint0) = {
|
||||
.clint_vtable.interrupt_init = __metal_driver_riscv_clint0_init,
|
||||
.clint_vtable.interrupt_register = __metal_driver_riscv_clint0_register,
|
||||
.clint_vtable.interrupt_vector_register = __metal_driver_riscv_clint0_vector_register,
|
||||
.clint_vtable.interrupt_enable = __metal_driver_riscv_clint0_enable,
|
||||
.clint_vtable.interrupt_disable = __metal_driver_riscv_clint0_disable,
|
||||
.clint_vtable.interrupt_get_vector_mode = __metal_driver_riscv_clint0_get_vector_mode,
|
||||
.clint_vtable.interrupt_set_vector_mode = __metal_driver_riscv_clint0_set_vector_mode,
|
||||
.clint_vtable.interrupt_clear = __metal_driver_riscv_clint0_clear_interrupt,
|
||||
.clint_vtable.interrupt_set = __metal_driver_riscv_clint0_set_interrupt,
|
||||
.clint_vtable.command_request = __metal_driver_riscv_clint0_command_request,
|
||||
.clint_vtable.mtimecmp_set = __metal_driver_riscv_clint0_mtimecmp_set,
|
||||
};
|
||||
|
||||
#endif /* METAL_RISCV_CLINT0 */
|
||||
|
||||
typedef int no_empty_translation_units;
|
|
@ -0,0 +1,999 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/shutdown.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
extern void __metal_vector_table();
|
||||
unsigned long long __metal_driver_cpu_mtime_get(struct metal_cpu *cpu);
|
||||
int __metal_driver_cpu_mtimecmp_set(struct metal_cpu *cpu, unsigned long long time);
|
||||
|
||||
struct metal_cpu *__metal_driver_cpu_get(int hartid)
|
||||
{
|
||||
if (hartid < __METAL_DT_MAX_HARTS) {
|
||||
return &(__metal_cpu_table[hartid]->cpu);
|
||||
}
|
||||
return (struct metal_cpu *)NULL;
|
||||
}
|
||||
|
||||
uintptr_t __metal_myhart_id (void)
|
||||
{
|
||||
uintptr_t myhart;
|
||||
__asm__ volatile ("csrr %0, mhartid" : "=r"(myhart));
|
||||
return myhart;
|
||||
}
|
||||
|
||||
void __metal_zero_memory (unsigned char *base, unsigned int size)
|
||||
{
|
||||
volatile unsigned char *ptr;
|
||||
for (ptr = base; ptr < (base + size); ptr++){
|
||||
*ptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void __metal_interrupt_global_enable (void) {
|
||||
uintptr_t m;
|
||||
__asm__ volatile ("csrrs %0, mstatus, %1" : "=r"(m) : "r"(METAL_MIE_INTERRUPT));
|
||||
}
|
||||
|
||||
void __metal_interrupt_global_disable (void) {
|
||||
uintptr_t m;
|
||||
__asm__ volatile ("csrrc %0, mstatus, %1" : "=r"(m) : "r"(METAL_MIE_INTERRUPT));
|
||||
}
|
||||
|
||||
void __metal_interrupt_software_enable (void) {
|
||||
uintptr_t m;
|
||||
__asm__ volatile ("csrrs %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_SW));
|
||||
}
|
||||
|
||||
void __metal_interrupt_software_disable (void) {
|
||||
uintptr_t m;
|
||||
__asm__ volatile ("csrrc %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_SW));
|
||||
}
|
||||
|
||||
void __metal_interrupt_timer_enable (void) {
|
||||
uintptr_t m;
|
||||
__asm__ volatile ("csrrs %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_TMR));
|
||||
}
|
||||
|
||||
void __metal_interrupt_timer_disable (void) {
|
||||
uintptr_t m;
|
||||
__asm__ volatile ("csrrc %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_TMR));
|
||||
}
|
||||
|
||||
void __metal_interrupt_external_enable (void) {
|
||||
uintptr_t m;
|
||||
__asm__ volatile ("csrrs %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_EXT));
|
||||
}
|
||||
|
||||
void __metal_interrupt_external_disable (void) {
|
||||
unsigned long m;
|
||||
__asm__ volatile ("csrrc %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_EXT));
|
||||
}
|
||||
|
||||
void __metal_interrupt_local_enable (int id) {
|
||||
uintptr_t b = 1 << id;
|
||||
uintptr_t m;
|
||||
__asm__ volatile ("csrrs %0, mie, %1" : "=r"(m) : "r"(b));
|
||||
}
|
||||
|
||||
void __metal_interrupt_local_disable (int id) {
|
||||
uintptr_t b = 1 << id;
|
||||
uintptr_t m;
|
||||
__asm__ volatile ("csrrc %0, mie, %1" : "=r"(m) : "r"(b));
|
||||
}
|
||||
|
||||
void __metal_default_exception_handler (struct metal_cpu *cpu, int ecode) {
|
||||
metal_shutdown(100);
|
||||
}
|
||||
|
||||
void __metal_default_interrupt_handler (int id, void *priv) {
|
||||
metal_shutdown(200);
|
||||
}
|
||||
|
||||
/* The metal_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_interrupt_vector_handler (void) {
|
||||
metal_shutdown(300);
|
||||
}
|
||||
|
||||
/* The metal_software_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_software_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_SW].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_SW].handler(METAL_INTERRUPT_ID_SW, priv);
|
||||
}
|
||||
}
|
||||
|
||||
void __metal_default_sw_handler (int id, void *priv) {
|
||||
uintptr_t mcause;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
__asm__ volatile ("csrr %0, mcause" : "=r"(mcause));
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
intc->metal_exception_table[mcause & METAL_MCAUSE_CAUSE]((struct metal_cpu *)cpu, id);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_timer_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_timer_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_TMR].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_TMR].handler(METAL_INTERRUPT_ID_TMR, priv);
|
||||
}
|
||||
}
|
||||
|
||||
void __metal_default_timer_handler (int id, void *priv) {
|
||||
struct metal_cpu *cpu = __metal_driver_cpu_get(__metal_myhart_id());
|
||||
unsigned long long time = __metal_driver_cpu_mtime_get(cpu);
|
||||
|
||||
/* Set a 10 cycle timer */
|
||||
__metal_driver_cpu_mtimecmp_set(cpu, time + 10);
|
||||
}
|
||||
|
||||
/* The metal_external_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_external_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_EXT].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_EXT].handler(METAL_INTERRUPT_ID_EXT, priv);
|
||||
}
|
||||
}
|
||||
|
||||
void __metal_exception_handler(void) __attribute__((interrupt, aligned(128)));
|
||||
void __metal_exception_handler (void) {
|
||||
int id;
|
||||
void *priv;
|
||||
uintptr_t mcause, mepc, mtval, mtvec;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
__asm__ volatile ("csrr %0, mcause" : "=r"(mcause));
|
||||
__asm__ volatile ("csrr %0, mepc" : "=r"(mepc));
|
||||
__asm__ volatile ("csrr %0, mtval" : "=r"(mtval));
|
||||
__asm__ volatile ("csrr %0, mtvec" : "=r"(mtvec));
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
id = mcause & METAL_MCAUSE_CAUSE;
|
||||
if (mcause & METAL_MCAUSE_INTR) {
|
||||
if ((id < METAL_INTERRUPT_ID_CSW) ||
|
||||
((mtvec & METAL_MTVEC_MASK) == METAL_MTVEC_DIRECT)) {
|
||||
priv = intc->metal_int_table[id].exint_data;
|
||||
intc->metal_int_table[id].handler(id, priv);
|
||||
return;
|
||||
}
|
||||
if ((mtvec & METAL_MTVEC_MASK) == METAL_MTVEC_CLIC) {
|
||||
uintptr_t mtvt;
|
||||
metal_interrupt_handler_t mtvt_handler;
|
||||
|
||||
__asm__ volatile ("csrr %0, 0x307" : "=r"(mtvt));
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int;
|
||||
mtvt_handler = (metal_interrupt_handler_t)*(uintptr_t *)mtvt;
|
||||
mtvt_handler(id, priv);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
intc->metal_exception_table[id]((struct metal_cpu *)cpu, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc0_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc0_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC0].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC0].handler(METAL_INTERRUPT_ID_LC0, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc1_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc1_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC1].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC1].handler(METAL_INTERRUPT_ID_LC1, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc2_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc2_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC2].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC2].handler(METAL_INTERRUPT_ID_LC2, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc3_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc3_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC3].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC3].handler(METAL_INTERRUPT_ID_LC3, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc4_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc4_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC4].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC4].handler(METAL_INTERRUPT_ID_LC4, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc5_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc5_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC5].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC5].handler(METAL_INTERRUPT_ID_LC5, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc6_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc6_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC6].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC6].handler(METAL_INTERRUPT_ID_LC6, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc7_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc7_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC7].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC7].handler(METAL_INTERRUPT_ID_LC7, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc8_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc8_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC8].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC8].handler(METAL_INTERRUPT_ID_LC8, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc9_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc9_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC9].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC9].handler(METAL_INTERRUPT_ID_LC9, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc10_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc10_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC10].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC10].handler(METAL_INTERRUPT_ID_LC10, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc11_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc11_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC11].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC11].handler(METAL_INTERRUPT_ID_LC11, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc12_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc12_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC12].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC12].handler(METAL_INTERRUPT_ID_LC12, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc13_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc13_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC13].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC13].handler(METAL_INTERRUPT_ID_LC13, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc14_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc14_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC14].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC14].handler(METAL_INTERRUPT_ID_LC14, priv);
|
||||
}
|
||||
}
|
||||
|
||||
/* The metal_lc15_interrupt_vector_handler() function can be redefined. */
|
||||
void __attribute__((weak, interrupt)) metal_lc15_interrupt_vector_handler (void) {
|
||||
void *priv;
|
||||
struct __metal_driver_riscv_cpu_intc *intc;
|
||||
struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
|
||||
|
||||
if ( cpu ) {
|
||||
intc = (struct __metal_driver_riscv_cpu_intc *)
|
||||
__metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
|
||||
priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC15].exint_data;
|
||||
intc->metal_int_table[METAL_INTERRUPT_ID_LC15].handler(METAL_INTERRUPT_ID_LC15, priv);
|
||||
}
|
||||
}
|
||||
|
||||
metal_vector_mode __metal_controller_interrupt_vector_mode (void)
|
||||
{
|
||||
uintptr_t val;
|
||||
|
||||
asm volatile ("csrr %0, mtvec" : "=r"(val));
|
||||
val &= METAL_MTVEC_MASK;
|
||||
|
||||
switch (val) {
|
||||
case METAL_MTVEC_CLIC:
|
||||
return METAL_SELECTIVE_VECTOR_MODE;
|
||||
case METAL_MTVEC_CLIC_VECTORED:
|
||||
return METAL_HARDWARE_VECTOR_MODE;
|
||||
case METAL_MTVEC_VECTORED:
|
||||
return METAL_VECTOR_MODE;
|
||||
}
|
||||
return METAL_DIRECT_MODE;
|
||||
}
|
||||
|
||||
void __metal_controller_interrupt_vector (metal_vector_mode mode, void *vec_table)
|
||||
{
|
||||
uintptr_t trap_entry, val;
|
||||
|
||||
__asm__ volatile ("csrr %0, mtvec" : "=r"(val));
|
||||
val &= ~(METAL_MTVEC_CLIC_VECTORED | METAL_MTVEC_CLIC_RESERVED);
|
||||
trap_entry = (uintptr_t)vec_table;
|
||||
|
||||
switch (mode) {
|
||||
case METAL_SELECTIVE_NONVECTOR_MODE:
|
||||
case METAL_SELECTIVE_VECTOR_MODE:
|
||||
__asm__ volatile ("csrw 0x307, %0" :: "r"(trap_entry));
|
||||
__asm__ volatile ("csrw mtvec, %0" :: "r"(val | METAL_MTVEC_CLIC));
|
||||
break;
|
||||
case METAL_HARDWARE_VECTOR_MODE:
|
||||
__asm__ volatile ("csrw 0x307, %0" :: "r"(trap_entry));
|
||||
__asm__ volatile ("csrw mtvec, %0" :: "r"(val | METAL_MTVEC_CLIC_VECTORED));
|
||||
break;
|
||||
case METAL_VECTOR_MODE:
|
||||
__asm__ volatile ("csrw mtvec, %0" :: "r"(trap_entry | METAL_MTVEC_VECTORED));
|
||||
break;
|
||||
case METAL_DIRECT_MODE:
|
||||
__asm__ volatile ("csrw mtvec, %0" :: "r"(trap_entry & ~METAL_MTVEC_CLIC_VECTORED));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int __metal_valid_interrupt_id (int id)
|
||||
{
|
||||
switch (id) {
|
||||
case METAL_INTERRUPT_ID_SW:
|
||||
case METAL_INTERRUPT_ID_TMR:
|
||||
case METAL_INTERRUPT_ID_EXT:
|
||||
case METAL_INTERRUPT_ID_LC0:
|
||||
case METAL_INTERRUPT_ID_LC1:
|
||||
case METAL_INTERRUPT_ID_LC2:
|
||||
case METAL_INTERRUPT_ID_LC3:
|
||||
case METAL_INTERRUPT_ID_LC4:
|
||||
case METAL_INTERRUPT_ID_LC5:
|
||||
case METAL_INTERRUPT_ID_LC6:
|
||||
case METAL_INTERRUPT_ID_LC7:
|
||||
case METAL_INTERRUPT_ID_LC8:
|
||||
case METAL_INTERRUPT_ID_LC9:
|
||||
case METAL_INTERRUPT_ID_LC10:
|
||||
case METAL_INTERRUPT_ID_LC11:
|
||||
case METAL_INTERRUPT_ID_LC12:
|
||||
case METAL_INTERRUPT_ID_LC13:
|
||||
case METAL_INTERRUPT_ID_LC14:
|
||||
case METAL_INTERRUPT_ID_LC15:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int __metal_local_interrupt_enable (struct metal_interrupt *controller,
|
||||
metal_interrupt_id_e id, int enable)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if ( !controller) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case METAL_INTERRUPT_ID_BASE:
|
||||
if (enable) {
|
||||
__metal_interrupt_global_enable();
|
||||
} else {
|
||||
__metal_interrupt_global_disable();
|
||||
}
|
||||
break;
|
||||
case METAL_INTERRUPT_ID_SW:
|
||||
if (enable) {
|
||||
__metal_interrupt_software_enable();
|
||||
} else {
|
||||
__metal_interrupt_software_disable();
|
||||
}
|
||||
break;
|
||||
case METAL_INTERRUPT_ID_TMR:
|
||||
if (enable) {
|
||||
__metal_interrupt_timer_enable();
|
||||
} else {
|
||||
__metal_interrupt_timer_disable();
|
||||
}
|
||||
break;
|
||||
case METAL_INTERRUPT_ID_EXT:
|
||||
if (enable) {
|
||||
__metal_interrupt_external_enable();
|
||||
} else {
|
||||
__metal_interrupt_external_disable();
|
||||
}
|
||||
break;
|
||||
case METAL_INTERRUPT_ID_LC0:
|
||||
case METAL_INTERRUPT_ID_LC1:
|
||||
case METAL_INTERRUPT_ID_LC2:
|
||||
case METAL_INTERRUPT_ID_LC3:
|
||||
case METAL_INTERRUPT_ID_LC4:
|
||||
case METAL_INTERRUPT_ID_LC5:
|
||||
case METAL_INTERRUPT_ID_LC6:
|
||||
case METAL_INTERRUPT_ID_LC7:
|
||||
case METAL_INTERRUPT_ID_LC8:
|
||||
case METAL_INTERRUPT_ID_LC9:
|
||||
case METAL_INTERRUPT_ID_LC10:
|
||||
case METAL_INTERRUPT_ID_LC11:
|
||||
case METAL_INTERRUPT_ID_LC12:
|
||||
case METAL_INTERRUPT_ID_LC13:
|
||||
case METAL_INTERRUPT_ID_LC14:
|
||||
case METAL_INTERRUPT_ID_LC15:
|
||||
if (enable) {
|
||||
__metal_interrupt_local_enable(id);
|
||||
} else {
|
||||
__metal_interrupt_local_disable(id);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rc = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_exception_register (struct metal_interrupt *controller,
|
||||
int ecode, metal_exception_handler_t isr)
|
||||
{
|
||||
struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller);
|
||||
|
||||
if ((ecode < METAL_MAX_EXCEPTION_CODE) && isr) {
|
||||
intc->metal_exception_table[ecode] = isr;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void __metal_driver_riscv_cpu_controller_interrupt_init (struct metal_interrupt *controller)
|
||||
{
|
||||
struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller);
|
||||
uintptr_t val;
|
||||
|
||||
if ( !intc->init_done ) {
|
||||
/* Disable and clear all interrupt sources */
|
||||
__asm__ volatile ("csrc mie, %0" :: "r"(-1));
|
||||
__asm__ volatile ("csrc mip, %0" :: "r"(-1));
|
||||
|
||||
/* Read the misa CSR to determine if the delegation registers exist */
|
||||
uintptr_t misa;
|
||||
__asm__ volatile ("csrr %0, misa" : "=r" (misa));
|
||||
|
||||
/* The delegation CSRs exist if user mode interrupts (N extension) or
|
||||
* supervisor mode (S extension) are supported */
|
||||
if((misa & METAL_ISA_N_EXTENSIONS) || (misa & METAL_ISA_S_EXTENSIONS)) {
|
||||
/* Disable interrupt and exception delegation */
|
||||
__asm__ volatile ("csrc mideleg, %0" :: "r"(-1));
|
||||
__asm__ volatile ("csrc medeleg, %0" :: "r"(-1));
|
||||
}
|
||||
|
||||
/* The satp CSR exists if supervisor mode (S extension) is supported */
|
||||
if(misa & METAL_ISA_S_EXTENSIONS) {
|
||||
/* Clear the entire CSR to make sure that satp.MODE = 0 */
|
||||
__asm__ volatile ("csrc satp, %0" :: "r"(-1));
|
||||
}
|
||||
|
||||
/* Default to use direct interrupt, setup sw cb table*/
|
||||
for (int i = 0; i < METAL_MAX_MI; i++) {
|
||||
intc->metal_int_table[i].handler = NULL;
|
||||
intc->metal_int_table[i].sub_int = NULL;
|
||||
intc->metal_int_table[i].exint_data = NULL;
|
||||
}
|
||||
for (int i = 0; i < METAL_MAX_ME; i++) {
|
||||
intc->metal_exception_table[i] = __metal_default_exception_handler;
|
||||
}
|
||||
__metal_controller_interrupt_vector(METAL_DIRECT_MODE, (void *)(uintptr_t)&__metal_exception_handler);
|
||||
__asm__ volatile ("csrr %0, misa" : "=r"(val));
|
||||
if (val & (METAL_ISA_D_EXTENSIONS | METAL_ISA_F_EXTENSIONS | METAL_ISA_Q_EXTENSIONS)) {
|
||||
/* Floating point architecture, so turn on FP register saving*/
|
||||
__asm__ volatile ("csrr %0, mstatus" : "=r"(val));
|
||||
__asm__ volatile ("csrw mstatus, %0" :: "r"(val | METAL_MSTATUS_FS_INIT));
|
||||
}
|
||||
intc->init_done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_cpu_controller_interrupt_register(struct metal_interrupt *controller,
|
||||
int id, metal_interrupt_handler_t isr,
|
||||
void *priv)
|
||||
{
|
||||
int rc = 0;
|
||||
struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller);
|
||||
|
||||
if ( !__metal_valid_interrupt_id(id) ) {
|
||||
return -11;
|
||||
}
|
||||
|
||||
if (isr) {
|
||||
intc->metal_int_table[id].handler = isr;
|
||||
intc->metal_int_table[id].exint_data = priv;
|
||||
} else {
|
||||
switch (id) {
|
||||
case METAL_INTERRUPT_ID_SW:
|
||||
intc->metal_int_table[id].handler = __metal_default_sw_handler;
|
||||
intc->metal_int_table[id].sub_int = priv;
|
||||
break;
|
||||
case METAL_INTERRUPT_ID_TMR:
|
||||
intc->metal_int_table[id].handler = __metal_default_timer_handler;
|
||||
intc->metal_int_table[id].sub_int = priv;
|
||||
break;
|
||||
case METAL_INTERRUPT_ID_EXT:
|
||||
case METAL_INTERRUPT_ID_LC0:
|
||||
case METAL_INTERRUPT_ID_LC1:
|
||||
case METAL_INTERRUPT_ID_LC2:
|
||||
case METAL_INTERRUPT_ID_LC3:
|
||||
case METAL_INTERRUPT_ID_LC4:
|
||||
case METAL_INTERRUPT_ID_LC5:
|
||||
case METAL_INTERRUPT_ID_LC6:
|
||||
case METAL_INTERRUPT_ID_LC7:
|
||||
case METAL_INTERRUPT_ID_LC8:
|
||||
case METAL_INTERRUPT_ID_LC9:
|
||||
case METAL_INTERRUPT_ID_LC10:
|
||||
case METAL_INTERRUPT_ID_LC11:
|
||||
case METAL_INTERRUPT_ID_LC12:
|
||||
case METAL_INTERRUPT_ID_LC13:
|
||||
case METAL_INTERRUPT_ID_LC14:
|
||||
case METAL_INTERRUPT_ID_LC15:
|
||||
intc->metal_int_table[id].handler = __metal_default_interrupt_handler;
|
||||
intc->metal_int_table[id].sub_int = priv;
|
||||
break;
|
||||
default:
|
||||
rc = -12;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_cpu_controller_interrupt_enable (struct metal_interrupt *controller,
|
||||
int id)
|
||||
{
|
||||
return __metal_local_interrupt_enable(controller, id, METAL_ENABLE);
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_cpu_controller_interrupt_disable (struct metal_interrupt *controller,
|
||||
int id)
|
||||
{
|
||||
return __metal_local_interrupt_enable(controller, id, METAL_DISABLE);
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_cpu_controller_interrupt_enable_vector(struct metal_interrupt *controller,
|
||||
int id, metal_vector_mode mode)
|
||||
{
|
||||
struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller);
|
||||
|
||||
if (id == METAL_INTERRUPT_ID_BASE) {
|
||||
if (mode == METAL_DIRECT_MODE) {
|
||||
__metal_controller_interrupt_vector(mode, (void *)(uintptr_t)&__metal_exception_handler);
|
||||
return 0;
|
||||
}
|
||||
if (mode == METAL_VECTOR_MODE) {
|
||||
__metal_controller_interrupt_vector(mode, (void *)&intc->metal_mtvec_table);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_cpu_controller_interrupt_disable_vector(struct metal_interrupt *controller,
|
||||
int id)
|
||||
{
|
||||
if (id == METAL_INTERRUPT_ID_BASE) {
|
||||
__metal_controller_interrupt_vector(METAL_DIRECT_MODE, (void *)(uintptr_t)&__metal_exception_handler);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
metal_vector_mode __metal_driver_riscv_cpu_controller_get_vector_mode (struct metal_interrupt *controller)
|
||||
{
|
||||
return __metal_controller_interrupt_vector_mode();
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_cpu_controller_set_vector_mode (struct metal_interrupt *controller,
|
||||
metal_vector_mode mode)
|
||||
{
|
||||
struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller);
|
||||
( void ) intc;
|
||||
|
||||
if (mode == METAL_DIRECT_MODE) {
|
||||
__metal_controller_interrupt_vector(mode, (void *)(uintptr_t)&__metal_exception_handler);
|
||||
return 0;
|
||||
}
|
||||
if (mode == METAL_VECTOR_MODE) {
|
||||
__metal_controller_interrupt_vector(mode, (void *)__metal_vector_table);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_cpu_controller_command_request (struct metal_interrupt *controller,
|
||||
int cmd, void *data)
|
||||
{
|
||||
/* NOP for now, unless local interrupt lines the like of clic, clint, plic */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CPU driver !!! */
|
||||
|
||||
unsigned long long __metal_driver_cpu_mcycle_get(struct metal_cpu *cpu)
|
||||
{
|
||||
unsigned long long val = 0;
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
unsigned long hi, hi1, lo;
|
||||
|
||||
__asm__ volatile ("csrr %0, mcycleh" : "=r"(hi));
|
||||
__asm__ volatile ("csrr %0, mcycle" : "=r"(lo));
|
||||
__asm__ volatile ("csrr %0, mcycleh" : "=r"(hi1));
|
||||
if (hi == hi1) {
|
||||
val = ((unsigned long long)hi << 32) | lo;
|
||||
}
|
||||
#else
|
||||
__asm__ volatile ("csrr %0, mcycle" : "=r"(val));
|
||||
#endif
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned long long __metal_driver_cpu_timebase_get(struct metal_cpu *cpu)
|
||||
{
|
||||
int timebase;
|
||||
if (!cpu) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
timebase = __metal_driver_cpu_timebase((struct metal_cpu *)cpu);
|
||||
return timebase;
|
||||
}
|
||||
|
||||
unsigned long long __metal_driver_cpu_mtime_get (struct metal_cpu *cpu)
|
||||
{
|
||||
unsigned long long time = 0;
|
||||
struct metal_interrupt *tmr_intc;
|
||||
struct __metal_driver_riscv_cpu_intc *intc =
|
||||
(struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
|
||||
|
||||
if (intc) {
|
||||
tmr_intc = intc->metal_int_table[METAL_INTERRUPT_ID_TMR].sub_int;
|
||||
if (tmr_intc) {
|
||||
tmr_intc->vtable->command_request(tmr_intc,
|
||||
METAL_TIMER_MTIME_GET, &time);
|
||||
}
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_mtimecmp_set (struct metal_cpu *cpu, unsigned long long time)
|
||||
{
|
||||
int rc = -1;
|
||||
struct metal_interrupt *tmr_intc;
|
||||
struct __metal_driver_riscv_cpu_intc *intc =
|
||||
(struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
|
||||
|
||||
if (intc) {
|
||||
tmr_intc = intc->metal_int_table[METAL_INTERRUPT_ID_TMR].sub_int;
|
||||
if (tmr_intc) {
|
||||
rc = tmr_intc->vtable->mtimecmp_set(tmr_intc,
|
||||
__metal_driver_cpu_hartid(cpu),
|
||||
time);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct metal_interrupt *
|
||||
__metal_driver_cpu_timer_controller_interrupt(struct metal_cpu *cpu)
|
||||
{
|
||||
#ifdef __METAL_DT_RISCV_CLINT0_HANDLE
|
||||
return __METAL_DT_RISCV_CLINT0_HANDLE;
|
||||
#else
|
||||
#ifdef __METAL_DT_SIFIVE_CLIC0_HANDLE
|
||||
return __METAL_DT_SIFIVE_CLIC0_HANDLE;
|
||||
#else
|
||||
#pragma message("There is no interrupt controller for Timer interrupt")
|
||||
return NULL;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_get_timer_interrupt_id(struct metal_cpu *cpu)
|
||||
{
|
||||
return METAL_INTERRUPT_ID_TMR;
|
||||
}
|
||||
|
||||
struct metal_interrupt *
|
||||
__metal_driver_cpu_sw_controller_interrupt(struct metal_cpu *cpu)
|
||||
{
|
||||
#ifdef __METAL_DT_RISCV_CLINT0_HANDLE
|
||||
return __METAL_DT_RISCV_CLINT0_HANDLE;
|
||||
#else
|
||||
#ifdef __METAL_DT_SIFIVE_CLIC0_HANDLE
|
||||
return __METAL_DT_SIFIVE_CLIC0_HANDLE;
|
||||
#else
|
||||
#pragma message("There is no interrupt controller for Software interrupt")
|
||||
return NULL;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_get_sw_interrupt_id(struct metal_cpu *cpu)
|
||||
{
|
||||
return METAL_INTERRUPT_ID_SW;
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_set_sw_ipi (struct metal_cpu *cpu, int hartid)
|
||||
{
|
||||
int rc = -1;
|
||||
struct metal_interrupt *sw_intc;
|
||||
struct __metal_driver_riscv_cpu_intc *intc =
|
||||
(struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
|
||||
|
||||
if (intc) {
|
||||
sw_intc = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int;
|
||||
if (sw_intc) {
|
||||
rc = sw_intc->vtable->command_request(sw_intc,
|
||||
METAL_SOFTWARE_IPI_SET, &hartid);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_clear_sw_ipi (struct metal_cpu *cpu, int hartid)
|
||||
{
|
||||
int rc = -1;
|
||||
struct metal_interrupt *sw_intc;
|
||||
struct __metal_driver_riscv_cpu_intc *intc =
|
||||
(struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
|
||||
|
||||
if (intc) {
|
||||
sw_intc = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int;
|
||||
if (sw_intc) {
|
||||
rc = sw_intc->vtable->command_request(sw_intc,
|
||||
METAL_SOFTWARE_IPI_CLEAR, &hartid);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_get_msip (struct metal_cpu *cpu, int hartid)
|
||||
{
|
||||
int rc = 0;
|
||||
struct metal_interrupt *sw_intc;
|
||||
struct __metal_driver_riscv_cpu_intc *intc =
|
||||
(struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
|
||||
|
||||
if (intc) {
|
||||
sw_intc = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int;
|
||||
if (sw_intc) {
|
||||
rc = sw_intc->vtable->command_request(sw_intc,
|
||||
METAL_SOFTWARE_MSIP_GET, &hartid);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct metal_interrupt *
|
||||
__metal_driver_cpu_controller_interrupt(struct metal_cpu *cpu)
|
||||
{
|
||||
return __metal_driver_cpu_interrupt_controller(cpu);
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_enable_interrupt(struct metal_cpu *cpu, void *priv)
|
||||
{
|
||||
if ( __metal_driver_cpu_interrupt_controller(cpu) ) {
|
||||
/* Only support machine mode for now */
|
||||
__metal_interrupt_global_enable();
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_disable_interrupt(struct metal_cpu *cpu, void *priv)
|
||||
{
|
||||
if ( __metal_driver_cpu_interrupt_controller(cpu) ) {
|
||||
/* Only support machine mode for now */
|
||||
__metal_interrupt_global_disable();
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_exception_register(struct metal_cpu *cpu, int ecode,
|
||||
metal_exception_handler_t isr)
|
||||
{
|
||||
struct __metal_driver_riscv_cpu_intc *intc =
|
||||
(struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
|
||||
|
||||
if (intc) {
|
||||
return __metal_exception_register((struct metal_interrupt *)intc, ecode, isr);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_get_instruction_length(struct metal_cpu *cpu, uintptr_t epc)
|
||||
{
|
||||
/**
|
||||
* Per ISA compressed instruction has last two bits of opcode set.
|
||||
* The encoding '00' '01' '10' are used for compressed instruction.
|
||||
* Only enconding '11' isn't regarded as compressed instruction (>16b).
|
||||
*/
|
||||
return ((*(unsigned short*)epc & METAL_INSN_LENGTH_MASK)
|
||||
== METAL_INSN_NOT_COMPRESSED) ? 4 : 2;
|
||||
}
|
||||
|
||||
uintptr_t __metal_driver_cpu_get_exception_pc(struct metal_cpu *cpu)
|
||||
{
|
||||
uintptr_t mepc;
|
||||
__asm__ volatile ("csrr %0, mepc" : "=r"(mepc));
|
||||
return mepc;
|
||||
}
|
||||
|
||||
int __metal_driver_cpu_set_exception_pc(struct metal_cpu *cpu, uintptr_t mepc)
|
||||
{
|
||||
__asm__ volatile ("csrw mepc, %0" :: "r"(mepc));
|
||||
return 0;
|
||||
}
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_cpu_intc) = {
|
||||
.controller_vtable.interrupt_init = __metal_driver_riscv_cpu_controller_interrupt_init,
|
||||
.controller_vtable.interrupt_register = __metal_driver_riscv_cpu_controller_interrupt_register,
|
||||
.controller_vtable.interrupt_enable = __metal_driver_riscv_cpu_controller_interrupt_enable,
|
||||
.controller_vtable.interrupt_disable = __metal_driver_riscv_cpu_controller_interrupt_disable,
|
||||
.controller_vtable.interrupt_get_vector_mode = __metal_driver_riscv_cpu_controller_get_vector_mode,
|
||||
.controller_vtable.interrupt_set_vector_mode = __metal_driver_riscv_cpu_controller_set_vector_mode,
|
||||
.controller_vtable.command_request = __metal_driver_riscv_cpu_controller_command_request,
|
||||
};
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_cpu) = {
|
||||
.cpu_vtable.mcycle_get = __metal_driver_cpu_mcycle_get,
|
||||
.cpu_vtable.timebase_get = __metal_driver_cpu_timebase_get,
|
||||
.cpu_vtable.mtime_get = __metal_driver_cpu_mtime_get,
|
||||
.cpu_vtable.mtimecmp_set = __metal_driver_cpu_mtimecmp_set,
|
||||
.cpu_vtable.tmr_controller_interrupt = __metal_driver_cpu_timer_controller_interrupt,
|
||||
.cpu_vtable.get_tmr_interrupt_id = __metal_driver_cpu_get_timer_interrupt_id,
|
||||
.cpu_vtable.sw_controller_interrupt = __metal_driver_cpu_sw_controller_interrupt,
|
||||
.cpu_vtable.get_sw_interrupt_id = __metal_driver_cpu_get_sw_interrupt_id,
|
||||
.cpu_vtable.set_sw_ipi = __metal_driver_cpu_set_sw_ipi,
|
||||
.cpu_vtable.clear_sw_ipi = __metal_driver_cpu_clear_sw_ipi,
|
||||
.cpu_vtable.get_msip = __metal_driver_cpu_get_msip,
|
||||
.cpu_vtable.controller_interrupt = __metal_driver_cpu_controller_interrupt,
|
||||
.cpu_vtable.exception_register = __metal_driver_cpu_exception_register,
|
||||
.cpu_vtable.get_ilen = __metal_driver_cpu_get_instruction_length,
|
||||
.cpu_vtable.get_epc = __metal_driver_cpu_get_exception_pc,
|
||||
.cpu_vtable.set_epc = __metal_driver_cpu_set_exception_pc,
|
||||
};
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_RISCV_PLIC0
|
||||
|
||||
#include <metal/io.h>
|
||||
#include <metal/shutdown.h>
|
||||
#include <metal/drivers/riscv_plic0.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
unsigned int __metal_plic0_claim_interrupt (struct __metal_driver_riscv_plic0 *plic)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic);
|
||||
return __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
METAL_RISCV_PLIC0_CLAIM));
|
||||
}
|
||||
|
||||
void __metal_plic0_complete_interrupt(struct __metal_driver_riscv_plic0 *plic,
|
||||
unsigned int id)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic);
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
METAL_RISCV_PLIC0_CLAIM)) = id;
|
||||
}
|
||||
|
||||
int __metal_plic0_set_threshold(struct metal_interrupt *controller, unsigned int threshold)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_plic0_control_base(controller);
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
METAL_RISCV_PLIC0_THRESHOLD)) = threshold;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int __metal_plic0_get_threshold(struct metal_interrupt *controller)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_plic0_control_base(controller);
|
||||
|
||||
return __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
METAL_RISCV_PLIC0_THRESHOLD));
|
||||
}
|
||||
|
||||
int __metal_plic0_set_priority(struct metal_interrupt *controller, int id, unsigned int priority)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)controller);
|
||||
unsigned int max_priority = __metal_driver_sifive_plic0_max_priority((struct metal_interrupt *)controller);
|
||||
if ( (max_priority) && (priority < max_priority) ) {
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
METAL_RISCV_PLIC0_PRIORITY_BASE +
|
||||
(id << METAL_PLIC_SOURCE_PRIORITY_SHIFT))) = priority;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int __metal_plic0_get_priority(struct metal_interrupt *controller, int id)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_plic0_control_base(controller);
|
||||
|
||||
return __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
METAL_RISCV_PLIC0_PRIORITY_BASE +
|
||||
(id << METAL_PLIC_SOURCE_PRIORITY_SHIFT)));
|
||||
}
|
||||
|
||||
void __metal_plic0_enable(struct __metal_driver_riscv_plic0 *plic, int id, int enable)
|
||||
{
|
||||
unsigned int current;
|
||||
unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic);
|
||||
|
||||
current = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
METAL_RISCV_PLIC0_ENABLE_BASE +
|
||||
(id >> METAL_PLIC_SOURCE_SHIFT) * 4));
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
METAL_RISCV_PLIC0_ENABLE_BASE +
|
||||
((id >> METAL_PLIC_SOURCE_SHIFT) * 4))) =
|
||||
enable ? (current | (1 << (id & METAL_PLIC_SOURCE_MASK)))
|
||||
: (current & ~(1 << (id & METAL_PLIC_SOURCE_MASK)));
|
||||
}
|
||||
|
||||
void __metal_plic0_default_handler (int id, void *priv) {
|
||||
metal_shutdown(300);
|
||||
}
|
||||
|
||||
void __metal_plic0_handler (int id, void *priv)
|
||||
{
|
||||
struct __metal_driver_riscv_plic0 *plic = priv;
|
||||
unsigned int idx = __metal_plic0_claim_interrupt(plic);
|
||||
unsigned int num_interrupts = __metal_driver_sifive_plic0_num_interrupts((struct metal_interrupt *)plic);
|
||||
|
||||
if ( (idx < num_interrupts) && (plic->metal_exint_table[idx]) ) {
|
||||
plic->metal_exint_table[idx](idx,
|
||||
plic->metal_exdata_table[idx].exint_data);
|
||||
}
|
||||
|
||||
__metal_plic0_complete_interrupt(plic, idx);
|
||||
}
|
||||
|
||||
void __metal_driver_riscv_plic0_init (struct metal_interrupt *controller)
|
||||
{
|
||||
struct __metal_driver_riscv_plic0 *plic = (void *)(controller);
|
||||
|
||||
if ( !plic->init_done ) {
|
||||
int num_interrupts, line;
|
||||
struct metal_interrupt *intc;
|
||||
|
||||
for(int parent = 0; parent < __METAL_PLIC_NUM_PARENTS; parent++) {
|
||||
num_interrupts = __metal_driver_sifive_plic0_num_interrupts(controller);
|
||||
intc = __metal_driver_sifive_plic0_interrupt_parents(controller, parent);
|
||||
line = __metal_driver_sifive_plic0_interrupt_lines(controller, parent);
|
||||
|
||||
/* Initialize ist parent controller, aka cpu_intc. */
|
||||
intc->vtable->interrupt_init(intc);
|
||||
|
||||
for (int i = 0; i < num_interrupts; i++) {
|
||||
__metal_plic0_enable(plic, i, METAL_DISABLE);
|
||||
__metal_plic0_set_priority(controller, i, 0);
|
||||
plic->metal_exint_table[i] = NULL;
|
||||
plic->metal_exdata_table[i].sub_int = NULL;
|
||||
plic->metal_exdata_table[i].exint_data = NULL;
|
||||
}
|
||||
|
||||
__metal_plic0_set_threshold(controller, 0);
|
||||
|
||||
/* Register plic (ext) interrupt with with parent controller */
|
||||
intc->vtable->interrupt_register(intc, line, NULL, plic);
|
||||
/* Register plic handler for dispatching its device interrupts */
|
||||
intc->vtable->interrupt_register(intc, line, __metal_plic0_handler, plic);
|
||||
/* Enable plic (ext) interrupt with with parent controller */
|
||||
intc->vtable->interrupt_enable(intc, line);
|
||||
}
|
||||
plic->init_done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_plic0_register (struct metal_interrupt *controller,
|
||||
int id, metal_interrupt_handler_t isr,
|
||||
void *priv)
|
||||
{
|
||||
struct __metal_driver_riscv_plic0 *plic = (void *)(controller);
|
||||
|
||||
if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (isr) {
|
||||
__metal_plic0_set_priority(controller, id, 2);
|
||||
plic->metal_exint_table[id] = isr;
|
||||
plic->metal_exdata_table[id].exint_data = priv;
|
||||
} else {
|
||||
__metal_plic0_set_priority(controller, id, 1);
|
||||
plic->metal_exint_table[id] = __metal_plic0_default_handler;
|
||||
plic->metal_exdata_table[id].sub_int = priv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_plic0_enable (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
struct __metal_driver_riscv_plic0 *plic = (void *)(controller);
|
||||
|
||||
if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
__metal_plic0_enable(plic, id, METAL_ENABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __metal_driver_riscv_plic0_disable (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
struct __metal_driver_riscv_plic0 *plic = (void *)(controller);
|
||||
|
||||
if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) {
|
||||
return -1;
|
||||
}
|
||||
__metal_plic0_enable(plic, id, METAL_DISABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_plic0) = {
|
||||
.plic_vtable.interrupt_init = __metal_driver_riscv_plic0_init,
|
||||
.plic_vtable.interrupt_register = __metal_driver_riscv_plic0_register,
|
||||
.plic_vtable.interrupt_enable = __metal_driver_riscv_plic0_enable,
|
||||
.plic_vtable.interrupt_disable = __metal_driver_riscv_plic0_disable,
|
||||
.plic_vtable.interrupt_get_threshold = __metal_plic0_get_threshold,
|
||||
.plic_vtable.interrupt_set_threshold = __metal_plic0_set_threshold,
|
||||
.plic_vtable.interrupt_get_priority = __metal_plic0_get_priority,
|
||||
.plic_vtable.interrupt_set_priority = __metal_plic0_set_priority,
|
||||
};
|
||||
|
||||
#endif /* METAL_RISCV_PLIC0 */
|
||||
|
||||
typedef int no_empty_translation_units;
|
|
@ -0,0 +1,84 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_SIFIVE_CCACHE0
|
||||
|
||||
#include <stdint.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/drivers/sifive_ccache0.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
#define L2_CONFIG_WAYS_SHIFT 8
|
||||
#define L2_CONFIG_WAYS_MASK (0xFF << L2_CONFIG_WAYS_SHIFT)
|
||||
|
||||
void __metal_driver_sifive_ccache0_init(struct metal_cache *l2, int ways);
|
||||
|
||||
static void metal_driver_sifive_ccache0_init(void) __attribute__((constructor));
|
||||
static void metal_driver_sifive_ccache0_init(void)
|
||||
{
|
||||
#ifdef __METAL_DT_SIFIVE_CCACHE0_HANDLE
|
||||
/* Get the handle for the L2 cache controller */
|
||||
struct metal_cache *l2 = __METAL_DT_SIFIVE_CCACHE0_HANDLE;
|
||||
if(!l2) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the number of available ways per bank */
|
||||
unsigned long control_base = __metal_driver_sifive_ccache0_control_base(l2);
|
||||
uint32_t ways = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_SIFIVE_CCACHE0_CONFIG));
|
||||
ways = ((ways & L2_CONFIG_WAYS_MASK) >> L2_CONFIG_WAYS_SHIFT);
|
||||
|
||||
/* Enable all the ways */
|
||||
__metal_driver_sifive_ccache0_init(l2, ways);
|
||||
#endif
|
||||
}
|
||||
|
||||
void __metal_driver_sifive_ccache0_init(struct metal_cache *l2, int ways)
|
||||
{
|
||||
metal_cache_set_enabled_ways(l2, ways);
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_ccache0_get_enabled_ways(struct metal_cache *cache)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_ccache0_control_base(cache);
|
||||
|
||||
uint32_t way_enable = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_SIFIVE_CCACHE0_WAYENABLE));
|
||||
|
||||
/* The stored number is the index, so add one */
|
||||
return (0xFF & way_enable) + 1;
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_ccache0_set_enabled_ways(struct metal_cache *cache, int ways)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_ccache0_control_base(cache);
|
||||
|
||||
/* We can't decrease the number of enabled ways */
|
||||
if(metal_cache_get_enabled_ways(cache) > ways) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* The stored value is the index, so subtract one */
|
||||
uint32_t value = 0xFF & (ways - 1);
|
||||
|
||||
/* Set the number of enabled ways */
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_SIFIVE_CCACHE0_WAYENABLE)) = value;
|
||||
|
||||
/* Make sure the number of ways was set correctly */
|
||||
if(metal_cache_get_enabled_ways(cache) != ways) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_ccache0) = {
|
||||
.cache.init = __metal_driver_sifive_ccache0_init,
|
||||
.cache.get_enabled_ways = __metal_driver_sifive_ccache0_get_enabled_ways,
|
||||
.cache.set_enabled_ways = __metal_driver_sifive_ccache0_set_enabled_ways,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
typedef int no_empty_translation_units;
|
|
@ -0,0 +1,736 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_SIFIVE_CLIC0
|
||||
|
||||
#include <stdint.h>
|
||||
#include <metal/io.h>
|
||||
#include <metal/shutdown.h>
|
||||
#include <metal/drivers/sifive_clic0.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
typedef enum metal_clic_vector_{
|
||||
METAL_CLIC_NONVECTOR = 0,
|
||||
METAL_CLIC_VECTORED = 1
|
||||
} metal_clic_vector;
|
||||
|
||||
struct __metal_clic_cfg {
|
||||
unsigned char : 1,
|
||||
nmbits : 2,
|
||||
nlbits : 4,
|
||||
nvbit : 1;
|
||||
};
|
||||
|
||||
const struct __metal_clic_cfg __metal_clic_defaultcfg = {
|
||||
.nmbits = METAL_INTR_PRIV_M_MODE,
|
||||
.nlbits = 0,
|
||||
.nvbit = METAL_CLIC_NONVECTOR
|
||||
};
|
||||
|
||||
void __metal_clic0_handler(int id, void *priv) __attribute__((aligned(64)));
|
||||
|
||||
void __metal_clic0_default_vector_handler (void) __attribute__((interrupt, aligned(64)));
|
||||
|
||||
struct __metal_clic_cfg __metal_clic0_configuration (struct __metal_driver_sifive_clic0 *clic,
|
||||
struct __metal_clic_cfg *cfg)
|
||||
{
|
||||
volatile unsigned char val;
|
||||
struct __metal_clic_cfg cliccfg;
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
|
||||
if ( cfg ) {
|
||||
val = cfg->nmbits << 5 | cfg->nlbits << 1 | cfg->nvbit;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICCFG)) = val;
|
||||
}
|
||||
val = __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICCFG));
|
||||
cliccfg.nmbits = (val & METAL_SIFIVE_CLIC0_CLICCFG_NMBITS_MASK) >> 5;
|
||||
cliccfg.nlbits = (val & METAL_SIFIVE_CLIC0_CLICCFG_NLBITS_MASK) >> 1;
|
||||
cliccfg.nvbit = val & METAL_SIFIVE_CLIC0_CLICCFG_NVBIT_MASK;
|
||||
return cliccfg;
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_set_mode (struct __metal_driver_sifive_clic0 *clic, int id, int mode)
|
||||
{
|
||||
uint8_t mask, val;
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
|
||||
if (mode >= (cfg.nmbits << 1)) {
|
||||
/* Do nothing, mode request same or exceed what configured in CLIC */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Mask out nmbits and retain other values */
|
||||
mask = ((uint8_t)(-1)) >> cfg.nmbits;
|
||||
val = __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)) & mask;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)) = val | (mode << (8 - cfg.nmbits));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_set_level (struct __metal_driver_sifive_clic0 *clic, int id, unsigned int level)
|
||||
{
|
||||
uint8_t mask, nmmask, nlmask, val;
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
|
||||
/* Drop the LSBs that don't fit in nlbits */
|
||||
level = level >> (METAL_CLIC_MAX_NLBITS - cfg.nlbits);
|
||||
|
||||
nmmask = ~( ((uint8_t)(-1)) >> (cfg.nmbits) );
|
||||
nlmask = ((uint8_t)(-1)) >> (cfg.nmbits + cfg.nlbits);
|
||||
mask = ~(nlmask | nmmask);
|
||||
|
||||
val = __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id));
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)) = __METAL_SET_FIELD(val, mask, level);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int __metal_clic0_interrupt_get_level (struct __metal_driver_sifive_clic0 *clic, int id)
|
||||
{
|
||||
int level;
|
||||
uint8_t mask, val, freebits, nlbits;
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_intbits = __metal_driver_sifive_clic0_num_intbits((struct metal_interrupt *)clic);
|
||||
|
||||
if ((cfg.nmbits + cfg.nlbits) >= num_intbits) {
|
||||
nlbits = num_intbits - cfg.nmbits;
|
||||
} else {
|
||||
nlbits = cfg.nlbits;
|
||||
}
|
||||
|
||||
mask = ((1 << nlbits) - 1) << (8 - (cfg.nmbits + nlbits));
|
||||
freebits = ((1 << METAL_CLIC_MAX_NLBITS) - 1) >> nlbits;
|
||||
|
||||
if (mask == 0) {
|
||||
level = (1 << METAL_CLIC_MAX_NLBITS) - 1;
|
||||
} else {
|
||||
val = __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id));
|
||||
val = __METAL_GET_FIELD(val, mask);
|
||||
level = (val << (METAL_CLIC_MAX_NLBITS - nlbits)) | freebits;
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_set_priority (struct __metal_driver_sifive_clic0 *clic, int id, int priority)
|
||||
{
|
||||
uint8_t mask, npmask, val, npbits;
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_intbits = __metal_driver_sifive_clic0_num_intbits((struct metal_interrupt *)clic);
|
||||
|
||||
if ((cfg.nmbits + cfg.nlbits) < num_intbits) {
|
||||
npbits = num_intbits - (cfg.nmbits + cfg.nlbits);
|
||||
priority = priority >> (8 - npbits);
|
||||
|
||||
mask = ((uint8_t)(-1)) >> (cfg.nmbits + cfg.nlbits + npbits);
|
||||
npmask = ~(((uint8_t)(-1)) >> (cfg.nmbits + cfg.nlbits));
|
||||
mask = ~(mask | npmask);
|
||||
|
||||
val = __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id));
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)) = __METAL_SET_FIELD(val, mask, priority);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_get_priority (struct __metal_driver_sifive_clic0 *clic, int id)
|
||||
{
|
||||
int priority;
|
||||
uint8_t mask, val, freebits, nlbits;
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_intbits = __metal_driver_sifive_clic0_num_intbits((struct metal_interrupt *)clic);
|
||||
|
||||
if ((cfg.nmbits + cfg.nlbits) >= num_intbits) {
|
||||
nlbits = num_intbits - cfg.nmbits;
|
||||
} else {
|
||||
nlbits = cfg.nlbits;
|
||||
}
|
||||
|
||||
mask = ((1 << nlbits) - 1) << (8 - (cfg.nmbits + nlbits));
|
||||
freebits = ((1 << METAL_CLIC_MAX_NLBITS) - 1) >> nlbits;
|
||||
|
||||
if (mask == 0) {
|
||||
priority = (1 << METAL_CLIC_MAX_NLBITS) - 1;
|
||||
} else {
|
||||
val = __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id));
|
||||
priority = __METAL_GET_FIELD(val, freebits);
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_set_vector_mode (struct __metal_driver_sifive_clic0 *clic, int id, int enable)
|
||||
{
|
||||
uint8_t mask, val;
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_intbits = __metal_driver_sifive_clic0_num_intbits((struct metal_interrupt *)clic);
|
||||
|
||||
mask = 1 << (8 - num_intbits);
|
||||
val = __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id));
|
||||
/* Ensure its value is 1 bit wide */
|
||||
enable &= 0x1;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id)) = __METAL_SET_FIELD(val, mask, enable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_is_vectored (struct __metal_driver_sifive_clic0 *clic, int id)
|
||||
{
|
||||
uint8_t mask, val;
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_intbits = __metal_driver_sifive_clic0_num_intbits((struct metal_interrupt *)clic);
|
||||
|
||||
mask = 1 << (8 - num_intbits);
|
||||
val = __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTCTL_BASE + id));
|
||||
return __METAL_GET_FIELD(val, mask);
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_enable (struct __metal_driver_sifive_clic0 *clic, int id)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts((struct metal_interrupt *)clic);
|
||||
|
||||
if (id >= num_subinterrupts) {
|
||||
return -1;
|
||||
}
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTIE_BASE + id)) = METAL_ENABLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_disable (struct __metal_driver_sifive_clic0 *clic, int id)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts((struct metal_interrupt *)clic);
|
||||
|
||||
if (id >= num_subinterrupts) {
|
||||
return -1;
|
||||
}
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTIE_BASE + id)) = METAL_DISABLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_is_enabled (struct __metal_driver_sifive_clic0 *clic, int id)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts((struct metal_interrupt *)clic);
|
||||
|
||||
if (id >= num_subinterrupts) {
|
||||
return 0;
|
||||
}
|
||||
return __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTIE_BASE + id));
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_is_pending (struct __metal_driver_sifive_clic0 *clic, int id)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts((struct metal_interrupt *)clic);
|
||||
|
||||
if (id >= num_subinterrupts) {
|
||||
return 0;
|
||||
}
|
||||
return __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTIP_BASE + id));
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_set (struct __metal_driver_sifive_clic0 *clic, int id)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts((struct metal_interrupt *)clic);
|
||||
|
||||
if (id < num_subinterrupts) {
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTIP_BASE + id)) = METAL_ENABLE;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __metal_clic0_interrupt_clear (struct __metal_driver_sifive_clic0 *clic, int id)
|
||||
{
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts((struct metal_interrupt *)clic);
|
||||
|
||||
if (id < num_subinterrupts) {
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTIP_BASE + id)) = METAL_DISABLE;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __metal_clic0_configure_set_vector_mode (struct __metal_driver_sifive_clic0 *clic, metal_vector_mode mode)
|
||||
{
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
|
||||
switch (mode) {
|
||||
case METAL_SELECTIVE_NONVECTOR_MODE:
|
||||
cfg.nvbit = METAL_CLIC_NONVECTOR;
|
||||
__metal_controller_interrupt_vector(mode, &clic->metal_mtvt_table);
|
||||
break;
|
||||
case METAL_SELECTIVE_VECTOR_MODE:
|
||||
cfg.nvbit = METAL_CLIC_VECTORED;
|
||||
__metal_controller_interrupt_vector(mode, &clic->metal_mtvt_table);
|
||||
break;
|
||||
case METAL_HARDWARE_VECTOR_MODE:
|
||||
cfg.nvbit = METAL_CLIC_VECTORED;
|
||||
__metal_controller_interrupt_vector(mode, &clic->metal_mtvt_table);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
__metal_clic0_configuration(clic, &cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
metal_vector_mode __metal_clic0_configure_get_vector_mode (struct __metal_driver_sifive_clic0 *clic)
|
||||
{
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
metal_vector_mode mode = __metal_controller_interrupt_vector_mode();
|
||||
|
||||
if (mode == METAL_SELECTIVE_VECTOR_MODE) {
|
||||
if (cfg.nvbit) {
|
||||
return METAL_SELECTIVE_VECTOR_MODE;
|
||||
} else {
|
||||
return METAL_SELECTIVE_NONVECTOR_MODE;
|
||||
}
|
||||
} else {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
|
||||
int __metal_clic0_configure_set_privilege (struct __metal_driver_sifive_clic0 *clic, metal_intr_priv_mode priv)
|
||||
{
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
|
||||
cfg.nmbits = priv;
|
||||
__metal_clic0_configuration(clic, &cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
metal_intr_priv_mode __metal_clic0_configure_get_privilege (struct __metal_driver_sifive_clic0 *clic)
|
||||
{
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
|
||||
return cfg.nmbits;
|
||||
}
|
||||
|
||||
int __metal_clic0_configure_set_level (struct __metal_driver_sifive_clic0 *clic, int level)
|
||||
{
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
|
||||
cfg.nlbits = level & 0xF;
|
||||
__metal_clic0_configuration(clic, &cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __metal_clic0_configure_get_level (struct __metal_driver_sifive_clic0 *clic)
|
||||
{
|
||||
struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL);
|
||||
|
||||
return cfg.nlbits;
|
||||
}
|
||||
|
||||
unsigned long long __metal_clic0_mtime_get (struct __metal_driver_sifive_clic0 *clic)
|
||||
{
|
||||
__metal_io_u32 lo, hi;
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
|
||||
/* Guard against rollover when reading */
|
||||
do {
|
||||
hi = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_SIFIVE_CLIC0_MTIME + 4));
|
||||
lo = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_SIFIVE_CLIC0_MTIME));
|
||||
} while (__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + METAL_SIFIVE_CLIC0_MTIME + 4)) != hi);
|
||||
|
||||
return (((unsigned long long)hi) << 32) | lo;
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_mtimecmp_set(struct metal_interrupt *controller,
|
||||
int hartid,
|
||||
unsigned long long time)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic);
|
||||
/* Per spec, the RISC-V MTIME/MTIMECMP registers are 64 bit,
|
||||
* and are NOT internally latched for multiword transfers.
|
||||
* Need to be careful about sequencing to avoid triggering
|
||||
* spurious interrupts: For that set the high word to a max
|
||||
* value first.
|
||||
*/
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + METAL_SIFIVE_CLIC0_MTIMECMP_BASE + 4)) = 0xFFFFFFFF;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + METAL_SIFIVE_CLIC0_MTIMECMP_BASE)) = (__metal_io_u32)time;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + (8 * hartid) + METAL_SIFIVE_CLIC0_MTIMECMP_BASE + 4)) = (__metal_io_u32)(time >> 32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __metal_clic0_handler (int id, void *priv)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic = priv;
|
||||
int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts((struct metal_interrupt *)clic);
|
||||
|
||||
if ( (id < num_subinterrupts) && (clic->metal_exint_table[id].handler) ) {
|
||||
clic->metal_exint_table[id].handler(id, clic->metal_exint_table[id].exint_data);
|
||||
}
|
||||
}
|
||||
|
||||
void __metal_clic0_default_handler (int id, void *priv) {
|
||||
metal_shutdown(300);
|
||||
}
|
||||
|
||||
void __metal_clic0_default_vector_handler (void) {
|
||||
metal_shutdown(400);
|
||||
}
|
||||
|
||||
void __metal_driver_sifive_clic0_init (struct metal_interrupt *controller)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
|
||||
if ( !clic->init_done ) {
|
||||
int level, max_levels, line, num_interrupts, num_subinterrupts;
|
||||
struct __metal_clic_cfg cfg = __metal_clic_defaultcfg;
|
||||
struct metal_interrupt *intc =
|
||||
__metal_driver_sifive_clic0_interrupt_parent(controller);
|
||||
|
||||
/* Initialize ist parent controller, aka cpu_intc. */
|
||||
intc->vtable->interrupt_init(intc);
|
||||
__metal_controller_interrupt_vector(METAL_SELECTIVE_NONVECTOR_MODE,
|
||||
&clic->metal_mtvt_table);
|
||||
|
||||
/*
|
||||
* Register its interrupts with with parent controller,
|
||||
* aka sw, timer and ext to its default isr
|
||||
*/
|
||||
num_interrupts = __metal_driver_sifive_clic0_num_interrupts(controller);
|
||||
for (int i = 0; i < num_interrupts; i++) {
|
||||
line = __metal_driver_sifive_clic0_interrupt_lines(controller, i);
|
||||
intc->vtable->interrupt_register(intc, line, NULL, clic);
|
||||
}
|
||||
|
||||
/* Default CLIC mode to per dts */
|
||||
max_levels = __metal_driver_sifive_clic0_max_levels(controller);
|
||||
cfg.nlbits = (max_levels > METAL_CLIC_MAX_NLBITS) ?
|
||||
METAL_CLIC_MAX_NLBITS : max_levels;
|
||||
__metal_clic0_configuration(clic, &cfg);
|
||||
|
||||
level = (1 << cfg.nlbits) - 1;
|
||||
num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller);
|
||||
clic->metal_mtvt_table[0] = &__metal_clic0_handler;
|
||||
for (int i = 1; i < num_subinterrupts; i++) {
|
||||
clic->metal_mtvt_table[i] = NULL;
|
||||
clic->metal_exint_table[i].handler = NULL;
|
||||
clic->metal_exint_table[i].sub_int = NULL;
|
||||
clic->metal_exint_table[i].exint_data = NULL;
|
||||
__metal_clic0_interrupt_disable(clic, i);
|
||||
__metal_clic0_interrupt_set_level(clic, i, level);
|
||||
}
|
||||
clic->init_done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_register (struct metal_interrupt *controller,
|
||||
int id, metal_interrupt_handler_t isr,
|
||||
void *priv)
|
||||
{
|
||||
int rc = -1;
|
||||
int num_subinterrupts;
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
struct metal_interrupt *intc =
|
||||
__metal_driver_sifive_clic0_interrupt_parent(controller);
|
||||
metal_vector_mode mode = __metal_clic0_configure_get_vector_mode(clic);
|
||||
|
||||
if ( ( (mode == METAL_SELECTIVE_VECTOR_MODE) &&
|
||||
(__metal_clic0_interrupt_is_vectored(clic, id)) ) ||
|
||||
(mode == METAL_HARDWARE_VECTOR_MODE) ||
|
||||
(mode == METAL_VECTOR_MODE) ||
|
||||
(mode == METAL_DIRECT_MODE) ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Register its interrupts with parent controller */
|
||||
if (id < METAL_INTERRUPT_ID_CSW) {
|
||||
return intc->vtable->interrupt_register(intc, id, isr, priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* CLIC (sub-interrupts) devices interrupts start at 16 but offset from 0
|
||||
* Reset the IDs to reflects this.
|
||||
*/
|
||||
num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller);
|
||||
if (id < num_subinterrupts) {
|
||||
if ( isr) {
|
||||
clic->metal_exint_table[id].handler = isr;
|
||||
clic->metal_exint_table[id].exint_data = priv;
|
||||
} else {
|
||||
clic->metal_exint_table[id].handler = __metal_clic0_default_handler;
|
||||
clic->metal_exint_table[id].sub_int = priv;
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_vector_register (struct metal_interrupt *controller,
|
||||
int id, metal_interrupt_vector_handler_t isr,
|
||||
void *priv)
|
||||
{
|
||||
int rc = -1;
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
struct metal_interrupt *intc =
|
||||
__metal_driver_sifive_clic0_interrupt_parent(controller);
|
||||
int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller);
|
||||
metal_vector_mode mode = __metal_clic0_configure_get_vector_mode(clic);
|
||||
|
||||
if ((mode != METAL_SELECTIVE_VECTOR_MODE) && (mode != METAL_HARDWARE_VECTOR_MODE)) {
|
||||
return rc;
|
||||
}
|
||||
if ((mode == METAL_SELECTIVE_VECTOR_MODE) &&
|
||||
(__metal_clic0_interrupt_is_vectored(clic, id) == 0) ) {
|
||||
return rc;
|
||||
}
|
||||
if (id < num_subinterrupts) {
|
||||
if ( isr) {
|
||||
clic->metal_mtvt_table[id] = isr;
|
||||
clic->metal_exint_table[id].exint_data = priv;
|
||||
} else {
|
||||
clic->metal_mtvt_table[id] = __metal_clic0_default_vector_handler;
|
||||
clic->metal_exint_table[id].sub_int = priv;
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_enable (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_interrupt_enable(clic, id);
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_disable (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_interrupt_disable(clic, id);
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_enable_interrupt_vector(struct metal_interrupt *controller, int id)
|
||||
{
|
||||
int rc = -1;
|
||||
int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller);
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
metal_vector_mode mode = __metal_clic0_configure_get_vector_mode(clic);
|
||||
|
||||
if ((mode != METAL_SELECTIVE_VECTOR_MODE) && (mode != METAL_HARDWARE_VECTOR_MODE)) {
|
||||
return rc;
|
||||
}
|
||||
if (id < num_subinterrupts) {
|
||||
__metal_clic0_interrupt_set_vector_mode(clic, id, METAL_ENABLE);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_disable_interrupt_vector(struct metal_interrupt *controller, int id)
|
||||
{
|
||||
int num_subinterrupts;
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
|
||||
num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller);
|
||||
if (id < num_subinterrupts) {
|
||||
__metal_clic0_interrupt_set_vector_mode(clic, id, METAL_DISABLE);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
metal_vector_mode __metal_driver_sifive_clic0_get_vector_mode (struct metal_interrupt *controller)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_configure_get_vector_mode(clic);
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_set_vector_mode (struct metal_interrupt *controller, metal_vector_mode mode)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_configure_set_vector_mode(clic, mode);
|
||||
}
|
||||
|
||||
metal_intr_priv_mode __metal_driver_sifive_clic0_get_privilege (struct metal_interrupt *controller)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_configure_get_privilege(clic);
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_set_privilege (struct metal_interrupt *controller, metal_intr_priv_mode priv)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_configure_set_privilege(clic, priv);
|
||||
}
|
||||
|
||||
unsigned int __metal_driver_sifive_clic0_get_threshold (struct metal_interrupt *controller)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_configure_get_level(clic);
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_set_threshold (struct metal_interrupt *controller, unsigned int level)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_configure_set_level(clic, level);
|
||||
}
|
||||
|
||||
unsigned int __metal_driver_sifive_clic0_get_priority (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_interrupt_get_priority(clic, id);
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_set_priority (struct metal_interrupt *controller, int id, unsigned int priority)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_interrupt_set_priority(clic, id, priority);
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_clear_interrupt (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_interrupt_clear(clic, id);
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_set_interrupt (struct metal_interrupt *controller, int id)
|
||||
{
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
return __metal_clic0_interrupt_set(clic, id);
|
||||
}
|
||||
|
||||
int __metal_driver_sifive_clic0_command_request (struct metal_interrupt *controller,
|
||||
int command, void *data)
|
||||
{
|
||||
int hartid;
|
||||
int rc = -1;
|
||||
struct __metal_driver_sifive_clic0 *clic =
|
||||
(struct __metal_driver_sifive_clic0 *)(controller);
|
||||
unsigned long control_base = __metal_driver_sifive_clic0_control_base(controller);
|
||||
|
||||
switch (command) {
|
||||
case METAL_TIMER_MTIME_GET:
|
||||
if (data) {
|
||||
*(unsigned long long *)data = __metal_clic0_mtime_get(clic);
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
case METAL_SOFTWARE_IPI_CLEAR:
|
||||
if (data) {
|
||||
hartid = *(int *)data;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
(hartid * 4))) = METAL_DISABLE;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTIP_BASE)) = METAL_DISABLE;
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
case METAL_SOFTWARE_IPI_SET:
|
||||
if (data) {
|
||||
hartid = *(int *)data;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
(hartid * 4))) = METAL_ENABLE;
|
||||
__METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base +
|
||||
METAL_SIFIVE_CLIC0_MMODE_APERTURE +
|
||||
METAL_SIFIVE_CLIC0_CLICINTIP_BASE)) = METAL_ENABLE;
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
case METAL_SOFTWARE_MSIP_GET:
|
||||
rc = 0;
|
||||
if (data) {
|
||||
hartid = *(int *)data;
|
||||
rc = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base +
|
||||
(hartid * 4)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_clic0) = {
|
||||
.clic_vtable.interrupt_init = __metal_driver_sifive_clic0_init,
|
||||
.clic_vtable.interrupt_register = __metal_driver_sifive_clic0_register,
|
||||
.clic_vtable.interrupt_vector_register = __metal_driver_sifive_clic0_vector_register,
|
||||
.clic_vtable.interrupt_enable = __metal_driver_sifive_clic0_enable,
|
||||
.clic_vtable.interrupt_disable = __metal_driver_sifive_clic0_disable,
|
||||
.clic_vtable.interrupt_vector_enable = __metal_driver_sifive_clic0_enable_interrupt_vector,
|
||||
.clic_vtable.interrupt_vector_disable = __metal_driver_sifive_clic0_disable_interrupt_vector,
|
||||
.clic_vtable.interrupt_get_vector_mode = __metal_driver_sifive_clic0_get_vector_mode,
|
||||
.clic_vtable.interrupt_set_vector_mode = __metal_driver_sifive_clic0_set_vector_mode,
|
||||
.clic_vtable.interrupt_get_privilege = __metal_driver_sifive_clic0_get_privilege,
|
||||
.clic_vtable.interrupt_set_privilege = __metal_driver_sifive_clic0_set_privilege,
|
||||
.clic_vtable.interrupt_get_threshold = __metal_driver_sifive_clic0_get_threshold,
|
||||
.clic_vtable.interrupt_set_threshold = __metal_driver_sifive_clic0_set_threshold,
|
||||
.clic_vtable.interrupt_get_priority = __metal_driver_sifive_clic0_get_priority,
|
||||
.clic_vtable.interrupt_set_priority = __metal_driver_sifive_clic0_set_priority,
|
||||
.clic_vtable.interrupt_clear = __metal_driver_sifive_clic0_clear_interrupt,
|
||||
.clic_vtable.interrupt_set = __metal_driver_sifive_clic0_set_interrupt,
|
||||
.clic_vtable.command_request = __metal_driver_sifive_clic0_command_request,
|
||||
.clic_vtable.mtimecmp_set = __metal_driver_sifive_clic0_mtimecmp_set,
|
||||
};
|
||||
|
||||
#endif /* METAL_SIFIVE_CLIC0 */
|
||||
|
||||
typedef int no_empty_translation_units;
|
|
@ -0,0 +1,44 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_SIFIVE_FE310_G000_HFROSC
|
||||
|
||||
#include <metal/drivers/sifive_fe310-g000_hfrosc.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
#define CONFIG_DIVIDER 0x0000003FUL
|
||||
#define CONFIG_TRIM 0x001F0000UL
|
||||
#define CONFIG_ENABLE 0x40000000UL
|
||||
#define CONFIG_READY 0x80000000UL
|
||||
|
||||
long __metal_driver_sifive_fe310_g000_hfrosc_get_rate_hz(const struct metal_clock *clock)
|
||||
{
|
||||
struct metal_clock *ref = __metal_driver_sifive_fe310_g000_hfrosc_ref(clock);
|
||||
long config_offset = __metal_driver_sifive_fe310_g000_hfrosc_config_offset(clock);
|
||||
struct __metal_driver_sifive_fe310_g000_prci *config_base =
|
||||
__metal_driver_sifive_fe310_g000_hfrosc_config_base(clock);
|
||||
const struct __metal_driver_vtable_sifive_fe310_g000_prci *vtable =
|
||||
__metal_driver_sifive_fe310_g000_prci_vtable();
|
||||
long cfg = vtable->get_reg(config_base, config_offset);
|
||||
|
||||
if ((cfg & CONFIG_ENABLE) == 0)
|
||||
return -1;
|
||||
if ((cfg & CONFIG_READY) == 0)
|
||||
return -1;
|
||||
return metal_clock_get_rate_hz(ref) / ((cfg & CONFIG_DIVIDER) + 1);
|
||||
}
|
||||
|
||||
long __metal_driver_sifive_fe310_g000_hfrosc_set_rate_hz(struct metal_clock *clock, long rate)
|
||||
{
|
||||
return __metal_driver_sifive_fe310_g000_hfrosc_get_rate_hz(clock);
|
||||
}
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_hfrosc) = {
|
||||
.clock.get_rate_hz = &__metal_driver_sifive_fe310_g000_hfrosc_get_rate_hz,
|
||||
.clock.set_rate_hz = &__metal_driver_sifive_fe310_g000_hfrosc_set_rate_hz,
|
||||
};
|
||||
#endif /* METAL_SIFIVE_FE310_G000_HFROSC */
|
||||
|
||||
typedef int no_empty_translation_units;
|
|
@ -0,0 +1,43 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_SIFIVE_FE310_G000_HFXOSC
|
||||
|
||||
#include <metal/drivers/sifive_fe310-g000_hfxosc.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
#define CONFIG_ENABLE 0x40000000UL
|
||||
#define CONFIG_READY 0x80000000UL
|
||||
|
||||
long __metal_driver_sifive_fe310_g000_hfxosc_get_rate_hz(const struct metal_clock *clock)
|
||||
{
|
||||
struct metal_clock *ref = __metal_driver_sifive_fe310_g000_hfxosc_ref(clock);
|
||||
long config_offset = __metal_driver_sifive_fe310_g000_hfxosc_config_offset(clock);
|
||||
struct __metal_driver_sifive_fe310_g000_prci *config_base =
|
||||
__metal_driver_sifive_fe310_g000_hfxosc_config_base(clock);
|
||||
const struct __metal_driver_vtable_sifive_fe310_g000_prci *vtable =
|
||||
__metal_driver_sifive_fe310_g000_prci_vtable();
|
||||
long cfg = vtable->get_reg(config_base, config_offset);
|
||||
|
||||
if ((cfg & CONFIG_ENABLE) == 0)
|
||||
return -1;
|
||||
if ((cfg & CONFIG_READY) == 0)
|
||||
return -1;
|
||||
return metal_clock_get_rate_hz(ref);
|
||||
}
|
||||
|
||||
long __metal_driver_sifive_fe310_g000_hfxosc_set_rate_hz(struct metal_clock *clock, long rate)
|
||||
{
|
||||
return __metal_driver_sifive_fe310_g000_hfxosc_get_rate_hz(clock);
|
||||
}
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_hfxosc) = {
|
||||
.clock.get_rate_hz = __metal_driver_sifive_fe310_g000_hfxosc_get_rate_hz,
|
||||
.clock.set_rate_hz = __metal_driver_sifive_fe310_g000_hfxosc_set_rate_hz,
|
||||
};
|
||||
|
||||
#endif /* METAL_SIFIVE_FE310_G000_HFXOSC */
|
||||
|
||||
typedef int no_empty_translation_units;
|
|
@ -0,0 +1,53 @@
|
|||
/* Copyright 2019 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_SIFIVE_FE310_G000_LFROSC
|
||||
|
||||
#include <metal/drivers/sifive_fe310-g000_lfrosc.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
/* LFROSCCFG */
|
||||
#define METAL_LFROSCCFG_DIV_MASK 0x3F
|
||||
#define METAL_LFROSCCFG_TRIM_SHIFT 16
|
||||
#define METAL_LFROSCCFG_TRIM_MASK (0x1F << METAL_LFROSCCFG_TRIM_SHIFT)
|
||||
#define METAL_LFROSCCFG_EN (1 << 30)
|
||||
#define METAL_LFROSCCFG_RDY (1 << 31)
|
||||
|
||||
/* LFCLKMUX */
|
||||
#define METAL_LFCLKMUX_SEL 1
|
||||
#define METAL_LFCLKMUX_EXT_MUX_STATUS (1 << 31)
|
||||
|
||||
#define LFROSC_REGW(addr) (__METAL_ACCESS_ONCE((__metal_io_u32 *)addr))
|
||||
|
||||
long __metal_driver_sifive_fe310_g000_lfrosc_get_rate_hz(const struct metal_clock *clock)
|
||||
{
|
||||
struct metal_clock *internal_ref = __metal_driver_sifive_fe310_g000_lfrosc_lfrosc(clock);
|
||||
struct metal_clock *external_ref = __metal_driver_sifive_fe310_g000_lfrosc_psdlfaltclk(clock);
|
||||
|
||||
unsigned long int cfg_reg = __metal_driver_sifive_fe310_g000_lfrosc_config_reg(clock);
|
||||
unsigned long int mux_reg = __metal_driver_sifive_fe310_g000_lfrosc_mux_reg(clock);
|
||||
|
||||
if(LFROSC_REGW(mux_reg) & METAL_LFCLKMUX_EXT_MUX_STATUS) {
|
||||
return metal_clock_get_rate_hz(external_ref);
|
||||
}
|
||||
|
||||
const unsigned long int div = (LFROSC_REGW(cfg_reg) & METAL_LFROSCCFG_DIV_MASK) + 1;
|
||||
|
||||
return metal_clock_get_rate_hz(internal_ref) / div;
|
||||
}
|
||||
|
||||
long __metal_driver_sifive_fe310_g000_lfrosc_set_rate_hz(struct metal_clock *clock, long rate)
|
||||
{
|
||||
return __metal_driver_sifive_fe310_g000_lfrosc_get_rate_hz(clock);
|
||||
}
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_lfrosc) = {
|
||||
.clock.get_rate_hz = &__metal_driver_sifive_fe310_g000_lfrosc_get_rate_hz,
|
||||
.clock.set_rate_hz = &__metal_driver_sifive_fe310_g000_lfrosc_set_rate_hz,
|
||||
};
|
||||
#endif /* METAL_SIFIVE_FE310_G000_LFROSC */
|
||||
|
||||
typedef int no_empty_translation_units;
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_SIFIVE_FE310_G000_PLL
|
||||
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <metal/machine.h>
|
||||
#include <metal/drivers/sifive_fe310-g000_pll.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define PLL_R 0x00000007UL
|
||||
#define PLL_F 0x000003F0UL
|
||||
#define PLL_Q 0x00000C00UL
|
||||
#define PLL_SEL 0x00010000UL
|
||||
#define PLL_REFSEL 0x00020000UL
|
||||
#define PLL_BYPASS 0x00040000UL
|
||||
#define PLL_LOCK 0x80000000UL
|
||||
|
||||
#define DIV_DIV 0x0000003FUL
|
||||
#define DIV_1 0x00000100UL
|
||||
|
||||
#define PLL_R_SHIFT(r) ((r << 0) & PLL_R)
|
||||
#define PLL_F_SHIFT(f) ((f << 4) & PLL_F)
|
||||
#define PLL_Q_SHIFT(q) ((q << 10) & PLL_Q)
|
||||
#define PLL_DIV_SHIFT(d) ((d << 0) & DIV_DIV)
|
||||
|
||||
struct pll_config_t {
|
||||
unsigned long multiplier;
|
||||
unsigned long divisor;
|
||||
unsigned long min_input_rate;
|
||||
unsigned long max_input_rate;
|
||||
unsigned long r;
|
||||
unsigned long f;
|
||||
unsigned long q;
|
||||
long d; /* < 0 if disabled */
|
||||
};
|
||||
|
||||
static const struct pll_config_t pll_configs[] = {
|
||||
/*
|
||||
* multiplier
|
||||
* ^ divisor
|
||||
* | ^ min_input_rate
|
||||
* | | ^ max_input_rate
|
||||
* | | | ^ r
|
||||
* | | | | ^ f
|
||||
* | | | | | ^ q
|
||||
* | | | | | | ^ d
|
||||
* | | | | | | | ^
|
||||
* | | | | | | | | */
|
||||
{ 1, 32, 12000000, 24000000, 1, 31, 3, 63},
|
||||
{ 1, 32, 24000000, 48000000, 3, 31, 2, 63},
|
||||
{ 1, 16, 6000000, 12000000, 0, 31, 3, 63},
|
||||
{ 1, 16, 12000000, 24000000, 1, 31, 2, 63},
|
||||
{ 1, 16, 24000000, 48000000, 3, 31, 2, 31},
|
||||
{ 1, 8, 6000000, 12000000, 0, 31, 3, 31},
|
||||
{ 1, 8, 12000000, 24000000, 1, 31, 2, 31},
|
||||
{ 1, 8, 24000000, 48000000, 3, 31, 2, 15},
|
||||
{ 1, 4, 6000000, 12000000, 0, 31, 3, 15},
|
||||
{ 1, 4, 12000000, 24000000, 1, 31, 2, 15},
|
||||
{ 1, 4, 24000000, 48000000, 3, 31, 2, 7},
|
||||
{ 1, 2, 6000000, 12000000, 0, 31, 2, 15},
|
||||
{ 1, 2, 12000000, 24000000, 1, 31, 1, 15},
|
||||
{ 1, 2, 24000000, 48000000, 3, 31, 1, 7},
|
||||
{ 2, 1, 6000000, 12000000, 0, 31, 1, 7},
|
||||
{ 2, 1, 12000000, 24000000, 1, 31, 1, 3},
|
||||
{ 2, 1, 24000000, 48000000, 3, 31, 3, -1},
|
||||
{ 4, 1, 6000000, 12000000, 0, 31, 3, 0},
|
||||
{ 4, 1, 12000000, 24000000, 1, 31, 3, -1},
|
||||
{ 4, 1, 24000000, 48000000, 3, 31, 2, -1},
|
||||
{ 6, 1, 6000000, 10666666, 0, 35, 1, 2},
|
||||
{ 6, 1, 10666666, 12000000, 0, 23, 3, -1},
|
||||
{ 6, 1, 12000000, 16000000, 1, 47, 3, -1},
|
||||
{ 6, 1, 16000000, 18000000, 1, 23, 2, -1},
|
||||
{ 6, 1, 18000000, 21333333, 2, 35, 2, -1},
|
||||
{ 8, 1, 6000000, 12000000, 0, 31, 3, -1},
|
||||
{ 8, 1, 12000000, 24000000, 1, 31, 2, -1},
|
||||
{ 8, 1, 24000000, 48000000, 3, 31, 1, -1},
|
||||
{10, 1, 6000000, 9600000, 0, 39, 3, -1},
|
||||
{10, 1, 9600000, 12000000, 0, 19, 2, -1},
|
||||
{10, 1, 12000000, 19200000, 1, 39, 2, -1},
|
||||
{10, 1, 19200000, 24000000, 1, 19, 1, -1},
|
||||
{10, 1, 24000000, 38400000, 3, 39, 1, -1},
|
||||
{12, 1, 6000000, 8000000, 0, 47, 3, -1},
|
||||
{12, 1, 8000000, 12000000, 0, 23, 2, -1},
|
||||
{12, 1, 12000000, 16000000, 1, 47, 2, -1},
|
||||
{12, 1, 16000000, 24000000, 1, 23, 1, -1},
|
||||
{12, 1, 24000000, 30000000, 3, 47, 1, -1},
|
||||
{12, 1, 30000000, 32000000, 3, 47, 1, -1},
|
||||
{14, 1, 6000000, 6857142, 0, 55, 3, -1},
|
||||
{14, 1, 6857143, 12000000, 0, 27, 2, -1},
|
||||
{14, 1, 12000000, 13714285, 1, 55, 2, -1},
|
||||
{14, 1, 13714286, 24000000, 1, 27, 1, -1},
|
||||
{14, 1, 24000000, 27428571, 3, 55, 1, -1},
|
||||
{16, 1, 6000000, 12000000, 0, 31, 2, -1},
|
||||
{16, 1, 12000000, 24000000, 1, 31, 1, -1},
|
||||
{18, 1, 6000000, 10666666, 0, 35, 2, -1},
|
||||
{18, 1, 10666667, 12000000, 0, 17, 1, -1},
|
||||
{18, 1, 12000000, 21333333, 1, 35, 1, -1},
|
||||
{20, 1, 6000000, 9600000, 0, 39, 2, -1},
|
||||
{20, 1, 9600000, 12000000, 0, 19, 1, -1},
|
||||
{20, 1, 12000000, 19200000, 1, 39, 1, -1},
|
||||
{22, 1, 6000000, 8727272, 0, 43, 2, -1},
|
||||
{22, 1, 8727273, 12000000, 0, 21, 1, -1},
|
||||
{22, 1, 12000000, 17454545, 1, 43, 1, -1},
|
||||
{24, 1, 6000000, 8000000, 0, 47, 2, -1},
|
||||
{24, 1, 8000000, 12000000, 0, 23, 1, -1},
|
||||
{24, 1, 12000000, 16000000, 1, 47, 1, -1},
|
||||
{26, 1, 6000000, 7384615, 0, 51, 2, -1},
|
||||
{26, 1, 7384616, 12000000, 0, 25, 1, -1},
|
||||
{26, 1, 12000000, 14768230, 1, 51, 1, -1},
|
||||
{28, 1, 6000000, 6857142, 0, 55, 2, -1},
|
||||
{28, 1, 6857143, 12000000, 0, 27, 1, -1},
|
||||
{28, 1, 12000000, 13714285, 1, 55, 1, -1},
|
||||
{30, 1, 6000000, 6400000, 0, 59, 2, -1},
|
||||
{30, 1, 6400000, 12000000, 0, 29, 1, -1},
|
||||
{30, 1, 12000000, 12800000, 1, 59, 1, -1},
|
||||
{32, 1, 6000000, 12000000, 0, 31, 1, -1}
|
||||
};
|
||||
|
||||
#define PLL_CONFIG_NOT_VALID -1
|
||||
|
||||
void __metal_driver_sifive_fe310_g000_pll_init(struct __metal_driver_sifive_fe310_g000_pll *pll);
|
||||
|
||||
/* Given the rate of the PLL input frequency and a PLL configuration, what
|
||||
* will the resulting PLL output frequency be?
|
||||
* Arguments:
|
||||
* - pll_input_rate the PLL input frequency in hertz
|
||||
* - config the PLL configuration
|
||||
* Returns:
|
||||
* - PLL_CONFIG_NOT_VALID if the configuration is not valid for the input frequency
|
||||
* - the output frequency, in hertz */
|
||||
static long get_pll_config_freq(unsigned long pll_input_rate, const struct pll_config_t *config)
|
||||
{
|
||||
if(pll_input_rate < config->min_input_rate || pll_input_rate > config->max_input_rate)
|
||||
return PLL_CONFIG_NOT_VALID;
|
||||
|
||||
return pll_input_rate * config->multiplier / config->divisor;
|
||||
}
|
||||
|
||||
#ifdef __METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE
|
||||
|
||||
static void metal_sifive_fe310_g000_pll_init(void) __attribute__((constructor));
|
||||
static void metal_sifive_fe310_g000_pll_init(void) {
|
||||
long init_rate = __metal_driver_sifive_fe310_g000_pll_init_rate();
|
||||
/* If the PLL init_rate is zero, don't initialize the PLL */
|
||||
if(init_rate != 0)
|
||||
__metal_driver_sifive_fe310_g000_pll_init(__METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE);
|
||||
}
|
||||
|
||||
#endif /* __METAL_DT_SIFIVE_FE310_G000__PLL_HANDLE */
|
||||
|
||||
void __metal_driver_sifive_fe310_g000_pll_init(struct __metal_driver_sifive_fe310_g000_pll *pll) {
|
||||
struct metal_clock *pllref = __metal_driver_sifive_fe310_g000_pll_pllref(&(pll->clock));
|
||||
long init_rate = __metal_driver_sifive_fe310_g000_pll_init_rate();
|
||||
long config_offset = __metal_driver_sifive_fe310_g000_pll_config_offset();
|
||||
long base = __metal_driver_sifive_fe310_g000_prci_base();
|
||||
|
||||
__metal_io_u32 *pllcfg = (__metal_io_u32 *) (base + config_offset);
|
||||
|
||||
/* If the PLL clock has had a _pre_rate_change_callback configured, call it */
|
||||
_metal_clock_call_all_callbacks(pll->clock._pre_rate_change_callback);
|
||||
|
||||
/* If we're running off of the PLL, switch off before we start configuring it*/
|
||||
if((__METAL_ACCESS_ONCE(pllcfg) & PLL_SEL) == 0)
|
||||
__METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_SEL);
|
||||
|
||||
/* Make sure we're running off of the external oscillator for stability */
|
||||
if(pllref != NULL)
|
||||
__METAL_ACCESS_ONCE(pllcfg) |= PLL_REFSEL;
|
||||
|
||||
/* Configure the PLL to run at the requested init frequency.
|
||||
* Using the vtable instead of the user API because we want to control
|
||||
* when the callbacks occur. */
|
||||
pll->clock.vtable->set_rate_hz(&(pll->clock), init_rate);
|
||||
|
||||
/* If the PLL clock has had a rate_change_callback configured, call it */
|
||||
_metal_clock_call_all_callbacks(pll->clock._post_rate_change_callback);
|
||||
}
|
||||
|
||||
long __metal_driver_sifive_fe310_g000_pll_get_rate_hz(const struct metal_clock *clock)
|
||||
{
|
||||
struct metal_clock *pllref = __metal_driver_sifive_fe310_g000_pll_pllref(clock);
|
||||
struct metal_clock *pllsel0 = __metal_driver_sifive_fe310_g000_pll_pllsel0(clock);
|
||||
long config_offset = __metal_driver_sifive_fe310_g000_pll_config_offset(clock);
|
||||
struct __metal_driver_sifive_fe310_g000_prci *config_base =
|
||||
__metal_driver_sifive_fe310_g000_pll_config_base(clock);
|
||||
long divider_offset = __metal_driver_sifive_fe310_g000_pll_divider_offset(clock);
|
||||
struct __metal_driver_sifive_fe310_g000_prci *divider_base =
|
||||
__metal_driver_sifive_fe310_g000_pll_divider_base(clock);
|
||||
const struct __metal_driver_vtable_sifive_fe310_g000_prci *vtable =
|
||||
__metal_driver_sifive_fe310_g000_prci_vtable();
|
||||
|
||||
long cfg = vtable->get_reg(config_base, config_offset);
|
||||
long div = vtable->get_reg(divider_base, divider_offset);
|
||||
|
||||
/* At the end of the PLL there's one big mux: it either selects the HFROSC
|
||||
* (bypassing the PLL entirely) or uses the PLL. */
|
||||
if (__METAL_GET_FIELD(cfg, PLL_SEL) == 0)
|
||||
return metal_clock_get_rate_hz(pllsel0);
|
||||
|
||||
/* There's a clock mux before the PLL that selects between the HFROSC adn
|
||||
* the HFXOSC as the PLL's input clock. */
|
||||
long ref_hz = metal_clock_get_rate_hz(__METAL_GET_FIELD(cfg, PLL_REFSEL) ? pllref : pllsel0);
|
||||
|
||||
/* It's possible to bypass the PLL, which is an internal bpyass. This
|
||||
* still obays the PLL's input clock mu. */
|
||||
if (__METAL_GET_FIELD(cfg, PLL_BYPASS))
|
||||
return ref_hz;
|
||||
|
||||
/* Logically the PLL is a three stage div-mul-div. */
|
||||
long div_r = __METAL_GET_FIELD(cfg, PLL_R) + 1;
|
||||
long mul_f = 2 * (__METAL_GET_FIELD(cfg, PLL_F) + 1);
|
||||
if (__METAL_GET_FIELD(cfg, PLL_Q) == 0)
|
||||
return -1;
|
||||
long div_q = 1 << __METAL_GET_FIELD(cfg, PLL_Q);
|
||||
|
||||
/* In addition to the dividers inherent in the PLL, there's an additional
|
||||
* clock divider that lives after the PLL and lets us pick a more
|
||||
* interesting range of frequencies. */
|
||||
long pllout = (((ref_hz / div_r) * mul_f) / div_q);
|
||||
if (__METAL_GET_FIELD(div, DIV_1))
|
||||
return pllout;
|
||||
|
||||
return pllout / (2 * (__METAL_GET_FIELD(div, DIV_DIV) + 1));
|
||||
}
|
||||
|
||||
/* Find a valid configuration for the PLL which is closest to the desired
|
||||
* output frequency.
|
||||
* Arguments:
|
||||
* - ref_hz PLL input frequency
|
||||
* - rate desired PLL output frequency
|
||||
* Returns:
|
||||
* -1 if no valid configuration is available
|
||||
* the index into pll_configs of a valid configuration */
|
||||
static int find_closest_config(long ref_hz, long rate)
|
||||
{
|
||||
int closest_index = -1;
|
||||
long closest_diff = LONG_MAX;
|
||||
|
||||
/* We're probably trying for a fast output frequency, so start from
|
||||
* the high end of the configs. */
|
||||
for(int i = (sizeof(pll_configs) / sizeof(pll_configs[0])) - 1; i >= 0; i--)
|
||||
{
|
||||
long config_freq = get_pll_config_freq(ref_hz, &(pll_configs[i]));
|
||||
if(config_freq != PLL_CONFIG_NOT_VALID)
|
||||
{
|
||||
long freq_diff = abs(config_freq - rate);
|
||||
if(freq_diff < closest_diff)
|
||||
{
|
||||
closest_index = i;
|
||||
closest_diff = freq_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest_index;
|
||||
}
|
||||
|
||||
/* Configure the PLL and wait for it to lock */
|
||||
static void configure_pll(__metal_io_u32 *pllcfg, __metal_io_u32 *plloutdiv, const struct pll_config_t *config)
|
||||
{
|
||||
__METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_R);
|
||||
__METAL_ACCESS_ONCE(pllcfg) |= PLL_R_SHIFT(config->r);
|
||||
|
||||
__METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_F);
|
||||
__METAL_ACCESS_ONCE(pllcfg) |= PLL_F_SHIFT(config->f);
|
||||
|
||||
__METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_Q);
|
||||
__METAL_ACCESS_ONCE(pllcfg) |= PLL_Q_SHIFT(config->q);
|
||||
|
||||
if(config->d < 0)
|
||||
{
|
||||
/* disable final divider */
|
||||
__METAL_ACCESS_ONCE(plloutdiv) |= DIV_1;
|
||||
|
||||
__METAL_ACCESS_ONCE(plloutdiv) &= ~(DIV_DIV);
|
||||
__METAL_ACCESS_ONCE(plloutdiv) |= PLL_DIV_SHIFT(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
__METAL_ACCESS_ONCE(plloutdiv) &= ~(DIV_1);
|
||||
|
||||
__METAL_ACCESS_ONCE(plloutdiv) &= ~(DIV_DIV);
|
||||
__METAL_ACCESS_ONCE(plloutdiv) |= PLL_DIV_SHIFT(config->d);
|
||||
}
|
||||
|
||||
__METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_BYPASS);
|
||||
|
||||
/* Wait for PLL to lock */
|
||||
while((__METAL_ACCESS_ONCE(pllcfg) & PLL_LOCK) == 0) ;
|
||||
}
|
||||
|
||||
long __metal_driver_sifive_fe310_g000_pll_set_rate_hz(struct metal_clock *clock, long rate)
|
||||
{
|
||||
struct metal_clock *pllref = __metal_driver_sifive_fe310_g000_pll_pllref(clock);
|
||||
struct metal_clock *pllsel0 = __metal_driver_sifive_fe310_g000_pll_pllsel0(clock);
|
||||
long config_offset = __metal_driver_sifive_fe310_g000_pll_config_offset(clock);
|
||||
long divider_offset = __metal_driver_sifive_fe310_g000_pll_divider_offset(clock);
|
||||
long base = __metal_driver_sifive_fe310_g000_prci_base();
|
||||
|
||||
__metal_io_u32 *pllcfg = (__metal_io_u32 *) (base + config_offset);
|
||||
__metal_io_u32 *plloutdiv = (__metal_io_u32 *) (base + divider_offset);
|
||||
|
||||
/* We can't modify the PLL if coreclk is driven by it, so switch it off */
|
||||
if (__METAL_ACCESS_ONCE(pllcfg) & PLL_SEL)
|
||||
__METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_SEL);
|
||||
|
||||
/* There's a clock mux before the PLL that selects between the HFROSC and
|
||||
* the HFXOSC as the PLL's input clock. */
|
||||
long ref_hz = metal_clock_get_rate_hz(__METAL_ACCESS_ONCE(pllcfg) & PLL_REFSEL ? pllref : pllsel0);
|
||||
|
||||
/* if the desired rate is within 75%-125% of the input clock, bypass the PLL */
|
||||
if((ref_hz * 3 / 4) <= rate && (ref_hz * 5 / 4) >= rate)
|
||||
{
|
||||
__METAL_ACCESS_ONCE(pllcfg) |= PLL_BYPASS;
|
||||
}
|
||||
else
|
||||
{
|
||||
int config_index = find_closest_config(ref_hz, rate);
|
||||
if(config_index != -1)
|
||||
{
|
||||
configure_pll(pllcfg, plloutdiv, &(pll_configs[config_index]));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* unable to find a valid configuration */
|
||||
__METAL_ACCESS_ONCE(pllcfg) |= PLL_BYPASS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable the PLL */
|
||||
__METAL_ACCESS_ONCE(pllcfg) |= PLL_SEL;
|
||||
|
||||
return __metal_driver_sifive_fe310_g000_pll_get_rate_hz(clock);
|
||||
}
|
||||
|
||||
#ifdef __METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE
|
||||
static void use_hfxosc(void) __attribute__((constructor));
|
||||
static void use_hfxosc(void)
|
||||
{
|
||||
long init_rate = __metal_driver_sifive_fe310_g000_pll_init_rate();
|
||||
metal_clock_set_rate_hz(
|
||||
&__METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE->clock, init_rate
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_pll) = {
|
||||
.init = __metal_driver_sifive_fe310_g000_pll_init,
|
||||
.clock.get_rate_hz = __metal_driver_sifive_fe310_g000_pll_get_rate_hz,
|
||||
.clock.set_rate_hz = __metal_driver_sifive_fe310_g000_pll_set_rate_hz,
|
||||
};
|
||||
|
||||
#endif /* METAL_SIFIVE_FE310_G000_PLL */
|
||||
|
||||
typedef int no_empty_translation_units;
|
|
@ -0,0 +1,28 @@
|
|||
/* Copyright 2018 SiFive, Inc */
|
||||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include <metal/machine/platform.h>
|
||||
|
||||
#ifdef METAL_SIFIVE_FE310_G000_PRCI
|
||||
|
||||
#include <metal/drivers/sifive_fe310-g000_prci.h>
|
||||
#include <metal/machine.h>
|
||||
|
||||
long __metal_driver_sifive_fe310_g000_prci_get_reg(const struct __metal_driver_sifive_fe310_g000_prci *prci, long offset) {
|
||||
unsigned long base = __metal_driver_sifive_fe310_g000_prci_base();
|
||||
return __METAL_ACCESS_ONCE((__metal_io_u32 *)(base + offset));
|
||||
}
|
||||
|
||||
long __metal_driver_sifive_fe310_g000_prci_set_reg(const struct __metal_driver_sifive_fe310_g000_prci *prci, long offset, long value) {
|
||||
unsigned long base = __metal_driver_sifive_fe310_g000_prci_base();
|
||||
return __METAL_ACCESS_ONCE((__metal_io_u32 *)(base + offset)) = value;
|
||||
}
|
||||
|
||||
__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_prci) = {
|
||||
.get_reg = __metal_driver_sifive_fe310_g000_prci_get_reg,
|
||||
.set_reg = __metal_driver_sifive_fe310_g000_prci_set_reg,
|
||||
};
|
||||
|
||||
#endif /* METAL_SIFIVE_FE310_G000_PRCI */
|
||||
|
||||
typedef int no_empty_translation_units;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue