This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push: new eb0e05be0d arch/armv7-r: add armv7-r smp support eb0e05be0d is described below commit eb0e05be0db6386c806200f88e2840996edfe225 Author: zhangyuan21 <zhangyua...@xiaomi.com> AuthorDate: Thu Mar 2 22:54:29 2023 +0800 arch/armv7-r: add armv7-r smp support Signed-off-by: zhangyuan21 <zhangyua...@xiaomi.com> --- arch/arm/src/armv7-r/Make.defs | 6 + arch/arm/src/armv7-r/{arm_head.S => arm_cpuhead.S} | 451 ++++++++++----------- arch/arm/src/armv7-r/arm_cpuidlestack.c | 132 ++++++ .../arm/src/armv7-r/{barriers.h => arm_cpuindex.c} | 57 ++- arch/arm/src/armv7-r/arm_cpupause.c | 326 +++++++++++++++ arch/arm/src/armv7-r/arm_cpustart.c | 136 +++++++ arch/arm/src/armv7-r/arm_head.S | 51 ++- arch/arm/src/armv7-r/arm_schedulesigaction.c | 220 ++++++++++ arch/arm/src/armv7-r/arm_scu.c | 123 ++++++ arch/arm/src/armv7-r/arm_sigdeliver.c | 44 ++ arch/arm/src/armv7-r/arm_vectors.S | 44 +- arch/arm/src/armv7-r/barriers.h | 6 +- arch/arm/src/armv7-r/sctlr.h | 32 +- arch/arm/src/armv7-r/scu.h | 252 ++++++++++++ arch/arm/src/armv7-r/smp.h | 128 ++++++ 15 files changed, 1744 insertions(+), 264 deletions(-) diff --git a/arch/arm/src/armv7-r/Make.defs b/arch/arm/src/armv7-r/Make.defs index 7cb439f27b..c246414600 100644 --- a/arch/arm/src/armv7-r/Make.defs +++ b/arch/arm/src/armv7-r/Make.defs @@ -56,3 +56,9 @@ ifeq ($(CONFIG_ARCH_FPU),y) CMN_CSRCS += arm_fpucmp.c CMN_ASRCS += arm_fpuconfig.S endif + +ifeq ($(CONFIG_SMP),y) + CMN_ASRCS += arm_cpuhead.S + CMN_CSRCS += arm_cpuindex.c arm_cpustart.c arm_cpupause.c + CMN_CSRCS += arm_cpuidlestack.c arm_scu.c +endif diff --git a/arch/arm/src/armv7-r/arm_head.S b/arch/arm/src/armv7-r/arm_cpuhead.S similarity index 53% copy from arch/arm/src/armv7-r/arm_head.S copy to arch/arm/src/armv7-r/arm_cpuhead.S index d6433910e9..338506c68b 100644 --- a/arch/arm/src/armv7-r/arm_head.S +++ b/arch/arm/src/armv7-r/arm_cpuhead.S @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/arm/src/armv7-r/arm_head.S + * arch/arm/src/armv7-r/arm_cpuhead.S * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -24,12 +24,18 @@ #include <nuttx/config.h> +#include <arch/irq.h> + #include "arm.h" #include "cp15.h" #include "sctlr.h" +#include "smp.h" +#include "chip.h" #include "arm_internal.h" - .file "arm_head.S" +#ifdef CONFIG_SMP + + .file "arm_cpuhead.S" /**************************************************************************** * Configuration @@ -75,63 +81,130 @@ * not require initialization (such as internal SRAM) */ -#ifndef IDLE_STACK_BASE -# define IDLE_STACK_BASE _ebss -#endif +/**************************************************************************** + * .text + ****************************************************************************/ -#define IDLE_STACK_TOP IDLE_STACK_BASE+CONFIG_IDLETHREAD_STACKSIZE + .text + .syntax unified + .arm /**************************************************************************** - * Global Symbols + * Name: __cpu[n]_start + * + * Description: + * Boot functions for each CPU (other than CPU0). These functions set up + * the ARM operating mode, the initial stack, and configure co-processor + * registers. At the end of the boot, arm_cpu_boot() is called. + * + * These functions are provided by the common ARMv7-R logic. + * + * Input Parameters: + * None + * + * Returned Value: + * Do not return. + * ****************************************************************************/ -/* Imported symbols */ +#if CONFIG_SMP_NCPUS > 1 + .global __cpu1_start + .type __cpu1_start, #function - .global arm_boot /* Branch to continue initialization in C */ +__cpu1_start: + /* Make sure that we are in SYS mode with IRQs and FIQs disabled */ - .global _sbss /* Start of .bss in RAM */ - .global _ebss /* End+1 of .bss in RAM */ -#ifdef CONFIG_BOOT_RUNFROMFLASH - .global _eronly /* Where .data defaults are stored in FLASH */ - .global _sdata /* Where .data needs to reside in SDRAM */ - .global _edata -#endif -#ifdef CONFIG_ARCH_RAMFUNCS - .global _framfuncs /* Where RAM functions are stored in FLASH */ - .global _sramfuncs /* Where RAM functions needs to reside in RAM */ - .global _eramfuncs -#endif + cpsid if, #PSR_MODE_SYS -/* Exported symbols */ + /* Set up the stack pointer and the CPU index */ - .global __start /* Power-up/Reset entry point */ - .global arm_data_initialize /* Perform C data initialization */ - .global g_idle_topstack /* Top of the initial/IDLE stack */ + ldr sp, .Lcpu1_stackpointer + sub sp, sp, #XCPTCONTEXT_SIZE + mov r5, #1 -/**************************************************************************** - * Name: __start - ****************************************************************************/ + /* Then branch to the common startup logic (PC-relative) */ -/* We assume the bootloader has already initialized most of the h/w for - * us and that only leaves us having to do some os specific things - * below. - */ + b .Lcpu_start - .text - .syntax unified - .arm - .global __start - .type __start, #function +.Lcpu1_stackpointer: + .long .Lcpu1_stacktop + .size __cpu1_start, .-__cpu1_start + +#if CONFIG_SMP_NCPUS > 2 + .global __cpu2_start + .type __cpu2_start, #function + +__cpu2_start: + /* Make sure that we are in SYS mode with IRQs and FIQs disabled */ + + cpsid if, #PSR_MODE_SYS + + /* Set up the stack pointer and the CPU index */ + + ldr sp, .Lcpu2_stackpointer + sub sp, sp, #XCPTCONTEXT_SIZE + mov r5, #2 + + /* Then branch to the common startup logic (PC-relative) */ + + b .Lcpu_start + +.Lcpu2_stackpointer: + .long .Lcpu2_stacktop + .size __cpu2_start, .-__cpu2_start + +#if CONFIG_SMP_NCPUS > 3 + .global __cpu3_start + .type __cpu3_start, #function -__start: +__cpu3_start: /* Make sure that we are in SYS mode with IRQs and FIQs disabled */ cpsid if, #PSR_MODE_SYS - /* Set up the stack pointer and clear the frame pointer. */ + /* Set up the stack pointer and the CPU index */ - ldr sp, .Lstackpointer - mov fp, #0 + ldr sp, .Lcpu3_stackpointer + sub sp, sp, #XCPTCONTEXT_SIZE + mov r5, #3 + + /* Then branch to the common startup logic (PC-relative) */ + + b .Lcpu_start + +.Lcpu3_stackpointer: + .long .Lcpu3_stacktop + .size __cpu3_start, .-__cpu3_start + +#if CONFIG_SMP_NCPUS > 4 +# error This logic needs to extended for CONFIG_SMP_NCPUS > 4 + +#endif /* CONFIG_SMP_NCPUS > 4 */ +#endif /* CONFIG_SMP_NCPUS > 3 */ +#endif /* CONFIG_SMP_NCPUS > 2 */ +#endif /* CONFIG_SMP_NCPUS > 1 */ + +/**************************************************************************** + * Name: .Lcpu_start + * + * Description: + * Common CPUn startup logic (n > 0) + * + * On input: + * SP = Set to top of CPU IDLE stack + * R5 = CPU number + * + ****************************************************************************/ + + .type .Lcpu_start, #function + +.Lcpu_start: + /* The MPU and caches should be disabled */ + + mrc CP15_SCTLR(r0) + bic r0, r0, #(SCTLR_M | SCTLR_C) + bic r0, r0, #(SCTLR_I) + mcr CP15_SCTLR(r0) /* Invalidate caches and TLBs. * @@ -152,9 +225,13 @@ __start: mcr CP15_DCIALLU(r0) /* Invalidate D-cache */ isb + /* Set lr = Resume at .Lcpu_vstart with the MMU enabled */ + + ldr lr, .LCcpu_vstart /* Abs. address */ + /* Configure the system control register (see sctrl.h) */ - mrc CP15_SCTLR(r0) /* Get control register */ + mrc CP15_SCTLR(r0) /* Get control register */ /* Clear bits to reset values. This is only necessary in situations like, for * example, we get here via a bootloader and the control register is in some @@ -182,12 +259,17 @@ __start: * SCTLR_TE Bit 30: All exceptions handled in ARM state. */ - /* Clear all configurable bits */ - - bic r0, r0, #(SCTLR_M | SCTLR_A | SCTLR_C | SCTLR_CCP15BEN | SCTLR_B) - bic r0, r0, #(SCTLR_SW | SCTLR_I | SCTLR_V | SCTLR_RR) + bic r0, r0, #(SCTLR_A | SCTLR_C | SCTLR_CCP15BEN | SCTLR_B) + bic r0, r0, #(SCTLR_SW | SCTLR_I | SCTLR_V | SCTLR_RR) bic r0, r0, #(SCTLR_BR | SCTLR_DZ | SCTLR_FI) - bic r0, r0, #(SCTLR_VE | SCTLR_EE | SCTLR_NMFI | SCTLR_TE) + bic r0, r0, #(SCTLR_VE | SCTLR_EE | SCTLR_NMFI | SCTLR_TE) + + /* Set bits to enable the MPU + * + * SCTLR_M Bit 0: Enable the MPU + */ + + orr r0, r0, #(SCTLR_M) /* Set configured bits */ @@ -200,15 +282,6 @@ __start: orr r0, r0, #(SCTLR_A) #endif -#ifndef CONFIG_ARMV7R_DCACHE_DISABLE - /* Dcache enable - * - * SCTLR_C Bit 2: DCache enable - */ - - orr r0, r0, #(SCTLR_C) -#endif - #ifdef CONFIG_ARMV7R_SCTLR_CCP15BEN /* Enable memory barriers * @@ -218,15 +291,6 @@ __start: orr r0, r0, #(SCTLR_CCP15BEN) #endif -#ifndef CONFIG_ARMV7R_ICACHE_DISABLE - /* Icache enable - * - * SCTLR_I Bit 12: ICache enable - */ - - orr r0, r0, #(SCTLR_I) -#endif - #ifndef CONFIG_ARCH_LOWVECTORS /* Position vectors to 0xffff0000 if so configured. * @@ -307,193 +371,124 @@ __start: nop .endr -#if 0 - /* Cortex-R/RF Errata Work-Around - * - * Errata 754269: Register corruption during a load-multiple instruction - * at an exception vector. - * Workaround: To workaround this erratum, set bit [7] of the - * Auxiliary Control Register to disable out-of-order - * completion for divide instructions. Code performance - * may be reduced depending on how often divide - * operations are used. - * NOTE: The ARMv7-A/R Architecture reference many lists the bits - * bits of the ACTLR as "implementation defined" - * - * REVISIT: I do not think this is needed because the NuttX vectors do not - * begin with an LDM instruction. - */ + /* And "jump" to .Lcpu_vstart in the newly mapped virtual address space */ - mrc CP15_ACTLR(r0) /* Read Auxiliary Control register */ - orr r0, r0, #(1 << 7) /* Disable out-of-order completion */ - mcr CP15_ACTLR(r0) /* Write Auxiliary Control register */ -#endif - -#ifndef CONFIG_ARMV7R_MEMINIT - /* Initialize .bss and .data ONLY if .bss and .data lie in RAM that is - * ready to use. Other memory, such as SDRAM, must be initialized before - * it can be used. arm_boot() will perform that memory initialization and - * .bss and .data can be initialized by arm_boot() by calling this - * arm_data_initialize() later. - */ + mov pc, lr - bl arm_data_initialize -#endif - - /* Perform early C-level, platform-specific initialization. Logic - * within arm_boot() must configure SDRAM and call arm_data_initialize() - * if CONFIG_ARMV7R_MEMINIT=y. - * - * This function does not return. It must give control to nx_start() - * at the completion of its initialization. - * - * Why not just call arm_boot() and branch to nx_start() when it returns? - * If the stack pointer initialized above lies in SDRAM, then that may - * not be possible. Also, in the special case of the TMS570, it may - * perform a destructive test, losing the pushed content of the stack. - */ - - b arm_boot - - /* .text Data */ - -.Lstackpointer: - .long IDLE_STACK_TOP - .size __start, .-__start +/**************************************************************************** + * PC_Relative Data + ****************************************************************************/ -/*************************************************************************** - * Name: arm_data_initialize - ***************************************************************************/ + /* The start address of the second phase boot logic */ - .global arm_data_initialize - .type arm_data_initialize, #function + .type .LCcpu_vstart, %object +.LCcpu_vstart: + .long .Lcpu_vstart + .size .LCcpu_vstart, . -.LCcpu_vstart -arm_data_initialize: + .size .Lcpu_start, .-.Lcpu_start - /* Zero BSS */ +/**************************************************************************** + * Name: .Lcpu_vstart + * + * Description: + * Continue initialization after the MPU has been enabled. + * + * The following is executed after the MPU has been enabled. This uses + * absolute addresses; this is not position independent. + * + * On input: + * SP = Set to top of CPU IDLE stack + * R5 = CPU number + * + ****************************************************************************/ - adr r0, .Linitparms - ldmia r0, {r0, r1} + .align 8 + .globl arm_cpu_boot + .type .Lcpu_vstart, %function - mov r2, #0 -1: - cmp r0, r1 /* Clear up to _bss_end_ */ - strcc r2, [r0], #4 - bcc 1b +.Lcpu_vstart: -#ifdef CONFIG_BOOT_RUNFROMFLASH - /* If the .data section is in a separate, uninitialized address space, - * then we will also need to copy the initial values of the .data - * section from the .text region into that .data region. This would - * be the case if we are executing from FLASH and the .data section - * lies in a different physical address region OR if we are support - * on-demand paging and the .data section lies in a different virtual - * address region. +#ifdef CONFIG_STACK_COLORATION + /* Write a known value to the IDLE thread stack to support stack + * monitoring logic */ - adr r3, .Ldatainit - ldmia r3, {r0, r1, r2} + adr r3, .Lstkinit + mov r0, sp /* R0 = end of IDLE stack */ + ldmia r3, {r1, r2} /* R1 = Size of stack; R2 = coloration */ -2: - ldr r3, [r0], #4 - str r3, [r1], #4 - cmp r1, r2 - blt 2b +1: /* Top of the loop */ + sub r1, r1, #1 /* R1 = Number of words remaining */ + cmp r1, #0 /* Check (nwords == 0) */ + str r2, [r0, #-4]! /* Save stack color word, increment stack address */ + bne 1b /* Bottom of the loop */ #endif -#ifdef CONFIG_ARCH_RAMFUNCS - /* Copy any necessary code sections from FLASH to RAM. The correct - * destination in SRAM is given by _sramfuncs and _eramfuncs. The - * temporary location is in flash after the data initialization code - * at _framfuncs - */ - - adr r3, .Lfuncinit - ldmia r3, {r0, r1, r2} - -3: - ldr r3, [r0], #4 - str r3, [r1], #4 - cmp r1, r2 - blt 3b - -#ifndef CONFIG_ARMV7R_DCACHE_DISABLE - /* Flush the copied RAM functions into physical RAM so that will - * be available when fetched into the I-Cache. - * - * Note that this is a branch, not a call and so will return - * directly to the caller without returning here. - */ + /* Branch to continue C level CPU initialization */ - adr r3, ..Lramfunc - ldmia r3, {r0, r1} - ldr r3, =up_clean_dcache - b r3 -#else - /* Otherwise return to the caller */ - - bx lr -#endif -#else - /* Return to the caller */ - - bx lr -#endif + mov fp, #0 /* Clear framepointer */ + mov lr, #0 /* LR = return address (none) */ + mov r0, r5 /* Input parameter = CPU index */ + b arm_cpu_boot /* Branch to C level CPU initialization */ + .size .Lcpu_vstart, .-.Lcpu_vstart /*************************************************************************** * Text-section constants ***************************************************************************/ - /* Text-section constants: - * - * _sbss is the start of the BSS region (see linker script) - * _ebss is the end of the BSS region (see linker script) - * - * Typical Configuration: - * The idle task stack usually starts at the end of BSS and is of size - * CONFIG_IDLETHREAD_STACKSIZE. The heap continues from there until the - * end of memory. See g_idle_topstack below. - */ + /* Text-section constants: */ - .type .Linitparms, %object -.Linitparms: - .long _sbss - .long _ebss - -#ifdef CONFIG_BOOT_RUNFROMFLASH - .type .Ldatainit, %object -.Ldatainit: - .long _eronly /* Where .data defaults are stored in FLASH */ - .long _sdata /* Where .data needs to reside in SDRAM */ - .long _edata +#ifdef CONFIG_STACK_COLORATION + .type .Lstkinit, %object +.Lstkinit: + .long SMP_STACK_WORDS - (XCPTCONTEXT_SIZE / 4) + .long STACK_COLOR /* Stack coloration word */ + .size .Lstkinit, . -.Lstkinit #endif -#ifdef CONFIG_ARCH_RAMFUNCS - .type .Lfuncinit, %object -.Lfuncinit: - .long _framfuncs /* Where RAM functions are stored in FLASH */ -.Lramfuncs: - .long _sramfuncs /* Where RAM functions needs to reside in RAM */ - .long _eramfuncs -#endif - .size arm_data_initialize, . - arm_data_initialize - /*************************************************************************** - * Data section variables + * .noinit section data ***************************************************************************/ - /* This global variable is unsigned long g_idle_topstack and is - * exported from here only because of its coupling to .Lstackpointer - * above. - */ - - .section .rodata, "a" - .align 4 - .globl g_idle_topstack - .type g_idle_topstack, object - -g_idle_topstack: - - .long IDLE_STACK_TOP - .size g_idle_topstack, .-g_idle_topstack + .section .noinit, "aw" + +#if CONFIG_SMP_NCPUS > 1 + .align 8 + .globl g_cpu1_idlestack + .type g_cpu1_idlestack, object + +g_cpu1_idlestack: + .space SMP_STACK_SIZE +.Lcpu1_stacktop: + .size g_cpu1_idlestack, .Lcpu1_stacktop-g_cpu1_idlestack + +#if CONFIG_SMP_NCPUS > 2 + .align 8 + .globl g_cpu2_idlestack + .type g_cpu2_idlestack, object + +g_cpu2_idlestack: + .space SMP_STACK_SIZE +.Lcpu2_stacktop: + .size g_cpu2_idlestack, .Lcpu2_stacktop-g_cpu2_idlestack + +#if CONFIG_SMP_NCPUS > 3 + .align 8 + .globl g_cpu3_idlestack + .type g_cpu3_idlestack, object + +g_cpu3_idlestack: + .space SMP_STACK_SIZE +.Lcpu3_stacktop: + .size g_cpu3_idlestack, .Lcpu3_stacktop-g_cpu3_idlestack + +#if CONFIG_SMP_NCPUS > 4 +# error This logic needs to extended for CONFIG_SMP_NCPUS > 4 + +#endif /* CONFIG_SMP_NCPUS > 4 */ +#endif /* CONFIG_SMP_NCPUS > 3 */ +#endif /* CONFIG_SMP_NCPUS > 2 */ +#endif /* CONFIG_SMP_NCPUS > 1 */ +#endif /* CONFIG_SMP */ .end diff --git a/arch/arm/src/armv7-r/arm_cpuidlestack.c b/arch/arm/src/armv7-r/arm_cpuidlestack.c new file mode 100644 index 0000000000..00b0bebe98 --- /dev/null +++ b/arch/arm/src/armv7-r/arm_cpuidlestack.c @@ -0,0 +1,132 @@ +/**************************************************************************** + * arch/arm/src/armv7-r/arm_cpuidlestack.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <assert.h> + +#include <nuttx/arch.h> +#include <nuttx/sched.h> + +#include "smp.h" +#include "arm_internal.h" + +#ifdef CONFIG_SMP + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Stack alignment macros */ + +#define STACK_ISALIGNED(a) ((uintptr_t)(a) & ~SMP_STACK_MASK) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#if CONFIG_SMP_NCPUS > 1 +static const uint32_t *g_cpu_stackalloc[CONFIG_SMP_NCPUS] = +{ + 0 + , g_cpu1_idlestack +#if CONFIG_SMP_NCPUS > 2 + , g_cpu2_idlestack +#if CONFIG_SMP_NCPUS > 3 + , g_cpu3_idlestack +#endif /* CONFIG_SMP_NCPUS > 3 */ +#endif /* CONFIG_SMP_NCPUS > 2 */ +}; +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_cpu_idlestack + * + * Description: + * Allocate a stack for the CPU[n] IDLE task (n > 0) if appropriate and + * setup up stack-related information in the IDLE task's TCB. This + * function is always called before up_cpu_start(). This function is + * only called for the CPU's initial IDLE task; up_create_task is used for + * all normal tasks, pthreads, and kernel threads for all CPUs. + * + * The initial IDLE task is a special case because the CPUs can be started + * in different wans in different environments: + * + * 1. The CPU may already have been started and waiting in a low power + * state for up_cpu_start(). In this case, the IDLE thread's stack + * has already been allocated and is already in use. Here + * up_cpu_idlestack() only has to provide information about the + * already allocated stack. + * + * 2. The CPU may be disabled but started when up_cpu_start() is called. + * In this case, a new stack will need to be created for the IDLE + * thread and this function is then equivalent to: + * + * return up_create_stack(tcb, stack_size, TCB_FLAG_TTYPE_KERNEL); + * + * The following TCB fields must be initialized by this function: + * + * - adj_stack_size: Stack size after adjustment for hardware, processor, + * etc. This value is retained only for debug purposes. + * - stack_alloc_ptr: Pointer to allocated stack + * - stack_base_ptr: Adjusted stack base pointer after the TLS Data and + * Arguments has been removed from the stack allocation. + * + * Input Parameters: + * - cpu: CPU index that indicates which CPU the IDLE task is + * being created for. + * - tcb: The TCB of new CPU IDLE task + * - stack_size: The requested stack size for the IDLE task. At least + * this much must be allocated. This should be + * CONFIG_SMP_STACK_SIZE. + * + ****************************************************************************/ + +int up_cpu_idlestack(int cpu, struct tcb_s *tcb, size_t stack_size) +{ +#if CONFIG_SMP_NCPUS > 1 + uintptr_t stack_alloc; + + DEBUGASSERT(cpu > 0 && cpu < CONFIG_SMP_NCPUS && tcb != NULL && + stack_size <= SMP_STACK_SIZE); + + /* Get the top of the stack */ + + stack_alloc = (uintptr_t)g_cpu_stackalloc[cpu]; + DEBUGASSERT(stack_alloc != 0 && STACK_ISALIGNED(stack_alloc)); + + tcb->adj_stack_size = SMP_STACK_SIZE; + tcb->stack_alloc_ptr = (void *)stack_alloc; + tcb->stack_base_ptr = tcb->stack_alloc_ptr; +#endif + + return OK; +} + +#endif /* CONFIG_SMP */ diff --git a/arch/arm/src/armv7-r/barriers.h b/arch/arm/src/armv7-r/arm_cpuindex.c similarity index 53% copy from arch/arm/src/armv7-r/barriers.h copy to arch/arm/src/armv7-r/arm_cpuindex.c index 030b5f4a50..c36cd0d2f8 100644 --- a/arch/arm/src/armv7-r/barriers.h +++ b/arch/arm/src/armv7-r/arm_cpuindex.c @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/arm/src/armv7-r/barriers.h + * arch/arm/src/armv7-r/arm_cpuindex.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -18,27 +18,54 @@ * ****************************************************************************/ -#ifndef __ARCH_ARM_SRC_ARMV7_R_BARRIERS_H -#define __ARCH_ARM_SRC_ARMV7_R_BARRIERS_H - /**************************************************************************** * Included Files ****************************************************************************/ +#include <nuttx/config.h> + +#include <stdint.h> + +#include <nuttx/arch.h> + +#include "cp15.h" +#include "sctlr.h" + +#ifdef CONFIG_SMP + /**************************************************************************** - * Pre-processor Definitions + * Public Functions ****************************************************************************/ -/* ARMv7-R memory barriers */ +/**************************************************************************** + * Name: up_cpu_index + * + * Description: + * Return an index in the range of 0 through (CONFIG_SMP_NCPUS-1) that + * corresponds to the currently executing CPU. + * + * If TLS is enabled, then the RTOS can get this information from the TLS + * info structure. Otherwise, the MCU-specific logic must provide some + * mechanism to provide the CPU index. + * + * Input Parameters: + * None + * + * Returned Value: + * An integer index in the range of 0 through (CONFIG_SMP_NCPUS-1) that + * corresponds to the currently executing CPU. + * + ****************************************************************************/ + +int up_cpu_index(void) +{ + /* Read the Multiprocessor Affinity Register (MPIDR) */ + + uint32_t mpidr = cp15_rdmpidr(); -#define arm_isb(n) __asm__ __volatile__ ("isb " #n : : : "memory") -#define arm_dsb(n) __asm__ __volatile__ ("dsb " #n : : : "memory") -#define arm_dmb(n) __asm__ __volatile__ ("dmb " #n : : : "memory") -#define arm_nop(n) __asm__ __volatile__ ("nop\n") + /* And return the CPU ID field */ -#define ARM_DSB() arm_dsb(15) -#define ARM_ISB() arm_isb(15) -#define ARM_DMB() arm_dmb(15) -#define ARM_NOP() arm_nop(15) + return (mpidr & MPIDR_CPUID_MASK) >> MPIDR_CPUID_SHIFT; +} -#endif /* __ARCH_ARM_SRC_ARMV7_R_BARRIERS_H */ +#endif /* CONFIG_SMP */ diff --git a/arch/arm/src/armv7-r/arm_cpupause.c b/arch/arm/src/armv7-r/arm_cpupause.c new file mode 100644 index 0000000000..ccd865809e --- /dev/null +++ b/arch/arm/src/armv7-r/arm_cpupause.c @@ -0,0 +1,326 @@ +/**************************************************************************** + * arch/arm/src/armv7-r/arm_cpupause.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <assert.h> + +#include <nuttx/arch.h> +#include <nuttx/sched.h> +#include <nuttx/spinlock.h> +#include <nuttx/sched_note.h> + +#include "arm_internal.h" +#include "gic.h" +#include "sched/sched.h" + +#ifdef CONFIG_SMP + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* These spinlocks are used in the SMP configuration in order to implement + * up_cpu_pause(). The protocol for CPUn to pause CPUm is as follows + * + * 1. The up_cpu_pause() implementation on CPUn locks both g_cpu_wait[m] + * and g_cpu_paused[m]. CPUn then waits spinning on g_cpu_paused[m]. + * 2. CPUm receives the interrupt it (1) unlocks g_cpu_paused[m] and + * (2) locks g_cpu_wait[m]. The first unblocks CPUn and the second + * blocks CPUm in the interrupt handler. + * + * When CPUm resumes, CPUn unlocks g_cpu_wait[m] and the interrupt handler + * on CPUm continues. CPUm must, of course, also then unlock g_cpu_wait[m] + * so that it will be ready for the next pause operation. + */ + +static volatile spinlock_t g_cpu_wait[CONFIG_SMP_NCPUS]; +static volatile spinlock_t g_cpu_paused[CONFIG_SMP_NCPUS]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_cpu_pausereq + * + * Description: + * Return true if a pause request is pending for this CPU. + * + * Input Parameters: + * cpu - The index of the CPU to be queried + * + * Returned Value: + * true = a pause request is pending. + * false = no pasue request is pending. + * + ****************************************************************************/ + +bool up_cpu_pausereq(int cpu) +{ + return spin_islocked(&g_cpu_paused[cpu]); +} + +/**************************************************************************** + * Name: up_cpu_paused + * + * Description: + * Handle a pause request from another CPU. Normally, this logic is + * executed from interrupt handling logic within the architecture-specific + * However, it is sometimes necessary necessary to perform the pending + * pause operation in other contexts where the interrupt cannot be taken + * in order to avoid deadlocks. + * + * This function performs the following operations: + * + * 1. It saves the current task state at the head of the current assigned + * task list. + * 2. It waits on a spinlock, then + * 3. Returns from interrupt, restoring the state of the new task at the + * head of the ready to run list. + * + * Input Parameters: + * cpu - The index of the CPU to be paused + * + * Returned Value: + * On success, OK is returned. Otherwise, a negated errno value indicating + * the nature of the failure is returned. + * + ****************************************************************************/ + +int up_cpu_paused(int cpu) +{ + struct tcb_s *tcb = this_task(); + + /* Update scheduler parameters */ + + nxsched_suspend_scheduler(tcb); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify that we are paused */ + + sched_note_cpu_paused(tcb); +#endif + + /* Save the current context at CURRENT_REGS into the TCB at the head + * of the assigned task list for this CPU. + */ + + arm_savestate(tcb->xcp.regs); + + /* Release the g_cpu_paused spinlock to synchronize with the + * requesting CPU. + */ + + spin_unlock(&g_cpu_paused[cpu]); + + /* Wait for the spinlock to be released. The requesting CPU will release + * the spinlock when the CPU is resumed. + */ + + spin_lock(&g_cpu_wait[cpu]); + + /* This CPU has been resumed. Restore the exception context of the TCB at + * the (new) head of the assigned task list. + */ + + tcb = this_task(); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify that we have resumed */ + + sched_note_cpu_resumed(tcb); +#endif + + /* Reset scheduler parameters */ + + nxsched_resume_scheduler(tcb); + + /* Then switch contexts. Any necessary address environment changes + * will be made when the interrupt returns. + */ + + arm_restorestate(tcb->xcp.regs); + spin_unlock(&g_cpu_wait[cpu]); + + return OK; +} + +/**************************************************************************** + * Name: arm_pause_handler + * + * Description: + * This is the handler for SGI2. It performs the following operations: + * + * 1. It saves the current task state at the head of the current assigned + * task list. + * 2. It waits on a spinlock, then + * 3. Returns from interrupt, restoring the state of the new task at the + * head of the ready to run list. + * + * Input Parameters: + * Standard interrupt handling + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int arm_pause_handler(int irq, void *context, void *arg) +{ + int cpu = this_cpu(); + + /* Check for false alarms. Such false could occur as a consequence of + * some deadlock breaking logic that might have already serviced the SG2 + * interrupt by calling up_cpu_paused(). If the pause event has already + * been processed then g_cpu_paused[cpu] will not be locked. + */ + + if (up_cpu_pausereq(cpu)) + { + /* NOTE: The following enter_critical_section() will call + * up_cpu_paused() to process a pause request to break a deadlock + * because the caller held a critical section. Once up_cpu_paused() + * finished, the caller will proceed and release the g_cpu_irqlock. + * Then this CPU will acquire g_cpu_irqlock in the function. + */ + + irqstate_t flags = enter_critical_section(); + + /* NOTE: the pause request should not exist here */ + + DEBUGVERIFY(!up_cpu_pausereq(cpu)); + + leave_critical_section(flags); + } + + return OK; +} + +/**************************************************************************** + * Name: up_cpu_pause + * + * Description: + * Save the state of the current task at the head of the + * g_assignedtasks[cpu] task list and then pause task execution on the + * CPU. + * + * This function is called by the OS when the logic executing on one CPU + * needs to modify the state of the g_assignedtasks[cpu] list for another + * CPU. + * + * Input Parameters: + * cpu - The index of the CPU to be stopped + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_cpu_pause(int cpu) +{ + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu()); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify of the pause event */ + + sched_note_cpu_pause(this_task(), cpu); +#endif + + /* Take the both spinlocks. The g_cpu_wait spinlock will prevent the SGI2 + * handler from returning until up_cpu_resume() is called; g_cpu_paused + * is a handshake that will prefent this function from returning until + * the CPU is actually paused. + * Note that we might spin before getting g_cpu_wait, this just means that + * the other CPU still hasn't finished responding to the previous resume + * request. + */ + + DEBUGASSERT(!spin_islocked(&g_cpu_paused[cpu])); + + spin_lock(&g_cpu_wait[cpu]); + spin_lock(&g_cpu_paused[cpu]); + + /* Execute SGI2 */ + + arm_cpu_sgi(GIC_IRQ_SGI2, (1 << cpu)); + + /* Wait for the other CPU to unlock g_cpu_paused meaning that + * it is fully paused and ready for up_cpu_resume(); + */ + + spin_lock(&g_cpu_paused[cpu]); + spin_unlock(&g_cpu_paused[cpu]); + + /* On successful return g_cpu_wait will be locked, the other CPU will be + * spinning on g_cpu_wait and will not continue until g_cpu_resume() is + * called. g_cpu_paused will be unlocked in any case. + */ + + return OK; +} + +/**************************************************************************** + * Name: up_cpu_resume + * + * Description: + * Restart the cpu after it was paused via up_cpu_pause(), restoring the + * state of the task at the head of the g_assignedtasks[cpu] list, and + * resume normal tasking. + * + * This function is called after up_cpu_pause in order resume operation of + * the CPU after modifying its g_assignedtasks[cpu] list. + * + * Input Parameters: + * cpu - The index of the CPU being re-started. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_cpu_resume(int cpu) +{ + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu()); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify of the resume event */ + + sched_note_cpu_resume(this_task(), cpu); +#endif + + /* Release the spinlock. Releasing the spinlock will cause the SGI2 + * handler on 'cpu' to continue and return from interrupt to the newly + * established thread. + */ + + DEBUGASSERT(spin_islocked(&g_cpu_wait[cpu]) && + !spin_islocked(&g_cpu_paused[cpu])); + + spin_unlock(&g_cpu_wait[cpu]); + return OK; +} + +#endif /* CONFIG_SMP */ diff --git a/arch/arm/src/armv7-r/arm_cpustart.c b/arch/arm/src/armv7-r/arm_cpustart.c new file mode 100644 index 0000000000..51e24476a7 --- /dev/null +++ b/arch/arm/src/armv7-r/arm_cpustart.c @@ -0,0 +1,136 @@ +/**************************************************************************** + * arch/arm/src/armv7-r/arm_cpustart.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <assert.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/sched.h> +#include <nuttx/sched_note.h> + +#include "arm_internal.h" +#include "cp15_cacheops.h" +#include "gic.h" +#include "sched/sched.h" + +#ifdef CONFIG_SMP + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_start_handler + * + * Description: + * This is the handler for SGI1. This handler simply returns from the + * interrupt, restoring the state of the new task at the head of the ready + * to run list. + * + * Input Parameters: + * Standard interrupt handling + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int arm_start_handler(int irq, void *context, void *arg) +{ + struct tcb_s *tcb = this_task(); + + sinfo("CPU%d Started\n", this_cpu()); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify that this CPU has started */ + + sched_note_cpu_started(tcb); +#endif + + /* Reset scheduler parameters */ + + nxsched_resume_scheduler(tcb); + + /* Then switch contexts. This instantiates the exception context of the + * tcb at the head of the assigned task list. In this case, this should + * be the CPUs NULL task. + */ + + arm_restorestate(tcb->xcp.regs); + return OK; +} + +/**************************************************************************** + * Name: up_cpu_start + * + * Description: + * In an SMP configuration, only one CPU is initially active (CPU 0). + * System initialization occurs on that single thread. At the completion of + * the initialization of the OS, just before beginning normal multitasking, + * the additional CPUs would be started by calling this function. + * + * Each CPU is provided the entry point to its IDLE task when started. A + * TCB for each CPU's IDLE task has been initialized and placed in the + * CPU's g_assignedtasks[cpu] list. No stack has been allocated or + * initialized. + * + * The OS initialization logic calls this function repeatedly until each + * CPU has been started, 1 through (CONFIG_SMP_NCPUS-1). + * + * Input Parameters: + * cpu - The index of the CPU being started. This will be a numeric + * value in the range of one to (CONFIG_SMP_NCPUS-1). + * (CPU 0 is already active) + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_cpu_start(int cpu) +{ + sinfo("Starting CPU%d\n", cpu); + + DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu()); + +#ifdef CONFIG_SCHED_INSTRUMENTATION + /* Notify of the start event */ + + sched_note_cpu_start(this_task(), cpu); +#endif + + /* Execute SGI1 */ + + arm_cpu_sgi(GIC_IRQ_SGI1, (1 << cpu)); + return OK; +} + +#endif /* CONFIG_SMP */ diff --git a/arch/arm/src/armv7-r/arm_head.S b/arch/arm/src/armv7-r/arm_head.S index d6433910e9..428b0b2d3d 100644 --- a/arch/arm/src/armv7-r/arm_head.S +++ b/arch/arm/src/armv7-r/arm_head.S @@ -76,7 +76,11 @@ */ #ifndef IDLE_STACK_BASE -# define IDLE_STACK_BASE _ebss +# ifdef CONFIG_SMP +# define IDLE_STACK_BASE _enoinit +# else +# define IDLE_STACK_BASE _ebss +# endif #endif #define IDLE_STACK_TOP IDLE_STACK_BASE+CONFIG_IDLETHREAD_STACKSIZE @@ -124,6 +128,32 @@ .type __start, #function __start: +#if defined(CONFIG_SMP) && CONFIG_SMP_NCPUS > 1 + /* Get cpuindex, cpu0 continue boot, others wait event from cpu0 */ + + mrc CP15_MPIDR(r0) + and r0, r0, #0x3 + cmp r0, #0 + beq __cpu0_start + wfe + cmp r0, #1 + beq __cpu1_start +# if CONFIG_SMP_NCPUS > 2 + cmp r0, #2 + beq __cpu2_start +# endif +# if CONFIG_SMP_NCPUS > 3 + cmp r0, #3 + beq __cpu3_start +# endif +# if CONFIG_SMP_NCPUS > 4 + cmp r0, #4 + beq __cpu4_start +# endif + +__cpu0_start: +#endif + /* Make sure that we are in SYS mode with IRQs and FIQs disabled */ cpsid if, #PSR_MODE_SYS @@ -184,10 +214,19 @@ __start: /* Clear all configurable bits */ - bic r0, r0, #(SCTLR_M | SCTLR_A | SCTLR_C | SCTLR_CCP15BEN | SCTLR_B) - bic r0, r0, #(SCTLR_SW | SCTLR_I | SCTLR_V | SCTLR_RR) + bic r0, r0, #(SCTLR_A | SCTLR_C | SCTLR_CCP15BEN | SCTLR_B) + bic r0, r0, #(SCTLR_SW | SCTLR_I | SCTLR_V | SCTLR_RR) bic r0, r0, #(SCTLR_BR | SCTLR_DZ | SCTLR_FI) - bic r0, r0, #(SCTLR_VE | SCTLR_EE | SCTLR_NMFI | SCTLR_TE) + bic r0, r0, #(SCTLR_VE | SCTLR_EE | SCTLR_NMFI | SCTLR_TE) + +#ifndef CONFIG_SMP + /* Set bits to enable the MPU + * + * SCTLR_M Bit 0: Enable the MPU + */ + + orr r0, r0, #(SCTLR_M) +#endif /* Set configured bits */ @@ -200,7 +239,7 @@ __start: orr r0, r0, #(SCTLR_A) #endif -#ifndef CONFIG_ARMV7R_DCACHE_DISABLE +#if !defined(CONFIG_ARMV7R_DCACHE_DISABLE) && !defined(CONFIG_SMP) /* Dcache enable * * SCTLR_C Bit 2: DCache enable @@ -218,7 +257,7 @@ __start: orr r0, r0, #(SCTLR_CCP15BEN) #endif -#ifndef CONFIG_ARMV7R_ICACHE_DISABLE +#if !defined(CONFIG_ARMV7R_ICACHE_DISABLE) && !defined(CONFIG_SMP) /* Icache enable * * SCTLR_I Bit 12: ICache enable diff --git a/arch/arm/src/armv7-r/arm_schedulesigaction.c b/arch/arm/src/armv7-r/arm_schedulesigaction.c index 472699930b..bf44b93de5 100644 --- a/arch/arm/src/armv7-r/arm_schedulesigaction.c +++ b/arch/arm/src/armv7-r/arm_schedulesigaction.c @@ -75,6 +75,7 @@ * ****************************************************************************/ +#ifndef CONFIG_SMP void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) { sinfo("tcb=%p sigdeliver=%p\n", tcb, sigdeliver); @@ -208,3 +209,222 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) } } } +#else +void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver) +{ + int cpu; + int me; + + sinfo("tcb=%p sigdeliver=%p\n", tcb, sigdeliver); + + /* Refuse to handle nested signal actions */ + + if (!tcb->xcp.sigdeliver) + { + /* First, handle some special cases when the signal is being delivered + * to task that is currently executing on any CPU. + */ + + sinfo("rtcb=%p CURRENT_REGS=%p\n", this_task(), CURRENT_REGS); + + if (tcb->task_state == TSTATE_TASK_RUNNING) + { + me = this_cpu(); + cpu = tcb->cpu; + + /* CASE 1: We are not in an interrupt handler and a task is + * signaling itself for some reason. + */ + + if (cpu == me && !CURRENT_REGS) + { + /* In this case just deliver the signal now. + * REVISIT: Signal handler will run in a critical section! + */ + + sigdeliver(tcb); + } + + /* CASE 2: The task that needs to receive the signal is running. + * This could happen if the task is running on another CPU OR if + * we are in an interrupt handler and the task is running on this + * CPU. In the former case, we will have to PAUSE the other CPU + * first. But in either case, we will have to modify the return + * state as well as the state in the TCB. + */ + + else + { + /* If we signaling a task running on the other CPU, we have + * to PAUSE the other CPU. + */ + + if (cpu != me) + { + /* Pause the CPU */ + + up_cpu_pause(cpu); + + /* Wait while the pause request is pending */ + + while (up_cpu_pausereq(cpu)) + { + } + + /* Now tcb on the other CPU can be accessed safely */ + + /* Copy tcb->xcp.regs to tcp.xcp.saved. These will be + * restored by the signal trampoline after the signal has + * been delivered. + */ + + tcb->xcp.sigdeliver = sigdeliver; + + /* Save the current register context location */ + + tcb->xcp.saved_regs = tcb->xcp.regs; + + /* Duplicate the register context. These will be + * restored by the signal trampoline after the signal has + * been delivered. + */ + + tcb->xcp.regs = (void *) + ((uint32_t)tcb->xcp.regs - + (uint32_t)XCPTCONTEXT_SIZE); + memcpy(tcb->xcp.regs, tcb->xcp.saved_regs, + XCPTCONTEXT_SIZE); + + tcb->xcp.regs[REG_SP] = (uint32_t)tcb->xcp.regs + + (uint32_t)XCPTCONTEXT_SIZE; + + /* Then set up to vector to the trampoline with interrupts + * disabled + */ + + tcb->xcp.regs[REG_PC] = (uint32_t)arm_sigdeliver; + tcb->xcp.regs[REG_CPSR] = (PSR_MODE_SYS | PSR_I_BIT | + PSR_F_BIT); +#ifdef CONFIG_ARM_THUMB + tcb->xcp.regs[REG_CPSR] |= PSR_T_BIT; +#endif + } + else + { + /* tcb is running on the same CPU */ + + /* Save the return PC, CPSR and either the BASEPRI or + * PRIMASK registers (and perhaps also the LR). These will + * be restored by the signal trampoline after the signal + * has been delivered. + */ + + tcb->xcp.sigdeliver = (void *)sigdeliver; + + /* And make sure that the saved context in the TCB is the + * same as the interrupt return context. + */ + + arm_savestate(tcb->xcp.saved_regs); + + /* Duplicate the register context. These will be + * restored by the signal trampoline after the signal has + * been delivered. + */ + + CURRENT_REGS = (void *) + ((uint32_t)CURRENT_REGS - + (uint32_t)XCPTCONTEXT_SIZE); + memcpy((uint32_t *)CURRENT_REGS, tcb->xcp.saved_regs, + XCPTCONTEXT_SIZE); + + CURRENT_REGS[REG_SP] = (uint32_t)CURRENT_REGS + + (uint32_t)XCPTCONTEXT_SIZE; + + /* Then set up vector to the trampoline with interrupts + * disabled. The kernel-space trampoline must run in + * privileged thread mode. + */ + + CURRENT_REGS[REG_PC] = (uint32_t)arm_sigdeliver; + CURRENT_REGS[REG_CPSR] = (PSR_MODE_SYS | PSR_I_BIT | + PSR_F_BIT); +#ifdef CONFIG_ARM_THUMB + CURRENT_REGS[REG_CPSR] |= PSR_T_BIT; +#endif + } + + /* Increment the IRQ lock count so that when the task is + * restarted, it will hold the IRQ spinlock. + */ + + DEBUGASSERT(tcb->irqcount < INT16_MAX); + tcb->irqcount++; + + /* NOTE: If the task runs on another CPU(cpu), adjusting + * global IRQ controls will be done in the pause handler + * on the CPU(cpu) by taking a critical section. + * If the task is scheduled on this CPU(me), do nothing + * because this CPU already took a critical section + */ + + /* RESUME the other CPU if it was PAUSED */ + + if (cpu != me) + { + up_cpu_resume(cpu); + } + } + } + + /* Otherwise, we are (1) signaling a task is not running from an + * interrupt handler or (2) we are not in an interrupt handler and the + * running task is signaling some other non-running task. + */ + + else + { + /* Save the return lr and cpsr and one scratch register. These + * will be restored by the signal trampoline after the signals + * have been delivered. + */ + + tcb->xcp.sigdeliver = sigdeliver; + + /* Save the current register context location */ + + tcb->xcp.saved_regs = tcb->xcp.regs; + + /* Duplicate the register context. These will be + * restored by the signal trampoline after the signal has been + * delivered. + */ + + tcb->xcp.regs = (void *) + ((uint32_t)tcb->xcp.regs - + (uint32_t)XCPTCONTEXT_SIZE); + memcpy(tcb->xcp.regs, tcb->xcp.saved_regs, XCPTCONTEXT_SIZE); + + tcb->xcp.regs[REG_SP] = (uint32_t)tcb->xcp.regs + + (uint32_t)XCPTCONTEXT_SIZE; + + /* Increment the IRQ lock count so that when the task is restarted, + * it will hold the IRQ spinlock. + */ + + DEBUGASSERT(tcb->irqcount < INT16_MAX); + tcb->irqcount++; + + /* Then set up to vector to the trampoline with interrupts + * disabled + */ + + tcb->xcp.regs[REG_PC] = (uint32_t)arm_sigdeliver; + tcb->xcp.regs[REG_CPSR] = (PSR_MODE_SYS | PSR_I_BIT | PSR_F_BIT); +#ifdef CONFIG_ARM_THUMB + tcb->xcp.regs[REG_CPSR] |= PSR_T_BIT; +#endif + } + } +} +#endif diff --git a/arch/arm/src/armv7-r/arm_scu.c b/arch/arm/src/armv7-r/arm_scu.c new file mode 100644 index 0000000000..9e2d04f7dc --- /dev/null +++ b/arch/arm/src/armv7-r/arm_scu.c @@ -0,0 +1,123 @@ +/**************************************************************************** + * arch/arm/src/armv7-r/arm_scu.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> + +#include "arm_internal.h" +#include "cp15_cacheops.h" +#include "barriers.h" +#include "sctlr.h" +#include "scu.h" +#include "cp15.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_enable_smp + * + * Description: + * Enable the SCU and make certain that current CPU is participating in + * the SMP cache coherency. + * + * Assumption: + * Called early in the CPU start-up. No special critical sections are + * needed if only CPU-private registers are modified. + * + ****************************************************************************/ + +void arm_enable_smp(int cpu) +{ + uint32_t regval; + + /* Handle actions unique to CPU0 which comes up first */ + + if (cpu == 0) + { + /* Invalidate the SCU duplicate tags for all processors */ + + putreg32((SCU_INVALIDATE_ALL_WAYS << SCU_INVALIDATE_CPU0_SHIFT) + | (SCU_INVALIDATE_ALL_WAYS << SCU_INVALIDATE_CPU1_SHIFT) +#if CONFIG_SMP_NCPUS > 2 + | (SCU_INVALIDATE_ALL_WAYS << SCU_INVALIDATE_CPU2_SHIFT) + | (SCU_INVALIDATE_ALL_WAYS << SCU_INVALIDATE_CPU3_SHIFT) +#endif + , SCU_INVALIDATE); + + /* Invalidate CPUn L1 data cache so that is will we be reloaded from + * coherent L2. + */ + + cp15_invalidate_dcache_all(); + ARM_DSB(); + + /* Invalidate the L2C-310 -- Missing logic. */ + + /* Enable the SCU */ + + regval = getreg32(SCU_CTRL); + regval |= SCU_CTRL_ENABLE; + putreg32(regval, SCU_CTRL); + + /* Initialize done, kick other cpus which waiting on __start */ + + ARM_SEV(); + } + + /* Actions for other CPUs */ + + else + { + /* Invalidate CPUn L1 data cache so that is will we be reloaded from + * coherent L2. + */ + + cp15_dcache_op_level(0, CP15_CACHE_INVALIDATE); + ARM_DSB(); + + /* Wait for the SCU to be enabled by the primary processor -- should + * not be necessary. + */ + } + + /* Enable the data cache, set the SMP mode with ACTLR.SMP=1. + * + * SMP - Sgnals if the processor is taking part in coherency + * or not. + * + * FW - Cache and TLB maintenance broadcast. + */ + + regval = CP15_GET(ACTLR); + regval |= ACTLR_SMP; + regval |= ACTLR_FW; + CP15_SET(ACTLR, regval); + + regval = CP15_GET(SCTLR); + regval |= SCTLR_C | SCTLR_I | SCTLR_M; + CP15_SET(SCTLR, regval); +} diff --git a/arch/arm/src/armv7-r/arm_sigdeliver.c b/arch/arm/src/armv7-r/arm_sigdeliver.c index 6699aaa777..be1553b0f6 100644 --- a/arch/arm/src/armv7-r/arm_sigdeliver.c +++ b/arch/arm/src/armv7-r/arm_sigdeliver.c @@ -56,12 +56,41 @@ void arm_sigdeliver(void) struct tcb_s *rtcb = this_task(); uint32_t *regs = rtcb->xcp.saved_regs; +#ifdef CONFIG_SMP + /* In the SMP case, we must terminate the critical section while the signal + * handler executes, but we also need to restore the irqcount when the + * we resume the main thread of the task. + */ + + int16_t saved_irqcount; +#endif + board_autoled_on(LED_SIGNAL); sinfo("rtcb=%p sigdeliver=%p sigpendactionq.head=%p\n", rtcb, rtcb->xcp.sigdeliver, rtcb->sigpendactionq.head); DEBUGASSERT(rtcb->xcp.sigdeliver != NULL); +#ifdef CONFIG_SMP + /* In the SMP case, up_schedule_sigaction(0) will have incremented + * 'irqcount' in order to force us into a critical section. Save the + * pre-incremented irqcount. + */ + + saved_irqcount = rtcb->irqcount - 1; + DEBUGASSERT(saved_irqcount >= 0); + + /* Now we need call leave_critical_section() repeatedly to get the irqcount + * to zero, freeing all global spinlocks that enforce the critical section. + */ + + do + { + leave_critical_section(regs[REG_CPSR]); + } + while (rtcb->irqcount > 0); +#endif /* CONFIG_SMP */ + #ifndef CONFIG_SUPPRESS_INTERRUPTS /* Then make sure that interrupts are enabled. Signal handlers must always * run with interrupts enabled. @@ -80,7 +109,22 @@ void arm_sigdeliver(void) */ sinfo("Resuming\n"); + +#ifdef CONFIG_SMP + /* Restore the saved 'irqcount' and recover the critical section + * spinlocks. + */ + + DEBUGASSERT(rtcb->irqcount == 0); + while (rtcb->irqcount < saved_irqcount) + { + enter_critical_section(); + } +#endif + +#ifndef CONFIG_SUPPRESS_INTERRUPTS up_irq_save(); +#endif /* Modify the saved return state with the actual saved values in the * TCB. This depends on the fact that nested signal handling is diff --git a/arch/arm/src/armv7-r/arm_vectors.S b/arch/arm/src/armv7-r/arm_vectors.S index 659ac8d249..0f8e2224b9 100644 --- a/arch/arm/src/armv7-r/arm_vectors.S +++ b/arch/arm/src/armv7-r/arm_vectors.S @@ -42,6 +42,36 @@ * Assembly Macros ****************************************************************************/ +/**************************************************************************** + * Name: setirqstack + * + * Description: + * Set the current stack pointer to the "top" of the IRQ interrupt stack. Single + * CPU case. Must be provided by MCU-specific logic in chip.h for the SMP case. + * + ****************************************************************************/ + +#if !defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 7 + .macro setirqstack, tmp1, tmp2 + ldr sp, .Lirqstacktop /* SP = IRQ stack top */ + .endm +#endif + +/**************************************************************************** + * Name: setfiqstack + * + * Description: + * Set the current stack pointer to the "top" of the FIQ interrupt stack. Single + * CPU case. Must be provided by MCU-specific logic in chip.h for the SMP case. + * + ****************************************************************************/ + +#if !defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 7 + .macro setfiqstack, tmp1, tmp2 + ldr sp, .Lfiqstacktop /* SP = FIQ stack top */ + .endm +#endif + /**************************************************************************** * Name: savefpu * @@ -169,7 +199,7 @@ arm_vectorirq: #if CONFIG_ARCH_INTERRUPTSTACK > 7 /* Call arm_decodeirq() on the interrupt stack */ - ldr sp, .Lirqstacktop /* SP = interrupt stack top */ + setirqstack r1, r3 /* SP = interrupt stack top */ #else /* Call arm_decodeirq() on the user stack */ @@ -216,7 +246,7 @@ arm_vectorirq: rfeia r14 -#if CONFIG_ARCH_INTERRUPTSTACK > 7 +#if !defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 7 .Lirqstacktop: .word g_intstacktop #endif @@ -281,7 +311,7 @@ arm_vectorsvc: #if CONFIG_ARCH_INTERRUPTSTACK > 7 /* Call arm_syscall() on the interrupt stack */ - ldr sp, .Lirqstacktop /* SP = interrupt stack top */ + setirqstack r1, r3 /* SP = interrupt stack top */ #else /* Call arm_syscall() on the user stack */ @@ -641,7 +671,7 @@ arm_vectorfiq: #if CONFIG_ARCH_INTERRUPTSTACK > 7 /* Call arm_decodefiq() on the interrupt stack */ - ldr sp, .Lfiqstacktop /* SP = interrupt stack top */ + setfiqstack r1, r3 /* SP = interrupt stack top */ #endif bic sp, sp, #7 /* Force 8-byte alignment */ @@ -676,7 +706,7 @@ arm_vectorfiq: rfeia r14 -#if CONFIG_ARCH_INTERRUPTSTACK > 7 +#if !defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 7 .Lfiqstacktop: .word g_fiqstacktop #endif @@ -690,7 +720,7 @@ arm_vectorfiq: * Name: g_intstackalloc/g_intstacktop ****************************************************************************/ -#if CONFIG_ARCH_INTERRUPTSTACK > 7 +#if !defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 7 .bss .balign 8 @@ -722,5 +752,5 @@ g_fiqstacktop: .size g_fiqstackalloc, (CONFIG_ARCH_INTERRUPTSTACK & ~7) #endif -#endif /* CONFIG_ARCH_INTERRUPTSTACK > 7 */ +#endif /* !CONFIG_SMP && CONFIG_ARCH_INTERRUPTSTACK > 7 */ .end diff --git a/arch/arm/src/armv7-r/barriers.h b/arch/arm/src/armv7-r/barriers.h index 030b5f4a50..fe17d57db3 100644 --- a/arch/arm/src/armv7-r/barriers.h +++ b/arch/arm/src/armv7-r/barriers.h @@ -34,11 +34,13 @@ #define arm_isb(n) __asm__ __volatile__ ("isb " #n : : : "memory") #define arm_dsb(n) __asm__ __volatile__ ("dsb " #n : : : "memory") #define arm_dmb(n) __asm__ __volatile__ ("dmb " #n : : : "memory") -#define arm_nop(n) __asm__ __volatile__ ("nop\n") +#define arm_nop() __asm__ __volatile__ ("nop\n") +#define arm_sev() __asm__ __volatile__ ("sev\n") #define ARM_DSB() arm_dsb(15) #define ARM_ISB() arm_isb(15) #define ARM_DMB() arm_dmb(15) -#define ARM_NOP() arm_nop(15) +#define ARM_NOP() arm_nop() +#define ARM_SEV() arm_sev() #endif /* __ARCH_ARM_SRC_ARMV7_R_BARRIERS_H */ diff --git a/arch/arm/src/armv7-r/sctlr.h b/arch/arm/src/armv7-r/sctlr.h index 532cc3617c..5da84ed8f2 100644 --- a/arch/arm/src/armv7-r/sctlr.h +++ b/arch/arm/src/armv7-r/sctlr.h @@ -61,9 +61,19 @@ * TODO: To be provided */ -/* Multiprocessor Affinity Register (MPIDR): CRn=c0, opc1=0, CRm=c0, opc2=5 - * TODO: To be provided - */ +/* Multiprocessor Affinity Register (MPIDR): CRn=c0, opc1=0, CRm=c0, opc2=5 */ + +#define MPIDR_CPUID_SHIFT (0) /* Bits 0-1: CPU ID */ +#define MPIDR_CPUID_MASK (3 << MPIDR_CPUID_SHIFT) +# define MPIDR_CPUID_CPU0 (0 << MPIDR_CPUID_SHIFT) +# define MPIDR_CPUID_CPU1 (1 << MPIDR_CPUID_SHIFT) +# define MPIDR_CPUID_CPU2 (2 << MPIDR_CPUID_SHIFT) +# define MPIDR_CPUID_CPU3 (3 << MPIDR_CPUID_SHIFT) + /* Bits 2-7: Reserved */ +#define MPIDR_CLUSTID_SHIFT (8) /* Bits 8-11: Cluster ID value */ +#define MPIDR_CLUSTID_MASK (15 << MPIDR_CLUSTID_SHIFT) + /* Bits 12-29: Reserved */ +#define MPIDR_U (1 << 30) /* Bit 30: Multiprocessing Extensions. */ /* Revision ID Register (REVIDR): CRn=c0, opc1=0, CRm=c0, opc2=6 * TODO: To be provided @@ -160,9 +170,19 @@ /* Bits 28-29: Reserved */ #define SCTLR_TE (1 << 30) /* Bit 30: Thumb exception enable */ -/* Auxiliary Control Register (ACTLR): CRn=c1, opc1=0, CRm=c0, opc2=1 - * Implementation defined - */ +/* Auxiliary Control Register (ACTLR): CRn=c1, opc1=0, CRm=c0, opc2=1 */ + +#define ACTLR_FW (1 << 0) /* Bit 0: Enable Cache/TLB maintenance broadcast */ + /* Bits 1-2: Reserved */ +#define ACTLR_MRP (1 << 3) /* Bit 3: Enable MRP */ + /* Bits 4-5: Reserved */ +#define ACTLR_SMP (1 << 6) /* Bit 6: Cortex-A9 taking part in coherency */ + /* Bits 7: Reserved */ +#define ACTLR_ALLOC_1WAY (1 << 8) /* Bit 8: Allocation in 1-way cache only */ +#define ACTLR_DTCM_ECC (1 << 9) /* Bit 9: ECC on caches and DTCM */ +#define ACTLR_ITCM_ECC (1 << 10) /* Bit 10: ECC on caches and ITCM */ +#define ACTLR_ITCM_QOS (1 << 11) /* Bit 11: Enable QoS*/ + /* Bits 12-31: Reserved */ /* Coprocessor Access Control Register (CPACR): * CRn=c1, opc1=0, CRm=c0, opc2=2 diff --git a/arch/arm/src/armv7-r/scu.h b/arch/arm/src/armv7-r/scu.h new file mode 100644 index 0000000000..df732d9ca7 --- /dev/null +++ b/arch/arm/src/armv7-r/scu.h @@ -0,0 +1,252 @@ +/**************************************************************************** + * arch/arm/src/armv7-r/scu.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Reference: + * Cortex™-R8 MPCore, Revision: r0p3, Technical Reference Manual. + */ + +#ifndef __ARCH_ARM_SRC_ARMV7_R_SCU_H +#define __ARCH_ARM_SRC_ARMV7_R_SCU_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "mpcore.h" /* For MPCORE_SCU_VBASE */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +#define SCU_CTRL_OFFSET 0x0000 /* SCU Control Register (Implementation defined) */ +#define SCU_CONFIG_OFFSET 0x0004 /* SCU Configuration Register (Implementation defined) */ +#define SCU_PWRSTATUS_OFFSET 0x0008 /* SCU CPU Power Status Register */ +#define SCU_INVALIDATE_OFFSET 0x000c /* SCU Invalidate All Registers in Secure State */ +#define SCU_FILTERSTART_OFFSET 0x0040 /* Filtering Start Address Register Defined by FILTERSTART input */ +#define SCU_FILTEREND_OFFSET 0x0044 /* Filtering End Address Register Defined by FILTEREND input */ +#define SCU_PFILTERSTART_OFFSET 0x0048 /* Peripherals Filtering Start Address Register */ +#define SCU_PFILTEREND_OFFSET 0x004c /* Peripherals Filtering End Address Register */ +#define SCU_SAC_OFFSET 0x0050 /* SCU Access Control (SAC) Register */ +#define SCU_ERRBANKFST_OFFSET 0x0060 /* SCU Error Bank First Entry Register */ +#define SCU_ERRBANKSND_OFFSET 0x0064 /* SCU Error Bank Second Entry Register */ +#define SCU_DEBUGRAM_OFFSET 0x0070 /* SCU Debug Tag RAM Operation Register */ +#define SCU_DEBUGRAMDATA_OFFSET 0x0074 /* SCU Debug Tag RAM Data Value Register */ +#define SCU_DEBUGRAMECC_OFFSET 0x0078 /* SCU Debug Tag RAM ECC Chunk Register */ +#define SCU_ECCERR_OFFSET 0x007c /* ECC Fatal Error Registe */ +#define SCU_FPPFILTERSTART_OFFSET(n) (0x0080 + (n)*8) /* FPP Filtering Start Address Register for core n */ +#define SCU_FPPFILTEREND_OFFSET(n) (0x0084 + (n)*8) /* FPP Filtering End Address Register for core n */ + +/* Register addresses *******************************************************/ + +#define SCU_CTRL (MPCORE_SCU_VBASE+SCU_CTRL_OFFSET) +#define SCU_CONFIG (MPCORE_SCU_VBASE+SCU_CONFIG_OFFSET) +#define SCU_PWRSTATUS (MPCORE_SCU_VBASE+SCU_PWRSTATUS_OFFSET) +#define SCU_INVALIDATE (MPCORE_SCU_VBASE+SCU_INVALIDATE_OFFSET) +#define SCU_FILTERSTART (MPCORE_SCU_VBASE+SCU_FILTERSTART_OFFSET) +#define SCU_FILTEREND (MPCORE_SCU_VBASE+SCU_FILTEREND_OFFSET) +#define SCU_PFILTERSTART (MPCORE_SCU_VBASE+SCU_PFILTERSTART_OFFSET) +#define SCU_PFILTEREND (MPCORE_SCU_VBASE+SCU_PFILTEREND_OFFSET) +#define SCU_SAC (MPCORE_SCU_VBASE+SCU_SAC_OFFSET) +#define SCU_ERRBANKFST (MPCORE_SCU_VBASE+SCU_ERRBANKFST_OFFSET) +#define SCU_ERRBANKSND (MPCORE_SCU_VBASE+SCU_ERRBANKSND_OFFSET) +#define SCU_DEBUGRAM (MPCORE_SCU_VBASE+SCU_DEBUGRAM_OFFSET) +#define SCU_DEBUGRAMDATA (MPCORE_SCU_VBASE+SCU_DEBUGRAMDATA_OFFSET) +#define SCU_DEBUGRAMECC (MPCORE_SCU_VBASE+SCU_DEBUGRAMECC_OFFSET) +#define SCU_ECCERR (MPCORE_SCU_VBASE+SCU_ECCERR_OFFSET) +#define SCU_FPPFILTERSTART(n) (MPCORE_SCU_VBASE+SCU_FPPFILTERSTART0_OFFSET(n)) +#define SCU_FPPFILTEREND(n) (MPCORE_SCU_VBASE+SCU_FPPFILTEREND0_OFFSET(n)) + +/* Register bit-field definitions *******************************************/ + +/* SCU Control Register (Implementation defined) */ + +#define SCU_CTRL_ENABLE (1 << 0) /* SCU enable */ +#define SCU_CTRL_ADDRFILTER (1 << 1) /* Address filtering enable */ +#define SCU_CTRL_RAMPARITY (1 << 2) /* SCU RAMs ECC enable */ +#define SCU_CTRL_LINFILL (1 << 3) /* SCU speculative linefill enable */ +#define SCU_CTRL_STANDBY (1 << 5) /* SCU standby enable */ +#define SCU_CTRL_ICSTANDBY (1 << 6) /* IC standby enable */ +#define SCU_CTRL_ECCCHKEN_M0 (1 << 12) /* ECC check enable on M0 */ +#define SCU_CTRL_ECCCHKEN_M1 (1 << 13) /* ECC check enable on M1 */ +#define SCU_CTRL_ECCCHKEN_MP (1 << 14) /* ECC check enable on MP */ +#define SCU_CTRL_ECCCHKEN_ACP (1 << 15) /* ECC check enable on ACP */ +#define SCU_CTRL_ECCCHKEN_FPP(n) (1 << ((n)+16)) /* ECC check enable on FPP for core n */ +#define SCU_CTRL_ECCCHKEN_TCM (1 << 20) /* ECC check enable on AXI TCM */ + +/* SCU Configuration Register (Implementation defined) */ + +#define SCU_CONFIG_NCPUS_SHIFT 0 /* CPU number Number of CPUs present */ +#define SCU_CONFIG_NCPUS_MASK (3 << SCU_CONFIG_NCPUS_SHIFT) +# define SCU_CONFIG_NCPUS(r) ((((uint32_t)(r) & SCU_CONFIG_NCPUS_MASK) >> SCU_CONFIG_NCPUS_SHIFT) + 1) +#define SCU_CONFIG_SMPCPUS_SHIFT 4 /* Processors that are in SMP or AMP mode */ +#define SCU_CONFIG_SMPCPUS_MASK (15 << SCU_CONFIG_SMPCPUS_SHIFT) +# define SCU_CONFIG_CPU_SMP(n) (1 << ((n)+4)) +# define SCU_CONFIG_CPU0_SMP (1 << 4) +# define SCU_CONFIG_CPU1_SMP (1 << 5) +# define SCU_CONFIG_CPU2_SMP (1 << 6) +# define SCU_CONFIG_CPU3_SMP (1 << 7) + +#define SCU_CONFIG_CACHE_0KB 0 +#define SCU_CONFIG_CACHE_4KB 1 +#define SCU_CONFIG_CACHE_8KB 2 +#define SCU_CONFIG_CACHE_16KB 3 +#define SCU_CONFIG_CACHE_32KB 4 +#define SCU_CONFIG_CACHE_64KB 5 + +#define SCU_CONFIG_CPU0_CACHE_SHIFT 8 /* CPU 0 cache size */ +#define SCU_CONFIG_CPU0_CACHE_MASK (4 << SCU_CONFIG_CPU0_CACHE_SHIFT) +#define SCU_CONFIG_CPU1_CACHE_SHIFT 12 /* CPU 1 cache size */ +#define SCU_CONFIG_CPU1_CACHE_MASK (4 << SCU_CONFIG_CPU1_CACHE_SHIFT) +#define SCU_CONFIG_CPU2_CACHE_SHIFT 16 /* CPU 2 cache size */ +#define SCU_CONFIG_CPU2_CACHE_MASK (4 << SCU_CONFIG_CPU2_CACHE_SHIFT) +#define SCU_CONFIG_CPU3_CACHE_SHIFT 20 /* CPU 3 cache size */ +#define SCU_CONFIG_CPU3_CACHE_MASK (4 << SCU_CONFIG_CPU3_CACHE_SHIFT) + +#define SCU_CONFIG_AXI_PORT1_SHIFT 31 + +/* SCU CPU Power Status Register */ + +#define SCU_PWRSTATUS_NORMAL 0 +#define SCU_PWRSTATUS_DORMANT 2 +#define SCU_PWRSTATUS_PWROFF 3 + +#define SCU_PWRSTATUS_CPU0_SHIFT 0 /* CPU0 status Power status */ +#define SCU_PWRSTATUS_CPU0_MASK (3 << SCU_PWRSTATUS_CPU0_SHIFT) +#define SCU_PWRSTATUS_CPU1_SHIFT 8 /* CPU1 status Power status */ +#define SCU_PWRSTATUS_CPU1_MASK (3 << SCU_PWRSTATUS_CPU1_SHIFT) +#define SCU_PWRSTATUS_CPU2_SHIFT 16 /* CPU2 status Power status */ +#define SCU_PWRSTATUS_CPU2_MASK (3 << SCU_PWRSTATUS_CPU2_SHIFT) +#define SCU_PWRSTATUS_CPU3_SHIFT 24 /* CPU3 status Power status */ +#define SCU_PWRSTATUS_CPU3_MASK (3 << SCU_PWRSTATUS_CPU3_SHIFT) + +/* SCU Invalidate All Registers in Secure State */ + +#define SCU_INVALIDATE_ALL_WAYS 15 +#define SCU_INVALIDATE_CPU0_SHIFT 0 /* Ways that must be invalidated for CPU0 */ +#define SCU_INVALIDATE_CPU0_MASK (15 << SCU_INVALIDATE_CPU0_SHIFT) +#define SCU_INVALIDATE_CPU1_SHIFT 4 /* Ways that must be invalidated for CPU1 */ +#define SCU_INVALIDATE_CPU1_MASK (15 << SCU_INVALIDATE_CPU1_SHIFT) +#define SCU_INVALIDATE_CPU2_SHIFT 8 /* Ways that must be invalidated for CPU2 */ +#define SCU_INVALIDATE_CPU2_MASK (15 << SCU_INVALIDATE_CPU2_SHIFT) +#define SCU_INVALIDATE_CPU3_SHIFT 12 /* Ways that must be invalidated for CPU3 */ +#define SCU_INVALIDATE_CPU3_MASK (15 << SCU_INVALIDATE_CPU3_SHIFT) + +/* Filtering Start Address Register Defined by FILTERSTART input */ + +#define SCU_FILTERSTART_SHIFT 20 /* Filtering start address */ +#define SCU_FILTERSTART_MASK (0xfff << SCU_FILTERSTART_SHIFT) + +/* Filtering End Address Register Defined by FILTEREND input */ + +#define SCU_FILTEREND_SHIFT 20 /* Filtering start address */ +#define SCU_FILTEREND_MASK (0xfff << SCU_FILTEREND_SHIFT) + +/* LLP Filtering Start Address Register */ + +#define SCU_LLPFILTERSTART_SHIFT 20 +#define SCU_LLPFILTERSTART_MASK (0xfff << SCU_LLPFILTERSTART_SHIFT) + +/* LLP Filtering End Address Register */ + +#define SCU_LLPFILTEREND_SHIFT 20 +#define SCU_LLPFILTEREND_MASK (0xfff << SCU_LLPFILTEREND_SHIFT) + +/* SCU Access Control (SAC) Register */ + +#define SCU_SAC_CPU(n) (1 << (n)) /* CPUn may access components */ + +/* SCU Error Bank First Entry Register */ + +#define SCU_ERRBANKFST_STATUS_SHIFT 0 +#define SCU_ERRBANKFST_STATUS_MASK (3 << SCU_ERRBANKFST_STATUS_SHIFT) + +#define SCU_ERRBANKFST_WAYS_SHIFT(n) (16 + (n)*4) +#define SCU_ERRBANKFST_WAYS_MASK(n) (0xf << SCU_ERRBANKFST_WAYS_SHIFT(n)) + +/* SCU Error Bank Second Entry Register */ + +#define SCU_ERRBANKSND_STATUS_SHIFT 0 +#define SCU_ERRBANKSND_STATUS_MASK (3 << SCU_ERRBANKSND_STATUS_SHIFT) + +#define SCU_ERRBANKSND_INDEX_SHIFT 5 +#define SCU_ERRBANKSND_INDEX_MASK (0x1ff << SCU_ERRBANKSND_INDEX_SHIFT) + +#define SCU_ERRBANKSND_WAYS_SHIFT 16 +#define SCU_ERRBANKSND_WAYS_MASK (0xffff << SCU_ERRBANKSND_WAYS_SHIFT) + +/* SCU Debug Tag RAM Operation Register */ + +#define SCU_DEBUGRAM_READ 0 +#define SCU_DEBUGRAM_WRITE 1 + +#define SCU_DEBUGRAM_INDEX_SHIFT 5 +#define SCU_DEBUGRAM_INDEX_MASK (0x1ff << SCU_DEBUGRAM_INDEX_SHIFT) + +#define SCU_DEBUGRAM_CORE_SHIFT 24 +#define SCU_DEBUGRAM_CORE_MASK (3 << SCU_DEBUGRAM_CORE_SHIFT) + +#define SCU_DEBUGRAM_WAY_SHIFT 30 +#define SCU_DEBUGRAM_WAY_MASK (3 << SCU_DEBUGRAM_WAY_SHIFT) + +/* SCU Debug Tag RAM Data Value Register */ + +#define SCU_DEBUGRAMDATA_VALUE_SHIFT 17 +#define SCU_DEBUGRAMDATA_VALUE_MASK (0x1f << SCU_DEBUGRAMDATA_VALUE_SHIFT) + +#define SCU_DEBUGRAMDATA_VALID (1 << 22) + +/* SCU Debug Tag RAM ECC Chunk Register */ + +#define SCU_DEBUGRAMECC_CHUNK_SHIFT 0 +#define SCU_DEBUGRAMECC_CHUNK_MASK (0x3f << SCU_DEBUGRAMECC_CHUNK_SHIFT) + +/* ECC Fatal Error Register */ + +#define SCU_ECCERR_CORE_DETECTED(n) (n) +#define SCU_ECCERR_DETECTED (1 << 8) + +/* FPP Filtering Start Address Registers 0-3 */ + +#define SCU_FPPFILTERSTART_SHIFT 20 +#define SCU_FPPFILTERSTART_MASK (0xfff << SCU_FPPFILTERSTART_SHIFT) + +/* FPP Filtering End Address Registers 0-3 */ + +#define SCU_FPPFILTEREND_SHIFT 20 +#define SCU_FPPFILTEREND_MASK (0xfff << SCU_FPPFILTEREND_SHIFT) + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_enable_smp + * + * Description: + * Enable the SCU and make certain that current CPU is participating in + * the SMP cache coherency. + * + ****************************************************************************/ + +void arm_enable_smp(int cpu); + +#endif /* __ARCH_ARM_SRC_ARMV7_R_SCU_H */ diff --git a/arch/arm/src/armv7-r/smp.h b/arch/arm/src/armv7-r/smp.h new file mode 100644 index 0000000000..b0d37fbff4 --- /dev/null +++ b/arch/arm/src/armv7-r/smp.h @@ -0,0 +1,128 @@ +/**************************************************************************** + * arch/arm/src/armv7-r/smp.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_ARMV7_R_SMP_H +#define __ARCH_ARM_SRC_ARMV7_R_SMP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#ifdef CONFIG_SMP + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* ARM requires at least a 4-byte stack alignment. For use with EABI and + * floating point, the stack must be aligned to 8-byte addresses. We will + * always use the EABI stack alignment + */ + +#define SMP_STACK_ALIGNMENT 8 +#define SMP_STACK_MASK 7 +#define SMP_STACK_SIZE ((CONFIG_IDLETHREAD_STACKSIZE + 7) & ~7) +#define SMP_STACK_WORDS (SMP_STACK_SIZE >> 2) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#if CONFIG_SMP_NCPUS > 1 +extern uint32_t g_cpu1_idlestack[SMP_STACK_WORDS]; +#if CONFIG_SMP_NCPUS > 2 +extern uint32_t g_cpu2_idlestack[SMP_STACK_WORDS]; +#if CONFIG_SMP_NCPUS > 3 +extern uint32_t g_cpu3_idlestack[SMP_STACK_WORDS]; +#if CONFIG_SMP_NCPUS > 4 +# error This logic needs to extended for CONFIG_SMP_NCPUS > 4 +#endif /* CONFIG_SMP_NCPUS > 4 */ +#endif /* CONFIG_SMP_NCPUS > 3 */ +#endif /* CONFIG_SMP_NCPUS > 2 */ +#endif /* CONFIG_SMP_NCPUS > 1 */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: __cpu[n]_start + * + * Description: + * Boot functions for each CPU (other than CPU0). These functions set up + * the ARM operating mode, the initial stack, and configure co-processor + * registers. At the end of the boot, arm_cpu_boot() is called. + * + * These functions are provided by the common ARMv7-R logic. + * + * Input Parameters: + * None + * + * Returned Value: + * Do not return. + * + ****************************************************************************/ + +#if CONFIG_SMP_NCPUS > 1 +void __cpu1_start(void); +#endif + +#if CONFIG_SMP_NCPUS > 2 +void __cpu2_start(void); +#endif + +#if CONFIG_SMP_NCPUS > 3 +void __cpu3_start(void); +#endif + +#if CONFIG_SMP_NCPUS > 4 +# error This logic needs to extended for CONFIG_SMP_NCPUS > 4 +#endif + +/**************************************************************************** + * Name: arm_cpu_boot + * + * Description: + * Continues the C-level initialization started by the assembly language + * __cpu[n]_start function. At a minimum, this function needs to + * initialize interrupt handling and, perhaps, wait on WFI for + * arm_cpu_start() to issue an SGI. + * + * This function must be provided by the each ARMv7-R MCU and implement + * MCU-specific initialization logic. + * + * Input Parameters: + * cpu - The CPU index. This is the same value that would be obtained by + * calling up_cpu_index(); + * + * Returned Value: + * Does not return. + * + ****************************************************************************/ + +void arm_cpu_boot(int cpu); + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_SMP */ +#endif /* __ARCH_ARM_SRC_ARMV7_R_SMP_H */