创建,添加sdram
This commit is contained in:
commit
90c74bc31a
2944 files changed
+1430509
No files matched your search
@@ -0,0 +1,44 @@
|
||||
# RT-Thread building script for component
|
||||
|
||||
from building import *
|
||||
|
||||
Import('rtconfig')
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Split('''
|
||||
cache.c
|
||||
cpuport.c
|
||||
gtimer.c
|
||||
mmu.c
|
||||
pmu.c
|
||||
stack.c
|
||||
backtrace.c
|
||||
''')
|
||||
CPPPATH = [cwd]
|
||||
|
||||
if GetDepend('RT_USING_GIC_V2'):
|
||||
src += ['interrupt.c']
|
||||
src += ['gic.c']
|
||||
src += ['trap.c']
|
||||
|
||||
if GetDepend('RT_USING_GIC_V3'):
|
||||
src += ['interrupt.c']
|
||||
src += ['gicv3.c']
|
||||
src += ['trap.c']
|
||||
|
||||
if rtconfig.PLATFORM in ['armcc', 'armclang']:
|
||||
src += Glob('*_rvds.S')
|
||||
|
||||
if rtconfig.PLATFORM in ['gcc']:
|
||||
src += Glob('*_init.S')
|
||||
src += Glob('*_gcc.S')
|
||||
|
||||
if rtconfig.PLATFORM in ['iccarm']:
|
||||
src += Glob('*_iar.S')
|
||||
|
||||
if rtconfig.PLATFORM in ['iccarm']:
|
||||
src += Glob('*_iar.S')
|
||||
|
||||
group = DefineGroup('libcpu', src, depend = [''], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023 RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2023-03-12 WangXiaoyao the first version
|
||||
*/
|
||||
#ifndef __ASM_GENERIC_H__
|
||||
#define __ASM_GENERIC_H__
|
||||
|
||||
#define START_POINT(funcname) \
|
||||
.type funcname, %function; \
|
||||
.global funcname; \
|
||||
funcname: \
|
||||
.cfi_sections .debug_frame, .eh_frame; \
|
||||
.cfi_startproc; \
|
||||
.cfi_undefined lr
|
||||
|
||||
#define START_POINT_END(name) \
|
||||
.cfi_endproc; \
|
||||
.size name, .-name;
|
||||
|
||||
#endif /* __ASM_GENERIC_H__ */
|
||||
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2019-03-29 Jesven the first version
|
||||
*/
|
||||
|
||||
#ifndef __CHECKER__
|
||||
#if !defined (__ARM_EABI__)
|
||||
#warning Your compiler does not have EABI support.
|
||||
#warning ARM unwind is known to compile only with EABI compilers.
|
||||
#warning Change compiler or disable ARM_UNWIND option.
|
||||
#elif (__GNUC__ == 4 && __GNUC_MINOR__ <= 2) && !defined(__clang__)
|
||||
#warning Your compiler is too buggy; it is known to not compile ARM unwind support.
|
||||
#warning Change compiler or disable ARM_UNWIND option.
|
||||
#endif
|
||||
#endif /* __CHECKER__ */
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rthw.h>
|
||||
#include <backtrace.h>
|
||||
|
||||
#define DBG_TAG "BACKTRACE"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
#include <lwp.h>
|
||||
#include <lwp_user_mm.h>
|
||||
#include <lwp_arch.h>
|
||||
#endif
|
||||
|
||||
rt_inline void arm_get_current_stackframe(struct pt_regs *regs, struct stackframe *frame)
|
||||
{
|
||||
frame->fp = frame_pointer(regs);
|
||||
frame->sp = regs->ARM_sp;
|
||||
frame->lr = regs->ARM_lr;
|
||||
frame->pc = regs->ARM_pc;
|
||||
}
|
||||
|
||||
struct unwind_ctrl_block {
|
||||
unsigned long vrs[16]; /* virtual register set */
|
||||
const unsigned long *insn; /* pointer to the current instructions word */
|
||||
unsigned long sp_high; /* highest value of sp allowed */
|
||||
/*
|
||||
* 1 : check for stack overflow for each register pop.
|
||||
* 0 : save overhead if there is plenty of stack remaining.
|
||||
*/
|
||||
int check_each_pop;
|
||||
int entries; /* number of entries left to interpret */
|
||||
int byte; /* current byte number in the instructions word */
|
||||
};
|
||||
|
||||
enum regs
|
||||
{
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
FP = 7,
|
||||
#else
|
||||
FP = 11,
|
||||
#endif
|
||||
SP = 13,
|
||||
LR = 14,
|
||||
PC = 15
|
||||
};
|
||||
|
||||
static int core_kernel_text(unsigned long addr)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Convert a prel31 symbol to an absolute address */
|
||||
#define prel31_to_addr(ptr) \
|
||||
({ \
|
||||
/* sign-extend to 32 bits */ \
|
||||
long offset = (((long)*(ptr)) << 1) >> 1; \
|
||||
(unsigned long)(ptr) + offset; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Binary search in the unwind index. The entries are
|
||||
* guaranteed to be sorted in ascending order by the linker.
|
||||
*
|
||||
* start = first entry
|
||||
* origin = first entry with positive offset (or stop if there is no such entry)
|
||||
* stop - 1 = last entry
|
||||
*/
|
||||
static const struct unwind_idx *search_index(unsigned long addr,
|
||||
const struct unwind_idx *start,
|
||||
const struct unwind_idx *origin,
|
||||
const struct unwind_idx *stop)
|
||||
{
|
||||
unsigned long addr_prel31;
|
||||
|
||||
LOG_D("%s(%08lx, %x, %x, %x)",
|
||||
__func__, addr, start, origin, stop);
|
||||
|
||||
/*
|
||||
* only search in the section with the matching sign. This way the
|
||||
* prel31 numbers can be compared as unsigned longs.
|
||||
*/
|
||||
if (addr < (unsigned long)start)
|
||||
/* negative offsets: [start; origin) */
|
||||
stop = origin;
|
||||
else
|
||||
/* positive offsets: [origin; stop) */
|
||||
start = origin;
|
||||
|
||||
/* prel31 for address relavive to start */
|
||||
addr_prel31 = (addr - (unsigned long)start) & 0x7fffffff;
|
||||
|
||||
while (start < stop - 1)
|
||||
{
|
||||
const struct unwind_idx *mid = start + ((stop - start) >> 1);
|
||||
|
||||
/*
|
||||
* As addr_prel31 is relative to start an offset is needed to
|
||||
* make it relative to mid.
|
||||
*/
|
||||
if (addr_prel31 - ((unsigned long)mid - (unsigned long)start) <
|
||||
mid->addr_offset)
|
||||
stop = mid;
|
||||
else
|
||||
{
|
||||
/* keep addr_prel31 relative to start */
|
||||
addr_prel31 -= ((unsigned long)mid -
|
||||
(unsigned long)start);
|
||||
start = mid;
|
||||
}
|
||||
}
|
||||
|
||||
if (start->addr_offset <= addr_prel31)
|
||||
return start;
|
||||
else
|
||||
{
|
||||
LOG_W("unwind: Unknown symbol address %08lx", addr);
|
||||
return RT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct unwind_idx *unwind_find_origin(
|
||||
const struct unwind_idx *start, const struct unwind_idx *stop)
|
||||
{
|
||||
LOG_D("%s(%x, %x)", __func__, start, stop);
|
||||
while (start < stop)
|
||||
{
|
||||
const struct unwind_idx *mid = start + ((stop - start) >> 1);
|
||||
|
||||
if (mid->addr_offset >= 0x40000000)
|
||||
/* negative offset */
|
||||
start = mid + 1;
|
||||
else
|
||||
/* positive offset */
|
||||
stop = mid;
|
||||
}
|
||||
LOG_D("%s -> %x", __func__, stop);
|
||||
return stop;
|
||||
}
|
||||
|
||||
static const struct unwind_idx *unwind_find_idx(unsigned long addr, const struct unwind_idx **origin_idx, const struct unwind_idx exidx_start[], const struct unwind_idx exidx_end[])
|
||||
{
|
||||
const struct unwind_idx *idx = RT_NULL;
|
||||
|
||||
LOG_D("%s(%08lx)", __func__, addr);
|
||||
|
||||
if (core_kernel_text(addr))
|
||||
{
|
||||
if (!*origin_idx)
|
||||
*origin_idx =
|
||||
unwind_find_origin(exidx_start,
|
||||
exidx_end);
|
||||
|
||||
/* main unwind table */
|
||||
idx = search_index(addr, exidx_start,
|
||||
*origin_idx,
|
||||
exidx_end);
|
||||
}
|
||||
|
||||
LOG_D("%s: idx = %x", __func__, idx);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static unsigned long unwind_get_byte(struct unwind_ctrl_block *ctrl)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
if (ctrl->entries <= 0)
|
||||
{
|
||||
LOG_W("unwind: Corrupt unwind table");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = (*ctrl->insn >> (ctrl->byte * 8)) & 0xff;
|
||||
|
||||
if (ctrl->byte == 0)
|
||||
{
|
||||
ctrl->insn++;
|
||||
ctrl->entries--;
|
||||
ctrl->byte = 3;
|
||||
}
|
||||
else
|
||||
ctrl->byte--;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Before poping a register check whether it is feasible or not */
|
||||
static int unwind_pop_register(struct unwind_ctrl_block *ctrl,
|
||||
unsigned long **vsp, unsigned int reg)
|
||||
{
|
||||
if (ctrl->check_each_pop)
|
||||
if (*vsp >= (unsigned long *)ctrl->sp_high)
|
||||
return -URC_FAILURE;
|
||||
|
||||
ctrl->vrs[reg] = *(*vsp)++;
|
||||
return URC_OK;
|
||||
}
|
||||
|
||||
/* Helper functions to execute the instructions */
|
||||
static int unwind_exec_pop_subset_r4_to_r13(struct unwind_ctrl_block *ctrl,
|
||||
unsigned long mask)
|
||||
{
|
||||
unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
|
||||
int load_sp, reg = 4;
|
||||
|
||||
load_sp = mask & (1 << (13 - 4));
|
||||
while (mask)
|
||||
{
|
||||
if (mask & 1)
|
||||
if (unwind_pop_register(ctrl, &vsp, reg))
|
||||
return -URC_FAILURE;
|
||||
mask >>= 1;
|
||||
reg++;
|
||||
}
|
||||
if (!load_sp)
|
||||
ctrl->vrs[SP] = (unsigned long)vsp;
|
||||
|
||||
return URC_OK;
|
||||
}
|
||||
|
||||
static int unwind_exec_pop_r4_to_rN(struct unwind_ctrl_block *ctrl,
|
||||
unsigned long insn)
|
||||
{
|
||||
unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
|
||||
int reg;
|
||||
|
||||
/* pop R4-R[4+bbb] */
|
||||
for (reg = 4; reg <= 4 + (insn & 7); reg++)
|
||||
if (unwind_pop_register(ctrl, &vsp, reg))
|
||||
return -URC_FAILURE;
|
||||
|
||||
if (insn & 0x8)
|
||||
if (unwind_pop_register(ctrl, &vsp, 14))
|
||||
return -URC_FAILURE;
|
||||
|
||||
ctrl->vrs[SP] = (unsigned long)vsp;
|
||||
|
||||
return URC_OK;
|
||||
}
|
||||
|
||||
static int unwind_exec_pop_subset_r0_to_r3(struct unwind_ctrl_block *ctrl,
|
||||
unsigned long mask)
|
||||
{
|
||||
unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
|
||||
int reg = 0;
|
||||
|
||||
/* pop R0-R3 according to mask */
|
||||
while (mask)
|
||||
{
|
||||
if (mask & 1)
|
||||
if (unwind_pop_register(ctrl, &vsp, reg))
|
||||
return -URC_FAILURE;
|
||||
mask >>= 1;
|
||||
reg++;
|
||||
}
|
||||
ctrl->vrs[SP] = (unsigned long)vsp;
|
||||
|
||||
return URC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the current unwind instruction.
|
||||
*/
|
||||
static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
|
||||
{
|
||||
unsigned long insn = unwind_get_byte(ctrl);
|
||||
int ret = URC_OK;
|
||||
|
||||
LOG_D("%s: insn = %08lx", __func__, insn);
|
||||
|
||||
if ((insn & 0xc0) == 0x00)
|
||||
ctrl->vrs[SP] += ((insn & 0x3f) << 2) + 4;
|
||||
else if ((insn & 0xc0) == 0x40)
|
||||
ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4;
|
||||
else if ((insn & 0xf0) == 0x80)
|
||||
{
|
||||
unsigned long mask;
|
||||
|
||||
insn = (insn << 8) | unwind_get_byte(ctrl);
|
||||
mask = insn & 0x0fff;
|
||||
if (mask == 0)
|
||||
{
|
||||
LOG_W("unwind: 'Refuse to unwind' instruction %04lx",
|
||||
insn);
|
||||
return -URC_FAILURE;
|
||||
}
|
||||
|
||||
ret = unwind_exec_pop_subset_r4_to_r13(ctrl, mask);
|
||||
if (ret)
|
||||
goto error;
|
||||
}
|
||||
else if ((insn & 0xf0) == 0x90 &&
|
||||
(insn & 0x0d) != 0x0d)
|
||||
ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f];
|
||||
else if ((insn & 0xf0) == 0xa0)
|
||||
{
|
||||
ret = unwind_exec_pop_r4_to_rN(ctrl, insn);
|
||||
if (ret)
|
||||
goto error;
|
||||
}
|
||||
else if (insn == 0xb0)
|
||||
{
|
||||
if (ctrl->vrs[PC] == 0)
|
||||
ctrl->vrs[PC] = ctrl->vrs[LR];
|
||||
/* no further processing */
|
||||
ctrl->entries = 0;
|
||||
}
|
||||
else if (insn == 0xb1)
|
||||
{
|
||||
unsigned long mask = unwind_get_byte(ctrl);
|
||||
|
||||
if (mask == 0 || mask & 0xf0)
|
||||
{
|
||||
LOG_W("unwind: Spare encoding %04lx",
|
||||
(insn << 8) | mask);
|
||||
return -URC_FAILURE;
|
||||
}
|
||||
|
||||
ret = unwind_exec_pop_subset_r0_to_r3(ctrl, mask);
|
||||
if (ret)
|
||||
goto error;
|
||||
}
|
||||
else if (insn == 0xb2)
|
||||
{
|
||||
unsigned long uleb128 = unwind_get_byte(ctrl);
|
||||
|
||||
ctrl->vrs[SP] += 0x204 + (uleb128 << 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_W("unwind: Unhandled instruction %02lx", insn);
|
||||
return -URC_FAILURE;
|
||||
}
|
||||
|
||||
LOG_D("%s: fp = %08lx sp = %08lx lr = %08lx pc = %08lx", __func__,
|
||||
ctrl->vrs[FP], ctrl->vrs[SP], ctrl->vrs[LR], ctrl->vrs[PC]);
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef RT_BACKTRACE_FUNCTION_NAME
|
||||
static char *unwind_get_function_name(void *address)
|
||||
{
|
||||
uint32_t flag_word = *(uint32_t *)((char*)address - 4);
|
||||
|
||||
if ((flag_word & 0xff000000) == 0xff000000)
|
||||
{
|
||||
return (char *)((char*)address - 4 - (flag_word & 0x00ffffff));
|
||||
}
|
||||
return RT_NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Unwind a single frame starting with *sp for the symbol at *pc. It
|
||||
* updates the *pc and *sp with the new values.
|
||||
*/
|
||||
int unwind_frame(struct stackframe *frame, const struct unwind_idx **origin_idx, const struct unwind_idx exidx_start[], const struct unwind_idx exidx_end[])
|
||||
{
|
||||
unsigned long low;
|
||||
const struct unwind_idx *idx;
|
||||
struct unwind_ctrl_block ctrl;
|
||||
struct rt_thread *rt_c_thread;
|
||||
|
||||
/* store the highest address on the stack to avoid crossing it*/
|
||||
low = frame->sp;
|
||||
rt_c_thread = rt_thread_self();
|
||||
ctrl.sp_high = (unsigned long)((char*)rt_c_thread->stack_addr + rt_c_thread->stack_size);
|
||||
|
||||
LOG_D("%s(pc = %08lx lr = %08lx sp = %08lx)", __func__,
|
||||
frame->pc, frame->lr, frame->sp);
|
||||
|
||||
idx = unwind_find_idx(frame->pc, origin_idx, exidx_start, exidx_end);
|
||||
if (!idx)
|
||||
{
|
||||
LOG_W("unwind: Index not found %08lx", frame->pc);
|
||||
return -URC_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef RT_BACKTRACE_FUNCTION_NAME
|
||||
{
|
||||
char *fun_name;
|
||||
fun_name = unwind_get_function_name((void *)prel31_to_addr(&idx->addr_offset));
|
||||
if (fun_name)
|
||||
{
|
||||
rt_kprintf("0x%08x @ %s\n", frame->pc, fun_name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ctrl.vrs[FP] = frame->fp;
|
||||
ctrl.vrs[SP] = frame->sp;
|
||||
ctrl.vrs[LR] = frame->lr;
|
||||
ctrl.vrs[PC] = 0;
|
||||
|
||||
if (idx->insn == 1)
|
||||
/* can't unwind */
|
||||
return -URC_FAILURE;
|
||||
else if ((idx->insn & 0x80000000) == 0)
|
||||
/* prel31 to the unwind table */
|
||||
ctrl.insn = (unsigned long *)prel31_to_addr(&idx->insn);
|
||||
else if ((idx->insn & 0xff000000) == 0x80000000)
|
||||
/* only personality routine 0 supported in the index */
|
||||
ctrl.insn = &idx->insn;
|
||||
else
|
||||
{
|
||||
LOG_W("unwind: Unsupported personality routine %08lx in the index at %x",
|
||||
idx->insn, idx);
|
||||
return -URC_FAILURE;
|
||||
}
|
||||
|
||||
/* check the personality routine */
|
||||
if ((*ctrl.insn & 0xff000000) == 0x80000000)
|
||||
{
|
||||
ctrl.byte = 2;
|
||||
ctrl.entries = 1;
|
||||
}
|
||||
else if ((*ctrl.insn & 0xff000000) == 0x81000000)
|
||||
{
|
||||
ctrl.byte = 1;
|
||||
ctrl.entries = 1 + ((*ctrl.insn & 0x00ff0000) >> 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_W("unwind: Unsupported personality routine %08lx at %x",
|
||||
*ctrl.insn, ctrl.insn);
|
||||
return -URC_FAILURE;
|
||||
}
|
||||
|
||||
ctrl.check_each_pop = 0;
|
||||
|
||||
while (ctrl.entries > 0)
|
||||
{
|
||||
int urc;
|
||||
if ((ctrl.sp_high - ctrl.vrs[SP]) < sizeof(ctrl.vrs))
|
||||
ctrl.check_each_pop = 1;
|
||||
urc = unwind_exec_insn(&ctrl);
|
||||
if (urc < 0)
|
||||
return urc;
|
||||
if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= ctrl.sp_high)
|
||||
return -URC_FAILURE;
|
||||
}
|
||||
|
||||
if (ctrl.vrs[PC] == 0)
|
||||
ctrl.vrs[PC] = ctrl.vrs[LR];
|
||||
|
||||
/* check for infinite loop */
|
||||
if (frame->pc == ctrl.vrs[PC])
|
||||
return -URC_FAILURE;
|
||||
|
||||
frame->fp = ctrl.vrs[FP];
|
||||
frame->sp = ctrl.vrs[SP];
|
||||
frame->lr = ctrl.vrs[LR];
|
||||
frame->pc = ctrl.vrs[PC];
|
||||
|
||||
return URC_OK;
|
||||
}
|
||||
|
||||
void unwind_backtrace(struct pt_regs *regs, const struct unwind_idx exidx_start[], const struct unwind_idx exidx_end[])
|
||||
{
|
||||
struct stackframe frame;
|
||||
const struct unwind_idx *origin_idx = RT_NULL;
|
||||
|
||||
LOG_D("%s(regs = %x)", __func__, regs);
|
||||
|
||||
arm_get_current_stackframe(regs, &frame);
|
||||
|
||||
#ifndef RT_BACKTRACE_FUNCTION_NAME
|
||||
rt_kprintf("please use: addr2line -e rtthread.elf -a -f %08x", frame.pc);
|
||||
#endif
|
||||
LOG_D("pc = %08x, sp = %08x", frame.pc, frame.sp);
|
||||
|
||||
while (1)
|
||||
{
|
||||
int urc;
|
||||
|
||||
urc = unwind_frame(&frame, &origin_idx, exidx_start, exidx_end);
|
||||
if (urc < 0)
|
||||
break;
|
||||
//dump_backtrace_entry(where, frame.pc, frame.sp - 4);
|
||||
#ifndef RT_BACKTRACE_FUNCTION_NAME
|
||||
rt_kprintf(" %08x", frame.pc);
|
||||
#endif
|
||||
LOG_D("from: pc = %08x, frame = %08x", frame.pc, frame.sp - 4);
|
||||
}
|
||||
rt_kprintf("\n");
|
||||
}
|
||||
|
||||
extern const struct unwind_idx __exidx_start[];
|
||||
extern const struct unwind_idx __exidx_end[];
|
||||
|
||||
void rt_unwind(struct rt_hw_exp_stack *regs, unsigned int pc_adj)
|
||||
{
|
||||
struct pt_regs e_regs;
|
||||
|
||||
e_regs.ARM_fp = regs->fp;
|
||||
e_regs.ARM_sp = regs->sp;
|
||||
e_regs.ARM_lr = regs->lr;
|
||||
e_regs.ARM_pc = regs->pc - pc_adj;
|
||||
#ifdef RT_USING_SMART
|
||||
if (!lwp_user_accessable((void *)e_regs.ARM_pc, sizeof (void *)))
|
||||
{
|
||||
e_regs.ARM_pc = regs->lr - sizeof(void *);
|
||||
}
|
||||
#endif
|
||||
rt_kprintf("backtrace:\n");
|
||||
unwind_backtrace(&e_regs, __exidx_start, __exidx_end);
|
||||
}
|
||||
|
||||
rt_err_t rt_backtrace(void)
|
||||
{
|
||||
struct rt_hw_exp_stack regs;
|
||||
|
||||
asm volatile ("mov %0, fp":"=r"(regs.fp));
|
||||
asm volatile ("mov %0, sp":"=r"(regs.sp));
|
||||
asm volatile ("mov %0, lr":"=r"(regs.lr));
|
||||
asm volatile ("mov %0, pc":"=r"(regs.pc));
|
||||
rt_unwind(®s, 8);
|
||||
return RT_EOK;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifndef __BACKTRACE_H
|
||||
#define __BACKTRACE_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#include <rtthread.h>
|
||||
#include <cpuport.h>
|
||||
|
||||
/* Unwind reason code according the the ARM EABI documents */
|
||||
enum unwind_reason_code
|
||||
{
|
||||
URC_OK = 0, /* operation completed successfully */
|
||||
URC_CONTINUE_UNWIND = 8,
|
||||
URC_FAILURE = 9 /* unspecified failure of some kind */
|
||||
};
|
||||
|
||||
struct unwind_idx
|
||||
{
|
||||
unsigned long addr_offset;
|
||||
unsigned long insn;
|
||||
};
|
||||
|
||||
struct unwind_table
|
||||
{
|
||||
const struct unwind_idx *start;
|
||||
const struct unwind_idx *origin;
|
||||
const struct unwind_idx *stop;
|
||||
unsigned long begin_addr;
|
||||
unsigned long end_addr;
|
||||
};
|
||||
|
||||
struct stackframe
|
||||
{
|
||||
/*
|
||||
* FP member should hold R7 when CONFIG_THUMB2_KERNEL is enabled
|
||||
* and R11 otherwise.
|
||||
*/
|
||||
unsigned long fp;
|
||||
unsigned long sp;
|
||||
unsigned long lr;
|
||||
unsigned long pc;
|
||||
};
|
||||
|
||||
struct pt_regs
|
||||
{
|
||||
unsigned long uregs[18];
|
||||
};
|
||||
|
||||
#define ARM_cpsr uregs[16]
|
||||
#define ARM_pc uregs[15]
|
||||
#define ARM_lr uregs[14]
|
||||
#define ARM_sp uregs[13]
|
||||
#define ARM_ip uregs[12]
|
||||
#define ARM_fp uregs[11]
|
||||
#define ARM_r10 uregs[10]
|
||||
#define ARM_r9 uregs[9]
|
||||
#define ARM_r8 uregs[8]
|
||||
#define ARM_r7 uregs[7]
|
||||
#define ARM_r6 uregs[6]
|
||||
#define ARM_r5 uregs[5]
|
||||
#define ARM_r4 uregs[4]
|
||||
#define ARM_r3 uregs[3]
|
||||
#define ARM_r2 uregs[2]
|
||||
#define ARM_r1 uregs[1]
|
||||
#define ARM_r0 uregs[0]
|
||||
#define ARM_ORIG_r0 uregs[17]
|
||||
|
||||
#define instruction_pointer(regs) (regs)->ARM_pc
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
#define frame_pointer(regs) (regs)->ARM_r7
|
||||
#else
|
||||
#define frame_pointer(regs) (regs)->ARM_fp
|
||||
#endif
|
||||
|
||||
int unwind_frame(struct stackframe *frame, const struct unwind_idx **origin_idx, const struct unwind_idx exidx_start[], const struct unwind_idx exidx_end[]);
|
||||
void unwind_backtrace(struct pt_regs *regs, const struct unwind_idx exidx_start[], const struct unwind_idx exidx_end[]);
|
||||
|
||||
void rt_unwind(struct rt_hw_exp_stack *regs, unsigned int pc_adj);
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
#endif /* __BACKTRACE_H */
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2019-03-29 quanzhao the first version
|
||||
*/
|
||||
#include <rthw.h>
|
||||
#include <rtdef.h>
|
||||
|
||||
rt_inline rt_uint32_t rt_cpu_icache_line_size(void)
|
||||
{
|
||||
rt_uint32_t ctr;
|
||||
asm volatile ("mrc p15, 0, %0, c0, c0, 1" : "=r"(ctr));
|
||||
return 4 << (ctr & 0xF);
|
||||
}
|
||||
|
||||
rt_inline rt_uint32_t rt_cpu_dcache_line_size(void)
|
||||
{
|
||||
rt_uint32_t ctr;
|
||||
asm volatile ("mrc p15, 0, %0, c0, c0, 1" : "=r"(ctr));
|
||||
return 4 << ((ctr >> 16) & 0xF);
|
||||
}
|
||||
|
||||
void rt_hw_cpu_icache_invalidate(void *addr, int size)
|
||||
{
|
||||
rt_uint32_t line_size = rt_cpu_icache_line_size();
|
||||
rt_uint32_t start_addr = (rt_uint32_t)addr;
|
||||
rt_uint32_t end_addr = (rt_uint32_t) addr + size + line_size - 1;
|
||||
|
||||
asm volatile ("dmb":::"memory");
|
||||
start_addr &= ~(line_size - 1);
|
||||
end_addr &= ~(line_size - 1);
|
||||
while (start_addr < end_addr)
|
||||
{
|
||||
asm volatile ("mcr p15, 0, %0, c7, c5, 1" :: "r"(start_addr)); /* icimvau */
|
||||
start_addr += line_size;
|
||||
}
|
||||
asm volatile ("dsb\n\tisb":::"memory");
|
||||
}
|
||||
|
||||
void rt_hw_cpu_dcache_invalidate(void *addr, int size)
|
||||
{
|
||||
rt_uint32_t line_size = rt_cpu_dcache_line_size();
|
||||
rt_uint32_t start_addr = (rt_uint32_t)addr;
|
||||
rt_uint32_t end_addr = (rt_uint32_t) addr + size + line_size - 1;
|
||||
|
||||
asm volatile ("dmb":::"memory");
|
||||
start_addr &= ~(line_size - 1);
|
||||
end_addr &= ~(line_size - 1);
|
||||
while (start_addr < end_addr)
|
||||
{
|
||||
asm volatile ("mcr p15, 0, %0, c7, c6, 1" :: "r"(start_addr)); /* dcimvac */
|
||||
start_addr += line_size;
|
||||
}
|
||||
asm volatile ("dsb":::"memory");
|
||||
}
|
||||
|
||||
void rt_hw_cpu_dcache_inv_range(void *addr, int size)
|
||||
{
|
||||
rt_uint32_t line_size = rt_cpu_dcache_line_size();
|
||||
rt_uint32_t start_addr = (rt_uint32_t)addr;
|
||||
rt_uint32_t end_addr = (rt_uint32_t)addr + size;
|
||||
|
||||
asm volatile ("dmb":::"memory");
|
||||
|
||||
if ((start_addr & (line_size - 1)) != 0)
|
||||
{
|
||||
start_addr &= ~(line_size - 1);
|
||||
asm volatile ("mcr p15, 0, %0, c7, c14, 1" :: "r"(start_addr));
|
||||
start_addr += line_size;
|
||||
asm volatile ("dsb":::"memory");
|
||||
}
|
||||
|
||||
if ((end_addr & (line_size - 1)) != 0)
|
||||
{
|
||||
end_addr &= ~(line_size - 1);
|
||||
asm volatile ("mcr p15, 0, %0, c7, c14, 1" :: "r"(end_addr));
|
||||
asm volatile ("dsb":::"memory");
|
||||
}
|
||||
|
||||
while (start_addr < end_addr)
|
||||
{
|
||||
asm volatile ("mcr p15, 0, %0, c7, c6, 1" :: "r"(start_addr)); /* dcimvac */
|
||||
start_addr += line_size;
|
||||
}
|
||||
asm volatile ("dsb":::"memory");
|
||||
}
|
||||
|
||||
void rt_hw_cpu_dcache_clean(void *addr, int size)
|
||||
{
|
||||
rt_uint32_t line_size = rt_cpu_dcache_line_size();
|
||||
rt_uint32_t start_addr = (rt_uint32_t)addr;
|
||||
rt_uint32_t end_addr = (rt_uint32_t) addr + size + line_size - 1;
|
||||
|
||||
asm volatile ("dmb":::"memory");
|
||||
start_addr &= ~(line_size - 1);
|
||||
end_addr &= ~(line_size - 1);
|
||||
while (start_addr < end_addr)
|
||||
{
|
||||
asm volatile ("mcr p15, 0, %0, c7, c10, 1" :: "r"(start_addr)); /* dccmvac */
|
||||
start_addr += line_size;
|
||||
}
|
||||
asm volatile ("dsb":::"memory");
|
||||
}
|
||||
|
||||
void rt_hw_cpu_dcache_clean_and_invalidate(void *addr, int size)
|
||||
{
|
||||
rt_uint32_t line_size = rt_cpu_dcache_line_size();
|
||||
rt_uint32_t start_addr = (rt_uint32_t)addr;
|
||||
rt_uint32_t end_addr = (rt_uint32_t) addr + size + line_size - 1;
|
||||
|
||||
asm volatile ("dmb":::"memory");
|
||||
start_addr &= ~(line_size - 1);
|
||||
end_addr &= ~(line_size - 1);
|
||||
while (start_addr < end_addr)
|
||||
{
|
||||
asm volatile ("mcr p15, 0, %0, c7, c10, 1" :: "r"(start_addr)); /* dccmvac */
|
||||
asm volatile ("mcr p15, 0, %0, c7, c6, 1" :: "r"(start_addr)); /* dcimvac */
|
||||
start_addr += line_size;
|
||||
}
|
||||
asm volatile ("dsb":::"memory");
|
||||
}
|
||||
|
||||
void rt_hw_cpu_icache_ops(int ops, void *addr, int size)
|
||||
{
|
||||
if (ops == RT_HW_CACHE_INVALIDATE)
|
||||
{
|
||||
rt_hw_cpu_icache_invalidate(addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
void rt_hw_cpu_dcache_ops(int ops, void *addr, int size)
|
||||
{
|
||||
if (ops == RT_HW_CACHE_FLUSH)
|
||||
{
|
||||
rt_hw_cpu_dcache_clean(addr, size);
|
||||
}
|
||||
else if (ops == RT_HW_CACHE_INVALIDATE)
|
||||
{
|
||||
rt_hw_cpu_dcache_invalidate(addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
rt_base_t rt_hw_cpu_icache_status(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
rt_base_t rt_hw_cpu_dcache_status(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2019-03-29 quanzhao the first version
|
||||
*/
|
||||
|
||||
#ifndef __CACHE_H__
|
||||
#define __CACHE_H__
|
||||
|
||||
void rt_hw_cpu_icache_invalidate(void *addr, int size);
|
||||
void rt_hw_cpu_dcache_clean_and_invalidate(void *addr, int size);
|
||||
|
||||
static inline void rt_hw_icache_invalidate_all(void)
|
||||
{
|
||||
__asm__ volatile("mcr p15, 0, %0, c7, c5, 0"::"r"(0ul));
|
||||
}
|
||||
|
||||
#endif /* __CACHE_H__ */
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-05 Bernard the first version
|
||||
*/
|
||||
|
||||
#include "rtconfig.h"
|
||||
.section .text, "ax"
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
#define rt_hw_interrupt_disable rt_hw_local_irq_disable
|
||||
#define rt_hw_interrupt_enable rt_hw_local_irq_enable
|
||||
#endif
|
||||
|
||||
/*
|
||||
* rt_base_t rt_hw_interrupt_disable();
|
||||
*/
|
||||
.globl rt_hw_interrupt_disable
|
||||
rt_hw_interrupt_disable:
|
||||
mrs r0, cpsr
|
||||
cpsid i
|
||||
bx lr
|
||||
|
||||
/*
|
||||
* void rt_hw_interrupt_enable(rt_base_t level);
|
||||
*/
|
||||
.globl rt_hw_interrupt_enable
|
||||
rt_hw_interrupt_enable:
|
||||
msr cpsr, r0
|
||||
bx lr
|
||||
|
||||
/*
|
||||
* void rt_hw_context_switch_to(rt_uint32 to, struct rt_thread *to_thread);
|
||||
* r0 --> to (thread stack)
|
||||
* r1 --> to_thread
|
||||
*/
|
||||
.globl rt_hw_context_switch_to
|
||||
rt_hw_context_switch_to:
|
||||
clrex
|
||||
ldr sp, [r0] @ get new task stack pointer
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
mov r0, r1
|
||||
bl rt_cpus_lock_status_restore
|
||||
#ifdef RT_USING_SMART
|
||||
bl rt_thread_self
|
||||
bl lwp_user_setting_restore
|
||||
#endif
|
||||
#else
|
||||
#ifdef RT_USING_SMART
|
||||
bl rt_thread_self
|
||||
mov r4, r0
|
||||
bl lwp_aspace_switch
|
||||
mov r0, r4
|
||||
bl lwp_user_setting_restore
|
||||
#endif
|
||||
#endif /*RT_USING_SMP*/
|
||||
|
||||
b rt_hw_context_switch_exit
|
||||
|
||||
.section .bss.share.isr
|
||||
_guest_switch_lvl:
|
||||
.word 0
|
||||
|
||||
.globl vmm_virq_update
|
||||
|
||||
.section .text.isr, "ax"
|
||||
/*
|
||||
* void rt_hw_context_switch(rt_uint32 from, rt_uint32 to, struct rt_thread *to_thread);
|
||||
* r0 --> from (from_thread stack)
|
||||
* r1 --> to (to_thread stack)
|
||||
* r2 --> to_thread
|
||||
*/
|
||||
.globl rt_hw_context_switch
|
||||
rt_hw_context_switch:
|
||||
clrex
|
||||
stmfd sp!, {lr} @ push pc (lr should be pushed in place of PC)
|
||||
stmfd sp!, {r0-r12, lr} @ push lr & register file
|
||||
|
||||
mrs r4, cpsr
|
||||
tst lr, #0x01
|
||||
orrne r4, r4, #0x20 @ it's thumb code
|
||||
|
||||
stmfd sp!, {r4} @ push cpsr
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
stmfd sp, {r13, r14}^ @ push usr_sp usr_lr
|
||||
sub sp, #8
|
||||
#endif
|
||||
#ifdef RT_USING_FPU
|
||||
/* fpu context */
|
||||
vmrs r6, fpexc
|
||||
tst r6, #(1<<30)
|
||||
beq 1f
|
||||
vstmdb sp!, {d0-d15}
|
||||
vstmdb sp!, {d16-d31}
|
||||
vmrs r5, fpscr
|
||||
stmfd sp!, {r5}
|
||||
1:
|
||||
stmfd sp!, {r6}
|
||||
#endif
|
||||
|
||||
str sp, [r0] @ store sp in preempted tasks TCB
|
||||
ldr sp, [r1] @ get new task stack pointer
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
mov r0, r2
|
||||
bl rt_cpus_lock_status_restore
|
||||
#ifdef RT_USING_SMART
|
||||
bl rt_thread_self
|
||||
bl lwp_user_setting_restore
|
||||
#endif
|
||||
#else
|
||||
#ifdef RT_USING_SMART
|
||||
bl rt_thread_self
|
||||
mov r4, r0
|
||||
bl lwp_aspace_switch
|
||||
mov r0, r4
|
||||
bl lwp_user_setting_restore
|
||||
#endif
|
||||
#endif /*RT_USING_SMP*/
|
||||
|
||||
b rt_hw_context_switch_exit
|
||||
|
||||
/*
|
||||
* void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to);
|
||||
*/
|
||||
.equ Mode_USR, 0x10
|
||||
.equ Mode_FIQ, 0x11
|
||||
.equ Mode_IRQ, 0x12
|
||||
.equ Mode_SVC, 0x13
|
||||
.equ Mode_ABT, 0x17
|
||||
.equ Mode_UND, 0x1B
|
||||
.equ Mode_SYS, 0x1F
|
||||
|
||||
.equ I_Bit, 0x80 @ when I bit is set, IRQ is disabled
|
||||
.equ F_Bit, 0x40 @ when F bit is set, FIQ is disabled
|
||||
|
||||
.globl rt_thread_switch_interrupt_flag
|
||||
.globl rt_interrupt_from_thread
|
||||
.globl rt_interrupt_to_thread
|
||||
.globl rt_hw_context_switch_interrupt
|
||||
rt_hw_context_switch_interrupt:
|
||||
clrex
|
||||
#ifdef RT_USING_SMP
|
||||
/* r0 :svc_mod context
|
||||
* r1 :addr of from_thread's sp
|
||||
* r2 :addr of to_thread's sp
|
||||
* r3 :to_thread's tcb
|
||||
*/
|
||||
#ifdef RT_USING_SMART
|
||||
push {r0 - r3, lr}
|
||||
#ifdef RT_USING_SMART
|
||||
bl rt_thread_self
|
||||
bl lwp_user_setting_save
|
||||
#endif
|
||||
pop {r0 - r3, lr}
|
||||
#endif
|
||||
str r0, [r1]
|
||||
|
||||
ldr sp, [r2]
|
||||
mov r0, r3
|
||||
#ifdef RT_USING_SMART
|
||||
mov r4, r0
|
||||
#endif
|
||||
bl rt_cpus_lock_status_restore
|
||||
#ifdef RT_USING_SMART
|
||||
mov r0, r4
|
||||
bl lwp_user_setting_restore
|
||||
#endif
|
||||
b rt_hw_context_switch_exit
|
||||
|
||||
#else /*RT_USING_SMP*/
|
||||
/* r0 :addr of from_thread's sp
|
||||
* r1 :addr of to_thread's sp
|
||||
* r2 :from_thread's tcb
|
||||
* r3 :to_thread's tcb
|
||||
*/
|
||||
#ifdef RT_USING_SMART
|
||||
/* now to_thread(r3) not used */
|
||||
ldr ip, =rt_thread_switch_interrupt_flag
|
||||
ldr r3, [ip]
|
||||
cmp r3, #1
|
||||
beq _reswitch
|
||||
ldr r3, =rt_interrupt_from_thread @ set rt_interrupt_from_thread
|
||||
str r0, [r3]
|
||||
mov r3, #1 @ set rt_thread_switch_interrupt_flag to 1
|
||||
str r3, [ip]
|
||||
#ifdef RT_USING_SMART
|
||||
push {r1, lr}
|
||||
mov r0, r2
|
||||
bl lwp_user_setting_save
|
||||
pop {r1, lr}
|
||||
#endif
|
||||
_reswitch:
|
||||
ldr ip, =rt_interrupt_to_thread @ set rt_interrupt_to_thread
|
||||
str r1, [ip]
|
||||
bx lr
|
||||
#else
|
||||
/* now from_thread(r2) to_thread(r3) not used */
|
||||
ldr ip, =rt_thread_switch_interrupt_flag
|
||||
ldr r3, [ip]
|
||||
cmp r3, #1
|
||||
beq _reswitch
|
||||
ldr r3, =rt_interrupt_from_thread @ set rt_interrupt_from_thread
|
||||
str r0, [r3]
|
||||
mov r3, #1 @ set rt_thread_switch_interrupt_flag to 1
|
||||
str r3, [ip]
|
||||
_reswitch:
|
||||
ldr ip, =rt_interrupt_to_thread @ set rt_interrupt_to_thread
|
||||
str r1, [ip]
|
||||
bx lr
|
||||
#endif
|
||||
#endif /*RT_USING_SMP*/
|
||||
|
||||
.global rt_hw_context_switch_exit
|
||||
rt_hw_context_switch_exit:
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
#ifdef RT_USING_SIGNALS
|
||||
mov r0, sp
|
||||
cps #Mode_IRQ
|
||||
bl rt_signal_check
|
||||
cps #Mode_SVC
|
||||
mov sp, r0
|
||||
#endif
|
||||
#endif
|
||||
#ifdef RT_USING_FPU
|
||||
/* fpu context */
|
||||
ldmfd sp!, {r6}
|
||||
vmsr fpexc, r6
|
||||
tst r6, #(1<<30)
|
||||
beq 1f
|
||||
ldmfd sp!, {r5}
|
||||
vmsr fpscr, r5
|
||||
vldmia sp!, {d16-d31}
|
||||
vldmia sp!, {d0-d15}
|
||||
1:
|
||||
#endif
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
ldmfd sp, {r13, r14}^ /* usr_sp, usr_lr */
|
||||
add sp, #8
|
||||
#endif
|
||||
ldmfd sp!, {r1}
|
||||
msr spsr_cxsf, r1 /* original mode */
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
and r1, #0x1f
|
||||
cmp r1, #0x10
|
||||
bne 1f
|
||||
ldmfd sp!, {r0-r12,lr}
|
||||
ldmfd sp!, {lr}
|
||||
b arch_ret_to_user
|
||||
1:
|
||||
#endif
|
||||
ldmfd sp!, {r0-r12,lr,pc}^ /* irq return */
|
||||
|
||||
#ifdef RT_USING_FPU
|
||||
.global set_fpexc
|
||||
set_fpexc:
|
||||
vmsr fpexc, r0
|
||||
bx lr
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2018-03-25 quanzhao the first version
|
||||
*/
|
||||
#ifndef __CP15_H__
|
||||
#define __CP15_H__
|
||||
|
||||
#define __get_cp(cp, op1, Rt, CRn, CRm, op2) __asm__ volatile("MRC p" # cp ", " # op1 ", %0, c" # CRn ", c" # CRm ", " # op2 : "=r" (Rt) : : "memory" )
|
||||
#define __set_cp(cp, op1, Rt, CRn, CRm, op2) __asm__ volatile("MCR p" # cp ", " # op1 ", %0, c" # CRn ", c" # CRm ", " # op2 : : "r" (Rt) : "memory" )
|
||||
#define __get_cp64(cp, op1, Rt, CRm) __asm__ volatile("MRRC p" # cp ", " # op1 ", %Q0, %R0, c" # CRm : "=r" (Rt) : : "memory" )
|
||||
#define __set_cp64(cp, op1, Rt, CRm) __asm__ volatile("MCRR p" # cp ", " # op1 ", %Q0, %R0, c" # CRm : : "r" (Rt) : "memory" )
|
||||
|
||||
int rt_hw_cpu_id(void);
|
||||
void rt_cpu_mmu_disable(void);
|
||||
void rt_cpu_mmu_enable(void);
|
||||
void rt_cpu_tlb_set(volatile unsigned long*);
|
||||
|
||||
void rt_cpu_dcache_clean_flush(void);
|
||||
void rt_cpu_icache_flush(void);
|
||||
|
||||
void rt_cpu_vector_set_base(unsigned int addr);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-05 Bernard the first version
|
||||
*/
|
||||
|
||||
.globl rt_cpu_get_smp_id
|
||||
rt_cpu_get_smp_id:
|
||||
mrc p15, #0, r0, c0, c0, #5
|
||||
bx lr
|
||||
|
||||
.globl rt_cpu_vector_set_base
|
||||
rt_cpu_vector_set_base:
|
||||
/* clear SCTRL.V to customize the vector address */
|
||||
mrc p15, #0, r1, c1, c0, #0
|
||||
bic r1, #(1 << 13)
|
||||
mcr p15, #0, r1, c1, c0, #0
|
||||
/* set up the vector address */
|
||||
mcr p15, #0, r0, c12, c0, #0
|
||||
dsb
|
||||
bx lr
|
||||
|
||||
.globl rt_hw_cpu_dcache_enable
|
||||
rt_hw_cpu_dcache_enable:
|
||||
mrc p15, #0, r0, c1, c0, #0
|
||||
orr r0, r0, #0x00000004
|
||||
mcr p15, #0, r0, c1, c0, #0
|
||||
bx lr
|
||||
|
||||
.globl rt_hw_cpu_icache_enable
|
||||
rt_hw_cpu_icache_enable:
|
||||
mrc p15, #0, r0, c1, c0, #0
|
||||
orr r0, r0, #0x00001000
|
||||
mcr p15, #0, r0, c1, c0, #0
|
||||
bx lr
|
||||
|
||||
_FLD_MAX_WAY:
|
||||
.word 0x3ff
|
||||
_FLD_MAX_IDX:
|
||||
.word 0x7fff
|
||||
|
||||
.globl rt_cpu_dcache_clean_flush
|
||||
rt_cpu_dcache_clean_flush:
|
||||
push {r4-r11}
|
||||
dmb
|
||||
mrc p15, #1, r0, c0, c0, #1 @ read clid register
|
||||
ands r3, r0, #0x7000000 @ get level of coherency
|
||||
mov r3, r3, lsr #23
|
||||
beq finished
|
||||
mov r10, #0
|
||||
loop1:
|
||||
add r2, r10, r10, lsr #1
|
||||
mov r1, r0, lsr r2
|
||||
and r1, r1, #7
|
||||
cmp r1, #2
|
||||
blt skip
|
||||
mcr p15, #2, r10, c0, c0, #0
|
||||
isb
|
||||
mrc p15, #1, r1, c0, c0, #0
|
||||
and r2, r1, #7
|
||||
add r2, r2, #4
|
||||
ldr r4, _FLD_MAX_WAY
|
||||
ands r4, r4, r1, lsr #3
|
||||
clz r5, r4
|
||||
ldr r7, _FLD_MAX_IDX
|
||||
ands r7, r7, r1, lsr #13
|
||||
loop2:
|
||||
mov r9, r4
|
||||
loop3:
|
||||
orr r11, r10, r9, lsl r5
|
||||
orr r11, r11, r7, lsl r2
|
||||
mcr p15, #0, r11, c7, c14, #2
|
||||
subs r9, r9, #1
|
||||
bge loop3
|
||||
subs r7, r7, #1
|
||||
bge loop2
|
||||
skip:
|
||||
add r10, r10, #2
|
||||
cmp r3, r10
|
||||
bgt loop1
|
||||
|
||||
finished:
|
||||
dsb
|
||||
isb
|
||||
pop {r4-r11}
|
||||
bx lr
|
||||
|
||||
.globl rt_cpu_icache_flush
|
||||
rt_cpu_icache_flush:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate
|
||||
dsb
|
||||
isb
|
||||
bx lr
|
||||
|
||||
.globl rt_hw_cpu_dcache_disable
|
||||
rt_hw_cpu_dcache_disable:
|
||||
push {r4-r11, lr}
|
||||
bl rt_cpu_dcache_clean_flush
|
||||
mrc p15, #0, r0, c1, c0, #0
|
||||
bic r0, r0, #0x00000004
|
||||
mcr p15, #0, r0, c1, c0, #0
|
||||
pop {r4-r11, lr}
|
||||
bx lr
|
||||
|
||||
.globl rt_hw_cpu_icache_disable
|
||||
rt_hw_cpu_icache_disable:
|
||||
mrc p15, #0, r0, c1, c0, #0
|
||||
bic r0, r0, #0x00001000
|
||||
mcr p15, #0, r0, c1, c0, #0
|
||||
bx lr
|
||||
|
||||
.globl rt_cpu_mmu_disable
|
||||
rt_cpu_mmu_disable:
|
||||
mcr p15, #0, r0, c8, c7, #0 @ invalidate tlb
|
||||
mrc p15, #0, r0, c1, c0, #0
|
||||
bic r0, r0, #1
|
||||
mcr p15, #0, r0, c1, c0, #0 @ clear mmu bit
|
||||
dsb
|
||||
bx lr
|
||||
|
||||
.globl rt_cpu_mmu_enable
|
||||
rt_cpu_mmu_enable:
|
||||
mrc p15, #0, r0, c1, c0, #0
|
||||
orr r0, r0, #0x001
|
||||
mcr p15, #0, r0, c1, c0, #0 @ set mmu enable bit
|
||||
dsb
|
||||
bx lr
|
||||
|
||||
.globl rt_cpu_tlb_set
|
||||
rt_cpu_tlb_set:
|
||||
mcr p15, #0, r0, c2, c0, #0
|
||||
dmb
|
||||
bx lr
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2011-09-15 Bernard first version
|
||||
* 2018-11-22 Jesven add rt_hw_cpu_id()
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <board.h>
|
||||
|
||||
rt_weak int rt_hw_cpu_id(void)
|
||||
{
|
||||
int cpu_id;
|
||||
__asm__ volatile (
|
||||
"mrc p15, 0, %0, c0, c0, 5"
|
||||
:"=r"(cpu_id)
|
||||
);
|
||||
cpu_id &= 0xf;
|
||||
return cpu_id;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
|
||||
void rt_hw_spin_lock_init(rt_hw_spinlock_t *lock)
|
||||
{
|
||||
lock->slock = 0;
|
||||
}
|
||||
|
||||
void rt_hw_spin_lock(rt_hw_spinlock_t *lock)
|
||||
{
|
||||
unsigned long tmp;
|
||||
unsigned long newval;
|
||||
rt_hw_spinlock_t lockval;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"pld [%0]"
|
||||
::"r"(&lock->slock)
|
||||
);
|
||||
|
||||
__asm__ __volatile__(
|
||||
"1: ldrex %0, [%3]\n"
|
||||
" add %1, %0, %4\n"
|
||||
" strex %2, %1, [%3]\n"
|
||||
" teq %2, #0\n"
|
||||
" bne 1b"
|
||||
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
|
||||
: "r" (&lock->slock), "I" (1 << 16)
|
||||
: "cc");
|
||||
|
||||
while (lockval.tickets.next != lockval.tickets.owner) {
|
||||
__asm__ __volatile__("wfe":::"memory");
|
||||
lockval.tickets.owner = *(volatile unsigned short *)(&lock->tickets.owner);
|
||||
}
|
||||
|
||||
__asm__ volatile ("dmb":::"memory");
|
||||
}
|
||||
|
||||
void rt_hw_spin_unlock(rt_hw_spinlock_t *lock)
|
||||
{
|
||||
__asm__ volatile ("dmb":::"memory");
|
||||
lock->tickets.owner++;
|
||||
__asm__ volatile ("dsb ishst\nsev":::"memory");
|
||||
}
|
||||
#endif /*RT_USING_SMP*/
|
||||
|
||||
/**
|
||||
* @addtogroup ARM CPU
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/** shutdown CPU */
|
||||
void rt_hw_cpu_shutdown(void)
|
||||
{
|
||||
rt_base_t level;
|
||||
rt_kprintf("shutdown...\n");
|
||||
|
||||
level = rt_hw_interrupt_disable();
|
||||
while (level)
|
||||
{
|
||||
RT_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RT_USING_CPU_FFS
|
||||
/**
|
||||
* This function finds the first bit set (beginning with the least significant bit)
|
||||
* in value and return the index of that bit.
|
||||
*
|
||||
* Bits are numbered starting at 1 (the least significant bit). A return value of
|
||||
* zero from any of these functions means that the argument was zero.
|
||||
*
|
||||
* @return return the index of the first bit set. If value is 0, then this function
|
||||
* shall return 0.
|
||||
*/
|
||||
int __rt_ffs(int value)
|
||||
{
|
||||
return __builtin_ffs(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
rt_bool_t rt_hw_interrupt_is_disabled(void)
|
||||
{
|
||||
int rc;
|
||||
__asm__ volatile("mrs %0, cpsr" : "=r" (rc));
|
||||
return !!(rc & 0x80);
|
||||
}
|
||||
|
||||
/*@}*/
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2020, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
|
||||
#ifndef CPUPORT_H__
|
||||
#define CPUPORT_H__
|
||||
|
||||
#include <rtcompiler.h>
|
||||
|
||||
/* the exception stack without VFP registers */
|
||||
struct rt_hw_exp_stack
|
||||
{
|
||||
unsigned long r0;
|
||||
unsigned long r1;
|
||||
unsigned long r2;
|
||||
unsigned long r3;
|
||||
unsigned long r4;
|
||||
unsigned long r5;
|
||||
unsigned long r6;
|
||||
unsigned long r7;
|
||||
unsigned long r8;
|
||||
unsigned long r9;
|
||||
unsigned long r10;
|
||||
unsigned long fp;
|
||||
unsigned long ip;
|
||||
unsigned long sp;
|
||||
unsigned long lr;
|
||||
unsigned long pc;
|
||||
unsigned long cpsr;
|
||||
};
|
||||
|
||||
struct rt_hw_stack
|
||||
{
|
||||
unsigned long cpsr;
|
||||
unsigned long r0;
|
||||
unsigned long r1;
|
||||
unsigned long r2;
|
||||
unsigned long r3;
|
||||
unsigned long r4;
|
||||
unsigned long r5;
|
||||
unsigned long r6;
|
||||
unsigned long r7;
|
||||
unsigned long r8;
|
||||
unsigned long r9;
|
||||
unsigned long r10;
|
||||
unsigned long fp;
|
||||
unsigned long ip;
|
||||
unsigned long lr;
|
||||
unsigned long pc;
|
||||
};
|
||||
|
||||
#define USERMODE 0x10
|
||||
#define FIQMODE 0x11
|
||||
#define IRQMODE 0x12
|
||||
#define SVCMODE 0x13
|
||||
#define MONITORMODE 0x16
|
||||
#define ABORTMODE 0x17
|
||||
#define HYPMODE 0x1b
|
||||
#define UNDEFMODE 0x1b
|
||||
#define MODEMASK 0x1f
|
||||
#define NOINT 0xc0
|
||||
|
||||
#define T_Bit (1<<5)
|
||||
#define F_Bit (1<<6)
|
||||
#define I_Bit (1<<7)
|
||||
#define A_Bit (1<<8)
|
||||
#define E_Bit (1<<9)
|
||||
#define J_Bit (1<<24)
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
typedef union {
|
||||
unsigned long slock;
|
||||
struct __arch_tickets {
|
||||
unsigned short owner;
|
||||
unsigned short next;
|
||||
} tickets;
|
||||
} rt_hw_spinlock_t;
|
||||
#endif
|
||||
|
||||
rt_inline void rt_hw_isb(void)
|
||||
{
|
||||
__asm volatile ("isb":::"memory");
|
||||
}
|
||||
|
||||
rt_inline void rt_hw_dmb(void)
|
||||
{
|
||||
__asm volatile ("dmb":::"memory");
|
||||
}
|
||||
|
||||
rt_inline void rt_hw_dsb(void)
|
||||
{
|
||||
__asm volatile ("dsb":::"memory");
|
||||
}
|
||||
|
||||
void _thread_start(void);
|
||||
|
||||
#endif /*CPUPORT_H__*/
|
||||
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-20 Bernard first version
|
||||
* 2014-04-03 Grissiom many enhancements
|
||||
* 2018-11-22 Jesven add rt_hw_ipi_send()
|
||||
* add rt_hw_ipi_handler_install()
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
|
||||
#include "gic.h"
|
||||
#include "cp15.h"
|
||||
|
||||
struct arm_gic
|
||||
{
|
||||
rt_uint32_t offset; /* the first interrupt index in the vector table */
|
||||
|
||||
rt_uint32_t dist_hw_base; /* the base address of the gic distributor */
|
||||
rt_uint32_t cpu_hw_base; /* the base addrees of the gic cpu interface */
|
||||
};
|
||||
|
||||
/* 'ARM_GIC_MAX_NR' is the number of cores */
|
||||
static struct arm_gic _gic_table[ARM_GIC_MAX_NR];
|
||||
|
||||
/** Macro to access the Generic Interrupt Controller Interface (GICC)
|
||||
*/
|
||||
#define GIC_CPU_CTRL(hw_base) __REG32((hw_base) + 0x00U)
|
||||
#define GIC_CPU_PRIMASK(hw_base) __REG32((hw_base) + 0x04U)
|
||||
#define GIC_CPU_BINPOINT(hw_base) __REG32((hw_base) + 0x08U)
|
||||
#define GIC_CPU_INTACK(hw_base) __REG32((hw_base) + 0x0cU)
|
||||
#define GIC_CPU_EOI(hw_base) __REG32((hw_base) + 0x10U)
|
||||
#define GIC_CPU_RUNNINGPRI(hw_base) __REG32((hw_base) + 0x14U)
|
||||
#define GIC_CPU_HIGHPRI(hw_base) __REG32((hw_base) + 0x18U)
|
||||
#define GIC_CPU_IIDR(hw_base) __REG32((hw_base) + 0xFCU)
|
||||
|
||||
/** Macro to access the Generic Interrupt Controller Distributor (GICD)
|
||||
*/
|
||||
#define GIC_DIST_CTRL(hw_base) __REG32((hw_base) + 0x000U)
|
||||
#define GIC_DIST_TYPE(hw_base) __REG32((hw_base) + 0x004U)
|
||||
#define GIC_DIST_IGROUP(hw_base, n) __REG32((hw_base) + 0x080U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_ENABLE_SET(hw_base, n) __REG32((hw_base) + 0x100U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_ENABLE_CLEAR(hw_base, n) __REG32((hw_base) + 0x180U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_PENDING_SET(hw_base, n) __REG32((hw_base) + 0x200U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_PENDING_CLEAR(hw_base, n) __REG32((hw_base) + 0x280U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_ACTIVE_SET(hw_base, n) __REG32((hw_base) + 0x300U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_ACTIVE_CLEAR(hw_base, n) __REG32((hw_base) + 0x380U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_PRI(hw_base, n) __REG32((hw_base) + 0x400U + ((n)/4U) * 4U)
|
||||
#define GIC_DIST_TARGET(hw_base, n) __REG32((hw_base) + 0x800U + ((n)/4U) * 4U)
|
||||
#define GIC_DIST_CONFIG(hw_base, n) __REG32((hw_base) + 0xc00U + ((n)/16U) * 4U)
|
||||
#define GIC_DIST_SOFTINT(hw_base) __REG32((hw_base) + 0xf00U)
|
||||
#define GIC_DIST_CPENDSGI(hw_base, n) __REG32((hw_base) + 0xf10U + ((n)/4U) * 4U)
|
||||
#define GIC_DIST_SPENDSGI(hw_base, n) __REG32((hw_base) + 0xf20U + ((n)/4U) * 4U)
|
||||
#define GIC_DIST_ICPIDR2(hw_base) __REG32((hw_base) + 0xfe8U)
|
||||
|
||||
static unsigned int _gic_max_irq;
|
||||
|
||||
int arm_gic_get_active_irq(rt_uint32_t index)
|
||||
{
|
||||
int irq;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = GIC_CPU_INTACK(_gic_table[index].cpu_hw_base);
|
||||
irq += _gic_table[index].offset;
|
||||
return irq;
|
||||
}
|
||||
|
||||
void arm_gic_ack(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t mask = 1U << (irq % 32U);
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
GIC_DIST_PENDING_CLEAR(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
GIC_CPU_EOI(_gic_table[index].cpu_hw_base) = irq;
|
||||
}
|
||||
|
||||
void arm_gic_mask(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t mask = 1U << (irq % 32U);
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
GIC_DIST_ENABLE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
|
||||
void arm_gic_umask(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t mask = 1U << (irq % 32U);
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t pend;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
if (irq >= 16U)
|
||||
{
|
||||
pend = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* INTID 0-15 Software Generated Interrupt */
|
||||
pend = (GIC_DIST_SPENDSGI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL;
|
||||
/* No CPU identification offered */
|
||||
if (pend != 0U)
|
||||
{
|
||||
pend = 1U;
|
||||
}
|
||||
else
|
||||
{
|
||||
pend = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
return (pend);
|
||||
}
|
||||
|
||||
void arm_gic_set_pending_irq(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
if (irq >= 16U)
|
||||
{
|
||||
GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) = 1U << (irq % 32U);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* INTID 0-15 Software Generated Interrupt */
|
||||
/* Forward the interrupt to the CPU interface that requested it */
|
||||
GIC_DIST_SOFTINT(_gic_table[index].dist_hw_base) = (irq | 0x02000000U);
|
||||
}
|
||||
}
|
||||
|
||||
void arm_gic_clear_pending_irq(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t mask;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
if (irq >= 16U)
|
||||
{
|
||||
mask = 1U << (irq % 32U);
|
||||
GIC_DIST_PENDING_CLEAR(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = 1U << ((irq % 4U) * 8U);
|
||||
GIC_DIST_CPENDSGI(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
}
|
||||
|
||||
void arm_gic_set_configuration(rt_uint32_t index, int irq, rt_uint32_t config)
|
||||
{
|
||||
rt_uint32_t icfgr;
|
||||
rt_uint32_t shift;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
icfgr = GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq);
|
||||
shift = (irq % 16U) << 1U;
|
||||
|
||||
icfgr &= (~(3U << shift));
|
||||
icfgr |= (config << (shift + 1));
|
||||
|
||||
GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) = icfgr;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
return (GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) >> ((irq % 16U) >> 1U));
|
||||
}
|
||||
|
||||
void arm_gic_clear_active(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t mask = 1U << (irq % 32U);
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
GIC_DIST_ACTIVE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
|
||||
/* Set up the cpu mask for the specific interrupt */
|
||||
void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask)
|
||||
{
|
||||
rt_uint32_t old_tgt;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
old_tgt = GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq);
|
||||
|
||||
old_tgt &= ~(0x0FFUL << ((irq % 4U)*8U));
|
||||
old_tgt |= cpumask << ((irq % 4U)*8U);
|
||||
|
||||
GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) = old_tgt;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
return (GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL;
|
||||
}
|
||||
|
||||
void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority)
|
||||
{
|
||||
rt_uint32_t mask;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
mask = GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq);
|
||||
mask &= ~(0xFFUL << ((irq % 4U) * 8U));
|
||||
mask |= ((priority & 0xFFUL) << ((irq % 4U) * 8U));
|
||||
GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
return (GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL;
|
||||
}
|
||||
|
||||
void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
/* set priority mask */
|
||||
GIC_CPU_PRIMASK(_gic_table[index].cpu_hw_base) = priority & 0xFFUL;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
return GIC_CPU_PRIMASK(_gic_table[index].cpu_hw_base);
|
||||
}
|
||||
|
||||
void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point)
|
||||
{
|
||||
GIC_CPU_BINPOINT(_gic_table[index].cpu_hw_base) = binary_point & 0x7U;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index)
|
||||
{
|
||||
return GIC_CPU_BINPOINT(_gic_table[index].cpu_hw_base);
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t pending;
|
||||
rt_uint32_t active;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
active = (GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL;
|
||||
pending = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL;
|
||||
|
||||
return ((active << 1U) | pending);
|
||||
}
|
||||
|
||||
void arm_gic_send_sgi(rt_uint32_t index, int irq, rt_uint32_t target_list, rt_uint32_t filter_list)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
GIC_DIST_SOFTINT(_gic_table[index].dist_hw_base) = ((filter_list & 0x3U) << 24U) | ((target_list & 0xFFUL) << 16U) | (irq & 0x0FUL);
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
return GIC_CPU_HIGHPRI(_gic_table[index].cpu_hw_base);
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
return GIC_CPU_IIDR(_gic_table[index].cpu_hw_base);
|
||||
}
|
||||
|
||||
void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group)
|
||||
{
|
||||
rt_uint32_t igroupr;
|
||||
rt_uint32_t shift;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
RT_ASSERT(group <= 1U);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
igroupr = GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq);
|
||||
shift = (irq % 32U);
|
||||
igroupr &= (~(1U << shift));
|
||||
igroupr |= ( (group & 0x1U) << shift);
|
||||
|
||||
GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) = igroupr;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
return (GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL;
|
||||
}
|
||||
|
||||
int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start)
|
||||
{
|
||||
unsigned int gic_type, i;
|
||||
rt_uint32_t cpumask = 1U << 0U;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
_gic_table[index].dist_hw_base = dist_base;
|
||||
_gic_table[index].offset = irq_start;
|
||||
|
||||
/* Find out how many interrupts are supported. */
|
||||
gic_type = GIC_DIST_TYPE(dist_base);
|
||||
_gic_max_irq = ((gic_type & 0x1fU) + 1U) * 32U;
|
||||
|
||||
/*
|
||||
* The GIC only supports up to 1020 interrupt sources.
|
||||
* Limit this to either the architected maximum, or the
|
||||
* platform maximum.
|
||||
*/
|
||||
if (_gic_max_irq > 1020U)
|
||||
_gic_max_irq = 1020U;
|
||||
if (_gic_max_irq > ARM_GIC_NR_IRQS) /* the platform maximum interrupts */
|
||||
_gic_max_irq = ARM_GIC_NR_IRQS;
|
||||
|
||||
cpumask |= cpumask << 8U;
|
||||
cpumask |= cpumask << 16U;
|
||||
cpumask |= cpumask << 24U;
|
||||
|
||||
GIC_DIST_CTRL(dist_base) = 0x0U;
|
||||
|
||||
/* Set all global interrupts to be level triggered, active low. */
|
||||
for (i = 32U; i < _gic_max_irq; i += 16U)
|
||||
GIC_DIST_CONFIG(dist_base, i) = 0x0U;
|
||||
|
||||
/* Set all global interrupts to this CPU only. */
|
||||
for (i = 32U; i < _gic_max_irq; i += 4U)
|
||||
GIC_DIST_TARGET(dist_base, i) = cpumask;
|
||||
|
||||
/* Set priority on all interrupts. */
|
||||
for (i = 0U; i < _gic_max_irq; i += 4U)
|
||||
GIC_DIST_PRI(dist_base, i) = 0xa0a0a0a0U;
|
||||
|
||||
/* Disable all interrupts. */
|
||||
for (i = 0U; i < _gic_max_irq; i += 32U)
|
||||
GIC_DIST_ENABLE_CLEAR(dist_base, i) = 0xffffffffU;
|
||||
|
||||
#if 0
|
||||
/* All interrupts defaults to IGROUP1(IRQ). */
|
||||
for (i = 0; i < _gic_max_irq; i += 32)
|
||||
GIC_DIST_IGROUP(dist_base, i) = 0xffffffff;
|
||||
#endif
|
||||
for (i = 0U; i < _gic_max_irq; i += 32U)
|
||||
GIC_DIST_IGROUP(dist_base, i) = 0U;
|
||||
|
||||
/* Enable group0 and group1 interrupt forwarding. */
|
||||
GIC_DIST_CTRL(dist_base) = 0x01U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_gic_cpu_init(rt_uint32_t index, rt_uint32_t cpu_base)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
if (!_gic_table[index].cpu_hw_base)
|
||||
{
|
||||
_gic_table[index].cpu_hw_base = cpu_base;
|
||||
}
|
||||
cpu_base = _gic_table[index].cpu_hw_base;
|
||||
|
||||
GIC_CPU_PRIMASK(cpu_base) = 0xf0U;
|
||||
GIC_CPU_BINPOINT(cpu_base) = 0x7U;
|
||||
/* Enable CPU interrupt */
|
||||
GIC_CPU_CTRL(cpu_base) = 0x01U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void arm_gic_dump_type(rt_uint32_t index)
|
||||
{
|
||||
unsigned int gic_type;
|
||||
|
||||
gic_type = GIC_DIST_TYPE(_gic_table[index].dist_hw_base);
|
||||
rt_kprintf("GICv%d on %p, max IRQs: %d, %s security extension(%08x)\n",
|
||||
(GIC_DIST_ICPIDR2(_gic_table[index].dist_hw_base) >> 4U) & 0xfUL,
|
||||
_gic_table[index].dist_hw_base,
|
||||
_gic_max_irq,
|
||||
gic_type & (1U << 10U) ? "has" : "no",
|
||||
gic_type);
|
||||
}
|
||||
|
||||
void arm_gic_dump(rt_uint32_t index)
|
||||
{
|
||||
unsigned int i, k;
|
||||
|
||||
k = GIC_CPU_HIGHPRI(_gic_table[index].cpu_hw_base);
|
||||
rt_kprintf("--- high pending priority: %d(%08x)\n", k, k);
|
||||
rt_kprintf("--- hw mask ---\n");
|
||||
for (i = 0U; i < _gic_max_irq / 32U; i++)
|
||||
{
|
||||
rt_kprintf("0x%08x, ",
|
||||
GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base,
|
||||
i * 32U));
|
||||
}
|
||||
rt_kprintf("\n--- hw pending ---\n");
|
||||
for (i = 0U; i < _gic_max_irq / 32U; i++)
|
||||
{
|
||||
rt_kprintf("0x%08x, ",
|
||||
GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base,
|
||||
i * 32U));
|
||||
}
|
||||
rt_kprintf("\n--- hw active ---\n");
|
||||
for (i = 0U; i < _gic_max_irq / 32U; i++)
|
||||
{
|
||||
rt_kprintf("0x%08x, ",
|
||||
GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base,
|
||||
i * 32U));
|
||||
}
|
||||
rt_kprintf("\n");
|
||||
}
|
||||
|
||||
long gic_dump(void)
|
||||
{
|
||||
arm_gic_dump_type(0);
|
||||
arm_gic_dump(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT(gic_dump, show gic status);
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-20 Bernard first version
|
||||
*/
|
||||
|
||||
#ifndef __GIC_H__
|
||||
#define __GIC_H__
|
||||
|
||||
#include <rthw.h>
|
||||
#include <board.h>
|
||||
|
||||
int arm_gic_get_active_irq(rt_uint32_t index);
|
||||
void arm_gic_ack(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_mask(rt_uint32_t index, int irq);
|
||||
void arm_gic_umask(rt_uint32_t index, int irq);
|
||||
|
||||
rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq);
|
||||
void arm_gic_set_pending_irq(rt_uint32_t index, int irq);
|
||||
void arm_gic_clear_pending_irq(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_set_configuration(rt_uint32_t index, int irq, rt_uint32_t config);
|
||||
rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_clear_active(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask);
|
||||
rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority);
|
||||
rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority);
|
||||
rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index);
|
||||
|
||||
void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point);
|
||||
rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index);
|
||||
|
||||
rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_send_sgi(rt_uint32_t index, int irq, rt_uint32_t target_list, rt_uint32_t filter_list);
|
||||
|
||||
rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index);
|
||||
|
||||
rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index);
|
||||
|
||||
void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group);
|
||||
rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq);
|
||||
|
||||
int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start);
|
||||
int arm_gic_cpu_init(rt_uint32_t index, rt_uint32_t cpu_base);
|
||||
|
||||
void arm_gic_dump_type(rt_uint32_t index);
|
||||
void arm_gic_dump(rt_uint32_t index);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,708 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-20 Bernard first version
|
||||
* 2014-04-03 Grissiom many enhancements
|
||||
* 2018-11-22 Jesven add rt_hw_ipi_send()
|
||||
* add rt_hw_ipi_handler_install()
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
|
||||
#include "gicv3.h"
|
||||
#include "cp15.h"
|
||||
|
||||
#ifndef RT_CPUS_NR
|
||||
#define RT_CPUS_NR 1
|
||||
#endif
|
||||
|
||||
struct arm_gic_v3
|
||||
{
|
||||
rt_uint32_t offset; /* the first interrupt index in the vector table */
|
||||
rt_uint32_t redist_hw_base[RT_CPUS_NR]; /* the pointer of the gic redistributor */
|
||||
rt_uint32_t dist_hw_base; /* the base address of the gic distributor */
|
||||
rt_uint32_t cpu_hw_base[RT_CPUS_NR]; /* the base addrees of the gic cpu interface */
|
||||
};
|
||||
|
||||
/* 'ARM_GIC_MAX_NR' is the number of cores */
|
||||
static struct arm_gic_v3 _gic_table[ARM_GIC_MAX_NR];
|
||||
static unsigned int _gic_max_irq;
|
||||
|
||||
/**
|
||||
* @name: arm_gic_cpumask_to_affval
|
||||
* @msg:
|
||||
* @in param cpu_mask:
|
||||
* @out param cluster_id: aff1 [0:7],aff2 [8:15],aff3 [16:23]
|
||||
* @out param target_list: Target List. The set of PEs for which SGI interrupts will be generated. Each bit corresponds to the
|
||||
* PE within a cluster with an Affinity 0 value equal to the bit number.
|
||||
* @return {rt_uint32_t} 0 is finish , 1 is data valid
|
||||
*/
|
||||
rt_weak rt_uint32_t arm_gic_cpumask_to_affval(rt_uint32_t *cpu_mask, rt_uint32_t *cluster_id, rt_uint32_t *target_list)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
rt_weak rt_uint64_t get_main_cpu_affval(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_gic_get_active_irq(rt_uint32_t index)
|
||||
{
|
||||
int irq;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
__get_gicv3_reg(ICC_IAR1, irq);
|
||||
|
||||
irq = (irq & 0x1FFFFFF) + _gic_table[index].offset;
|
||||
return irq;
|
||||
}
|
||||
|
||||
void arm_gic_ack(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
__asm__ volatile("dsb 0xF" ::
|
||||
: "memory");
|
||||
__set_gicv3_reg(ICC_EOIR1, irq);
|
||||
}
|
||||
|
||||
void arm_gic_mask(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t mask = 1U << (irq % 32U);
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
if (irq < 32U)
|
||||
{
|
||||
rt_int32_t cpu_id = rt_hw_cpu_id();
|
||||
RT_ASSERT((cpu_id) < RT_CPUS_NR);
|
||||
GIC_RDISTSGI_ICENABLER0(_gic_table[index].redist_hw_base[cpu_id]) = mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
GIC_DIST_ENABLE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
}
|
||||
|
||||
void arm_gic_umask(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t mask = 1U << (irq % 32U);
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
if (irq < 32U)
|
||||
{
|
||||
rt_int32_t cpu_id = rt_hw_cpu_id();
|
||||
RT_ASSERT((cpu_id) < RT_CPUS_NR);
|
||||
GIC_RDISTSGI_ISENABLER0(_gic_table[index].redist_hw_base[cpu_id]) = mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t pend;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
if (irq >= 16U)
|
||||
{
|
||||
pend = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* INTID 0-15 Software Generated Interrupt */
|
||||
pend = (GIC_DIST_SPENDSGI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL;
|
||||
/* No CPU identification offered */
|
||||
if (pend != 0U)
|
||||
{
|
||||
pend = 1U;
|
||||
}
|
||||
else
|
||||
{
|
||||
pend = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
return (pend);
|
||||
}
|
||||
|
||||
void arm_gic_set_pending_irq(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
if (irq >= 16U)
|
||||
{
|
||||
GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) = 1U << (irq % 32U);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* INTID 0-15 Software Generated Interrupt */
|
||||
/* Forward the interrupt to the CPU interface that requested it */
|
||||
GIC_DIST_SOFTINT(_gic_table[index].dist_hw_base) = (irq | 0x02000000U);
|
||||
}
|
||||
}
|
||||
|
||||
void arm_gic_clear_pending_irq(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t mask;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
if (irq >= 16U)
|
||||
{
|
||||
mask = 1U << (irq % 32U);
|
||||
GIC_DIST_PENDING_CLEAR(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = 1U << ((irq % 4U) * 8U);
|
||||
GIC_DIST_CPENDSGI(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
}
|
||||
|
||||
void arm_gic_set_configuration(rt_uint32_t index, int irq, rt_uint32_t config)
|
||||
{
|
||||
rt_uint32_t icfgr;
|
||||
rt_uint32_t shift;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
icfgr = GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq);
|
||||
shift = (irq % 16U) << 1U;
|
||||
|
||||
icfgr &= (~(3U << shift));
|
||||
icfgr |= (config << (shift + 1));
|
||||
|
||||
GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) = icfgr;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
return (GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) >> ((irq % 16U) >> 1U));
|
||||
}
|
||||
|
||||
void arm_gic_clear_active(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t mask = 1U << (irq % 32U);
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
GIC_DIST_ACTIVE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
|
||||
/* Set up the cpu mask for the specific interrupt */
|
||||
void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask)
|
||||
{
|
||||
rt_uint32_t old_tgt;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
old_tgt = GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq);
|
||||
|
||||
old_tgt &= ~(0x0FFUL << ((irq % 4U) * 8U));
|
||||
old_tgt |= cpumask << ((irq % 4U) * 8U);
|
||||
|
||||
GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) = old_tgt;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
return (GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL;
|
||||
}
|
||||
|
||||
void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority)
|
||||
{
|
||||
rt_uint32_t mask;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
if (irq < 32U)
|
||||
{
|
||||
rt_int32_t cpu_id = rt_hw_cpu_id();
|
||||
RT_ASSERT((cpu_id) < RT_CPUS_NR);
|
||||
|
||||
mask = GIC_RDISTSGI_IPRIORITYR(_gic_table[index].redist_hw_base[cpu_id], irq);
|
||||
mask &= ~(0xFFUL << ((irq % 4U) * 8U));
|
||||
mask |= ((priority & 0xFFUL) << ((irq % 4U) * 8U));
|
||||
GIC_RDISTSGI_IPRIORITYR(_gic_table[index].redist_hw_base[cpu_id], irq) = mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq);
|
||||
mask &= ~(0xFFUL << ((irq % 4U) * 8U));
|
||||
mask |= ((priority & 0xFFUL) << ((irq % 4U) * 8U));
|
||||
GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) = mask;
|
||||
}
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
if (irq < 32U)
|
||||
{
|
||||
rt_int32_t cpu_id = rt_hw_cpu_id();
|
||||
|
||||
RT_ASSERT((cpu_id) < RT_CPUS_NR);
|
||||
return (GIC_RDISTSGI_IPRIORITYR(_gic_table[index].redist_hw_base[cpu_id], irq) >> ((irq % 4U) * 8U)) & 0xFFUL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL;
|
||||
}
|
||||
}
|
||||
|
||||
void arm_gic_set_system_register_enable_mask(rt_uint32_t index, rt_uint32_t value)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
value &= 0xFFUL;
|
||||
/* set priority mask */
|
||||
__set_gicv3_reg(ICC_SRE, value);
|
||||
__asm__ volatile ("isb 0xF"::
|
||||
:"memory");
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_system_register_enable_mask(rt_uint32_t index)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
rt_uint32_t value;
|
||||
|
||||
__get_gicv3_reg(ICC_SRE, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
priority &= 0xFFUL;
|
||||
/* set priority mask */
|
||||
__set_gicv3_reg(ICC_PMR, priority);
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
rt_uint32_t priority;
|
||||
|
||||
__get_gicv3_reg(ICC_PMR, priority);
|
||||
return priority;
|
||||
}
|
||||
|
||||
void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point)
|
||||
{
|
||||
index = index;
|
||||
binary_point &= 0x7U;
|
||||
|
||||
__set_gicv3_reg(ICC_BPR1, binary_point);
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index)
|
||||
{
|
||||
rt_uint32_t binary_point;
|
||||
|
||||
index = index;
|
||||
__get_gicv3_reg(ICC_BPR1, binary_point);
|
||||
return binary_point;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq)
|
||||
{
|
||||
rt_uint32_t pending;
|
||||
rt_uint32_t active;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
active = (GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL;
|
||||
pending = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL;
|
||||
|
||||
return ((active << 1U) | pending);
|
||||
}
|
||||
|
||||
void arm_gic_send_affinity_sgi(rt_uint32_t index, int irq, rt_uint32_t cpu_mask, rt_uint32_t routing_mode)
|
||||
{
|
||||
rt_uint64_t sgi_val;
|
||||
|
||||
if (routing_mode)
|
||||
{
|
||||
sgi_val = (1ULL << 40) | ((irq & 0x0FULL) << 24); //Interrupts routed to all PEs in the system, excluding "self".
|
||||
/* Write the ICC_SGI1R registers */
|
||||
__asm__ volatile("dsb 0xF" ::
|
||||
: "memory");
|
||||
__set_cp64(15, 0, sgi_val, 12);
|
||||
__asm__ volatile("isb 0xF" ::
|
||||
: "memory");
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_uint32_t cluster_id, target_list;
|
||||
while (arm_gic_cpumask_to_affval(&cpu_mask, &cluster_id, &target_list))
|
||||
{
|
||||
sgi_val = ((irq & 0x0FULL) << 24 |
|
||||
target_list |
|
||||
((cluster_id >> 8) & 0xFFULL) << GIC_RSGI_AFF1_OFFSET |
|
||||
((cluster_id >> 16) & 0xFFULL) << GIC_RSGI_AFF2_OFFSET |
|
||||
((cluster_id >> 24) & 0xFFull) << GIC_RSGI_AFF3_OFFSET);
|
||||
|
||||
__asm__ volatile("dsb 0xF" ::
|
||||
: "memory");
|
||||
__set_cp64(15, 0, sgi_val, 12);
|
||||
__asm__ volatile("isb 0xF" ::
|
||||
: "memory");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index)
|
||||
{
|
||||
rt_uint32_t irq;
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
index = index;
|
||||
__get_gicv3_reg(ICC_HPPIR1, irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
return GIC_CPU_IIDR(_gic_table[index].cpu_hw_base);
|
||||
}
|
||||
|
||||
void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group)
|
||||
{
|
||||
rt_uint32_t igroupr;
|
||||
rt_uint32_t shift;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
RT_ASSERT(group <= 1U);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
igroupr = GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq);
|
||||
shift = (irq % 32U);
|
||||
igroupr &= (~(1U << shift));
|
||||
igroupr |= ((group & 0x1U) << shift);
|
||||
|
||||
GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) = igroupr;
|
||||
}
|
||||
|
||||
rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
irq = irq - _gic_table[index].offset;
|
||||
RT_ASSERT(irq >= 0U);
|
||||
|
||||
return (GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL;
|
||||
}
|
||||
|
||||
static int arm_gicv3_wait_rwp(rt_uint32_t index, rt_uint32_t irq)
|
||||
{
|
||||
rt_uint32_t rwp_bit;
|
||||
rt_uint32_t base;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
if (irq < 32u)
|
||||
{
|
||||
rt_int32_t cpu_id = rt_hw_cpu_id();
|
||||
|
||||
RT_ASSERT((cpu_id) < RT_CPUS_NR);
|
||||
base = _gic_table[index].redist_hw_base[cpu_id];
|
||||
rwp_bit = GICR_CTLR_RWP;
|
||||
}
|
||||
else
|
||||
{
|
||||
base = _gic_table[index].dist_hw_base;
|
||||
rwp_bit = GICD_CTLR_RWP;
|
||||
}
|
||||
|
||||
while (__REG32(base) & rwp_bit)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start)
|
||||
{
|
||||
rt_uint64_t cpu0_affval;
|
||||
unsigned int gic_type, i;
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
_gic_table[index].dist_hw_base = dist_base;
|
||||
_gic_table[index].offset = irq_start;
|
||||
|
||||
/* Find out how many interrupts are supported. */
|
||||
gic_type = GIC_DIST_TYPE(dist_base);
|
||||
_gic_max_irq = ((gic_type & 0x1fU) + 1U) * 32U;
|
||||
|
||||
/*
|
||||
* The GIC only supports up to 1020 interrupt sources.
|
||||
* Limit this to either the architected maximum, or the
|
||||
* platform maximum.
|
||||
*/
|
||||
if (_gic_max_irq > 1020U)
|
||||
_gic_max_irq = 1020U;
|
||||
if (_gic_max_irq > ARM_GIC_NR_IRQS) /* the platform maximum interrupts */
|
||||
_gic_max_irq = ARM_GIC_NR_IRQS;
|
||||
|
||||
GIC_DIST_CTRL(dist_base) = 0x0U;
|
||||
/* Wait for register write pending */
|
||||
arm_gicv3_wait_rwp(0, 32);
|
||||
|
||||
/* Set all global interrupts to be level triggered, active low. */
|
||||
for (i = 32U; i < _gic_max_irq; i += 16U)
|
||||
GIC_DIST_CONFIG(dist_base, i) = 0x0U;
|
||||
|
||||
arm_gicv3_wait_rwp(0, 32);
|
||||
|
||||
cpu0_affval = get_main_cpu_affval();
|
||||
/* Set all global interrupts to this CPU only. */
|
||||
for (i = 32U; i < _gic_max_irq; i++)
|
||||
{
|
||||
GIC_DIST_IROUTER_LOW(dist_base, i) = cpu0_affval;
|
||||
GIC_DIST_IROUTER_HIGH(dist_base, i) = cpu0_affval >> 32;
|
||||
}
|
||||
|
||||
arm_gicv3_wait_rwp(0, 32);
|
||||
|
||||
/* Set priority on spi interrupts. */
|
||||
for (i = 32U; i < _gic_max_irq; i += 4U)
|
||||
GIC_DIST_PRI(dist_base, i) = 0xa0a0a0a0U;
|
||||
|
||||
arm_gicv3_wait_rwp(0, 32);
|
||||
/* Disable all interrupts. */
|
||||
for (i = 0U; i < _gic_max_irq; i += 32U)
|
||||
{
|
||||
GIC_DIST_PENDING_CLEAR(dist_base, i) = 0xffffffffU;
|
||||
GIC_DIST_ENABLE_CLEAR(dist_base, i) = 0xffffffffU;
|
||||
}
|
||||
|
||||
arm_gicv3_wait_rwp(0, 32);
|
||||
/* All interrupts defaults to IGROUP1(IRQ). */
|
||||
for (i = 0U; i < _gic_max_irq; i += 32U)
|
||||
GIC_DIST_IGROUP(dist_base, i) = 0xffffffffU;
|
||||
|
||||
arm_gicv3_wait_rwp(0, 32);
|
||||
|
||||
/*
|
||||
The Distributor control register (GICD_CTLR) must be configured to enable the interrupt groups and to set the routing mode.
|
||||
Enable Affinity routing (ARE bits) The ARE bits in GICD_CTLR control whether affinity routing is enabled.
|
||||
If affinity routing is not enabled, GICv3 can be configured for legacy operation.
|
||||
Whether affinity routing is enabled or not can be controlled separately for Secure and Non-secure state.
|
||||
Enables GICD_CTLR contains separate enable bits for Group 0, Secure Group 1 and Non-secure Group 1:
|
||||
GICD_CTLR.EnableGrp1S enables distribution of Secure Group 1 interrupts.
|
||||
GICD_CTLR.EnableGrp1NS enables distribution of Non-secure Group 1 interrupts.
|
||||
GICD_CTLR.EnableGrp0 enables distribution of Group 0 interrupts.
|
||||
*/
|
||||
GIC_DIST_CTRL(dist_base) = GICD_CTLR_ARE_NS | GICD_CTLR_ENGRP1NS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_gic_redist_address_set(rt_uint32_t index, rt_uint32_t redist_addr, rt_uint32_t cpu_id)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
RT_ASSERT((cpu_id) < RT_CPUS_NR);
|
||||
_gic_table[index].redist_hw_base[cpu_id] = redist_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_gic_cpu_interface_address_set(rt_uint32_t index, rt_uint32_t interface_addr, rt_uint32_t cpu_id)
|
||||
{
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
RT_ASSERT((cpu_id) < RT_CPUS_NR);
|
||||
_gic_table[index].cpu_hw_base[cpu_id] = interface_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_gic_redist_init(rt_uint32_t index)
|
||||
{
|
||||
unsigned int i;
|
||||
rt_uint32_t base;
|
||||
rt_int32_t cpu_id = rt_hw_cpu_id();
|
||||
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
RT_ASSERT((cpu_id) < RT_CPUS_NR);
|
||||
|
||||
base = _gic_table[index].redist_hw_base[cpu_id];
|
||||
/* redistributor enable */
|
||||
GIC_RDIST_WAKER(base) &= ~(1U << 1);
|
||||
while (GIC_RDIST_WAKER(base) & (1 << 2))
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
/* Disable all sgi and ppi interrupt */
|
||||
GIC_RDISTSGI_ICENABLER0(base) = 0xFFFFFFFF;
|
||||
arm_gicv3_wait_rwp(0, 0);
|
||||
|
||||
/* Clear all inetrrupt pending */
|
||||
GIC_RDISTSGI_ICPENDR0(base) = 0xFFFFFFFF;
|
||||
|
||||
/* the corresponding interrupt is Group 1 or Non-secure Group 1. */
|
||||
GIC_RDISTSGI_IGROUPR0(base, 0) = 0xFFFFFFFF;
|
||||
GIC_RDISTSGI_IGRPMODR0(base, 0) = 0xFFFFFFFF;
|
||||
|
||||
/* Configure default priorities for SGI 0:15 and PPI 16:31. */
|
||||
for (i = 0; i < 32; i += 4)
|
||||
{
|
||||
GIC_RDISTSGI_IPRIORITYR(base, i) = 0xa0a0a0a0U;
|
||||
}
|
||||
|
||||
/* Trigger level for PPI interrupts*/
|
||||
GIC_RDISTSGI_ICFGR1(base) = 0x0U; // PPI is level-sensitive.
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_gic_cpu_init(rt_uint32_t index)
|
||||
{
|
||||
rt_uint32_t value;
|
||||
RT_ASSERT(index < ARM_GIC_MAX_NR);
|
||||
|
||||
value = arm_gic_get_system_register_enable_mask(index);
|
||||
value |= (1U << 0);
|
||||
arm_gic_set_system_register_enable_mask(index, value);
|
||||
__set_gicv3_reg(ICC_CTLR, 0);
|
||||
|
||||
arm_gic_set_interface_prior_mask(index, 0xFFU);
|
||||
|
||||
/* Enable group1 interrupt */
|
||||
value = 0x1U;
|
||||
__set_gicv3_reg(ICC_IGRPEN1, value);
|
||||
|
||||
arm_gic_set_binary_point(0, 0);
|
||||
|
||||
/* ICC_BPR0_EL1 determines the preemption group for both
|
||||
Group 0 and Group 1 interrupts.
|
||||
*/
|
||||
value = 0x1U;
|
||||
__set_gicv3_reg(ICC_CTLR, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
void arm_gic_secondary_cpu_init(void)
|
||||
{
|
||||
arm_gic_redist_init(0);
|
||||
|
||||
arm_gic_cpu_init(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void arm_gic_dump_type(rt_uint32_t index)
|
||||
{
|
||||
unsigned int gic_type;
|
||||
|
||||
gic_type = GIC_DIST_TYPE(_gic_table[index].dist_hw_base);
|
||||
rt_kprintf("GICv%d on %p, max IRQs: %d, %s security extension(%08x)\n",
|
||||
(GIC_DIST_ICPIDR2(_gic_table[index].dist_hw_base) >> 4U) & 0xfUL,
|
||||
_gic_table[index].dist_hw_base,
|
||||
_gic_max_irq,
|
||||
gic_type & (1U << 10U) ? "has" : "no",
|
||||
gic_type);
|
||||
}
|
||||
|
||||
void arm_gic_dump(rt_uint32_t index)
|
||||
{
|
||||
unsigned int i, k;
|
||||
|
||||
k = arm_gic_get_high_pending_irq(0);
|
||||
rt_kprintf("--- high pending priority: %d(%08x)\n", k, k);
|
||||
rt_kprintf("--- hw mask ---\n");
|
||||
for (i = 0U; i < _gic_max_irq / 32U; i++)
|
||||
{
|
||||
rt_kprintf("0x%08x, ",
|
||||
GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base,
|
||||
i * 32U));
|
||||
}
|
||||
rt_kprintf("\n--- hw pending ---\n");
|
||||
for (i = 0U; i < _gic_max_irq / 32U; i++)
|
||||
{
|
||||
rt_kprintf("0x%08x, ",
|
||||
GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base,
|
||||
i * 32U));
|
||||
}
|
||||
rt_kprintf("\n--- hw active ---\n");
|
||||
for (i = 0U; i < _gic_max_irq / 32U; i++)
|
||||
{
|
||||
rt_kprintf("0x%08x, ",
|
||||
GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base,
|
||||
i * 32U));
|
||||
}
|
||||
rt_kprintf("\n");
|
||||
}
|
||||
|
||||
long gic_dump(void)
|
||||
{
|
||||
arm_gic_dump_type(0);
|
||||
arm_gic_dump(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
MSH_CMD_EXPORT(gic_dump, show gic status);
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-20 Bernard first version
|
||||
*/
|
||||
|
||||
#ifndef __GIC_V3_H__
|
||||
#define __GIC_V3_H__
|
||||
|
||||
#include <rthw.h>
|
||||
#include <board.h>
|
||||
|
||||
#define __get_gicv3_reg(CR, Rt) __asm__ volatile("MRC " CR \
|
||||
: "=r"(Rt) \
|
||||
: \
|
||||
: "memory")
|
||||
#define __set_gicv3_reg(CR, Rt) __asm__ volatile("MCR " CR \
|
||||
: \
|
||||
: "r"(Rt) \
|
||||
: "memory")
|
||||
|
||||
|
||||
/* AArch32 System register interface to GICv3 */
|
||||
#define ICC_IAR0 "p15, 0, %0, c12, c8, 0"
|
||||
#define ICC_IAR1 "p15, 0, %0, c12, c12, 0"
|
||||
#define ICC_EOIR0 "p15, 0, %0, c12, c8, 1"
|
||||
#define ICC_EOIR1 "p15, 0, %0, c12, c12, 1"
|
||||
#define ICC_HPPIR0 "p15, 0, %0, c12, c8, 2"
|
||||
#define ICC_HPPIR1 "p15, 0, %0, c12, c12, 2"
|
||||
#define ICC_BPR0 "p15, 0, %0, c12, c8, 3"
|
||||
#define ICC_BPR1 "p15, 0, %0, c12, c12, 3"
|
||||
#define ICC_DIR "p15, 0, %0, c12, c11, 1"
|
||||
#define ICC_PMR "p15, 0, %0, c4, c6, 0"
|
||||
#define ICC_RPR "p15, 0, %0, c12, c11, 3"
|
||||
#define ICC_CTLR "p15, 0, %0, c12, c12, 4"
|
||||
#define ICC_MCTLR "p15, 6, %0, c12, c12, 4"
|
||||
#define ICC_SRE "p15, 0, %0, c12, c12, 5"
|
||||
#define ICC_HSRE "p15, 4, %0, c12, c9, 5"
|
||||
#define ICC_MSRE "p15, 6, %0, c12, c12, 5"
|
||||
#define ICC_IGRPEN0 "p15, 0, %0, c12, c12, 6"
|
||||
#define ICC_IGRPEN1 "p15, 0, %0, c12, c12, 7"
|
||||
#define ICC_MGRPEN1 "p15, 6, %0, c12, c12, 7"
|
||||
|
||||
#define __REG32(x) (*((volatile unsigned int*)((rt_uint32_t)x)))
|
||||
|
||||
#define ROUTED_TO_ALL (1)
|
||||
#define ROUTED_TO_SPEC (0)
|
||||
|
||||
/** Macro to access the Distributor Control Register (GICD_CTLR)
|
||||
*/
|
||||
#define GICD_CTLR_RWP (1<<31)
|
||||
#define GICD_CTLR_E1NWF (1<<7)
|
||||
#define GICD_CTLR_DS (1<<6)
|
||||
#define GICD_CTLR_ARE_NS (1<<5)
|
||||
#define GICD_CTLR_ARE_S (1<<4)
|
||||
#define GICD_CTLR_ENGRP1S (1<<2)
|
||||
#define GICD_CTLR_ENGRP1NS (1<<1)
|
||||
#define GICD_CTLR_ENGRP0 (1<<0)
|
||||
|
||||
/** Macro to access the Redistributor Control Register (GICR_CTLR)
|
||||
*/
|
||||
#define GICR_CTLR_UWP (1<<31)
|
||||
#define GICR_CTLR_DPG1S (1<<26)
|
||||
#define GICR_CTLR_DPG1NS (1<<25)
|
||||
#define GICR_CTLR_DPG0 (1<<24)
|
||||
#define GICR_CTLR_RWP (1<<3)
|
||||
#define GICR_CTLR_IR (1<<2)
|
||||
#define GICR_CTLR_CES (1<<1)
|
||||
#define GICR_CTLR_EnableLPI (1<<0)
|
||||
|
||||
/** Macro to access the Generic Interrupt Controller Interface (GICC)
|
||||
*/
|
||||
#define GIC_CPU_CTRL(hw_base) __REG32((hw_base) + 0x00U)
|
||||
#define GIC_CPU_PRIMASK(hw_base) __REG32((hw_base) + 0x04U)
|
||||
#define GIC_CPU_BINPOINT(hw_base) __REG32((hw_base) + 0x08U)
|
||||
#define GIC_CPU_INTACK(hw_base) __REG32((hw_base) + 0x0cU)
|
||||
#define GIC_CPU_EOI(hw_base) __REG32((hw_base) + 0x10U)
|
||||
#define GIC_CPU_RUNNINGPRI(hw_base) __REG32((hw_base) + 0x14U)
|
||||
#define GIC_CPU_HIGHPRI(hw_base) __REG32((hw_base) + 0x18U)
|
||||
#define GIC_CPU_IIDR(hw_base) __REG32((hw_base) + 0xFCU)
|
||||
|
||||
/** Macro to access the Generic Interrupt Controller Distributor (GICD)
|
||||
*/
|
||||
#define GIC_DIST_CTRL(hw_base) __REG32((hw_base) + 0x000U)
|
||||
#define GIC_DIST_TYPE(hw_base) __REG32((hw_base) + 0x004U)
|
||||
#define GIC_DIST_IGROUP(hw_base, n) __REG32((hw_base) + 0x080U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_ENABLE_SET(hw_base, n) __REG32((hw_base) + 0x100U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_ENABLE_CLEAR(hw_base, n) __REG32((hw_base) + 0x180U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_PENDING_SET(hw_base, n) __REG32((hw_base) + 0x200U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_PENDING_CLEAR(hw_base, n) __REG32((hw_base) + 0x280U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_ACTIVE_SET(hw_base, n) __REG32((hw_base) + 0x300U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_ACTIVE_CLEAR(hw_base, n) __REG32((hw_base) + 0x380U + ((n)/32U) * 4U)
|
||||
#define GIC_DIST_PRI(hw_base, n) __REG32((hw_base) + 0x400U + ((n)/4U) * 4U)
|
||||
#define GIC_DIST_TARGET(hw_base, n) __REG32((hw_base) + 0x800U + ((n)/4U) * 4U)
|
||||
#define GIC_DIST_CONFIG(hw_base, n) __REG32((hw_base) + 0xc00U + ((n)/16U) * 4U)
|
||||
#define GIC_DIST_SOFTINT(hw_base) __REG32((hw_base) + 0xf00U)
|
||||
#define GIC_DIST_CPENDSGI(hw_base, n) __REG32((hw_base) + 0xf10U + ((n)/4U) * 4U)
|
||||
#define GIC_DIST_SPENDSGI(hw_base, n) __REG32((hw_base) + 0xf20U + ((n)/4U) * 4U)
|
||||
#define GIC_DIST_ICPIDR2(hw_base) __REG32((hw_base) + 0xfe8U)
|
||||
#define GIC_DIST_IROUTER_LOW(hw_base, n) __REG32((hw_base) + 0x6000U + (n)*8U)
|
||||
#define GIC_DIST_IROUTER_HIGH(hw_base, n) __REG32((hw_base) + 0x6000U + (n)*8U + 4)
|
||||
|
||||
/* SGI base address is at 64K offset from Redistributor base address */
|
||||
#define GIC_RSGI_OFFSET 0x10000
|
||||
|
||||
/** Macro to access the Generic Interrupt Controller Redistributor (GICD)
|
||||
*/
|
||||
#define GIC_RDIST_CTRL(hw_base) __REG32((hw_base) + 0x000U)
|
||||
#define GIC_RDIST_IIDR(hw_base) __REG32((hw_base) + 0x004U)
|
||||
#define GIC_RDIST_TYPER(hw_base) __REG32((hw_base) + 0x008U)
|
||||
#define GIC_RDIST_TSTATUSR(hw_base) __REG32((hw_base) + 0x010U)
|
||||
#define GIC_RDIST_WAKER(hw_base) __REG32((hw_base) + 0x014U)
|
||||
#define GIC_RDIST_SETLPIR(hw_base) __REG32((hw_base) + 0x040U)
|
||||
#define GIC_RDIST_CLRLPIR(hw_base) __REG32((hw_base) + 0x048U)
|
||||
#define GIC_RDIST_PROPBASER(hw_base) __REG32((hw_base) + 0x070U)
|
||||
#define GIC_RDIST_PENDBASER(hw_base) __REG32((hw_base) + 0x078U)
|
||||
#define GIC_RDIST_INVLPIR(hw_base) __REG32((hw_base) + 0x0A0U)
|
||||
#define GIC_RDIST_INVALLR(hw_base) __REG32((hw_base) + 0x0B0U)
|
||||
#define GIC_RDIST_SYNCR(hw_base) __REG32((hw_base) + 0x0C0U)
|
||||
|
||||
#define GIC_RDISTSGI_IGROUPR0(hw_base, n) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x080U + (n)*4U)
|
||||
#define GIC_RDISTSGI_ISENABLER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x100U)
|
||||
#define GIC_RDISTSGI_ICENABLER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x180U)
|
||||
#define GIC_RDISTSGI_ISPENDR0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x200U)
|
||||
#define GIC_RDISTSGI_ICPENDR0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x280U)
|
||||
#define GIC_RDISTSGI_ISACTIVER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x300U)
|
||||
#define GIC_RDISTSGI_ICACTIVER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x380U)
|
||||
#define GIC_RDISTSGI_IPRIORITYR(hw_base, n) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x400U + ((n) / 4U) * 4U)
|
||||
#define GIC_RDISTSGI_ICFGR0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xC00U)
|
||||
#define GIC_RDISTSGI_ICFGR1(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xC04U)
|
||||
#define GIC_RDISTSGI_IGRPMODR0(hw_base, n) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xD00U + (n)*4)
|
||||
#define GIC_RDISTSGI_NSACR(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xE00U)
|
||||
|
||||
#define GIC_RSGI_AFF1_OFFSET 16
|
||||
#define GIC_RSGI_AFF2_OFFSET 32
|
||||
#define GIC_RSGI_AFF3_OFFSET 48
|
||||
|
||||
rt_uint32_t arm_gic_cpumask_to_affval(rt_uint32_t *cpu_mask, rt_uint32_t *cluster_id, rt_uint32_t *target_list);
|
||||
rt_uint64_t get_main_cpu_affval(void);
|
||||
int arm_gic_get_active_irq(rt_uint32_t index);
|
||||
void arm_gic_ack(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_mask(rt_uint32_t index, int irq);
|
||||
void arm_gic_umask(rt_uint32_t index, int irq);
|
||||
|
||||
rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq);
|
||||
void arm_gic_set_pending_irq(rt_uint32_t index, int irq);
|
||||
void arm_gic_clear_pending_irq(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_set_configuration(rt_uint32_t index, int irq, rt_uint32_t config);
|
||||
rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_clear_active(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask);
|
||||
rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority);
|
||||
rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority);
|
||||
rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index);
|
||||
|
||||
void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point);
|
||||
rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index);
|
||||
|
||||
rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq);
|
||||
|
||||
void arm_gic_send_affinity_sgi(rt_uint32_t index, int irq, rt_uint32_t cpu_mask, rt_uint32_t routing_mode);
|
||||
rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index);
|
||||
|
||||
rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index);
|
||||
|
||||
void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group);
|
||||
rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq);
|
||||
|
||||
int arm_gic_redist_address_set(rt_uint32_t index, rt_uint32_t redist_addr, rt_uint32_t cpu_id);
|
||||
int arm_gic_cpu_interface_address_set(rt_uint32_t index, rt_uint32_t interface_addr, rt_uint32_t cpu_id);
|
||||
int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start);
|
||||
int arm_gic_cpu_init(rt_uint32_t index);
|
||||
int arm_gic_redist_init(rt_uint32_t index);
|
||||
|
||||
void arm_gic_dump_type(rt_uint32_t index);
|
||||
void arm_gic_dump(rt_uint32_t index);
|
||||
|
||||
void arm_gic_set_system_register_enable_mask(rt_uint32_t index, rt_uint32_t value);
|
||||
rt_uint32_t arm_gic_get_system_register_enable_mask(rt_uint32_t index);
|
||||
void arm_gic_secondary_cpu_init(void);
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-03-30 huijie.feng first version
|
||||
*/
|
||||
|
||||
#include "cp15.h"
|
||||
#include <rtdef.h>
|
||||
|
||||
/** Set CNTFRQ
|
||||
* This function assigns the given value to PL1 Physical Timer Counter Frequency Register (CNTFRQ).
|
||||
* @param value: CNTFRQ Register value to set
|
||||
*/
|
||||
static inline void __set_cntfrq(rt_uint32_t value)
|
||||
{
|
||||
__set_cp(15, 0, value, 14, 0, 0);
|
||||
}
|
||||
|
||||
/** Get CNTFRQ
|
||||
* This function returns the value of the PL1 Physical Timer Counter Frequency Register (CNTFRQ).
|
||||
* return CNTFRQ Register value
|
||||
*/
|
||||
static inline rt_uint32_t __get_cntfrq(void)
|
||||
{
|
||||
rt_uint32_t result;
|
||||
__get_cp(15, 0, result, 14, 0 , 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Set CNTP_TVAL
|
||||
* This function assigns the given value to PL1 Physical Timer Value Register (CNTP_TVAL).
|
||||
* param value: CNTP_TVAL Register value to set
|
||||
*/
|
||||
static inline void __set_cntp_tval(rt_uint32_t value)
|
||||
{
|
||||
__set_cp(15, 0, value, 14, 2, 0);
|
||||
}
|
||||
|
||||
/** Get CNTP_TVAL
|
||||
* This function returns the value of the PL1 Physical Timer Value Register (CNTP_TVAL).
|
||||
* return CNTP_TVAL Register value
|
||||
*/
|
||||
static inline rt_uint32_t __get_cntp_tval(void)
|
||||
{
|
||||
rt_uint32_t result;
|
||||
__get_cp(15, 0, result, 14, 2, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Get CNTPCT
|
||||
* This function returns the value of the 64 bits PL1 Physical Count Register (CNTPCT).
|
||||
* return CNTPCT Register value
|
||||
*/
|
||||
static inline rt_uint64_t __get_cntpct(void)
|
||||
{
|
||||
rt_uint64_t result;
|
||||
__get_cp64(15, 0, result, 14);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Set CNTP_CVAL
|
||||
* This function assigns the given value to 64bits PL1 Physical Timer CompareValue Register (CNTP_CVAL).
|
||||
* param value: CNTP_CVAL Register value to set
|
||||
*/
|
||||
static inline void __set_cntp_cval(rt_uint64_t value)
|
||||
{
|
||||
__set_cp64(15, 2, value, 14);
|
||||
}
|
||||
|
||||
/** Get CNTP_CVAL
|
||||
* This function returns the value of the 64 bits PL1 Physical Timer CompareValue Register (CNTP_CVAL).
|
||||
* return CNTP_CVAL Register value
|
||||
*/
|
||||
static inline rt_uint64_t __get_cntp_cval(void)
|
||||
{
|
||||
rt_uint64_t result;
|
||||
__get_cp64(15, 2, result, 14);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Set CNTP_CTL
|
||||
* This function assigns the given value to PL1 Physical Timer Control Register (CNTP_CTL).
|
||||
* param value: CNTP_CTL Register value to set
|
||||
*/
|
||||
static inline void __set_cntp_ctl(rt_uint32_t value)
|
||||
{
|
||||
__set_cp(15, 0, value, 14, 2, 1);
|
||||
}
|
||||
|
||||
/** Get CNTP_CTL register
|
||||
* return CNTP_CTL Register value
|
||||
*/
|
||||
static inline rt_uint32_t __get_cntp_ctl(void)
|
||||
{
|
||||
rt_uint32_t result;
|
||||
__get_cp(15, 0, result, 14, 2, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Configures the frequency the timer shall run at.
|
||||
* param value The timer frequency in Hz.
|
||||
*/
|
||||
void gtimer_set_counter_frequency(rt_uint32_t value)
|
||||
{
|
||||
__set_cntfrq(value);
|
||||
__asm__ volatile ("isb 0xF":::"memory");
|
||||
}
|
||||
|
||||
/** Get the frequency the timer shall run at.
|
||||
* return timer frequency in Hz.
|
||||
*/
|
||||
rt_uint32_t gtimer_get_counter_frequency(void)
|
||||
{
|
||||
return(__get_cntfrq());
|
||||
}
|
||||
|
||||
/** Sets the reset value of the timer.
|
||||
* param value: The value the timer is loaded with.
|
||||
*/
|
||||
void gtimer_set_load_value(rt_uint32_t value)
|
||||
{
|
||||
__set_cntp_tval(value);
|
||||
__asm__ volatile ("isb 0xF":::"memory");
|
||||
}
|
||||
|
||||
/** Get the current counter value.
|
||||
* return Current counter value.
|
||||
*/
|
||||
rt_uint32_t gtimer_get_current_value(void)
|
||||
{
|
||||
return(__get_cntp_tval());
|
||||
}
|
||||
|
||||
/** Get the current physical counter value.
|
||||
* return Current physical counter value.
|
||||
*/
|
||||
rt_uint64_t gtimer_get_current_physical_value(void)
|
||||
{
|
||||
return(__get_cntpct());
|
||||
}
|
||||
|
||||
/** Set the physical compare value.
|
||||
* param value: New physical timer compare value.
|
||||
*/
|
||||
void gtimer_set_physical_compare_value(rt_uint64_t value)
|
||||
{
|
||||
__set_cntp_cval(value);
|
||||
__asm__ volatile ("isb 0xF":::"memory");
|
||||
}
|
||||
|
||||
/** Get the physical compare value.
|
||||
* return Physical compare value.
|
||||
*/
|
||||
rt_uint64_t gtimer_get_physical_compare_value(void)
|
||||
{
|
||||
return(__get_cntp_cval());
|
||||
}
|
||||
|
||||
/** Configure the timer by setting the control value.
|
||||
* param value: New timer control value.
|
||||
*/
|
||||
void gtimer_set_control(rt_uint32_t value)
|
||||
{
|
||||
__set_cntp_ctl(value);
|
||||
__asm__ volatile ("isb 0xF":::"memory");
|
||||
}
|
||||
|
||||
/** Get the control value.
|
||||
* return Control value.
|
||||
*/
|
||||
rt_uint32_t gtimer_get_control(void)
|
||||
{
|
||||
return(__get_cntp_ctl());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2021-03-30 huijie.feng first version
|
||||
*/
|
||||
|
||||
#ifndef __GTIMER_H__
|
||||
#define __GTIMER_H__
|
||||
|
||||
#include <rtdef.h>
|
||||
|
||||
void gtimer_set_counter_frequency(rt_uint32_t value);
|
||||
rt_uint32_t gtimer_get_counter_frequency(void);
|
||||
void gtimer_set_load_value(rt_uint32_t value);
|
||||
rt_uint32_t gtimer_get_current_value(void);
|
||||
rt_uint64_t gtimer_get_current_physical_value(void);
|
||||
void gtimer_set_physical_compare_value(rt_uint64_t value);
|
||||
rt_uint64_t gtimer_get_physical_compare_value(void);
|
||||
void gtimer_set_control(rt_uint32_t value);
|
||||
rt_uint32_t gtimer_get_control(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-06 Bernard first version
|
||||
* 2018-11-22 Jesven add smp support
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include "interrupt.h"
|
||||
|
||||
#ifdef RT_USING_GIC_V2
|
||||
#include "gic.h"
|
||||
#else
|
||||
#include "gicv3.h"
|
||||
#endif
|
||||
|
||||
/* exception and interrupt handler table */
|
||||
struct rt_irq_desc isr_table[MAX_HANDLERS];
|
||||
|
||||
#ifndef RT_USING_SMP
|
||||
/* Those varibles will be accessed in ISR, so we need to share them. */
|
||||
rt_uint32_t rt_interrupt_from_thread = 0;
|
||||
rt_uint32_t rt_interrupt_to_thread = 0;
|
||||
rt_uint32_t rt_thread_switch_interrupt_flag = 0;
|
||||
|
||||
#ifdef RT_USING_HOOK
|
||||
static void (*rt_interrupt_switch_hook)(void);
|
||||
|
||||
void rt_interrupt_switch_sethook(void (*hook)(void))
|
||||
{
|
||||
rt_interrupt_switch_hook = hook;
|
||||
}
|
||||
#endif
|
||||
|
||||
void rt_interrupt_hook(void)
|
||||
{
|
||||
RT_OBJECT_HOOK_CALL(rt_interrupt_switch_hook, ());
|
||||
}
|
||||
#endif
|
||||
|
||||
const unsigned int VECTOR_BASE = 0x00;
|
||||
extern void rt_cpu_vector_set_base(unsigned int addr);
|
||||
extern int system_vectors;
|
||||
|
||||
void rt_hw_vector_init(void)
|
||||
{
|
||||
rt_cpu_vector_set_base((unsigned int)&system_vectors);
|
||||
}
|
||||
|
||||
#ifdef RT_USING_GIC_V2
|
||||
/**
|
||||
* This function will initialize hardware interrupt
|
||||
*/
|
||||
void rt_hw_interrupt_init(void)
|
||||
{
|
||||
rt_uint32_t gic_cpu_base;
|
||||
rt_uint32_t gic_dist_base;
|
||||
rt_uint32_t gic_irq_start;
|
||||
|
||||
/* initialize vector table */
|
||||
rt_hw_vector_init();
|
||||
|
||||
/* initialize exceptions table */
|
||||
rt_memset(isr_table, 0x00, sizeof(isr_table));
|
||||
|
||||
/* initialize ARM GIC */
|
||||
#ifdef RT_USING_SMART
|
||||
gic_dist_base = (uint32_t)rt_ioremap((void*)platform_get_gic_dist_base(), 0x2000);
|
||||
gic_cpu_base = (uint32_t)rt_ioremap((void*)platform_get_gic_cpu_base(), 0x1000);
|
||||
#else
|
||||
gic_dist_base = platform_get_gic_dist_base();
|
||||
gic_cpu_base = platform_get_gic_cpu_base();
|
||||
#endif
|
||||
|
||||
gic_irq_start = GIC_IRQ_START;
|
||||
|
||||
arm_gic_dist_init(0, gic_dist_base, gic_irq_start);
|
||||
arm_gic_cpu_init(0, gic_cpu_base);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* This function will initialize hardware interrupt
|
||||
* Called by the primary cpu(cpu0)
|
||||
*/
|
||||
void rt_hw_interrupt_init(void)
|
||||
{
|
||||
rt_uint32_t gic_dist_base;
|
||||
rt_uint32_t gic_irq_start;
|
||||
|
||||
/* initialize vector table */
|
||||
rt_hw_vector_init();
|
||||
|
||||
/* initialize exceptions table */
|
||||
rt_memset(isr_table, 0x00, sizeof(isr_table));
|
||||
|
||||
/* initialize ARM GIC */
|
||||
#ifdef RT_USING_SMART
|
||||
gic_dist_base = (uint32_t)rt_ioremap((void*)platform_get_gic_dist_base(), 0x10000);
|
||||
#else
|
||||
gic_dist_base = platform_get_gic_dist_base();
|
||||
#endif
|
||||
|
||||
gic_irq_start = GIC_IRQ_START;
|
||||
|
||||
arm_gic_dist_init(0, gic_dist_base, gic_irq_start);
|
||||
|
||||
arm_gic_cpu_init(0);
|
||||
arm_gic_redist_init(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function will mask a interrupt.
|
||||
* @param vector the interrupt number
|
||||
*/
|
||||
void rt_hw_interrupt_mask(int vector)
|
||||
{
|
||||
arm_gic_mask(0, vector);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will un-mask a interrupt.
|
||||
* @param vector the interrupt number
|
||||
*/
|
||||
void rt_hw_interrupt_umask(int vector)
|
||||
{
|
||||
arm_gic_umask(0, vector);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the active interrupt number.
|
||||
* @param none
|
||||
*/
|
||||
int rt_hw_interrupt_get_irq(void)
|
||||
{
|
||||
return arm_gic_get_active_irq(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function acknowledges the interrupt.
|
||||
* @param vector the interrupt number
|
||||
*/
|
||||
void rt_hw_interrupt_ack(int vector)
|
||||
{
|
||||
arm_gic_ack(0, vector);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function set interrupt CPU targets.
|
||||
* @param vector: the interrupt number
|
||||
* cpu_mask: target cpus mask, one bit for one core
|
||||
*/
|
||||
void rt_hw_interrupt_set_target_cpus(int vector, unsigned int cpu_mask)
|
||||
{
|
||||
arm_gic_set_cpu(0, vector, cpu_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function get interrupt CPU targets.
|
||||
* @param vector: the interrupt number
|
||||
* @return target cpus mask, one bit for one core
|
||||
*/
|
||||
unsigned int rt_hw_interrupt_get_target_cpus(int vector)
|
||||
{
|
||||
return arm_gic_get_target_cpu(0, vector);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function set interrupt triger mode.
|
||||
* @param vector: the interrupt number
|
||||
* mode: interrupt triger mode; 0: level triger, 1: edge triger
|
||||
*/
|
||||
void rt_hw_interrupt_set_triger_mode(int vector, unsigned int mode)
|
||||
{
|
||||
arm_gic_set_configuration(0, vector, mode & IRQ_MODE_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function get interrupt triger mode.
|
||||
* @param vector: the interrupt number
|
||||
* @return interrupt triger mode; 0: level triger, 1: edge triger
|
||||
*/
|
||||
unsigned int rt_hw_interrupt_get_triger_mode(int vector)
|
||||
{
|
||||
return arm_gic_get_configuration(0, vector);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function set interrupt pending flag.
|
||||
* @param vector: the interrupt number
|
||||
*/
|
||||
void rt_hw_interrupt_set_pending(int vector)
|
||||
{
|
||||
arm_gic_set_pending_irq(0, vector);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function get interrupt pending flag.
|
||||
* @param vector: the interrupt number
|
||||
* @return interrupt pending flag, 0: not pending; 1: pending
|
||||
*/
|
||||
unsigned int rt_hw_interrupt_get_pending(int vector)
|
||||
{
|
||||
return arm_gic_get_pending_irq(0, vector);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function clear interrupt pending flag.
|
||||
* @param vector: the interrupt number
|
||||
*/
|
||||
void rt_hw_interrupt_clear_pending(int vector)
|
||||
{
|
||||
arm_gic_clear_pending_irq(0, vector);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function set interrupt priority value.
|
||||
* @param vector: the interrupt number
|
||||
* priority: the priority of interrupt to set
|
||||
*/
|
||||
void rt_hw_interrupt_set_priority(int vector, unsigned int priority)
|
||||
{
|
||||
arm_gic_set_priority(0, vector, priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function get interrupt priority.
|
||||
* @param vector: the interrupt number
|
||||
* @return interrupt priority value
|
||||
*/
|
||||
unsigned int rt_hw_interrupt_get_priority(int vector)
|
||||
{
|
||||
return arm_gic_get_priority(0, vector);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function set priority masking threshold.
|
||||
* @param priority: priority masking threshold
|
||||
*/
|
||||
void rt_hw_interrupt_set_priority_mask(unsigned int priority)
|
||||
{
|
||||
arm_gic_set_interface_prior_mask(0, priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function get priority masking threshold.
|
||||
* @param none
|
||||
* @return priority masking threshold
|
||||
*/
|
||||
unsigned int rt_hw_interrupt_get_priority_mask(void)
|
||||
{
|
||||
return arm_gic_get_interface_prior_mask(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function set priority grouping field split point.
|
||||
* @param bits: priority grouping field split point
|
||||
* @return 0: success; -1: failed
|
||||
*/
|
||||
int rt_hw_interrupt_set_prior_group_bits(unsigned int bits)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (bits < 8)
|
||||
{
|
||||
arm_gic_set_binary_point(0, (7 - bits));
|
||||
status = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = -1;
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function get priority grouping field split point.
|
||||
* @param none
|
||||
* @return priority grouping field split point
|
||||
*/
|
||||
unsigned int rt_hw_interrupt_get_prior_group_bits(void)
|
||||
{
|
||||
unsigned int bp;
|
||||
|
||||
bp = arm_gic_get_binary_point(0) & 0x07;
|
||||
|
||||
return (7 - bp);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will install a interrupt service routine to a interrupt.
|
||||
* @param vector the interrupt number
|
||||
* @param new_handler the interrupt service routine to be installed
|
||||
* @param old_handler the old interrupt service routine
|
||||
*/
|
||||
rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler,
|
||||
void *param, const char *name)
|
||||
{
|
||||
rt_isr_handler_t old_handler = RT_NULL;
|
||||
|
||||
if (vector < MAX_HANDLERS)
|
||||
{
|
||||
old_handler = isr_table[vector].handler;
|
||||
|
||||
if (handler != RT_NULL)
|
||||
{
|
||||
#ifdef RT_USING_INTERRUPT_INFO
|
||||
rt_strncpy(isr_table[vector].name, name, RT_NAME_MAX);
|
||||
#endif /* RT_USING_INTERRUPT_INFO */
|
||||
isr_table[vector].handler = handler;
|
||||
isr_table[vector].param = param;
|
||||
}
|
||||
}
|
||||
|
||||
return old_handler;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask)
|
||||
{
|
||||
#ifdef RT_USING_GIC_V2
|
||||
arm_gic_send_sgi(0, ipi_vector, cpu_mask, 0);
|
||||
#else
|
||||
arm_gic_send_affinity_sgi(0, ipi_vector, cpu_mask, ROUTED_TO_SPEC);
|
||||
#endif
|
||||
}
|
||||
|
||||
void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler)
|
||||
{
|
||||
/* note: ipi_vector maybe different with irq_vector */
|
||||
rt_hw_interrupt_install(ipi_vector, ipi_isr_handler, 0, "IPI_HANDLER");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-06 Bernard first version
|
||||
*/
|
||||
|
||||
#ifndef __INTERRUPT_H__
|
||||
#define __INTERRUPT_H__
|
||||
|
||||
#include <rthw.h>
|
||||
#include <board.h>
|
||||
|
||||
#define INT_IRQ 0x00
|
||||
#define INT_FIQ 0x01
|
||||
|
||||
#define IRQ_MODE_TRIG_LEVEL (0x00) /* Trigger: level triggered interrupt */
|
||||
#define IRQ_MODE_TRIG_EDGE (0x01) /* Trigger: edge triggered interrupt */
|
||||
#define IRQ_MODE_MASK (0x01)
|
||||
|
||||
void rt_hw_vector_init(void);
|
||||
|
||||
void rt_hw_interrupt_init(void);
|
||||
void rt_hw_interrupt_mask(int vector);
|
||||
void rt_hw_interrupt_umask(int vector);
|
||||
|
||||
int rt_hw_interrupt_get_irq(void);
|
||||
void rt_hw_interrupt_ack(int vector);
|
||||
|
||||
void rt_hw_interrupt_set_target_cpus(int vector, unsigned int cpu_mask);
|
||||
unsigned int rt_hw_interrupt_get_target_cpus(int vector);
|
||||
|
||||
void rt_hw_interrupt_set_triger_mode(int vector, unsigned int mode);
|
||||
unsigned int rt_hw_interrupt_get_triger_mode(int vector);
|
||||
|
||||
void rt_hw_interrupt_set_pending(int vector);
|
||||
unsigned int rt_hw_interrupt_get_pending(int vector);
|
||||
void rt_hw_interrupt_clear_pending(int vector);
|
||||
|
||||
void rt_hw_interrupt_set_priority(int vector, unsigned int priority);
|
||||
unsigned int rt_hw_interrupt_get_priority(int vector);
|
||||
|
||||
void rt_hw_interrupt_set_priority_mask(unsigned int priority);
|
||||
unsigned int rt_hw_interrupt_get_priority_mask(void);
|
||||
|
||||
int rt_hw_interrupt_set_prior_group_bits(unsigned int bits);
|
||||
unsigned int rt_hw_interrupt_get_prior_group_bits(void);
|
||||
|
||||
rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler,
|
||||
void *param, const char *name);
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask);
|
||||
void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2012-01-10 bernard porting to AM1808
|
||||
* 2023-10-10 Shell Add permission control API
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
|
||||
#define DBG_TAG "hw.mmu"
|
||||
#define DBG_LVL DBG_LOG
|
||||
#include <rtdbg.h>
|
||||
|
||||
#include <board.h>
|
||||
#include "cp15.h"
|
||||
#include "mm_page.h"
|
||||
#include "mmu.h"
|
||||
#include <mm_aspace.h>
|
||||
#include <tlb.h>
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
#include <lwp_mm.h>
|
||||
#include <lwp_arch.h>
|
||||
#include "ioremap.h"
|
||||
#else
|
||||
#define KERNEL_VADDR_START 0
|
||||
#endif
|
||||
|
||||
/* level1 page table, each entry for 1MB memory. */
|
||||
volatile unsigned long MMUTable[4 * 1024] __attribute__((aligned(16 * 1024)));
|
||||
|
||||
unsigned long rt_hw_set_domain_register(unsigned long domain_val)
|
||||
{
|
||||
unsigned long old_domain;
|
||||
|
||||
asm volatile ("mrc p15, 0, %0, c3, c0\n" : "=r" (old_domain));
|
||||
asm volatile ("mcr p15, 0, %0, c3, c0\n" : :"r" (domain_val) : "memory");
|
||||
|
||||
return old_domain;
|
||||
}
|
||||
|
||||
void rt_hw_mmu_setmtt(rt_uint32_t vaddrStart, rt_uint32_t vaddrEnd,
|
||||
rt_uint32_t paddrStart, rt_uint32_t attr)
|
||||
{
|
||||
volatile rt_uint32_t *pTT;
|
||||
volatile int i, nSec;
|
||||
pTT = (rt_uint32_t *)MMUTable + (vaddrStart >> 20);
|
||||
nSec = (vaddrEnd >> 20) - (vaddrStart >> 20);
|
||||
for(i = 0; i <= nSec; i++)
|
||||
{
|
||||
*pTT = attr | (((paddrStart >> 20) + i) << 20);
|
||||
pTT++;
|
||||
}
|
||||
}
|
||||
|
||||
void init_mm_setup(unsigned int *mtbl, unsigned int size, unsigned int pv_off)
|
||||
{
|
||||
unsigned int va;
|
||||
|
||||
for (va = 0; va < 0x1000; va++)
|
||||
{
|
||||
unsigned int vaddr = (va << 20);
|
||||
|
||||
if (vaddr >= KERNEL_VADDR_START && vaddr - KERNEL_VADDR_START < size)
|
||||
{
|
||||
mtbl[va] = ((va << 20) + pv_off) | NORMAL_MEM;
|
||||
}
|
||||
else if (vaddr >= (KERNEL_VADDR_START + pv_off) && vaddr - (KERNEL_VADDR_START + pv_off) < size)
|
||||
{
|
||||
mtbl[va] = (va << 20) | NORMAL_MEM;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtbl[va] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef RT_USING_SMART
|
||||
static void _init_map_section(rt_uint32_t *mmu_table, rt_uint32_t va,
|
||||
rt_uint32_t size,rt_uint32_t pa, rt_uint32_t attr)
|
||||
{
|
||||
volatile rt_uint32_t *ptt;
|
||||
volatile int i, num_section;
|
||||
ptt = (rt_uint32_t *)mmu_table + (va >> ARCH_SECTION_SHIFT);
|
||||
num_section = size >> ARCH_SECTION_SHIFT;
|
||||
for(i = 0; i <= num_section; i++)
|
||||
{
|
||||
*ptt = attr | (((pa >> ARCH_SECTION_SHIFT) + i) << ARCH_SECTION_SHIFT);
|
||||
ptt++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void rt_hw_mem_setup_early(rt_uint32_t *early_mmu_talbe,
|
||||
rt_uint32_t pv_off)
|
||||
{
|
||||
rt_uint32_t size = 0;
|
||||
|
||||
size = 0x100000 + (rt_uint32_t)&__bss_end;
|
||||
size &= ~(0x100000 - 1);
|
||||
#ifdef RT_USING_SMART
|
||||
size -= KERNEL_VADDR_START;
|
||||
init_mm_setup(early_mmu_talbe, size, pv_off);
|
||||
#else
|
||||
rt_uint32_t normal_attr = NORMAL_MEM;
|
||||
extern unsigned char _reset;
|
||||
rt_uint32_t va = (rt_uint32_t) &_reset;
|
||||
/* The starting virtual address is aligned along 0x1000000. */
|
||||
va &= (0x1000000 - 1);
|
||||
size -= va;
|
||||
_init_map_section(early_mmu_talbe, va, size, va + pv_off, normal_attr);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void rt_hw_init_mmu_table(struct mem_desc *mdesc, rt_uint32_t size)
|
||||
{
|
||||
void *vaddr;
|
||||
size_t length;
|
||||
/* init kernel space */
|
||||
#ifdef RT_USING_SMART
|
||||
rt_aspace_init(&rt_kernel_space, (void *)USER_VADDR_TOP, -USER_VADDR_TOP, (void *)MMUTable);
|
||||
#else
|
||||
rt_aspace_init(&rt_kernel_space, (void *)0x1000, 0 - 0x1000, (void *)MMUTable);
|
||||
#endif /* RT_USING_SMART */
|
||||
|
||||
/* set page table */
|
||||
for(; size > 0; size--)
|
||||
{
|
||||
if (mdesc->paddr_start == (rt_uint32_t)ARCH_MAP_FAILED)
|
||||
mdesc->paddr_start = mdesc->vaddr_start + PV_OFFSET;
|
||||
|
||||
vaddr = (void *)mdesc->vaddr_start;
|
||||
length = mdesc->vaddr_end - mdesc->vaddr_start;
|
||||
rt_aspace_map_static(&rt_kernel_space, &mdesc->varea, &vaddr, length,
|
||||
mdesc->attr, MMF_MAP_FIXED, &rt_mm_dummy_mapper, 0);
|
||||
|
||||
rt_hw_mmu_setmtt(mdesc->vaddr_start, mdesc->vaddr_end,
|
||||
mdesc->paddr_start, mdesc->attr);
|
||||
mdesc++;
|
||||
}
|
||||
|
||||
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, (void*)MMUTable, sizeof MMUTable);
|
||||
}
|
||||
|
||||
void rt_hw_mmu_init(void)
|
||||
{
|
||||
rt_cpu_dcache_clean_flush();
|
||||
rt_cpu_icache_flush();
|
||||
rt_hw_cpu_dcache_disable();
|
||||
rt_hw_cpu_icache_disable();
|
||||
rt_cpu_mmu_disable();
|
||||
|
||||
/*rt_hw_cpu_dump_page_table(MMUTable);*/
|
||||
rt_hw_set_domain_register(0x55555555);
|
||||
|
||||
rt_cpu_tlb_set(MMUTable);
|
||||
|
||||
rt_cpu_mmu_enable();
|
||||
|
||||
rt_hw_cpu_icache_enable();
|
||||
rt_hw_cpu_dcache_enable();
|
||||
}
|
||||
|
||||
int rt_hw_mmu_map_init(struct rt_aspace *aspace, void* v_address, size_t size, size_t *vtable, size_t pv_off)
|
||||
{
|
||||
size_t l1_off, va_s, va_e;
|
||||
|
||||
if (!aspace || !vtable)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
va_s = (size_t)v_address;
|
||||
va_e = (size_t)v_address + size - 1;
|
||||
|
||||
if ( va_e < va_s)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
va_s >>= ARCH_SECTION_SHIFT;
|
||||
va_e >>= ARCH_SECTION_SHIFT;
|
||||
|
||||
if (va_s == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (l1_off = va_s; l1_off <= va_e; l1_off++)
|
||||
{
|
||||
size_t v = vtable[l1_off];
|
||||
|
||||
if (v & ARCH_MMU_USED_MASK)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
rt_ioremap_start = v_address;
|
||||
rt_ioremap_size = size;
|
||||
rt_mpr_start = rt_ioremap_start - rt_mpr_size;
|
||||
#else
|
||||
rt_mpr_start = (void *)((rt_size_t)0 - rt_mpr_size);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rt_hw_mmu_ioremap_init(rt_aspace_t aspace, void* v_address, size_t size)
|
||||
{
|
||||
#ifdef RT_IOREMAP_LATE
|
||||
size_t loop_va;
|
||||
size_t l1_off;
|
||||
size_t *mmu_l1, *mmu_l2;
|
||||
size_t sections;
|
||||
|
||||
/* for kernel ioremap */
|
||||
if ((size_t)v_address < KERNEL_VADDR_START)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
/* must align to section */
|
||||
if ((size_t)v_address & ARCH_SECTION_MASK)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
/* must align to section */
|
||||
if (size & ARCH_SECTION_MASK)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
loop_va = (size_t)v_address;
|
||||
sections = (size >> ARCH_SECTION_SHIFT);
|
||||
while (sections--)
|
||||
{
|
||||
l1_off = (loop_va >> ARCH_SECTION_SHIFT);
|
||||
mmu_l1 = (size_t*)aspace->page_table + l1_off;
|
||||
|
||||
RT_ASSERT((*mmu_l1 & ARCH_MMU_USED_MASK) == 0);
|
||||
mmu_l2 = (size_t*)rt_pages_alloc(0);
|
||||
if (mmu_l2)
|
||||
{
|
||||
rt_memset(mmu_l2, 0, ARCH_PAGE_TBL_SIZE * 2);
|
||||
/* cache maintain */
|
||||
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_l2, ARCH_PAGE_TBL_SIZE);
|
||||
|
||||
*mmu_l1 = (((size_t)mmu_l2 + PV_OFFSET) | 0x1);
|
||||
/* cache maintain */
|
||||
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_l1, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* error */
|
||||
return -1;
|
||||
}
|
||||
|
||||
loop_va += ARCH_SECTION_SIZE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void _kenrel_unmap_4K(unsigned long *lv0_tbl, void *v_addr)
|
||||
{
|
||||
size_t loop_va = (size_t)v_addr & ~ARCH_PAGE_MASK;
|
||||
size_t l1_off, l2_off;
|
||||
size_t *mmu_l1, *mmu_l2;
|
||||
|
||||
l1_off = (loop_va >> ARCH_SECTION_SHIFT);
|
||||
|
||||
l2_off = ((loop_va & ARCH_SECTION_MASK) >> ARCH_PAGE_SHIFT);
|
||||
mmu_l1 = (size_t *)lv0_tbl + l1_off;
|
||||
|
||||
if (*mmu_l1 & ARCH_MMU_USED_MASK)
|
||||
{
|
||||
mmu_l2 = (size_t *)((*mmu_l1 & ~ARCH_PAGE_TBL_MASK) - PV_OFFSET);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (*(mmu_l2 + l2_off) & ARCH_MMU_USED_MASK)
|
||||
{
|
||||
*(mmu_l2 + l2_off) = 0;
|
||||
/* cache maintain */
|
||||
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_l2 + l2_off, 4);
|
||||
|
||||
if (rt_pages_free(mmu_l2, 0))
|
||||
{
|
||||
*mmu_l1 = 0;
|
||||
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_l1, 4);
|
||||
}
|
||||
}
|
||||
loop_va += ARCH_PAGE_SIZE;
|
||||
}
|
||||
|
||||
static int _kenrel_map_4K(unsigned long *lv0_tbl, void *v_addr, void *p_addr,
|
||||
size_t attr)
|
||||
{
|
||||
size_t loop_va = (size_t)v_addr & ~ARCH_PAGE_MASK;
|
||||
size_t loop_pa = (size_t)p_addr & ~ARCH_PAGE_MASK;
|
||||
size_t l1_off, l2_off;
|
||||
size_t *mmu_l1, *mmu_l2;
|
||||
|
||||
l1_off = (loop_va >> ARCH_SECTION_SHIFT);
|
||||
l2_off = ((loop_va & ARCH_SECTION_MASK) >> ARCH_PAGE_SHIFT);
|
||||
mmu_l1 = (size_t *)lv0_tbl + l1_off;
|
||||
|
||||
if (*mmu_l1 & ARCH_MMU_USED_MASK)
|
||||
{
|
||||
mmu_l2 = (size_t *)((*mmu_l1 & ~ARCH_PAGE_TBL_MASK) - PV_OFFSET);
|
||||
rt_page_ref_inc(mmu_l2, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mmu_l2 = (size_t *)rt_pages_alloc(0);
|
||||
if (mmu_l2)
|
||||
{
|
||||
rt_memset(mmu_l2, 0, ARCH_PAGE_TBL_SIZE * 2);
|
||||
/* cache maintain */
|
||||
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_l2, ARCH_PAGE_TBL_SIZE);
|
||||
|
||||
*mmu_l1 = (((size_t)mmu_l2 + PV_OFFSET) | 0x1);
|
||||
/* cache maintain */
|
||||
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_l1, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* error, quit */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*(mmu_l2 + l2_off) = (loop_pa | attr);
|
||||
/* cache maintain */
|
||||
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_l2 + l2_off, 4);
|
||||
|
||||
loop_va += ARCH_PAGE_SIZE;
|
||||
loop_pa += ARCH_PAGE_SIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *rt_hw_mmu_map(rt_aspace_t aspace, void *v_addr, void *p_addr, size_t size,
|
||||
size_t attr)
|
||||
{
|
||||
int ret = -1;
|
||||
void *unmap_va = v_addr;
|
||||
size_t npages = size >> ARCH_PAGE_SHIFT;
|
||||
|
||||
// TODO trying with HUGEPAGE here
|
||||
while (npages--)
|
||||
{
|
||||
ret = _kenrel_map_4K(aspace->page_table, v_addr, p_addr, attr);
|
||||
if (ret != 0)
|
||||
{
|
||||
/* error, undo map */
|
||||
while (unmap_va != v_addr)
|
||||
{
|
||||
rt_enter_critical();
|
||||
_kenrel_unmap_4K(aspace->page_table, (void *)unmap_va);
|
||||
rt_exit_critical();
|
||||
|
||||
unmap_va += ARCH_PAGE_SIZE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
v_addr += ARCH_PAGE_SIZE;
|
||||
p_addr += ARCH_PAGE_SIZE;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
return v_addr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rt_hw_mmu_unmap(rt_aspace_t aspace, void *v_addr, size_t size)
|
||||
{
|
||||
// caller guarantee that v_addr & size are page aligned
|
||||
size_t npages = size >> ARCH_PAGE_SHIFT;
|
||||
|
||||
if (!aspace->page_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (npages--)
|
||||
{
|
||||
rt_enter_critical();
|
||||
_kenrel_unmap_4K(aspace->page_table, v_addr);
|
||||
rt_exit_critical();
|
||||
|
||||
v_addr += ARCH_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
void rt_hw_aspace_switch(rt_aspace_t aspace)
|
||||
{
|
||||
if (aspace != &rt_kernel_space)
|
||||
{
|
||||
void *pgtbl = aspace->page_table;
|
||||
pgtbl = rt_kmem_v2p(pgtbl);
|
||||
|
||||
rt_hw_mmu_switch(pgtbl);
|
||||
|
||||
rt_hw_tlb_invalidate_all_local();
|
||||
}
|
||||
}
|
||||
|
||||
void *rt_hw_mmu_v2p(rt_aspace_t aspace, void* v_addr)
|
||||
{
|
||||
size_t l1_off, l2_off;
|
||||
size_t *mmu_l1, *mmu_l2;
|
||||
size_t tmp;
|
||||
size_t pa;
|
||||
|
||||
l1_off = (size_t)v_addr >> ARCH_SECTION_SHIFT;
|
||||
|
||||
RT_ASSERT(aspace);
|
||||
|
||||
mmu_l1 = (size_t*)aspace->page_table + l1_off;
|
||||
|
||||
tmp = *mmu_l1;
|
||||
|
||||
switch (tmp & ARCH_MMU_USED_MASK)
|
||||
{
|
||||
case 0: /* not used */
|
||||
break;
|
||||
case 1: /* page table */
|
||||
mmu_l2 = (size_t *)((tmp & ~ARCH_PAGE_TBL_MASK) - PV_OFFSET);
|
||||
l2_off = (((size_t)v_addr & ARCH_SECTION_MASK) >> ARCH_PAGE_SHIFT);
|
||||
pa = *(mmu_l2 + l2_off);
|
||||
if (pa & ARCH_MMU_USED_MASK)
|
||||
{
|
||||
if ((pa & ARCH_MMU_USED_MASK) == 1)
|
||||
{
|
||||
/* large page, not support */
|
||||
break;
|
||||
}
|
||||
pa &= ~(ARCH_PAGE_MASK);
|
||||
pa += ((size_t)v_addr & ARCH_PAGE_MASK);
|
||||
return (void*)pa;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
/* section */
|
||||
if (tmp & ARCH_TYPE_SUPERSECTION)
|
||||
{
|
||||
/* super section, not support */
|
||||
break;
|
||||
}
|
||||
pa = (tmp & ~ARCH_SECTION_MASK);
|
||||
pa += ((size_t)v_addr & ARCH_SECTION_MASK);
|
||||
return (void*)pa;
|
||||
}
|
||||
return ARCH_MAP_FAILED;
|
||||
}
|
||||
|
||||
int rt_hw_mmu_control(struct rt_aspace *aspace, void *vaddr, size_t size,
|
||||
enum rt_mmu_cntl cmd)
|
||||
{
|
||||
return -RT_ENOSYS;
|
||||
}
|
||||
|
||||
#define KPTE_START (KERNEL_VADDR_START >> ARCH_SECTION_SHIFT)
|
||||
|
||||
void *rt_hw_mmu_pgtbl_create(void)
|
||||
{
|
||||
size_t *mmu_table;
|
||||
mmu_table = (size_t *)rt_pages_alloc_ext(2, PAGE_ANY_AVAILABLE);
|
||||
if (!mmu_table)
|
||||
{
|
||||
return RT_NULL;
|
||||
}
|
||||
rt_memcpy(mmu_table + KPTE_START, (size_t *)rt_kernel_space.page_table + KPTE_START, ARCH_PAGE_SIZE);
|
||||
rt_memset(mmu_table, 0, 3 * ARCH_PAGE_SIZE);
|
||||
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_table, 4 * ARCH_PAGE_SIZE);
|
||||
|
||||
return mmu_table;
|
||||
}
|
||||
|
||||
void rt_hw_mmu_pgtbl_delete(void *pgtbl)
|
||||
{
|
||||
rt_pages_free(pgtbl, 2);
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2019-03-25 quanzhao the first version
|
||||
* 2023-10-10 Shell Add permission control API
|
||||
*/
|
||||
#ifndef __MMU_H_
|
||||
#define __MMU_H_
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <mm_aspace.h>
|
||||
|
||||
#define DESC_SEC (0x2)
|
||||
#define MEMWBWA ((1<<12)|(3<<2)) /* write back, write allocate */
|
||||
#define MEMWB (3<<2) /* write back, no write allocate */
|
||||
#define MEMWT (2<<2) /* write through, no write allocate */
|
||||
#define SHAREDEVICE (1<<2) /* shared device */
|
||||
#define STRONGORDER (0<<2) /* strong ordered */
|
||||
#define XN (1<<4) /* eXecute Never */
|
||||
#ifdef RT_USING_SMART
|
||||
#define AP_RW (1<<10) /* supervisor=RW, user=No */
|
||||
#define AP_RO ((1<<10) |(1 << 15)) /* supervisor=RW, user=No */
|
||||
#else
|
||||
#define AP_RW (3<<10) /* supervisor=RW, user=RW */
|
||||
#define AP_RO (2<<10) /* supervisor=RW, user=RO */
|
||||
#endif
|
||||
|
||||
#define SHARED (1<<16) /* shareable */
|
||||
|
||||
#define DOMAIN_FAULT (0x0)
|
||||
#define DOMAIN_CHK (0x1)
|
||||
#define DOMAIN_NOTCHK (0x3)
|
||||
#define DOMAIN0 (0x0<<5)
|
||||
#define DOMAIN1 (0x1<<5)
|
||||
|
||||
#define DOMAIN0_ATTR (DOMAIN_CHK<<0)
|
||||
#define DOMAIN1_ATTR (DOMAIN_FAULT<<2)
|
||||
|
||||
/* device mapping type */
|
||||
#define DEVICE_MEM (SHARED|AP_RW|DOMAIN0|SHAREDEVICE|DESC_SEC|XN)
|
||||
/* normal memory mapping type */
|
||||
#define NORMAL_MEM (SHARED|AP_RW|DOMAIN0|MEMWBWA|DESC_SEC)
|
||||
|
||||
#define STRONG_ORDER_MEM (SHARED|AP_RO|XN|DESC_SEC)
|
||||
|
||||
struct mem_desc
|
||||
{
|
||||
rt_uint32_t vaddr_start;
|
||||
rt_uint32_t vaddr_end;
|
||||
rt_uint32_t paddr_start;
|
||||
rt_uint32_t attr;
|
||||
struct rt_varea varea;
|
||||
};
|
||||
|
||||
#define MMU_MAP_MTBL_XN (1<<0)
|
||||
#define MMU_MAP_MTBL_A (1<<1)
|
||||
#define MMU_MAP_MTBL_B (1<<2)
|
||||
#define MMU_MAP_MTBL_C (1<<3)
|
||||
#define MMU_MAP_MTBL_AP01(x) (x<<4)
|
||||
#define MMU_MAP_MTBL_TEX(x) (x<<6)
|
||||
#define MMU_MAP_MTBL_AP2(x) (x<<9)
|
||||
#define MMU_MAP_MTBL_SHARE (1<<10)
|
||||
#define MMU_MAP_MTBL_NG(x) (x<<11)
|
||||
|
||||
#define MMU_MAP_K_ROCB ((MMU_MAP_MTBL_NG(0))|(MMU_MAP_MTBL_A|MMU_MAP_MTBL_AP2(1)|MMU_MAP_MTBL_AP01(1)|MMU_MAP_MTBL_TEX(0)|MMU_MAP_MTBL_B|MMU_MAP_MTBL_C|MMU_MAP_MTBL_SHARE))
|
||||
#define MMU_MAP_K_RO ((MMU_MAP_MTBL_NG(0))|(MMU_MAP_MTBL_A|MMU_MAP_MTBL_AP2(1)|MMU_MAP_MTBL_AP01(1)|MMU_MAP_MTBL_TEX(0)|MMU_MAP_MTBL_C|MMU_MAP_MTBL_SHARE))
|
||||
#define MMU_MAP_K_RWCB ((MMU_MAP_MTBL_NG(0))|(MMU_MAP_MTBL_A|MMU_MAP_MTBL_AP2(0)|MMU_MAP_MTBL_AP01(1)|MMU_MAP_MTBL_TEX(0)|MMU_MAP_MTBL_B|MMU_MAP_MTBL_C|MMU_MAP_MTBL_SHARE))
|
||||
#define MMU_MAP_K_RW ((MMU_MAP_MTBL_NG(0))|(MMU_MAP_MTBL_A|MMU_MAP_MTBL_AP2(0)|MMU_MAP_MTBL_AP01(1)|MMU_MAP_MTBL_TEX(0)|MMU_MAP_MTBL_SHARE))
|
||||
#define MMU_MAP_K_DEVICE ((MMU_MAP_MTBL_NG(0))|(MMU_MAP_MTBL_A|MMU_MAP_MTBL_AP2(0)|MMU_MAP_MTBL_AP01(1)|MMU_MAP_MTBL_TEX(0)|MMU_MAP_MTBL_B|MMU_MAP_MTBL_SHARE))
|
||||
#define MMU_MAP_U_ROCB ((MMU_MAP_MTBL_NG(1))|(MMU_MAP_MTBL_A|MMU_MAP_MTBL_AP2(0)|MMU_MAP_MTBL_AP01(2)|MMU_MAP_MTBL_TEX(0)|MMU_MAP_MTBL_B|MMU_MAP_MTBL_C|MMU_MAP_MTBL_SHARE))
|
||||
#define MMU_MAP_U_RO ((MMU_MAP_MTBL_NG(1))|(MMU_MAP_MTBL_A|MMU_MAP_MTBL_AP2(0)|MMU_MAP_MTBL_AP01(2)|MMU_MAP_MTBL_TEX(0)|MMU_MAP_MTBL_C|MMU_MAP_MTBL_SHARE))
|
||||
#define MMU_MAP_U_RWCB ((MMU_MAP_MTBL_NG(1))|(MMU_MAP_MTBL_A|MMU_MAP_MTBL_AP2(0)|MMU_MAP_MTBL_AP01(3)|MMU_MAP_MTBL_TEX(0)|MMU_MAP_MTBL_B|MMU_MAP_MTBL_C|MMU_MAP_MTBL_SHARE))
|
||||
#define MMU_MAP_U_RW ((MMU_MAP_MTBL_NG(1))|(MMU_MAP_MTBL_A|MMU_MAP_MTBL_AP2(0)|MMU_MAP_MTBL_AP01(3)|MMU_MAP_MTBL_TEX(0)|MMU_MAP_MTBL_SHARE))
|
||||
#define MMU_MAP_U_DEVICE ((MMU_MAP_MTBL_NG(1))|(MMU_MAP_MTBL_A|MMU_MAP_MTBL_AP2(0)|MMU_MAP_MTBL_AP01(3)|MMU_MAP_MTBL_TEX(0)|MMU_MAP_MTBL_B|MMU_MAP_MTBL_SHARE))
|
||||
#define MMU_MAP_TRACE(attr) (attr)
|
||||
|
||||
#define ARCH_SECTION_SHIFT 20
|
||||
#define ARCH_SECTION_SIZE (1 << ARCH_SECTION_SHIFT)
|
||||
#define ARCH_SECTION_MASK (ARCH_SECTION_SIZE - 1)
|
||||
#define ARCH_PAGE_SHIFT 12
|
||||
#define ARCH_PAGE_SIZE (1 << ARCH_PAGE_SHIFT)
|
||||
#define ARCH_PAGE_MASK (ARCH_PAGE_SIZE - 1)
|
||||
#define ARCH_PAGE_TBL_SHIFT 10
|
||||
#define ARCH_PAGE_TBL_SIZE (1 << ARCH_PAGE_TBL_SHIFT)
|
||||
#define ARCH_PAGE_TBL_MASK (ARCH_PAGE_TBL_SIZE - 1)
|
||||
|
||||
#define ARCH_MMU_USED_MASK 3
|
||||
|
||||
#define ARCH_TYPE_SUPERSECTION (1 << 18)
|
||||
|
||||
#define ARCH_ADDRESS_WIDTH_BITS 32
|
||||
#define ARCH_VADDR_WIDTH 32
|
||||
|
||||
/**
|
||||
* *info it's possible to map (-1ul & ~ARCH_PAGE_MASK) but a not aligned -1 is
|
||||
* never returned on a successful mapping
|
||||
*/
|
||||
#define ARCH_MAP_FAILED ((void *)-1)
|
||||
|
||||
#define RT_HW_MMU_PROT_READ 1
|
||||
#define RT_HW_MMU_PROT_WRITE 2
|
||||
#define RT_HW_MMU_PROT_EXECUTE 4
|
||||
#define RT_HW_MMU_PROT_KERNEL 8
|
||||
#define RT_HW_MMU_PROT_USER 16
|
||||
#define RT_HW_MMU_PROT_CACHE 32
|
||||
|
||||
int rt_hw_mmu_ioremap_init(struct rt_aspace *aspace, void *v_address, size_t size);
|
||||
void rt_hw_init_mmu_table(struct mem_desc *mdesc, rt_uint32_t size);
|
||||
|
||||
void rt_hw_mmu_setup(struct rt_aspace *aspace, struct mem_desc *mdesc, int desc_nr);
|
||||
void rt_hw_mmu_init(void);
|
||||
int rt_hw_mmu_map_init(struct rt_aspace *aspace, void *v_address, size_t size, size_t *vtable, size_t pv_off);
|
||||
void *rt_hw_mmu_map(struct rt_aspace *aspace, void *v_addr, void *p_addr, size_t size, size_t attr);
|
||||
void rt_hw_mmu_unmap(struct rt_aspace *aspace, void *v_addr, size_t size);
|
||||
|
||||
void rt_hw_aspace_switch(struct rt_aspace *aspace);
|
||||
void rt_hw_mmu_switch(void *tbl);
|
||||
|
||||
void *rt_hw_mmu_v2p(struct rt_aspace *aspace, void *vaddr);
|
||||
void rt_hw_mmu_kernel_map_init(struct rt_aspace *aspace, size_t vaddr_start, size_t size);
|
||||
void *rt_hw_mmu_tbl_get(void);
|
||||
|
||||
int rt_hw_mmu_control(struct rt_aspace *aspace, void *vaddr, size_t size, enum rt_mmu_cntl cmd);
|
||||
|
||||
void *rt_hw_mmu_pgtbl_create(void);
|
||||
void rt_hw_mmu_pgtbl_delete(void *pgtbl);
|
||||
|
||||
#define AP_APX_MASK (MMU_MAP_MTBL_AP2(0x1) | MMU_MAP_MTBL_AP01(0x3))
|
||||
#define AP_APX_URW_KRW (MMU_MAP_MTBL_AP2(0x0) | MMU_MAP_MTBL_AP01(0x3))
|
||||
#define AP_APX_URO_KRO (MMU_MAP_MTBL_AP2(0x1) | MMU_MAP_MTBL_AP01(0x2))
|
||||
|
||||
/**
|
||||
* @brief Remove permission from attribution
|
||||
*
|
||||
* @param attr architecture specified mmu attribution
|
||||
* @param prot protect that will be removed
|
||||
* @return size_t returned attribution
|
||||
*/
|
||||
rt_inline size_t rt_hw_mmu_attr_rm_perm(size_t attr, rt_base_t prot)
|
||||
{
|
||||
switch (prot)
|
||||
{
|
||||
/* remove write permission for user */
|
||||
case RT_HW_MMU_PROT_WRITE | RT_HW_MMU_PROT_USER:
|
||||
if ((attr & AP_APX_MASK) == AP_APX_URW_KRW)
|
||||
attr &= ~MMU_MAP_MTBL_AP01(0x1);
|
||||
break;
|
||||
case RT_HW_MMU_PROT_WRITE | RT_HW_MMU_PROT_KERNEL:
|
||||
switch (attr & AP_APX_MASK)
|
||||
{
|
||||
case MMU_MAP_MTBL_AP01(0):
|
||||
break;
|
||||
case MMU_MAP_MTBL_AP01(3):
|
||||
attr = (attr & AP_APX_MASK) | AP_APX_URO_KRO;
|
||||
default:
|
||||
attr |= MMU_MAP_MTBL_AP2(0x1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
RT_ASSERT(0);
|
||||
}
|
||||
return attr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add permission from attribution
|
||||
*
|
||||
* @param attr architecture specified mmu attribution
|
||||
* @param prot protect that will be added
|
||||
* @return size_t returned attribution
|
||||
*/
|
||||
rt_inline size_t rt_hw_mmu_attr_add_perm(size_t attr, rt_base_t prot)
|
||||
{
|
||||
switch (prot)
|
||||
{
|
||||
/* add write permission for user */
|
||||
case RT_HW_MMU_PROT_WRITE | RT_HW_MMU_PROT_USER:
|
||||
case RT_HW_MMU_PROT_WRITE | RT_HW_MMU_PROT_KERNEL:
|
||||
attr |= MMU_MAP_MTBL_AP01(0x3);
|
||||
attr &= ~MMU_MAP_MTBL_AP2(0x1);
|
||||
break;
|
||||
default:
|
||||
RT_ASSERT(0);
|
||||
}
|
||||
return attr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test permission from attribution
|
||||
*
|
||||
* @param attr architecture specified mmu attribution
|
||||
* @param prot protect that will be test
|
||||
* @return rt_bool_t RT_TRUE if the prot is allowed, otherwise RT_FALSE
|
||||
*/
|
||||
rt_inline rt_bool_t rt_hw_mmu_attr_test_perm(size_t attr, rt_base_t prot)
|
||||
{
|
||||
rt_bool_t rc = 0;
|
||||
switch (prot)
|
||||
{
|
||||
/* test write permission for user */
|
||||
case RT_HW_MMU_PROT_WRITE | RT_HW_MMU_PROT_USER:
|
||||
rc = (AP_APX_MASK & attr) == (AP_APX_URW_KRW);
|
||||
break;
|
||||
default:
|
||||
RT_ASSERT(0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
#include <rtthread.h>
|
||||
#include "pmu.h"
|
||||
|
||||
#define DBG_TAG "PMU"
|
||||
#define DBG_LVL DBG_WARNING
|
||||
#include <rtdbg.h>
|
||||
|
||||
void rt_hw_pmu_dump_feature(void)
|
||||
{
|
||||
unsigned long reg;
|
||||
|
||||
reg = rt_hw_pmu_get_control();
|
||||
LOG_D("ARM PMU Implementor: %c, ID code: %02x, %d counters\n",
|
||||
reg >> 24, (reg >> 16) & 0xff, (reg >> 11) & 0x1f);
|
||||
RT_ASSERT(ARM_PMU_CNTER_NR == ((reg >> 11) & 0x1f));
|
||||
RT_UNUSED(reg);
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
*/
|
||||
#ifndef __PMU_H__
|
||||
#define __PMU_H__
|
||||
|
||||
#include "board.h"
|
||||
|
||||
/* Number of counters */
|
||||
#define ARM_PMU_CNTER_NR 4
|
||||
|
||||
enum rt_hw_pmu_event_type {
|
||||
ARM_PMU_EVENT_PMNC_SW_INCR = 0x00,
|
||||
ARM_PMU_EVENT_L1_ICACHE_REFILL = 0x01,
|
||||
ARM_PMU_EVENT_ITLB_REFILL = 0x02,
|
||||
ARM_PMU_EVENT_L1_DCACHE_REFILL = 0x03,
|
||||
ARM_PMU_EVENT_L1_DCACHE_ACCESS = 0x04,
|
||||
ARM_PMU_EVENT_DTLB_REFILL = 0x05,
|
||||
ARM_PMU_EVENT_MEM_READ = 0x06,
|
||||
ARM_PMU_EVENT_MEM_WRITE = 0x07,
|
||||
ARM_PMU_EVENT_INSTR_EXECUTED = 0x08,
|
||||
ARM_PMU_EVENT_EXC_TAKEN = 0x09,
|
||||
ARM_PMU_EVENT_EXC_EXECUTED = 0x0A,
|
||||
ARM_PMU_EVENT_CID_WRITE = 0x0B,
|
||||
};
|
||||
|
||||
/* Enable bit */
|
||||
#define ARM_PMU_PMCR_E (0x01 << 0)
|
||||
/* Event counter reset */
|
||||
#define ARM_PMU_PMCR_P (0x01 << 1)
|
||||
/* Cycle counter reset */
|
||||
#define ARM_PMU_PMCR_C (0x01 << 2)
|
||||
/* Cycle counter divider */
|
||||
#define ARM_PMU_PMCR_D (0x01 << 3)
|
||||
|
||||
#ifdef __GNUC__
|
||||
rt_inline void rt_hw_pmu_enable_cnt(int divide64)
|
||||
{
|
||||
unsigned long pmcr;
|
||||
unsigned long pmcntenset;
|
||||
|
||||
asm volatile ("mrc p15, 0, %0, c9, c12, 0" : "=r"(pmcr));
|
||||
pmcr |= ARM_PMU_PMCR_E | ARM_PMU_PMCR_P | ARM_PMU_PMCR_C;
|
||||
if (divide64)
|
||||
pmcr |= ARM_PMU_PMCR_D;
|
||||
else
|
||||
pmcr &= ~ARM_PMU_PMCR_D;
|
||||
asm volatile ("mcr p15, 0, %0, c9, c12, 0" :: "r"(pmcr));
|
||||
|
||||
/* enable all the counters */
|
||||
pmcntenset = ~0;
|
||||
asm volatile ("mcr p15, 0, %0, c9, c12, 1" :: "r"(pmcntenset));
|
||||
/* clear overflows(just in case) */
|
||||
asm volatile ("mcr p15, 0, %0, c9, c12, 3" :: "r"(pmcntenset));
|
||||
}
|
||||
|
||||
rt_inline unsigned long rt_hw_pmu_get_control(void)
|
||||
{
|
||||
unsigned long pmcr;
|
||||
asm ("mrc p15, 0, %0, c9, c12, 0" : "=r"(pmcr));
|
||||
return pmcr;
|
||||
}
|
||||
|
||||
rt_inline unsigned long rt_hw_pmu_get_ceid(void)
|
||||
{
|
||||
unsigned long reg;
|
||||
/* only PMCEID0 is supported, PMCEID1 is RAZ. */
|
||||
asm ("mrc p15, 0, %0, c9, c12, 6" : "=r"(reg));
|
||||
return reg;
|
||||
}
|
||||
|
||||
rt_inline unsigned long rt_hw_pmu_get_cnten(void)
|
||||
{
|
||||
unsigned long pmcnt;
|
||||
asm ("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcnt));
|
||||
return pmcnt;
|
||||
}
|
||||
|
||||
rt_inline void rt_hw_pmu_reset_cycle(void)
|
||||
{
|
||||
unsigned long pmcr;
|
||||
|
||||
asm volatile ("mrc p15, 0, %0, c9, c12, 0" : "=r"(pmcr));
|
||||
pmcr |= ARM_PMU_PMCR_C;
|
||||
asm volatile ("mcr p15, 0, %0, c9, c12, 0" :: "r"(pmcr));
|
||||
asm volatile ("isb");
|
||||
}
|
||||
|
||||
rt_inline void rt_hw_pmu_reset_event(void)
|
||||
{
|
||||
unsigned long pmcr;
|
||||
|
||||
asm volatile ("mrc p15, 0, %0, c9, c12, 0" : "=r"(pmcr));
|
||||
pmcr |= ARM_PMU_PMCR_P;
|
||||
asm volatile ("mcr p15, 0, %0, c9, c12, 0" :: "r"(pmcr));
|
||||
asm volatile ("isb");
|
||||
}
|
||||
|
||||
rt_inline unsigned long rt_hw_pmu_get_cycle(void)
|
||||
{
|
||||
unsigned long cyc;
|
||||
asm volatile ("isb");
|
||||
asm volatile ("mrc p15, 0, %0, c9, c13, 0" : "=r"(cyc));
|
||||
return cyc;
|
||||
}
|
||||
|
||||
rt_inline void rt_hw_pmu_select_counter(int idx)
|
||||
{
|
||||
RT_ASSERT(idx < ARM_PMU_CNTER_NR);
|
||||
|
||||
asm volatile ("mcr p15, 0, %0, c9, c12, 5" : : "r"(idx));
|
||||
/* Linux add an isb here, don't know why here. */
|
||||
asm volatile ("isb");
|
||||
}
|
||||
|
||||
rt_inline void rt_hw_pmu_select_event(int idx,
|
||||
enum rt_hw_pmu_event_type eve)
|
||||
{
|
||||
RT_ASSERT(idx < ARM_PMU_CNTER_NR);
|
||||
|
||||
rt_hw_pmu_select_counter(idx);
|
||||
asm volatile ("mcr p15, 0, %0, c9, c13, 1" : : "r"(eve));
|
||||
}
|
||||
|
||||
rt_inline unsigned long rt_hw_pmu_read_counter(int idx)
|
||||
{
|
||||
unsigned long reg;
|
||||
|
||||
rt_hw_pmu_select_counter(idx);
|
||||
asm volatile ("isb");
|
||||
asm volatile ("mrc p15, 0, %0, c9, c13, 2" : "=r"(reg));
|
||||
return reg;
|
||||
}
|
||||
|
||||
rt_inline unsigned long rt_hw_pmu_get_ovsr(void)
|
||||
{
|
||||
unsigned long reg;
|
||||
asm volatile ("isb");
|
||||
asm ("mrc p15, 0, %0, c9, c12, 3" : "=r"(reg));
|
||||
return reg;
|
||||
}
|
||||
|
||||
rt_inline void rt_hw_pmu_clear_ovsr(unsigned long reg)
|
||||
{
|
||||
asm ("mcr p15, 0, %0, c9, c12, 3" : : "r"(reg));
|
||||
asm volatile ("isb");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void rt_hw_pmu_dump_feature(void);
|
||||
|
||||
#endif /* end of include guard: __PMU_H__ */
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2011-09-23 Bernard the first version
|
||||
* 2011-10-05 Bernard add thumb mode
|
||||
*/
|
||||
#include <rtthread.h>
|
||||
#include <cpuport.h>
|
||||
#include <board.h>
|
||||
|
||||
/**
|
||||
* @addtogroup ARM Cortex-A
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* This function will initialize thread stack
|
||||
*
|
||||
* @param tentry the entry of thread
|
||||
* @param parameter the parameter of entry
|
||||
* @param stack_addr the beginning stack address
|
||||
* @param texit the function will be called when thread exit
|
||||
*
|
||||
* @return stack address
|
||||
*/
|
||||
rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter,
|
||||
rt_uint8_t *stack_addr, void *texit)
|
||||
{
|
||||
rt_uint32_t *stk;
|
||||
|
||||
stack_addr += sizeof(rt_uint32_t);
|
||||
stack_addr = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stack_addr, 8);
|
||||
stk = (rt_uint32_t *)stack_addr;
|
||||
*(--stk) = (rt_uint32_t)_thread_start; /* entry point */
|
||||
*(--stk) = (rt_uint32_t)texit; /* lr */
|
||||
*(--stk) = 0xdeadbeef; /* r12 */
|
||||
*(--stk) = 0xdeadbeef; /* r11 */
|
||||
*(--stk) = 0xdeadbeef; /* r10 */
|
||||
*(--stk) = 0xdeadbeef; /* r9 */
|
||||
*(--stk) = 0xdeadbeef; /* r8 */
|
||||
*(--stk) = 0xdeadbeef; /* r7 */
|
||||
*(--stk) = 0xdeadbeef; /* r6 */
|
||||
*(--stk) = 0xdeadbeef; /* r5 */
|
||||
*(--stk) = 0xdeadbeef; /* r4 */
|
||||
*(--stk) = 0xdeadbeef; /* r3 */
|
||||
*(--stk) = 0xdeadbeef; /* r2 */
|
||||
*(--stk) = (rt_uint32_t)tentry; /* r1 : argument 2 for trampoline */
|
||||
*(--stk) = (rt_uint32_t)parameter; /* r0 : argument 1 */
|
||||
/* cpsr */
|
||||
if ((rt_uint32_t)tentry & 0x01)
|
||||
*(--stk) = SVCMODE | 0x20; /* thumb mode */
|
||||
else
|
||||
*(--stk) = SVCMODE; /* arm mode */
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
*(--stk) = 0; /* user lr */
|
||||
*(--stk) = 0; /* user sp*/
|
||||
#endif
|
||||
#ifdef RT_USING_FPU
|
||||
*(--stk) = 0; /* not use fpu*/
|
||||
#endif
|
||||
|
||||
/* return task's current stack address */
|
||||
return (rt_uint8_t *)stk;
|
||||
}
|
||||
|
||||
/*@}*/
|
||||
@@ -0,0 +1,695 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-05 Bernard the first version
|
||||
* 2018-11-22 Jesven in the interrupt context, use rt_scheduler_do_irq_switch checks
|
||||
* and switches to a new thread
|
||||
* 2024-01-16 huanghe restructure this code section following the aarch64 architectural style
|
||||
*/
|
||||
|
||||
#include "rtconfig.h"
|
||||
|
||||
#define ARM_CPU_STACK_SIZE_OFFSET 12
|
||||
#define ARM_CPU_STACK_SIZE (1<<ARM_CPU_STACK_SIZE_OFFSET)
|
||||
|
||||
.equ Mode_USR, 0x10
|
||||
.equ Mode_FIQ, 0x11
|
||||
.equ Mode_IRQ, 0x12
|
||||
.equ Mode_SVC, 0x13
|
||||
.equ Mode_ABT, 0x17
|
||||
.equ Mode_UND, 0x1B
|
||||
.equ Mode_SYS, 0x1F
|
||||
|
||||
.equ I_Bit, 0x80 /* when I bit is set, IRQ is disabled */
|
||||
.equ F_Bit, 0x40 /* when F bit is set, FIQ is disabled */
|
||||
|
||||
/*Load the physical address of a symbol into a register.
|
||||
Through pv_off calculates the offset of the physical address */
|
||||
.macro get_phy, reg, symbol, _pvoff
|
||||
ldr \reg, =\symbol
|
||||
add \reg, \_pvoff
|
||||
.endm
|
||||
/*Calculate the offset between the physical address and the virtual address of the "_reset".*/
|
||||
.macro get_pvoff, tmp, out
|
||||
ldr \tmp, =_reset
|
||||
adr \out, _reset
|
||||
sub \out, \out, \tmp
|
||||
.endm
|
||||
|
||||
pv_off .req r11 /* Used to store the offset between physical address and the virtual address */
|
||||
cpu_id .req r10 /* Used to store the cpu id */
|
||||
|
||||
/* reset entry */
|
||||
.globl _reset
|
||||
_reset:
|
||||
/* Calculate the offset between the physical address and the virtual address */
|
||||
get_pvoff r0, pv_off
|
||||
|
||||
/* exit hyp mode */
|
||||
bl init_cpu_mode
|
||||
/* clear bss section */
|
||||
bl init_kernel_bss
|
||||
/* Initializes the assembly environment stack */
|
||||
bl init_cpu_stack_early
|
||||
|
||||
/* init mmu */
|
||||
b init_mmu_page_table_early
|
||||
|
||||
init_cpu_stack_early:
|
||||
|
||||
cps #Mode_SVC
|
||||
|
||||
get_phy r0, svc_stack_top, pv_off
|
||||
mov sp, r0
|
||||
|
||||
#ifdef RT_USING_FPU
|
||||
mov r4, #0xfffffff
|
||||
mcr p15, 0, r4, c1, c0, 2
|
||||
#endif
|
||||
|
||||
mov pc, lr
|
||||
|
||||
init_kernel_bss:
|
||||
|
||||
/* enable I cache + branch prediction */
|
||||
mrc p15, 0, r0, c1, c0, 0
|
||||
orr r0, r0, #(1<<12)
|
||||
orr r0, r0, #(1<<11)
|
||||
mcr p15, 0, r0, c1, c0, 0
|
||||
|
||||
mov r0,#0 /* get a zero */
|
||||
get_phy r1, __bss_start, pv_off
|
||||
get_phy r2, __bss_end, pv_off
|
||||
|
||||
bss_loop:
|
||||
cmp r1,r2 /* check if data to clear */
|
||||
strlo r0,[r1],#4 /* clear 4 bytes */
|
||||
blo bss_loop /* loop until done */
|
||||
|
||||
mov pc, lr
|
||||
|
||||
init_cpu_mode:
|
||||
|
||||
#ifdef ARCH_ARMV8
|
||||
/* Check for HYP mode */
|
||||
mrs r0, cpsr_all
|
||||
and r0, r0, #0x1F
|
||||
mov r8, #0x1A
|
||||
cmp r0, r8
|
||||
beq overHyped
|
||||
b continue_exit
|
||||
|
||||
overHyped: /* Get out of HYP mode */
|
||||
mov r9, lr
|
||||
/* HYP mode has a dedicated register, called ELR_hyp,
|
||||
to store the exception return address.
|
||||
The lr register needs to be temporarily saved,
|
||||
otherwise "mov pc lr" cannot be used after switching modes. */
|
||||
adr r1, continue_exit
|
||||
msr ELR_hyp, r1
|
||||
mrs r1, cpsr_all
|
||||
and r1, r1, #0xFFFFFFE0 /* CPSR_MODE_MASK */
|
||||
orr r1, r1, #0x13 /* CPSR_MODE_SUPERVISOR */
|
||||
msr SPSR_hyp, r1
|
||||
eret
|
||||
|
||||
continue_exit:
|
||||
mov lr ,r9
|
||||
|
||||
#endif
|
||||
#ifdef SOC_BCM283x
|
||||
/* Suspend the other cpu cores */
|
||||
mrc p15, 0, r0, c0, c0, 5
|
||||
ands r0, #3
|
||||
bne _halt
|
||||
|
||||
/* Disable IRQ & FIQ */
|
||||
cpsid if
|
||||
|
||||
/* Check for HYP mode */
|
||||
mrs r0, cpsr_all
|
||||
and r0, r0, #0x1F
|
||||
mov r8, #0x1A
|
||||
cmp r0, r8
|
||||
beq overHyped
|
||||
b continue_exit
|
||||
|
||||
overHyped: /* Get out of HYP mode */
|
||||
mov r9, lr
|
||||
/* HYP mode has a dedicated register, called ELR_hyp,
|
||||
to store the exception return address.
|
||||
The lr register needs to be temporarily saved,
|
||||
otherwise "mov pc lr" cannot be used after switching modes. */
|
||||
adr r1, continue_exit
|
||||
msr ELR_hyp, r1
|
||||
mrs r1, cpsr_all
|
||||
and r1, r1, #0xFFFFFFE0 /* CPSR_MODE_MASK */
|
||||
orr r1, r1, #0x13 /* CPSR_MODE_SUPERVISOR */
|
||||
msr SPSR_hyp, r1
|
||||
eret
|
||||
|
||||
continue_exit:
|
||||
mov lr ,r9
|
||||
/* set the cpu to SVC32 mode and disable interrupt */
|
||||
mrs r0, cpsr
|
||||
bic r0, r0, #0x1f
|
||||
orr r0, r0, #0x13
|
||||
msr cpsr_c, r0
|
||||
|
||||
#endif
|
||||
|
||||
/* invalid tlb before enable mmu */
|
||||
mrc p15, 0, r0, c1, c0, 0
|
||||
bic r0, #1
|
||||
mcr p15, 0, r0, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c8, c7, 0
|
||||
mcr p15, 0, r0, c7, c5, 0 /* iciallu */
|
||||
mcr p15, 0, r0, c7, c5, 6 /* bpiall */
|
||||
dsb
|
||||
isb
|
||||
|
||||
mov pc, lr
|
||||
|
||||
init_mmu_page_table_early:
|
||||
get_phy r0, init_mtbl, pv_off
|
||||
mov r1, pv_off
|
||||
bl rt_hw_mem_setup_early
|
||||
|
||||
/* get cpu id */
|
||||
bl rt_hw_cpu_id_early
|
||||
mov cpu_id ,r0
|
||||
/* enable_mmu_page_table_early is changed to master_core_startup */
|
||||
ldr lr, =master_core_startup
|
||||
|
||||
cmp cpu_id, #0
|
||||
beq enable_mmu_page_table_early
|
||||
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
#ifdef RT_SMP_AUTO_BOOT
|
||||
/* if cpu id > 0, stop or wait */
|
||||
ldr r0, =secondary_cpu_entry
|
||||
mov r1, #0
|
||||
str r1, [r0] /* clean secondary_cpu_entry */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
secondary_loop:
|
||||
@ cpu core 1 goes into sleep until core 0 wakeup it
|
||||
wfe
|
||||
#ifdef RT_SMP_AUTO_BOOT
|
||||
ldr r1, =secondary_cpu_entry
|
||||
ldr r0, [r1]
|
||||
cmp r0, #0
|
||||
blxne r0 /* if(secondary_cpu_entry) secondary_cpu_entry(); */
|
||||
#endif /* RT_SMP_AUTO_BOOT */
|
||||
b secondary_loop
|
||||
|
||||
enable_mmu_page_table_early:
|
||||
/* init TTBR0 */
|
||||
get_phy r0, init_mtbl, pv_off
|
||||
mcr p15, #0, r0, c2, c0, #0
|
||||
dmb
|
||||
|
||||
ldr r0,=#0x55555555
|
||||
mcr p15, #0, r0, c3, c0, #0
|
||||
|
||||
/* disable ttbr1 */
|
||||
mov r0, #(1 << 5) /* PD1=1 */
|
||||
mcr p15, 0, r0, c2, c0, 2 /* ttbcr */
|
||||
|
||||
|
||||
/* init stack for cpu mod */
|
||||
cps #Mode_UND
|
||||
ldr r1,=und_stack_top
|
||||
sub sp, r1, cpu_id, asl #ARM_CPU_STACK_SIZE_OFFSET
|
||||
|
||||
|
||||
cps #Mode_IRQ
|
||||
ldr r1, =irq_stack_top
|
||||
sub sp, r1, cpu_id, asl #ARM_CPU_STACK_SIZE_OFFSET
|
||||
|
||||
|
||||
cps #Mode_FIQ
|
||||
ldr r1, =irq_stack_top
|
||||
sub sp, r1, cpu_id, asl #ARM_CPU_STACK_SIZE_OFFSET
|
||||
|
||||
|
||||
cps #Mode_ABT
|
||||
ldr r1, =abt_stack_top
|
||||
sub sp, r1, cpu_id, asl #ARM_CPU_STACK_SIZE_OFFSET
|
||||
|
||||
|
||||
cps #Mode_SVC
|
||||
ldr r1, =svc_stack_top
|
||||
sub sp, r1, cpu_id, asl #ARM_CPU_STACK_SIZE_OFFSET
|
||||
|
||||
|
||||
/* invalid tlb before enable mmu */
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c8, c7, 0
|
||||
mcr p15, 0, r0, c7, c5, 0 /* iciallu */
|
||||
mcr p15, 0, r0, c7, c5, 6 /* bpiall */
|
||||
|
||||
mrc p15, 0, r0, c1, c0, 0
|
||||
bic r0, r0, #0x7 /* clear bit1~3 */
|
||||
orr r0, #((1 << 12) | (1 << 11)) /* instruction cache, branch prediction */
|
||||
orr r0, #((1 << 2) | (1 << 0)) /* data cache, mmu enable */
|
||||
mcr p15, 0, r0, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
mov pc, lr
|
||||
|
||||
master_core_startup :
|
||||
mov r0 ,pv_off
|
||||
bl rt_kmem_pvoff_set
|
||||
|
||||
ldr lr, =rtthread_startup
|
||||
mov pc, lr
|
||||
|
||||
.global rt_hw_mmu_tbl_get
|
||||
rt_hw_mmu_tbl_get:
|
||||
mrc p15, 0, r0, c2, c0, 0 /* ttbr0 */
|
||||
bic r0, #0x18
|
||||
mov pc, lr
|
||||
|
||||
.weak rt_hw_cpu_id_early
|
||||
rt_hw_cpu_id_early:
|
||||
mrc p15, 0, r0, c0, c0, 5
|
||||
and r0, r0, #0xf
|
||||
mov pc, lr
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
.global rt_secondary_cpu_entry
|
||||
rt_secondary_cpu_entry:
|
||||
ldr r0, =_reset
|
||||
adr pv_off, _reset
|
||||
sub pv_off, pv_off, r0
|
||||
|
||||
bl init_cpu_stack_early
|
||||
|
||||
/* init mmu */
|
||||
bl rt_hw_cpu_id_early
|
||||
mov cpu_id ,r0
|
||||
|
||||
ldr lr ,= rt_hw_secondary_cpu_bsp_start
|
||||
b enable_mmu_page_table_early
|
||||
#endif
|
||||
|
||||
|
||||
/* exception handlers: undef, swi, padt, dabt, resv, irq, fiq */
|
||||
.section .text.isr, "ax"
|
||||
.align 5
|
||||
.globl vector_fiq
|
||||
vector_fiq:
|
||||
stmfd sp!,{r0-r7,lr}
|
||||
bl rt_hw_trap_fiq
|
||||
ldmfd sp!,{r0-r7,lr}
|
||||
subs pc, lr, #4
|
||||
|
||||
.globl rt_interrupt_enter
|
||||
.globl rt_interrupt_leave
|
||||
.globl rt_thread_switch_interrupt_flag
|
||||
.globl rt_interrupt_from_thread
|
||||
.globl rt_interrupt_to_thread
|
||||
|
||||
.globl rt_current_thread
|
||||
.globl vmm_thread
|
||||
.globl vmm_virq_check
|
||||
|
||||
.align 5
|
||||
.globl vector_irq
|
||||
vector_irq:
|
||||
#ifdef RT_USING_SMP
|
||||
stmfd sp!, {r0, r1}
|
||||
cps #Mode_SVC
|
||||
mov r0, sp /* svc_sp */
|
||||
mov r1, lr /* svc_lr */
|
||||
|
||||
cps #Mode_IRQ
|
||||
sub lr, #4
|
||||
stmfd r0!, {r1, lr} /* svc_lr, svc_pc */
|
||||
stmfd r0!, {r2 - r12}
|
||||
ldmfd sp!, {r1, r2} /* original r0, r1 */
|
||||
stmfd r0!, {r1 - r2}
|
||||
mrs r1, spsr /* original mode */
|
||||
stmfd r0!, {r1}
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
stmfd r0, {r13, r14}^ /* usr_sp, usr_lr */
|
||||
sub r0, #8
|
||||
#endif
|
||||
#ifdef RT_USING_FPU
|
||||
/* fpu context */
|
||||
vmrs r6, fpexc
|
||||
tst r6, #(1<<30)
|
||||
beq 1f
|
||||
vstmdb r0!, {d0-d15}
|
||||
vstmdb r0!, {d16-d31}
|
||||
vmrs r5, fpscr
|
||||
stmfd r0!, {r5}
|
||||
1:
|
||||
stmfd r0!, {r6}
|
||||
#endif
|
||||
|
||||
/* now irq stack is clean */
|
||||
/* r0 is task svc_sp */
|
||||
/* backup r0 -> r8 */
|
||||
mov r8, r0
|
||||
|
||||
cps #Mode_SVC
|
||||
mov sp, r8
|
||||
|
||||
bl rt_interrupt_enter
|
||||
bl rt_hw_trap_irq
|
||||
bl rt_interrupt_leave
|
||||
|
||||
mov r0, r8
|
||||
bl rt_scheduler_do_irq_switch
|
||||
|
||||
b rt_hw_context_switch_exit
|
||||
|
||||
#else
|
||||
stmfd sp!, {r0-r12,lr}
|
||||
|
||||
bl rt_interrupt_enter
|
||||
bl rt_hw_trap_irq
|
||||
bl rt_interrupt_leave
|
||||
|
||||
/* if rt_thread_switch_interrupt_flag set, jump to
|
||||
* rt_hw_context_switch_interrupt_do and don't return */
|
||||
ldr r0, =rt_thread_switch_interrupt_flag
|
||||
ldr r1, [r0]
|
||||
cmp r1, #1
|
||||
beq rt_hw_context_switch_interrupt_do
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
ldmfd sp!, {r0-r12,lr}
|
||||
cps #Mode_SVC
|
||||
push {r0-r12}
|
||||
mov r7, lr
|
||||
cps #Mode_IRQ
|
||||
mrs r4, spsr
|
||||
sub r5, lr, #4
|
||||
cps #Mode_SVC
|
||||
and r6, r4, #0x1f
|
||||
cmp r6, #0x10
|
||||
bne 1f
|
||||
msr spsr_csxf, r4
|
||||
mov lr, r5
|
||||
pop {r0-r12}
|
||||
b arch_ret_to_user
|
||||
1:
|
||||
mov lr, r7
|
||||
cps #Mode_IRQ
|
||||
msr spsr_csxf, r4
|
||||
mov lr, r5
|
||||
cps #Mode_SVC
|
||||
pop {r0-r12}
|
||||
cps #Mode_IRQ
|
||||
movs pc, lr
|
||||
#else
|
||||
ldmfd sp!, {r0-r12,lr}
|
||||
subs pc, lr, #4
|
||||
#endif
|
||||
|
||||
rt_hw_context_switch_interrupt_do:
|
||||
mov r1, #0 /* clear flag */
|
||||
str r1, [r0]
|
||||
|
||||
mov r1, sp /* r1 point to {r0-r3} in stack */
|
||||
add sp, sp, #4*4
|
||||
ldmfd sp!, {r4-r12,lr} /* reload saved registers */
|
||||
mrs r0, spsr /* get cpsr of interrupt thread */
|
||||
sub r2, lr, #4 /* save old task's pc to r2 */
|
||||
|
||||
/* Switch to SVC mode with no interrupt. If the usr mode guest is
|
||||
* interrupted, this will just switch to the stack of kernel space.
|
||||
* save the registers in kernel space won't trigger data abort. */
|
||||
msr cpsr_c, #I_Bit|F_Bit|Mode_SVC
|
||||
|
||||
stmfd sp!, {r2} /* push old task's pc */
|
||||
stmfd sp!, {r4-r12,lr} /* push old task's lr,r12-r4 */
|
||||
ldmfd r1, {r1-r4} /* restore r0-r3 of the interrupt thread */
|
||||
stmfd sp!, {r1-r4} /* push old task's r0-r3 */
|
||||
stmfd sp!, {r0} /* push old task's cpsr */
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
stmfd sp, {r13, r14}^ /*push usr_sp, usr_lr */
|
||||
sub sp, #8
|
||||
#endif
|
||||
|
||||
#ifdef RT_USING_FPU
|
||||
/* fpu context */
|
||||
vmrs r6, fpexc
|
||||
tst r6, #(1<<30)
|
||||
beq 1f
|
||||
vstmdb sp!, {d0-d15}
|
||||
vstmdb sp!, {d16-d31}
|
||||
vmrs r5, fpscr
|
||||
stmfd sp!, {r5}
|
||||
1:
|
||||
stmfd sp!, {r6}
|
||||
#endif
|
||||
|
||||
ldr r4, =rt_interrupt_from_thread
|
||||
ldr r5, [r4]
|
||||
str sp, [r5] /* store sp in preempted tasks's TCB */
|
||||
|
||||
ldr r6, =rt_interrupt_to_thread
|
||||
ldr r6, [r6]
|
||||
ldr sp, [r6] /* get new task's stack pointer */
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
bl rt_thread_self
|
||||
mov r4, r0
|
||||
bl lwp_aspace_switch
|
||||
mov r0, r4
|
||||
bl lwp_user_setting_restore
|
||||
#endif
|
||||
|
||||
#ifdef RT_USING_FPU
|
||||
/* fpu context */
|
||||
ldmfd sp!, {r6}
|
||||
vmsr fpexc, r6
|
||||
tst r6, #(1<<30)
|
||||
beq 1f
|
||||
ldmfd sp!, {r5}
|
||||
vmsr fpscr, r5
|
||||
vldmia sp!, {d16-d31}
|
||||
vldmia sp!, {d0-d15}
|
||||
1:
|
||||
#endif
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
ldmfd sp, {r13, r14}^ /*pop usr_sp, usr_lr */
|
||||
add sp, #8
|
||||
#endif
|
||||
|
||||
ldmfd sp!, {r4} /* pop new task's cpsr to spsr */
|
||||
msr spsr_cxsf, r4
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
and r4, #0x1f
|
||||
cmp r4, #0x10
|
||||
bne 1f
|
||||
ldmfd sp!, {r0-r12,lr}
|
||||
ldmfd sp!, {lr}
|
||||
b arch_ret_to_user
|
||||
1:
|
||||
#endif
|
||||
/* pop new task's r0-r12,lr & pc, copy spsr to cpsr */
|
||||
ldmfd sp!, {r0-r12,lr,pc}^
|
||||
|
||||
#endif
|
||||
|
||||
.macro push_svc_reg
|
||||
sub sp, sp, #17 * 4 /* Sizeof(struct rt_hw_exp_stack) */
|
||||
stmia sp, {r0 - r12} /* Calling r0-r12 */
|
||||
mov r0, sp
|
||||
add sp, sp, #17 * 4
|
||||
mrs r6, spsr /* Save CPSR */
|
||||
str lr, [r0, #15*4] /* Push PC */
|
||||
str r6, [r0, #16*4] /* Push CPSR */
|
||||
and r1, r6, #0x1f
|
||||
cmp r1, #0x10
|
||||
cps #Mode_SYS
|
||||
streq sp, [r0, #13*4] /* Save calling SP */
|
||||
streq lr, [r0, #14*4] /* Save calling PC */
|
||||
cps #Mode_SVC
|
||||
strne sp, [r0, #13*4] /* Save calling SP */
|
||||
strne lr, [r0, #14*4] /* Save calling PC */
|
||||
.endm
|
||||
|
||||
.align 5
|
||||
.weak vector_swi
|
||||
vector_swi:
|
||||
push_svc_reg
|
||||
bl rt_hw_trap_swi
|
||||
b .
|
||||
|
||||
.align 5
|
||||
.globl vector_undef
|
||||
vector_undef:
|
||||
push_svc_reg
|
||||
bl rt_hw_trap_undef
|
||||
#ifdef RT_USING_FPU
|
||||
cps #Mode_UND
|
||||
sub sp, sp, #17 * 4
|
||||
ldr lr, [sp, #15*4]
|
||||
ldmia sp, {r0 - r12}
|
||||
add sp, sp, #17 * 4
|
||||
movs pc, lr
|
||||
#endif
|
||||
b .
|
||||
|
||||
.align 5
|
||||
.globl vector_pabt
|
||||
vector_pabt:
|
||||
push_svc_reg
|
||||
#ifdef RT_USING_SMART
|
||||
/* cp Mode_ABT stack to SVC */
|
||||
sub sp, sp, #17 * 4 /* Sizeof(struct rt_hw_exp_stack) */
|
||||
mov lr, r0
|
||||
ldmia lr, {r0 - r12}
|
||||
stmia sp, {r0 - r12}
|
||||
add r1, lr, #13 * 4
|
||||
add r2, sp, #13 * 4
|
||||
ldmia r1, {r4 - r7}
|
||||
stmia r2, {r4 - r7}
|
||||
mov r0, sp
|
||||
bl rt_hw_trap_pabt
|
||||
/* return to user */
|
||||
ldr lr, [sp, #16*4] /* orign spsr */
|
||||
msr spsr_cxsf, lr
|
||||
ldr lr, [sp, #15*4] /* orign pc */
|
||||
ldmia sp, {r0 - r12}
|
||||
add sp, #17 * 4
|
||||
b arch_ret_to_user
|
||||
#else
|
||||
bl rt_hw_trap_pabt
|
||||
b .
|
||||
#endif
|
||||
|
||||
.align 5
|
||||
.globl vector_dabt
|
||||
vector_dabt:
|
||||
push_svc_reg
|
||||
#ifdef RT_USING_SMART
|
||||
/* cp Mode_ABT stack to SVC */
|
||||
sub sp, sp, #17 * 4 /* Sizeof(struct rt_hw_exp_stack) */
|
||||
mov lr, r0
|
||||
ldmia lr, {r0 - r12}
|
||||
stmia sp, {r0 - r12}
|
||||
add r1, lr, #13 * 4
|
||||
add r2, sp, #13 * 4
|
||||
ldmia r1, {r4 - r7}
|
||||
stmia r2, {r4 - r7}
|
||||
mov r0, sp
|
||||
bl rt_hw_trap_dabt
|
||||
/* return to user */
|
||||
ldr lr, [sp, #16*4] /* orign spsr */
|
||||
msr spsr_cxsf, lr
|
||||
ldr lr, [sp, #15*4] /* orign pc */
|
||||
ldmia sp, {r0 - r12}
|
||||
add sp, #17 * 4
|
||||
b arch_ret_to_user
|
||||
#else
|
||||
bl rt_hw_trap_dabt
|
||||
b .
|
||||
#endif
|
||||
|
||||
.align 5
|
||||
.globl vector_resv
|
||||
vector_resv:
|
||||
push_svc_reg
|
||||
bl rt_hw_trap_resv
|
||||
b .
|
||||
|
||||
.global rt_hw_clz
|
||||
rt_hw_clz:
|
||||
clz r0, r0
|
||||
bx lr
|
||||
|
||||
|
||||
#include "asm-generic.h"
|
||||
|
||||
START_POINT(_thread_start)
|
||||
mov r10, lr
|
||||
blx r1
|
||||
blx r10
|
||||
b . /* never here */
|
||||
START_POINT_END(_thread_start)
|
||||
|
||||
.data
|
||||
.align 14
|
||||
init_mtbl:
|
||||
.space (4*4096) /* The L1 translation table therefore contains 4096 32-bit (word-sized) entries. */
|
||||
|
||||
.global rt_hw_mmu_switch
|
||||
rt_hw_mmu_switch:
|
||||
orr r0, #0x18
|
||||
mcr p15, 0, r0, c2, c0, 0 // ttbr0
|
||||
//invalid tlb
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c8, c7, 0
|
||||
mcr p15, 0, r0, c7, c5, 0 //iciallu
|
||||
mcr p15, 0, r0, c7, c5, 6 //bpiall
|
||||
|
||||
dsb
|
||||
isb
|
||||
mov pc, lr
|
||||
|
||||
|
||||
.global rt_hw_set_process_id
|
||||
rt_hw_set_process_id:
|
||||
LSL r0, r0, #8
|
||||
MCR p15, 0, r0, c13, c0, 1
|
||||
mov pc, lr
|
||||
|
||||
|
||||
.bss
|
||||
.align 3 /* align to 2~3=8 */
|
||||
|
||||
.cpus_stack:
|
||||
svc_stack_n:
|
||||
#if defined(RT_USING_SMP) && (RT_CPUS_NR > 1)
|
||||
.space ((RT_CPUS_NR - 1) * ARM_CPU_STACK_SIZE)
|
||||
#endif
|
||||
.space (ARM_CPU_STACK_SIZE)
|
||||
svc_stack_top:
|
||||
|
||||
irq_stack_n:
|
||||
#if defined(RT_USING_SMP) && (RT_CPUS_NR > 1)
|
||||
.space ((RT_CPUS_NR - 1) * ARM_CPU_STACK_SIZE)
|
||||
#endif
|
||||
.space (ARM_CPU_STACK_SIZE)
|
||||
irq_stack_top:
|
||||
|
||||
|
||||
und_stack_n:
|
||||
#if defined(RT_USING_SMP) && (RT_CPUS_NR > 1)
|
||||
.space ((RT_CPUS_NR - 1) * ARM_CPU_STACK_SIZE)
|
||||
#endif
|
||||
.space (ARM_CPU_STACK_SIZE)
|
||||
und_stack_top:
|
||||
|
||||
abt_stack_n:
|
||||
#if defined(RT_USING_SMP) && (RT_CPUS_NR > 1)
|
||||
.space ((RT_CPUS_NR - 1) * ARM_CPU_STACK_SIZE)
|
||||
#endif
|
||||
.space (ARM_CPU_STACK_SIZE)
|
||||
abt_stack_top:
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2022-12-12 WangXiaoyao the first version
|
||||
*/
|
||||
#ifndef __TLB_H__
|
||||
#define __TLB_H__
|
||||
|
||||
#include "mm_aspace.h"
|
||||
#include <rtthread.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define dsb(scope) __asm__ volatile("dsb " #scope : : : "memory")
|
||||
#define isb() __asm__ volatile("isb" : : : "memory")
|
||||
#define STORE_CP32(r, name...) "mcr " RT_STRINGIFY(CP32(%r, name)) ";"
|
||||
|
||||
#define WRITE_CP32(v, name...) do { \
|
||||
register uint32_t _r = (v); \
|
||||
asm volatile(STORE_CP32(0, name) : : "r" (_r)); \
|
||||
} while (0)
|
||||
|
||||
static inline void rt_hw_tlb_invalidate_all(void)
|
||||
{
|
||||
asm volatile ("mcr p15, 0, r0, c8, c7, 0\ndsb\nisb" ::: "memory");
|
||||
}
|
||||
|
||||
static inline void rt_hw_tlb_invalidate_all_local(void)
|
||||
{
|
||||
rt_hw_tlb_invalidate_all();
|
||||
}
|
||||
|
||||
static inline void rt_hw_tlb_invalidate_aspace(rt_aspace_t aspace)
|
||||
{
|
||||
rt_hw_tlb_invalidate_all();
|
||||
}
|
||||
|
||||
static inline void rt_hw_tlb_invalidate_range(rt_aspace_t aspace, void *start,
|
||||
size_t size, size_t stride)
|
||||
{
|
||||
rt_hw_tlb_invalidate_all();
|
||||
}
|
||||
|
||||
#endif /* __TLB_H__ */
|
||||
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-20 Bernard first version
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <board.h>
|
||||
#include <backtrace.h>
|
||||
|
||||
#include "interrupt.h"
|
||||
#include "mm_fault.h"
|
||||
|
||||
#include <rtdbg.h>
|
||||
|
||||
#ifdef RT_USING_FINSH
|
||||
extern long list_thread(void);
|
||||
#endif
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
#include <lwp.h>
|
||||
#include <lwp_arch.h>
|
||||
|
||||
#ifdef LWP_USING_CORE_DUMP
|
||||
#include <lwp_core_dump.h>
|
||||
#endif
|
||||
|
||||
void check_user_fault(struct rt_hw_exp_stack *regs, uint32_t pc_adj, char *info)
|
||||
{
|
||||
uint32_t mode = regs->cpsr;
|
||||
|
||||
if ((mode & 0x1f) == 0x10)
|
||||
{
|
||||
rt_kprintf("%s! pc = 0x%08x\n", info, regs->pc - pc_adj);
|
||||
#ifdef LWP_USING_CORE_DUMP
|
||||
lwp_core_dump(regs, pc_adj);
|
||||
#endif
|
||||
sys_exit_group(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int check_data_abort(struct rt_hw_exp_stack *regs)
|
||||
{
|
||||
struct rt_lwp *lwp;
|
||||
void *dfar = RT_NULL;
|
||||
rt_base_t dfsr = RT_NULL;
|
||||
__asm__ volatile("mrc p15, 0, %0, c6, c0, 0" : "=r"(dfar));
|
||||
__asm__ volatile("mrc p15, 0, %0, c5, c0, 0" : "=r"(dfsr));
|
||||
|
||||
struct rt_aspace_fault_msg msg = {
|
||||
.fault_op = MM_FAULT_OP_WRITE,
|
||||
.fault_type = MM_FAULT_TYPE_PAGE_FAULT,
|
||||
.fault_vaddr = dfar,
|
||||
};
|
||||
lwp = lwp_self();
|
||||
if (lwp && rt_aspace_fault_try_fix(lwp->aspace, &msg))
|
||||
{
|
||||
regs->pc -= 8;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_prefetch_abort(struct rt_hw_exp_stack *regs)
|
||||
{
|
||||
struct rt_lwp *lwp;
|
||||
void *ifar = RT_NULL;
|
||||
rt_base_t ifsr = RT_NULL;
|
||||
__asm__ volatile("mrc p15, 0, %0, c6, c0, 2" : "=r"(ifar));
|
||||
__asm__ volatile("mrc p15, 0, %0, c5, c0, 1" : "=r"(ifsr));
|
||||
|
||||
struct rt_aspace_fault_msg msg = {
|
||||
.fault_op = MM_FAULT_OP_READ,
|
||||
.fault_type = MM_FAULT_TYPE_PAGE_FAULT,
|
||||
.fault_vaddr = ifar,
|
||||
};
|
||||
lwp = lwp_self();
|
||||
if (lwp && rt_aspace_fault_try_fix(lwp->aspace, &msg))
|
||||
{
|
||||
regs->pc -= 4;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* this function will show registers of CPU
|
||||
*
|
||||
* @param regs the registers point
|
||||
*/
|
||||
void rt_hw_show_register(struct rt_hw_exp_stack *regs)
|
||||
{
|
||||
rt_kprintf("Execption:\n");
|
||||
rt_kprintf("r00:0x%08x r01:0x%08x r02:0x%08x r03:0x%08x\n", regs->r0, regs->r1, regs->r2, regs->r3);
|
||||
rt_kprintf("r04:0x%08x r05:0x%08x r06:0x%08x r07:0x%08x\n", regs->r4, regs->r5, regs->r6, regs->r7);
|
||||
rt_kprintf("r08:0x%08x r09:0x%08x r10:0x%08x\n", regs->r8, regs->r9, regs->r10);
|
||||
rt_kprintf("fp :0x%08x ip :0x%08x\n", regs->fp, regs->ip);
|
||||
rt_kprintf("sp :0x%08x lr :0x%08x pc :0x%08x\n", regs->sp, regs->lr, regs->pc);
|
||||
rt_kprintf("cpsr:0x%08x\n", regs->cpsr);
|
||||
#ifdef RT_USING_SMART
|
||||
{
|
||||
uint32_t v;
|
||||
asm volatile ("MRC p15, 0, %0, c5, c0, 0":"=r"(v));
|
||||
rt_kprintf("dfsr:0x%08x\n", v);
|
||||
asm volatile ("MRC p15, 0, %0, c2, c0, 0":"=r"(v));
|
||||
rt_kprintf("ttbr0:0x%08x\n", v);
|
||||
asm volatile ("MRC p15, 0, %0, c6, c0, 0":"=r"(v));
|
||||
rt_kprintf("dfar:0x%08x\n", v);
|
||||
rt_kprintf("0x%08x -> 0x%08x\n", v, rt_kmem_v2p((void *)v));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* When comes across an instruction which it cannot handle,
|
||||
* it takes the undefined instruction trap.
|
||||
*
|
||||
* @param regs system registers
|
||||
*
|
||||
* @note never invoke this function in application
|
||||
*/
|
||||
#ifdef RT_USING_FPU
|
||||
void set_fpexc(rt_uint32_t val);
|
||||
#endif
|
||||
void rt_hw_trap_undef(struct rt_hw_exp_stack *regs)
|
||||
{
|
||||
#ifdef RT_USING_FPU
|
||||
{
|
||||
uint32_t ins;
|
||||
uint32_t addr;
|
||||
|
||||
if (regs->cpsr & (1 << 5))
|
||||
{
|
||||
/* thumb mode */
|
||||
addr = regs->pc - 2;
|
||||
ins = (uint32_t)*(uint16_t *)addr;
|
||||
if ((ins & (3 << 11)) != 0)
|
||||
{
|
||||
/* 32 bit ins */
|
||||
ins <<= 16;
|
||||
ins += *(uint16_t *)(addr + 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = regs->pc - 4;
|
||||
ins = *(uint32_t *)addr;
|
||||
}
|
||||
if ((ins & 0xe00) == 0xa00)
|
||||
{
|
||||
/* float ins */
|
||||
set_fpexc(1U << 30);
|
||||
regs->pc = addr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef RT_USING_SMART
|
||||
check_user_fault(regs, 4, "User undefined instruction");
|
||||
#endif
|
||||
rt_unwind(regs, 4);
|
||||
rt_kprintf("undefined instruction:\n");
|
||||
rt_hw_show_register(regs);
|
||||
#ifdef RT_USING_FINSH
|
||||
list_thread();
|
||||
#endif
|
||||
rt_hw_cpu_shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* The software interrupt instruction (SWI) is used for entering
|
||||
* Supervisor mode, usually to request a particular supervisor
|
||||
* function.
|
||||
*
|
||||
* @param regs system registers
|
||||
*
|
||||
* @note never invoke this function in application
|
||||
*/
|
||||
void rt_hw_trap_swi(struct rt_hw_exp_stack *regs)
|
||||
{
|
||||
rt_kprintf("software interrupt:\n");
|
||||
rt_hw_show_register(regs);
|
||||
#ifdef RT_USING_FINSH
|
||||
list_thread();
|
||||
#endif
|
||||
rt_hw_cpu_shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* An abort indicates that the current memory access cannot be completed,
|
||||
* which occurs during an instruction prefetch.
|
||||
*
|
||||
* @param regs system registers
|
||||
*
|
||||
* @note never invoke this function in application
|
||||
*/
|
||||
void rt_hw_trap_pabt(struct rt_hw_exp_stack *regs)
|
||||
{
|
||||
#ifdef RT_USING_SMART
|
||||
if (dbg_check_event(regs, 4))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (check_prefetch_abort(regs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
check_user_fault(regs, 4, "User prefetch abort");
|
||||
#endif
|
||||
rt_unwind(regs, 4);
|
||||
rt_kprintf("prefetch abort:\n");
|
||||
rt_hw_show_register(regs);
|
||||
#ifdef RT_USING_FINSH
|
||||
list_thread();
|
||||
#endif
|
||||
rt_hw_cpu_shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* An abort indicates that the current memory access cannot be completed,
|
||||
* which occurs during a data access.
|
||||
*
|
||||
* @param regs system registers
|
||||
*
|
||||
* @note never invoke this function in application
|
||||
*/
|
||||
void rt_hw_trap_dabt(struct rt_hw_exp_stack *regs)
|
||||
{
|
||||
#ifdef RT_USING_SMART
|
||||
if (dbg_check_event(regs, 8))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (check_data_abort(regs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
check_user_fault(regs, 8, "User data abort");
|
||||
#endif
|
||||
rt_unwind(regs, 8);
|
||||
rt_kprintf("data abort:");
|
||||
rt_hw_show_register(regs);
|
||||
#ifdef RT_USING_FINSH
|
||||
list_thread();
|
||||
#endif
|
||||
rt_hw_cpu_shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally, system will never reach here
|
||||
*
|
||||
* @param regs system registers
|
||||
*
|
||||
* @note never invoke this function in application
|
||||
*/
|
||||
void rt_hw_trap_resv(struct rt_hw_exp_stack *regs)
|
||||
{
|
||||
rt_kprintf("reserved trap:\n");
|
||||
rt_hw_show_register(regs);
|
||||
#ifdef RT_USING_FINSH
|
||||
list_thread();
|
||||
#endif
|
||||
rt_hw_cpu_shutdown();
|
||||
}
|
||||
|
||||
void rt_hw_trap_irq(void)
|
||||
{
|
||||
#ifdef SOC_BCM283x
|
||||
extern rt_uint8_t core_timer_flag;
|
||||
void *param;
|
||||
uint32_t irq;
|
||||
rt_isr_handler_t isr_func;
|
||||
extern struct rt_irq_desc isr_table[];
|
||||
uint32_t value = 0;
|
||||
value = IRQ_PEND_BASIC & 0x3ff;
|
||||
|
||||
if(core_timer_flag != 0)
|
||||
{
|
||||
uint32_t cpu_id = rt_hw_cpu_id();
|
||||
uint32_t int_source = CORE_IRQSOURCE(cpu_id);
|
||||
if (int_source & 0x0f)
|
||||
{
|
||||
if (int_source & 0x08)
|
||||
{
|
||||
isr_func = isr_table[IRQ_ARM_TIMER].handler;
|
||||
#ifdef RT_USING_INTERRUPT_INFO
|
||||
isr_table[IRQ_ARM_TIMER].counter++;
|
||||
#endif
|
||||
if (isr_func)
|
||||
{
|
||||
param = isr_table[IRQ_ARM_TIMER].param;
|
||||
isr_func(IRQ_ARM_TIMER, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* local interrupt*/
|
||||
if (value)
|
||||
{
|
||||
if (value & (1 << 8))
|
||||
{
|
||||
value = IRQ_PEND1;
|
||||
irq = __rt_ffs(value) - 1;
|
||||
}
|
||||
else if (value & (1 << 9))
|
||||
{
|
||||
value = IRQ_PEND2;
|
||||
irq = __rt_ffs(value) + 31;
|
||||
}
|
||||
else
|
||||
{
|
||||
value &= 0x0f;
|
||||
irq = __rt_ffs(value) + 63;
|
||||
}
|
||||
|
||||
/* get interrupt service routine */
|
||||
isr_func = isr_table[irq].handler;
|
||||
#ifdef RT_USING_INTERRUPT_INFO
|
||||
isr_table[irq].counter++;
|
||||
#endif
|
||||
if (isr_func)
|
||||
{
|
||||
/* Interrupt for myself. */
|
||||
param = isr_table[irq].param;
|
||||
/* turn to interrupt service routine */
|
||||
isr_func(irq, param);
|
||||
}
|
||||
}
|
||||
#else
|
||||
void *param;
|
||||
int ir, ir_real;
|
||||
rt_isr_handler_t isr_func;
|
||||
extern struct rt_irq_desc isr_table[];
|
||||
|
||||
ir = rt_hw_interrupt_get_irq();
|
||||
|
||||
ir_real = ir & 0x3ff;
|
||||
if (ir == 1023)
|
||||
{
|
||||
/* Spurious interrupt */
|
||||
return;
|
||||
}
|
||||
|
||||
/* get interrupt service routine */
|
||||
isr_func = isr_table[ir_real].handler;
|
||||
#ifdef RT_USING_INTERRUPT_INFO
|
||||
isr_table[ir_real].counter++;
|
||||
#endif
|
||||
if (isr_func)
|
||||
{
|
||||
/* Interrupt for myself. */
|
||||
param = isr_table[ir_real].param;
|
||||
/* turn to interrupt service routine */
|
||||
isr_func(ir, param);
|
||||
}
|
||||
|
||||
/* end of interrupt */
|
||||
rt_hw_interrupt_ack(ir);
|
||||
#endif
|
||||
}
|
||||
|
||||
void rt_hw_trap_fiq(void)
|
||||
{
|
||||
void *param;
|
||||
int ir;
|
||||
rt_isr_handler_t isr_func;
|
||||
extern struct rt_irq_desc isr_table[];
|
||||
|
||||
ir = rt_hw_interrupt_get_irq();
|
||||
|
||||
/* get interrupt service routine */
|
||||
isr_func = isr_table[ir].handler;
|
||||
param = isr_table[ir].param;
|
||||
|
||||
/* turn to interrupt service routine */
|
||||
isr_func(ir, param);
|
||||
|
||||
/* end of interrupt */
|
||||
rt_hw_interrupt_ack(ir);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2018, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-07-05 Bernard the first version
|
||||
*/
|
||||
|
||||
#include "rtconfig.h"
|
||||
|
||||
.section .vectors, "ax"
|
||||
.code 32
|
||||
|
||||
.globl system_vectors
|
||||
system_vectors:
|
||||
#ifdef RT_USING_SMART
|
||||
b _reset
|
||||
#else
|
||||
ldr pc, _vector_reset
|
||||
#endif
|
||||
ldr pc, _vector_undef
|
||||
ldr pc, _vector_swi
|
||||
ldr pc, _vector_pabt
|
||||
ldr pc, _vector_dabt
|
||||
ldr pc, _vector_resv
|
||||
ldr pc, _vector_irq
|
||||
ldr pc, _vector_fiq
|
||||
|
||||
.globl _reset
|
||||
.globl vector_undef
|
||||
.globl vector_swi
|
||||
.globl vector_pabt
|
||||
.globl vector_dabt
|
||||
.globl vector_resv
|
||||
.globl vector_irq
|
||||
.globl vector_fiq
|
||||
|
||||
_vector_reset:
|
||||
.word _reset
|
||||
_vector_undef:
|
||||
.word vector_undef
|
||||
_vector_swi:
|
||||
.word vector_swi
|
||||
_vector_pabt:
|
||||
.word vector_pabt
|
||||
_vector_dabt:
|
||||
.word vector_dabt
|
||||
_vector_resv:
|
||||
.word vector_resv
|
||||
_vector_irq:
|
||||
.word vector_irq
|
||||
_vector_fiq:
|
||||
.word vector_fiq
|
||||
|
||||
.balignl 16,0xdeadbeef
|
||||
Reference in new issue
Block a user