diff --git a/boards/xtensa/intel_s1000_crb/board.cmake b/boards/xtensa/intel_s1000_crb/board.cmake
index 6e5e996f782..58c03a3e23a 100644
--- a/boards/xtensa/intel_s1000_crb/board.cmake
+++ b/boards/xtensa/intel_s1000_crb/board.cmake
@@ -3,9 +3,18 @@
board_set_flasher_ifnset(intel_s1000)
board_set_debugger_ifnset(intel_s1000)
+if(CONFIG_SMP)
+board_finalize_runner_args(intel_s1000
+ "--xt-ocd-dir=/opt/tensilica/xocd-12.0.4/xt-ocd"
+ "--ocd-topology=topology_all_flyswatter2.xml"
+ "--ocd-jtag-instr=all_gdb.txt"
+ "--gdb-flash-file=load_elf.txt"
+)
+else()
board_finalize_runner_args(intel_s1000
"--xt-ocd-dir=/opt/tensilica/xocd-12.0.4/xt-ocd"
"--ocd-topology=topology_dsp0_flyswatter2.xml"
"--ocd-jtag-instr=dsp0_gdb.txt"
"--gdb-flash-file=load_elf.txt"
)
+endif()
diff --git a/boards/xtensa/intel_s1000_crb/support/all_gdb.txt b/boards/xtensa/intel_s1000_crb/support/all_gdb.txt
new file mode 100644
index 00000000000..f182671d520
--- /dev/null
+++ b/boards/xtensa/intel_s1000_crb/support/all_gdb.txt
@@ -0,0 +1,32 @@
+#Initialization script format
+#COMMAND LENGTH DATA
+#
+#COMMAND: 1 - Scan DR branch
+# 2 - Scan IR branch
+# 3 - Send TRST
+#
+#LENGTH: Number of bits to send out (in decimal format).
+#
+#DATA: Data to send out. A sequence of bytes separated by space.
+# The rightmost bit is scanned out first. E.g. (B4 B3 B2 B1 B0)
+#
+## Examples
+#
+# Perform TAP Reset:
+3
+## all stap disable
+2 8 12
+1 6 0
+# Scan out MTAP IDCODE command (0x2) a265013h
+## all stap enable
+2 8 12
+1 6 05
+#
+#
+# Scan out (2 cores):
+# Send Trax Access IR command to 1st core and bypass to 2nd
+#2 10 3 9F
+# Write to DOSR register of 1st core (NAR part)
+#1 9 1 07
+# Write 32 bits to DOSR register (NDR part)
+#1 33 1 98 13 45 ab
diff --git a/boards/xtensa/intel_s1000_crb/support/dsp1_gdb.txt b/boards/xtensa/intel_s1000_crb/support/dsp1_gdb.txt
new file mode 100644
index 00000000000..fccecac20c3
--- /dev/null
+++ b/boards/xtensa/intel_s1000_crb/support/dsp1_gdb.txt
@@ -0,0 +1,32 @@
+#Initialization script format
+#COMMAND LENGTH DATA
+#
+#COMMAND: 1 - Scan DR branch
+# 2 - Scan IR branch
+# 3 - Send TRST
+#
+#LENGTH: Number of bits to send out (in decimal format).
+#
+#DATA: Data to send out. A sequence of bytes separated by space.
+# The rightmost bit is scanned out first. E.g. (B4 B3 B2 B1 B0)
+#
+## Examples
+#
+# Perform TAP Reset:
+3
+## all stap disable
+2 8 12
+1 6 0
+# Scan out MTAP IDCODE command (0x2) a265013h
+## all stap enable
+2 8 12
+1 6 04
+#
+#
+# Scan out (2 cores):
+# Send Trax Access IR command to 1st core and bypass to 2nd
+#2 10 3 9F
+# Write to DOSR register of 1st core (NAR part)
+#1 9 1 07
+# Write 32 bits to DOSR register (NDR part)
+#1 33 1 98 13 45 ab
diff --git a/boards/xtensa/intel_s1000_crb/support/load_elf.txt b/boards/xtensa/intel_s1000_crb/support/load_elf.txt
index f92eab026f6..6351783c8bb 100644
--- a/boards/xtensa/intel_s1000_crb/support/load_elf.txt
+++ b/boards/xtensa/intel_s1000_crb/support/load_elf.txt
@@ -7,6 +7,9 @@ set *(0x71d20) = 0
# disable xtensa core power saving
set *(0x71F90) = 0x71
+# stall/reset second core
+set *(0x81C14) = 0x0202
+
set pagination off
set confirm off
load zephyr/zephyr.elf
diff --git a/boards/xtensa/intel_s1000_crb/support/topology_all_flyswatter2.xml b/boards/xtensa/intel_s1000_crb/support/topology_all_flyswatter2.xml
new file mode 100644
index 00000000000..3504ff9ae9e
--- /dev/null
+++ b/boards/xtensa/intel_s1000_crb/support/topology_all_flyswatter2.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/boards/xtensa/intel_s1000_crb/support/topology_dsp1_flyswatter2.xml b/boards/xtensa/intel_s1000_crb/support/topology_dsp1_flyswatter2.xml
new file mode 100644
index 00000000000..0dfc3f67bc8
--- /dev/null
+++ b/boards/xtensa/intel_s1000_crb/support/topology_dsp1_flyswatter2.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dts/xtensa/intel/intel_s1000.dtsi b/dts/xtensa/intel/intel_s1000.dtsi
index d37974f85c3..06e5cb901a9 100644
--- a/dts/xtensa/intel/intel_s1000.dtsi
+++ b/dts/xtensa/intel/intel_s1000.dtsi
@@ -87,6 +87,14 @@
interrupt-parent = <&core_intc>;
};
+ idc: idc@1200 {
+ compatible = "intel,cavs-idc";
+ label = "CAVS_IDC";
+ reg = <0x1200 0x80>;
+ interrupts = <8 0 0>;
+ interrupt-parent = <&cavs0>;
+ };
+
dw_intc: intc@81800 {
compatible = "snps,designware-intc";
reg = <0x00081800 0x400>;
diff --git a/soc/xtensa/intel_s1000/CMakeLists.txt b/soc/xtensa/intel_s1000/CMakeLists.txt
index ae8aa2050e4..252f85f6188 100644
--- a/soc/xtensa/intel_s1000/CMakeLists.txt
+++ b/soc/xtensa/intel_s1000/CMakeLists.txt
@@ -7,3 +7,7 @@ zephyr_library_sources(soc.c)
if("${ZEPHYR_TOOLCHAIN_VARIANT}" STREQUAL "xcc")
add_subdirectory(xcc)
endif()
+
+if(CONFIG_SMP)
+zephyr_library_sources(soc_mp.c)
+endif()
diff --git a/soc/xtensa/intel_s1000/Kconfig.defconfig b/soc/xtensa/intel_s1000/Kconfig.defconfig
index e0e813aa0af..2724148c34c 100644
--- a/soc/xtensa/intel_s1000/Kconfig.defconfig
+++ b/soc/xtensa/intel_s1000/Kconfig.defconfig
@@ -23,4 +23,26 @@ config SYS_CLOCK_HW_CYCLES_PER_SEC
default 400000000 if XTENSA_TIMER
default 38400000 if CAVS_TIMER
+if SMP
+
+config MP_NUM_CPUS
+ default 2
+
+config XTENSA_TIMER
+ default n
+
+config CAVS_TIMER
+ default y
+
+config IPM
+ default y
+
+config IPM_CAVS_IDC
+ default y if IPM
+
+config SCHED_IPI_SUPPORTED
+ default y if IPM_CAVS_IDC
+
+endif
+
endif
diff --git a/soc/xtensa/intel_s1000/linker.ld b/soc/xtensa/intel_s1000/linker.ld
index 000a053abc1..671a2e2e869 100644
--- a/soc/xtensa/intel_s1000/linker.ld
+++ b/soc/xtensa/intel_s1000/linker.ld
@@ -21,6 +21,8 @@ OUTPUT_ARCH(xtensa)
#include
#include
+#include
+
#define RAMABLE_REGION ram :ram_phdr
#define ROMABLE_REGION ram :ram_phdr
#define LPRAM_REGION lpram
@@ -179,7 +181,17 @@ _memmap_cacheattr_bp_allvalid = 0x22222222;
* as cacheattr_set macro sets them both to the same set of
* attributes.
*/
+#ifndef CONFIG_SMP
_memmap_cacheattr_intel_s1000 = 0x1212fff2;
+#else
+/*
+ * Since there is no cache coherence between cores,
+ * set the data section (0xA0000000 - 0xBFFFFFFF) to be
+ * non-cacheable, for now. Until we have proper support
+ * to manipulate cache lines.
+ */
+_memmap_cacheattr_intel_s1000 = 0x1222fff2;
+#endif
PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_intel_s1000);
SECTIONS
diff --git a/soc/xtensa/intel_s1000/memory.h b/soc/xtensa/intel_s1000/memory.h
index 8117c16406f..53e9639bda6 100644
--- a/soc/xtensa/intel_s1000/memory.h
+++ b/soc/xtensa/intel_s1000/memory.h
@@ -47,9 +47,6 @@
#define MEM_VECT_SIZE (MEM_VECT_TEXT_SIZE +\
MEM_VECT_LIT_SIZE)
-/* The memerror vector address is copied as is from core-isa.h */
-#define XCHAL_MEMERROR_VECTOR_PADDR 0xBEFE0400
-
#define MEM_ERROR_TEXT_SIZE 0x180
#define MEM_ERROR_LIT_SIZE 0x8
@@ -72,4 +69,7 @@
#define LPRAM_BASE (DT_LP_SRAM_BASE)
#define LPRAM_SIZE (DT_LP_SRAM_SIZE)
+/* Boot vector resideing in LP-SRAM for core #1 */
+#define LPSRAM_BOOT_VECTOR_ADDR (DT_LP_SRAM_BASE + 0x08)
+
#endif /* __INC_MEMORY_H */
diff --git a/soc/xtensa/intel_s1000/soc.c b/soc/xtensa/intel_s1000/soc.c
index ba0c4eaca7a..46480db82eb 100644
--- a/soc/xtensa/intel_s1000/soc.c
+++ b/soc/xtensa/intel_s1000/soc.c
@@ -284,8 +284,7 @@ static inline void soc_set_power_and_clock(void)
dsp_shim_regs->clkctl |= SOC_CLKCTL_REQ_FAST_CLK |
SOC_CLKCTL_OCS_FAST_CLK;
- dsp_shim_regs->pwrctl |= SOC_PWRCTL_DISABLE_PWR_GATING_DSP1 |
- SOC_PWRCTL_DISABLE_PWR_GATING_DSP0;
+ dsp_shim_regs->pwrctl |= SOC_PWRCTL_DISABLE_PWR_GATING_DSP0;
soc_set_dmic_power();
soc_set_gna_power();
diff --git a/soc/xtensa/intel_s1000/soc.h b/soc/xtensa/intel_s1000/soc.h
index 820b1c41888..6e43e945980 100644
--- a/soc/xtensa/intel_s1000/soc.h
+++ b/soc/xtensa/intel_s1000/soc.h
@@ -134,6 +134,11 @@ struct soc_resource_alloc_regs {
u32_t geno;
};
+/* L2 Local Memory Registers */
+#define SOC_L2RAM_LOCAL_MEM_REG_BASE 0x00071D00
+#define SOC_L2RAM_LOCAL_MEM_REG_LSPGCTL \
+ (SOC_L2RAM_LOCAL_MEM_REG_BASE + 0x50)
+
/* DMIC SHIM Registers */
#define SOC_DMIC_SHIM_REG_BASE 0x00071E80
#define SOC_DMIC_SHIM_DMICLCTL_SPA BIT(0)
@@ -198,15 +203,22 @@ struct soc_dsp_shim_regs {
#define SOC_GNA_POWER_CONTROL_CPA (BIT(8))
#define SOC_GNA_POWER_CONTROL_CLK_EN (BIT(16))
+#define SOC_S1000_GLB_CTRL_DSP1_PWRCTL_CRST BIT(1)
+#define SOC_S1000_GLB_CTRL_DSP1_PWRCTL_CSTALL BIT(9)
+#define SOC_S1000_GLB_CTRL_DSP1_PWRCTL_SPA BIT(17)
+#define SOC_S1000_GLB_CTRL_DSP1_PWRCTL_CPA BIT(25)
+
#define SOC_S1000_STRAP_REF_CLK (BIT_MASK(2) << 3)
#define SOC_S1000_STRAP_REF_CLK_38P4 (0 << 3)
#define SOC_S1000_STRAP_REF_CLK_19P2 (1 << 3)
#define SOC_S1000_STRAP_REF_CLK_24P576 (2 << 3)
struct soc_global_regs {
- u32_t reserved1[8];
+ u32_t reserved1[5];
+ u32_t cavs_dsp1power_control;
+ u32_t reserved2[2];
u32_t gna_power_control;
- u32_t reserved2[7];
+ u32_t reserved3[7];
u32_t straps;
};
diff --git a/soc/xtensa/intel_s1000/soc_mp.c b/soc/xtensa/intel_s1000/soc_mp.c
new file mode 100644
index 00000000000..c9d6529b141
--- /dev/null
+++ b/soc/xtensa/intel_s1000/soc_mp.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2018-2020 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+LOG_MODULE_REGISTER(soc_mp, CONFIG_SOC_LOG_LEVEL);
+
+#include "soc.h"
+#include "memory.h"
+
+#ifdef CONFIG_SCHED_IPI_SUPPORTED
+#include
+#include
+
+static struct device *idc;
+#endif
+extern void __start(void);
+
+struct cpustart_rec {
+ u32_t cpu;
+ arch_cpustart_t fn;
+ char *stack_top;
+ void *arg;
+ u32_t vecbase;
+ u32_t alive;
+ /* padding to cache line */
+ u8_t padding[XCHAL_DCACHE_LINESIZE - 6 * 4];
+};
+
+static __aligned(XCHAL_DCACHE_LINESIZE)
+struct cpustart_rec start_rec;
+
+static void *mp_top;
+
+static void mp_entry2(void)
+{
+ volatile int ps, ie;
+
+ /* Copy over VECBASE from the main CPU for an initial value
+ * (will need to revisit this if we ever allow a user API to
+ * change interrupt vectors at runtime). Make sure interrupts
+ * are locally disabled, then synthesize a PS value that will
+ * enable them for the user code to pass to irq_unlock()
+ * later.
+ */
+ __asm__ volatile("rsr.PS %0" : "=r"(ps));
+ ps &= ~(PS_EXCM_MASK | PS_INTLEVEL_MASK);
+ __asm__ volatile("wsr.PS %0" : : "r"(ps));
+
+ ie = 0;
+ __asm__ volatile("wsr.INTENABLE %0" : : "r"(ie));
+ __asm__ volatile("wsr.VECBASE %0" : : "r"(start_rec.vecbase));
+ __asm__ volatile("rsync");
+
+ /* Set up the CPU pointer. */
+ _cpu_t *cpu = &_kernel.cpus[start_rec.cpu];
+
+ __asm__ volatile(
+ "wsr." CONFIG_XTENSA_KERNEL_CPU_PTR_SR " %0" : : "r"(cpu));
+
+#ifdef CONFIG_IPM_CAVS_IDC
+ /* Interrupt must be enabled while running on current core */
+ irq_enable(XTENSA_IRQ_NUMBER(DT_INST_0_INTEL_CAVS_IDC_IRQ_0));
+#endif /* CONFIG_IPM_CAVS_IDC */
+
+ start_rec.alive = 1;
+ SOC_DCACHE_FLUSH(&start_rec, sizeof(start_rec));
+
+ start_rec.fn(start_rec.arg);
+
+#if CONFIG_MP_NUM_CPUS == 1
+ /* CPU#1 can be under manual control running custom functions
+ * instead of participating in general thread execution.
+ * Put the CPU into idle after those functions return
+ * so this won't return.
+ */
+ for (;;) {
+ k_cpu_idle();
+ }
+#endif
+}
+
+/* Defines a locally callable "function" named mp_stack_switch(). The
+ * first argument (in register a2 post-ENTRY) is the new stack pointer
+ * to go into register a1. The second (a3) is the entry point.
+ * Because this never returns, a0 is used as a scratch register then
+ * set to zero for the called function (a null return value is the
+ * signal for "top of stack" to the debugger).
+ */
+void mp_stack_switch(void *stack, void *entry);
+__asm__("\n"
+ ".align 4 \n"
+ "mp_stack_switch: \n\t"
+
+ "entry a1, 16 \n\t"
+
+ "movi a0, 0 \n\t"
+
+ "jx a3 \n\t");
+
+/* Carefully constructed to use no stack beyond compiler-generated ABI
+ * instructions. Stack pointer is pointing to __stack at this point.
+ */
+void z_mp_entry(void)
+{
+ mp_stack_switch(mp_top, mp_entry2);
+}
+
+void arch_start_cpu(int cpu_num, k_thread_stack_t *stack, int sz,
+ arch_cpustart_t fn, void *arg)
+{
+ volatile struct soc_dsp_shim_regs *dsp_shim_regs =
+ (volatile struct soc_dsp_shim_regs *)SOC_DSP_SHIM_REG_BASE;
+ volatile struct soc_global_regs *soc_glb_regs =
+ (volatile struct soc_global_regs *)SOC_S1000_GLB_CTRL_BASE;
+ u32_t vecbase;
+
+ __ASSERT(cpu_num == 1, "Intel S1000 supports only two CPUs!");
+
+ /* Setup data to boot core #1 */
+ __asm__ volatile("rsr.VECBASE %0\n\t" : "=r"(vecbase));
+
+ start_rec.cpu = cpu_num;
+ start_rec.fn = fn;
+ start_rec.stack_top = Z_THREAD_STACK_BUFFER(stack) + sz;
+ start_rec.arg = arg;
+ start_rec.vecbase = vecbase;
+ start_rec.alive = 0;
+
+ mp_top = Z_THREAD_STACK_BUFFER(stack) + sz;
+
+ SOC_DCACHE_FLUSH(&start_rec, sizeof(start_rec));
+
+#ifdef CONFIG_SCHED_IPI_SUPPORTED
+ idc = device_get_binding(DT_INST_0_INTEL_CAVS_IDC_LABEL);
+#endif
+
+ /*
+ * SoC Boot ROM has hard-coded address for boot vector in LP-SRAM,
+ * and will jump unconditionally to it. So power up the LP-SRAM
+ * and set the vector.
+ */
+ sys_write32(0x0, SOC_L2RAM_LOCAL_MEM_REG_LSPGCTL);
+ *((u32_t *)LPSRAM_BOOT_VECTOR_ADDR) = (u32_t)__start;
+
+ /* Disable power gating for DSP core #cpu_num */
+ dsp_shim_regs->pwrctl |= SOC_PWRCTL_DISABLE_PWR_GATING_DSP1;
+
+ /*
+ * Since we do not know the status of the core,
+ * power it down and force it into reset and stall.
+ */
+ soc_glb_regs->cavs_dsp1power_control |=
+ SOC_S1000_GLB_CTRL_DSP1_PWRCTL_CRST |
+ SOC_S1000_GLB_CTRL_DSP1_PWRCTL_CSTALL;
+
+ soc_glb_regs->cavs_dsp1power_control &=
+ ~SOC_S1000_GLB_CTRL_DSP1_PWRCTL_SPA;
+
+ /* Wait for core power down */
+ while ((soc_glb_regs->cavs_dsp1power_control &
+ SOC_S1000_GLB_CTRL_DSP1_PWRCTL_CPA) != 0) {
+ };
+
+ /* Now power up the core */
+ soc_glb_regs->cavs_dsp1power_control |=
+ SOC_S1000_GLB_CTRL_DSP1_PWRCTL_SPA;
+
+ /* Wait for core power up*/
+ while ((soc_glb_regs->cavs_dsp1power_control &
+ SOC_S1000_GLB_CTRL_DSP1_PWRCTL_CPA) == 0) {
+ };
+
+ /* Then step out of reset, and un-stall */
+ soc_glb_regs->cavs_dsp1power_control &=
+ ~SOC_S1000_GLB_CTRL_DSP1_PWRCTL_CRST;
+
+ soc_glb_regs->cavs_dsp1power_control &=
+ ~SOC_S1000_GLB_CTRL_DSP1_PWRCTL_CSTALL;
+
+ do {
+ SOC_DCACHE_INVALIDATE(&start_rec, sizeof(start_rec));
+ } while (start_rec.alive == 0);
+}
+
+#ifdef CONFIG_SCHED_IPI_SUPPORTED
+FUNC_ALIAS(soc_sched_ipi, arch_sched_ipi, void);
+void soc_sched_ipi(void)
+{
+ if (likely(idc != NULL)) {
+ ipm_send(idc, 0, IPM_CAVS_IDC_MSG_SCHED_IPI_ID,
+ IPM_CAVS_IDC_MSG_SCHED_IPI_DATA, 0);
+ }
+}
+#endif