summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2013-10-25 15:37:11 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2013-12-10 19:17:54 +0000
commit93cc00fde1ba15c1f995fe963f30c9caecb02f77 (patch)
treeabd1b6d512e3e94a1e72aac2a83b2a03d7e4b5cb
parent6f348ecf083a3182b7eed83cdd9a9bc19751a0ba (diff)
downloadchrome-ec-93cc00fde1ba15c1f995fe963f30c9caecb02f77.tar.gz
ite: Port OS layer to Andestar v3m architecture
This will be used to support ITE IT8380 chip which contains an Andes N801 core. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=chrome-os-partner:23574 TEST=make BOARD=it8380dev Change-Id: I91f9380c51c7712aa6a6418223a11551ab0091ce Reviewed-on: https://chromium-review.googlesource.com/175480 Reviewed-by: Randall Spangler <rspangler@chromium.org> Commit-Queue: Vincent Palatin <vpalatin@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--core/nds32/atomic.h56
-rw-r--r--core/nds32/build.mk15
-rw-r--r--core/nds32/config_core.h13
-rw-r--r--core/nds32/cpu.c52
-rw-r--r--core/nds32/cpu.h57
-rw-r--r--core/nds32/ec.lds.S213
-rw-r--r--core/nds32/init.S194
-rw-r--r--core/nds32/irq_chip.h54
-rw-r--r--core/nds32/panic.c42
-rw-r--r--core/nds32/switch.S99
-rw-r--r--core/nds32/task.c515
-rw-r--r--include/link_defs.h1
-rw-r--r--include/task.h9
13 files changed, 1320 insertions, 0 deletions
diff --git a/core/nds32/atomic.h b/core/nds32/atomic.h
new file mode 100644
index 0000000000..3214067c43
--- /dev/null
+++ b/core/nds32/atomic.h
@@ -0,0 +1,56 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Atomic operations for Andes */
+
+#ifndef __CROS_EC_ATOMIC_H
+#define __CROS_EC_ATOMIC_H
+
+#include "common.h"
+#include "cpu.h"
+
+static inline void atomic_clear(uint32_t *addr, uint32_t bits)
+{
+ uint32_t psw = get_psw();
+ asm volatile ("setgie.d");
+ *addr &= ~bits;
+ set_psw(psw);
+}
+
+static inline void atomic_or(uint32_t *addr, uint32_t bits)
+{
+ uint32_t psw = get_psw();
+ asm volatile ("setgie.d");
+ *addr |= bits;
+ set_psw(psw);
+}
+
+static inline void atomic_add(uint32_t *addr, uint32_t value)
+{
+ uint32_t psw = get_psw();
+ asm volatile ("setgie.d");
+ *addr += value;
+ set_psw(psw);
+}
+
+static inline void atomic_sub(uint32_t *addr, uint32_t value)
+{
+ uint32_t psw = get_psw();
+ asm volatile ("setgie.d");
+ *addr -= value;
+ set_psw(psw);
+}
+
+static inline uint32_t atomic_read_clear(uint32_t *addr)
+{
+ uint32_t val;
+ uint32_t psw = get_psw();
+ asm volatile ("setgie.d");
+ val = *addr;
+ *addr = 0;
+ set_psw(psw);
+ return val;
+}
+#endif /* __CROS_EC_ATOMIC_H */
diff --git a/core/nds32/build.mk b/core/nds32/build.mk
new file mode 100644
index 0000000000..810d5b8dd3
--- /dev/null
+++ b/core/nds32/build.mk
@@ -0,0 +1,15 @@
+# -*- makefile -*-
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Andestar v3m architecture core OS files build
+#
+
+# Select Andes bare-metal toolchain
+CROSS_COMPILE?=nds32le-cros-elf-
+
+# CPU specific compilation flags
+CFLAGS_CPU=-march=v3m -Os
+
+core-y=cpu.o init.o panic.o task.o switch.o
diff --git a/core/nds32/config_core.h b/core/nds32/config_core.h
new file mode 100644
index 0000000000..57909a263a
--- /dev/null
+++ b/core/nds32/config_core.h
@@ -0,0 +1,13 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CONFIG_CORE_H
+#define __CONFIG_CORE_H
+
+/* Linker binary architecture and format */
+#define BFD_ARCH nds32
+#define BFD_FORMAT "elf32-nds32le"
+
+#endif /* __CONFIG_CORE_H */
diff --git a/core/nds32/cpu.c b/core/nds32/cpu.c
new file mode 100644
index 0000000000..e4dc5db94c
--- /dev/null
+++ b/core/nds32/cpu.c
@@ -0,0 +1,52 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Set up the N8 core
+ */
+
+#include "cpu.h"
+
+void cpu_init(void)
+{
+ /* DLM initialization is done in init.S */
+}
+
+/**
+ * Count leading zeros
+ *
+ * @param x non null integer.
+ * @return the number of leading 0-bits in x,
+ * starting at the most significant bit position.
+ *
+ * The Andestar v3m architecture has no CLZ instruction (contrary to v3),
+ * so let's use the software implementation.
+ */
+int __clzsi2(int x)
+{
+ int r = 0;
+
+ if (!x)
+ return 32;
+ if (!(x & 0xffff0000u)) {
+ x <<= 16;
+ r += 16;
+ }
+ if (!(x & 0xff000000u)) {
+ x <<= 8;
+ r += 8;
+ }
+ if (!(x & 0xf0000000u)) {
+ x <<= 4;
+ r += 4;
+ }
+ if (!(x & 0xc0000000u)) {
+ x <<= 2;
+ r += 2;
+ }
+ if (!(x & 0x80000000u)) {
+ x <<= 1;
+ r += 1;
+ }
+ return r;
+}
diff --git a/core/nds32/cpu.h b/core/nds32/cpu.h
new file mode 100644
index 0000000000..b22ad62cbf
--- /dev/null
+++ b/core/nds32/cpu.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Registers map and defintions for Andes cores
+ */
+
+#ifndef __CPU_H
+#define __CPU_H
+
+#include <stdint.h>
+
+/* Process Status Word bits */
+#define PSW_GIE (1 << 0) /* Global Interrupt Enable */
+#define PSW_INTL_SHIFT 1 /* Interrupt Stack Level */
+#define PSW_INTL_MASK (0x3 << PSW_INTL_SHIFT)
+
+/* write Process Status Word privileged register */
+static inline void set_psw(uint32_t val)
+{
+ asm volatile ("mtsr %0, $PSW" : : "r"(val));
+}
+
+/* read Process Status Word privileged register */
+static inline uint32_t get_psw(void)
+{
+ uint32_t ret;
+ asm volatile ("mfsr %0, $PSW" : "=r"(ret));
+ return ret;
+}
+
+/* write Interruption Program Counter privileged register */
+static inline void set_ipc(uint32_t val)
+{
+ asm volatile ("mtsr %0, $IPC" : : "r"(val));
+}
+
+/* read Interruption Program Counter privileged register */
+static inline uint32_t get_ipc(void)
+{
+ uint32_t ret;
+ asm volatile ("mfsr %0, $IPC" : "=r"(ret));
+ return ret;
+}
+
+/* read Interruption Type privileged register */
+static inline uint32_t get_itype(void)
+{
+ uint32_t ret;
+ asm volatile ("mfsr %0, $ITYPE" : "=r"(ret));
+ return ret;
+}
+
+/* Generic CPU core initialization */
+void cpu_init(void);
+
+#endif /* __CPU_H */
diff --git a/core/nds32/ec.lds.S b/core/nds32/ec.lds.S
new file mode 100644
index 0000000000..f1d021c3b0
--- /dev/null
+++ b/core/nds32/ec.lds.S
@@ -0,0 +1,213 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "config.h"
+
+#define FW_OFF_(section) CONFIG_FW_##section##_OFF
+#define FW_OFF(section) (CONFIG_FLASH_BASE + FW_OFF_(section))
+
+#define FW_SIZE_(section) CONFIG_FW_##section##_SIZE
+#define FW_SIZE(section) FW_SIZE_(section)
+
+
+OUTPUT_FORMAT(BFD_FORMAT, BFD_FORMAT, BFD_FORMAT)
+OUTPUT_ARCH(BFD_ARCH)
+ENTRY(reset)
+MEMORY
+{
+ FLASH (rx) : ORIGIN = FW_OFF(SECTION), LENGTH = FW_SIZE(SECTION)
+ IRAM (rw) : ORIGIN = CONFIG_RAM_BASE, LENGTH = CONFIG_RAM_SIZE
+}
+SECTIONS
+{
+ .text : {
+ OUTDIR/core/CORE/init.o (.text.vecttable)
+ . = ALIGN(4);
+ __version_struct_offset = .;
+ *(.rodata.ver)
+#ifdef SHIFT_CODE_FOR_TEST
+ . = ALIGN(256);
+#else
+ . = ALIGN(4);
+#endif
+ OUTDIR/core/CORE/init.o (.text.vectirq)
+ OUTDIR/core/CORE/init.o (.text)
+ *(.text*)
+#ifdef COMPILE_FOR_RAM
+ } > IRAM
+#else
+ } > FLASH
+#endif
+ . = ALIGN(4);
+ .rodata : {
+ /* Symbols defined here are declared in link_defs.h */
+ __irqprio = .;
+ *(.rodata.irqprio)
+ __irqprio_end = .;
+
+ . = ALIGN(4);
+ __irqhandler = .;
+ OUTDIR/core/CORE/init.o (.rodata.vecthandlers)
+
+ . = ALIGN(4);
+ __cmds = .;
+ *(SORT(.rodata.cmds*))
+ __cmds_end = .;
+
+ . = ALIGN(4);
+ __hcmds = .;
+ *(.rodata.hcmds)
+ __hcmds_end = .;
+
+ . = ALIGN(4);
+ __hooks_init = .;
+ *(.rodata.HOOK_INIT)
+ __hooks_init_end = .;
+
+ __hooks_pre_freq_change = .;
+ *(.rodata.HOOK_PRE_FREQ_CHANGE)
+ __hooks_pre_freq_change_end = .;
+
+ __hooks_freq_change = .;
+ *(.rodata.HOOK_FREQ_CHANGE)
+ __hooks_freq_change_end = .;
+
+ __hooks_sysjump = .;
+ *(.rodata.HOOK_SYSJUMP)
+ __hooks_sysjump_end = .;
+
+ __hooks_chipset_pre_init = .;
+ *(.rodata.HOOK_CHIPSET_PRE_INIT)
+ __hooks_chipset_pre_init_end = .;
+
+ __hooks_chipset_startup = .;
+ *(.rodata.HOOK_CHIPSET_STARTUP)
+ __hooks_chipset_startup_end = .;
+
+ __hooks_chipset_resume = .;
+ *(.rodata.HOOK_CHIPSET_RESUME)
+ __hooks_chipset_resume_end = .;
+
+ __hooks_chipset_suspend = .;
+ *(.rodata.HOOK_CHIPSET_SUSPEND)
+ __hooks_chipset_suspend_end = .;
+
+ __hooks_chipset_shutdown = .;
+ *(.rodata.HOOK_CHIPSET_SHUTDOWN)
+ __hooks_chipset_shutdown_end = .;
+
+ __hooks_ac_change = .;
+ *(.rodata.HOOK_AC_CHANGE)
+ __hooks_ac_change_end = .;
+
+ __hooks_lid_change = .;
+ *(.rodata.HOOK_LID_CHANGE)
+ __hooks_lid_change_end = .;
+
+ __hooks_pwrbtn_change = .;
+ *(.rodata.HOOK_POWER_BUTTON_CHANGE)
+ __hooks_pwrbtn_change_end = .;
+
+ __hooks_charge_state_change = .;
+ *(.rodata.HOOK_CHARGE_STATE_CHANGE)
+ __hooks_charge_state_change_end = .;
+
+ __hooks_tick = .;
+ *(.rodata.HOOK_TICK)
+ __hooks_tick_end = .;
+
+ __hooks_second = .;
+ *(.rodata.HOOK_SECOND)
+ __hooks_second_end = .;
+
+ __deferred_funcs = .;
+ *(.rodata.deferred)
+ __deferred_funcs_end = .;
+
+ . = ALIGN(4);
+ *(.rodata*)
+
+#if defined(SECTION_IS_RO) && defined(CONFIG_FLASH)
+ . = ALIGN(64);
+ *(.google)
+#endif
+ . = ALIGN(4);
+#ifdef COMPILE_FOR_RAM
+ } > IRAM
+#else
+ } >FLASH
+#endif
+ __ro_end = . ;
+
+#ifdef COMPILE_FOR_RAM
+ .data : {
+#else
+ .data : {
+#endif
+ . = ALIGN(4);
+ __data_start = .;
+ *(.data.tasks)
+ *(.data)
+#ifdef CONFIG_MPU
+ /* It has to be aligned by 32 bytes to be a valid MPU region. */
+ . = ALIGN(32);
+ __iram_text_start = .;
+#else
+ . = ALIGN(4);
+#endif
+ *(.iram.text)
+#ifdef CONFIG_MPU
+ . = ALIGN(32);
+ __iram_text_end = .;
+#else
+ . = ALIGN(4);
+#endif
+ __data_end = .;
+
+ } > IRAM AT>FLASH
+
+
+ __deferred_funcs_count =
+ (__deferred_funcs_end - __deferred_funcs) / 4;
+ ASSERT(__deferred_funcs_count <= DEFERRABLE_MAX_COUNT,
+ "Increase DEFERRABLE_MAX_COUNT")
+
+ .bss : {
+ /* Stacks must be 64-bit aligned */
+ . = ALIGN(8);
+ __bss_start = .;
+ *(.bss.tasks)
+ *(.bss.task_scratchpad)
+ . = ALIGN(8);
+ *(.bss.system_stack)
+ /* Rest of .bss takes care of its own alignment */
+ *(.bss)
+ . = ALIGN(4);
+ __bss_end = .;
+
+ /* Shared memory buffer must be at the end of preallocated RAM, so it
+ * can expand to use all the remaining RAM. */
+ __shared_mem_buf = .;
+
+ } > IRAM
+
+ .flash.tag : {
+ /* Tag at end of firmware image so that we can find the image size.
+ * This may be overwritten by the shared memory buffer; that's ok
+ * because we only use it to find the image size in flash. */
+ . = ALIGN(4);
+ BYTE(0x45);
+ BYTE(0x4e);
+ BYTE(0x44);
+ BYTE(0xea);
+ /* NOTHING MAY GO IN FLASH AFTER THIS! */
+ } >FLASH
+#if !(defined(SECTION_IS_RO) && defined(CONFIG_FLASH))
+ /DISCARD/ : {
+ *(.google)
+ }
+#endif
+
+ /DISCARD/ : { *(.ARM.*) }
+}
diff --git a/core/nds32/init.S b/core/nds32/init.S
new file mode 100644
index 0000000000..bd166ef668
--- /dev/null
+++ b/core/nds32/init.S
@@ -0,0 +1,194 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * N8 CPU initialization
+ */
+
+#include "config.h"
+
+/* magic macro to implement IRQ prefix / exit */
+.macro vector name
+.weak \name\()_handler
+.set \name\()_handler, unhandled_irq
+j __entry_\()\name
+.pushsection .text.vectirq
+.global __entry_\()\name
+__entry_\()\name:
+ /* the context is stored on the current task stack*/
+ /* save r15, fp, lp and sp */
+ smw.adm $r15, [$sp], $r15, 0xb
+ /* r0-r5 are caller saved */
+ smw.adm $r0, [$sp], $r5, 0
+ /* switch to system stack if we are called from process stack */
+ la $r3, stack_end
+ mov55 $fp, $sp
+ slt45 $r3, $sp /* if sp > end of system stack, then r15 = 1 and */
+ cmovn $sp, $r3, $r15 /* point sp to the top of the system stack */
+ /* C routine handler */
+ jal \name\()_handler
+ /* check whether we need to change the scheduled task */
+ lwi.gp $r2, [ + need_resched]
+ bnez $r2, __switch_task
+ /* restore r0-r5 */
+ lmw.bim $r0, [$fp], $r5, 0
+ /* restore r15, fp, lp and sp */
+ lmw.bi $r15, [$fp], $r15, 0xb
+ /* restore PC and PSW */
+ iret
+.popsection
+.pushsection .rodata.vecthandlers
+.long \name\()_handler
+.popsection
+.endm
+
+.section .text.vecttable
+
+/* Exceptions vector */
+vectors:
+j reset /* reset / NMI */
+j excep_handler /* TLB fill */
+j excep_handler /* PTE not present */
+j excep_handler /* TLB misc */
+j excep_handler /* TLB VLPT miss */
+j excep_handler /* Machine error */
+j excep_handler /* Debug related */
+j excep_handler /* General exception */
+vector syscall /* Syscall */
+vector irq_0 /* HW 0 */
+vector irq_1 /* HW 1 */
+vector irq_2 /* HW 2 */
+vector irq_3 /* HW 3 */
+vector irq_4 /* HW 4 */
+vector irq_5 /* HW 5 */
+vector irq_6 /* HW 6 */
+vector irq_7 /* HW 7 */
+vector irq_8 /* HW 8 */
+vector irq_9 /* HW 9 */
+vector irq_10 /* HW 10 */
+vector irq_11 /* HW 11 */
+vector irq_12 /* HW 12 */
+vector irq_13 /* HW 13 */
+vector irq_14 /* HW 14 */
+vector irq_15 /* HW 15 */
+
+/* E-flash signature */
+.balign 16
+.global eflash_sig
+eflash_sig:
+.byte 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xB4
+.byte 0x85, 0x12, 0x5A, 0x5A, 0xAA, 0xAA, 0x55, 0x55
+/* flags: internal oscillator + implicit location */
+
+.text
+
+.global reset
+reset:
+ /* GP register is used to access .data and .bss */
+ la $gp, _SDA_BASE_
+
+ /* Set system stack pointer. */
+ la $sp, stack_end
+
+ /* map/enable the 16kB of DLM at 0x00080000 */
+ li $r0, 0x00080005
+ mtsr $r0, $mr7
+
+ /* Clear BSS */
+ la $r0, _bss_start
+ lwi $r1, [$r0]
+ la $r0, _bss_end
+ lwi $r2, [$r0]
+ movi $r0, #0
+bss_loop:
+ swi.bi $r0, [$r1], 4
+ bne $r1, $r2, bss_loop
+
+ /* Copy initialized data to DLM */
+ la $r0, _data_start
+ lwi $r1, [$r0]
+ la $r0, _data_end
+ lwi $r2, [$r0]
+ la $r0, _ro_end
+ lwi $r0, [$r0]
+data_loop:
+ lwi.bi $r3, [$r0], 4
+ swi.bi $r3, [$r1], 4
+ bne $r1, $r2, data_loop
+
+ /* we switch to our own exception vectors */
+ /* go back to it level 0 with HW interrupts globally disabled */
+ li $r4, 0x70008
+ mtsr $r4, $PSW
+ /* IT8380 specific: set vectors at 0 */
+ li $r5, 0x0F02041 /* IVTBAR in GCTRL */
+ movi $r15, 0
+ sbi $r15, [$r5]
+ /* Interrupt vectors are every 4 bytes */
+ li $r5, 0x00000007
+ mtsr $r5, $IVB
+
+ /* Jump to C routine */
+ jal main
+
+ /* That should not return. If it does, loop forever. */
+ j .
+
+.global unhandled_irq
+unhandled_irq:
+ mfsr $gp, $ITYPE
+ sethi $r15, 0xBAD0
+ or $r15, $r15, $gp
+ mtsr $r15, $ITYPE
+ dsb
+ j excep_handler /* display exception with ITYPE=bad00<irq> */
+
+.global excep_handler
+excep_handler:
+ /* safety: reload GP even though it should be already set */
+ la $gp, _SDA_BASE_
+ /* save r0 to free one register */
+ swi.gp $r0, [ + saved_regs]
+ /* save the remaining 15 registers */
+ la $r0, saved_regs + 4
+ smw.bim $r1, [$r0], $r10, 0
+ smw.bim $r15,[$r0], $r15, 0xF
+ /* put a sane stack pointer */
+ la $sp, stack_end
+ /* add IPC, IPSW to the context */
+ mfsr $r1, $IPC
+ mfsr $r2, $IPSW
+ smw.bi $r1, [$r0], $r2, 0
+ /* pass ir6/ITYPE as the second parameter */
+ mfsr $r1, $ITYPE
+ /* exception context pointer as first parameter */
+ addi $r0, $r0, -16*4
+ /* jump to panic dump C routine */
+ jal report_panic
+ /* we never return: exceptions are fatal */
+ j .
+
+_bss_start:
+.long __bss_start
+_bss_end:
+.long __bss_end
+_data_start:
+.long __data_start
+_data_end:
+.long __data_end
+_ro_end:
+.long __ro_end
+
+/* Reserve space for system stack */
+.section .bss.system_stack
+stack_start:
+.space CONFIG_STACK_SIZE, 0
+stack_end:
+.global stack_end
+/* registers state at exception entry */
+.global saved_regs
+saved_regs:
+.long 0, 0, 0, 0, 0, 0, 0, 0
+.long 0, 0, 0, 0, 0, 0, 0, 0
+/* IPC, IPSW for convenient access */
+.long 0, 0
diff --git a/core/nds32/irq_chip.h b/core/nds32/irq_chip.h
new file mode 100644
index 0000000000..fb3bf1b21b
--- /dev/null
+++ b/core/nds32/irq_chip.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Chip-specific part of the IRQ handling.
+ */
+
+#ifndef __IRQ_CHIP_H
+#define __IRQ_CHIP_H
+
+/**
+ * Enable an IRQ in the chip interrupt controller.
+ *
+ * @param irq interrupt request index.
+ * @return CPU interrupt number to enable if any, -1 else.
+ */
+int chip_enable_irq(int irq);
+
+/**
+ * Disable an IRQ in the chip interrupt controller.
+ *
+ * @param irq interrupt request index.
+ * @return CPU interrupt number to disable if any, -1 else.
+ */
+int chip_disable_irq(int irq);
+
+/**
+ * Clear a pending IRQ in the chip interrupt controller.
+ *
+ * @param irq interrupt request index.
+ * @return CPU interrupt number to clear if any, -1 else.
+ *
+ * Note that most interrupts can be removed from the pending state simply by
+ * handling whatever caused the interrupt in the first place. This only needs
+ * to be called if an interrupt handler disables itself without clearing the
+ * reason for the interrupt, and then the interrupt is re-enabled from a
+ * different context.
+ */
+int chip_clear_pending_irq(int irq);
+
+/**
+ * Software-trigger an IRQ in the chip interrupt controller.
+ *
+ * @param irq interrupt request index.
+ * @return CPU interrupt number to trigger if any, -1 else.
+ */
+int chip_trigger_irq(int irq);
+
+/**
+ * Initialize chip interrupt controller.
+ */
+void chip_init_irqs(void);
+
+#endif /* __IRQ_CHIP_H */
diff --git a/core/nds32/panic.c b/core/nds32/panic.c
new file mode 100644
index 0000000000..ebdffb32ea
--- /dev/null
+++ b/core/nds32/panic.c
@@ -0,0 +1,42 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "cpu.h"
+#include "panic.h"
+#include "printf.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+void report_panic(uint32_t *regs, uint32_t itype)
+{
+ panic_printf("=== EXCEP: ITYPE=%x ===\n", itype);
+ panic_printf("R0 %08x R1 %08x R2 %08x R3 %08x\n",
+ regs[0], regs[1], regs[2], regs[3]);
+ panic_printf("R4 %08x R5 %08x R6 %08x R7 %08x\n",
+ regs[4], regs[5], regs[6], regs[7]);
+ panic_printf("R8 %08x R9 %08x R10 %08x R15 %08x\n",
+ regs[8], regs[9], regs[10], regs[11]);
+ panic_printf("FP %08x GP %08x LP %08x SP %08x\n",
+ regs[12], regs[13], regs[14], regs[15]);
+ panic_printf("IPC %08x IPSW %05x\n", regs[16], regs[17]);
+ if ((regs[17] & PSW_INTL_MASK) == (2 << PSW_INTL_SHIFT)) {
+ /* 2nd level exception */
+ uint32_t oipc;
+
+ asm volatile("mfsr %0, $OIPC" : "=r"(oipc));
+ panic_printf("OIPC %08x\n", oipc);
+ }
+
+ panic_reboot();
+}
+
+void panic_data_print(const struct panic_data *pdata)
+{
+}
diff --git a/core/nds32/switch.S b/core/nds32/switch.S
new file mode 100644
index 0000000000..2c4ce0c273
--- /dev/null
+++ b/core/nds32/switch.S
@@ -0,0 +1,99 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Context switching
+ */
+
+#include "config.h"
+
+.text
+
+/**
+ * Task context switching
+ *
+ * Change the task scheduled after returning from an interruption.
+ *
+ * This function must be called in interrupt context.
+ *
+ * Save the registers of the current task below the interrupt context on
+ * its task, then restore the live registers of the next task and set the
+ * process stack pointer to the new stack.
+ *
+ * $r0: pointer to the task to switch from
+ * $r1: pointer to the task to switch to
+ * $r2: pointer to the stack where the interrupt entry context is saved
+ *
+ * the structure of the saved context on the stack is :
+ * (top to bottom)
+ * sp, lp, fp, r15, r5, r4, r3, r2, r1, r0, r10, r9, r8, r7, r6, ipc, ipsw
+ * interrupt entry frame <|>
+ */
+.global __switch_task
+__switch_task:
+ /* get the (new) highest priority task pointer in r0 */
+ jal next_sched_task
+ movi55 $r3, 0
+ /* pointer to the current task (which are switching from) */
+ lwi.gp $r1, [ + current_task]
+ /* reset the re-scheduling request */
+ swi.gp $r3, [ + need_resched]
+ /* Nothing to do: let's return to keep the same task scheduled */
+ beq $r1, $r0, 1f
+ /* save our new scheduled task */
+ swi.gp $r0, [ + current_task]
+ /* get the program status word saved at exception entry */
+ mfsr $r4, $IPSW /* to save SP_ADJ bit */
+ /* get the task program counter saved at exception entry */
+ mfsr $r5, $IPC
+ /* get the new scheduled task stack pointer */
+ lw $r3, [$r0]
+ /* save ipsw, ipc, r6, r7, r8, r9, r10 on the current process stack */
+ smw.adm $r4, [$fp], $r10, 0
+ /* restore ipsw, ipc, r6, r7, r8, r9, r10 from the next stack context */
+ lmw.bim $r4, [$r3], $r10, 0
+ /* set the program status word to restore SP_ADJ bit */
+ mtsr $r4, $IPSW
+ /* set the task program counter to restore at exception exit */
+ mtsr $r5, $IPC
+ /* save the task stack pointer in its context */
+ sw $fp, [$r1]
+ /* barrier: ensure IPC is taken into account before IRET */
+ dsb
+ /* exception frame pointer for the new task */
+ mov55 $fp, $r3
+1: /* un-pile the interruption entry context */
+ /* restore r0-r5 */
+ lmw.bim $r0, [$fp], $r5, 0
+ /* restore r15, fp, lp and sp */
+ lmw.bi $r15, [$fp], $r15, 0xb
+ /* restore PC and PSW */
+ iret
+
+/**
+ * Start the task scheduling.
+ *
+ * $r0 is a pointer to task_stack_ready, which is set to 1 after
+ * the task stack is set up.
+ */
+.global __task_start
+__task_start:
+ /* area used as dummy thread stack for the first switch */
+ la $r3, scratchpad
+
+ movi55 $r4, 1
+ movi55 $r2, 0 /* syscall 3rd parameter : not an IRQ emulation */
+ movi55 $r1, 0 /* syscall 2nd parameter : re-schedule nothing */
+ movi55 $r0, 0 /* syscall 1st parameter : de-schedule nothing */
+
+ /* put the dummy stack pointer at the top of the stack in scratchpad */
+ addi $sp, $r3, 4 * 16
+ /* we are ready to re-schedule */
+ swi.gp $r4, [ + need_resched]
+
+ /* trigger scheduling to execute the task with the highest priority */
+ syscall 0
+ /* we should never return here: set code to EC_ERROR_UNKNOWN */
+ movi55 $r0, 0x1
+ ret5 $lp
+
diff --git a/core/nds32/task.c b/core/nds32/task.c
new file mode 100644
index 0000000000..9fe371e716
--- /dev/null
+++ b/core/nds32/task.c
@@ -0,0 +1,515 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Task scheduling / events module for Chrome EC operating system */
+
+#include "atomic.h"
+#include "common.h"
+#include "console.h"
+#include "cpu.h"
+#include "irq_chip.h"
+#include "link_defs.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+typedef union {
+ struct {
+ /*
+ * Note that sp must be the first element in the task struct
+ * for __switchto() to work.
+ */
+ uint32_t sp; /* Saved stack pointer for context switch */
+ uint32_t events; /* Bitmaps of received events */
+ uint64_t runtime; /* Time spent in task */
+ uint32_t *stack; /* Start of stack */
+ };
+} task_;
+
+/* Value to store in unused stack */
+#define STACK_UNUSED_VALUE 0xdeadd00d
+
+/* declare task routine prototypes */
+#define TASK(n, r, d, s) int r(void *);
+void __idle(void);
+CONFIG_TASK_LIST
+CONFIG_TEST_TASK_LIST
+#undef TASK
+
+/* Task names for easier debugging */
+#define TASK(n, r, d, s) #n,
+static const char * const task_names[] = {
+ "<< idle >>",
+ CONFIG_TASK_LIST
+ CONFIG_TEST_TASK_LIST
+};
+#undef TASK
+
+extern int __task_start(void);
+
+#ifndef CONFIG_LOW_POWER_IDLE
+/* Idle task. Executed when no tasks are ready to be scheduled. */
+void __idle(void)
+{
+ /*
+ * Print when the idle task starts. This is the lowest priority task,
+ * so this only starts once all other tasks have gotten a chance to do
+ * their task inits and have gone to sleep.
+ */
+ cprintf(CC_TASK, "[%T idle task started]\n");
+
+ while (1) {
+ /*
+ * Wait for the next irq event. This stops the CPU clock
+ * (sleep / deep sleep, depending on chip config).
+ */
+ asm("standby no_wake_grant");
+ }
+}
+#endif /* !CONFIG_LOW_POWER_IDLE */
+
+static void task_exit_trap(void)
+{
+ int i = task_get_current();
+ cprintf(CC_TASK, "[%T Task %d (%s) exited!]\n", i, task_names[i]);
+ /* Exited tasks simply sleep forever */
+ while (1)
+ task_wait_event(-1);
+}
+
+/* Startup parameters for all tasks. */
+#define TASK(n, r, d, s) { \
+ .r0 = (uint32_t)d, \
+ .pc = (uint32_t)r, \
+ .stack_size = s, \
+},
+static const struct {
+ uint32_t r0;
+ uint32_t pc;
+ uint16_t stack_size;
+} const tasks_init[] = {
+ TASK(IDLE, __idle, 0, IDLE_TASK_STACK_SIZE)
+ CONFIG_TASK_LIST
+ CONFIG_TEST_TASK_LIST
+};
+#undef TASK
+
+/* Contexts for all tasks */
+static task_ tasks[TASK_ID_COUNT];
+/* Sanity checks about static task invariants */
+BUILD_ASSERT(TASK_ID_COUNT <= sizeof(unsigned) * 8);
+BUILD_ASSERT(TASK_ID_COUNT < (1 << (sizeof(task_id_t) * 8)));
+
+
+/* Stacks for all tasks */
+#define TASK(n, r, d, s) + s
+uint8_t task_stacks[0
+ TASK(IDLE, __idle, 0, IDLE_TASK_STACK_SIZE)
+ CONFIG_TASK_LIST
+ CONFIG_TEST_TASK_LIST
+] __aligned(8);
+
+#undef TASK
+
+/* Reserve space to discard context on first context switch. */
+#ifdef CONFIG_FPU
+uint32_t scratchpad[17+18];
+#else
+uint32_t scratchpad[17];
+#endif
+
+task_ *current_task = (task_ *)scratchpad;
+
+/*
+ * Should IRQs chain to svc_handler()? This should be set if either of the
+ * following is true:
+ *
+ * 1) Task scheduling has started, and task profiling is enabled. Task
+ * profiling does its tracking in svc_handler().
+ *
+ * 2) An event was set by an interrupt; this could result in a higher-priority
+ * task unblocking. After checking for a task switch, svc_handler() will clear
+ * the flag (unless profiling is also enabled; then the flag remains set).
+ */
+int need_resched;
+
+/*
+ * Bitmap of all tasks ready to be run.
+ *
+ * Currently all tasks are enabled at startup.
+ */
+static uint32_t tasks_ready = (1<<TASK_ID_COUNT) - 1;
+
+static int start_called; /* Has task swapping started */
+
+static inline task_ *__task_id_to_ptr(task_id_t id)
+{
+ return tasks + id;
+}
+
+void interrupt_disable(void)
+{
+ /* clear GIE (Global Interrupt Enable) bit */
+ asm volatile ("setgie.d");
+ asm volatile ("dsb");
+}
+
+void interrupt_enable(void)
+{
+ /* set GIE (Global Interrupt Enable) bit */
+ asm volatile ("setgie.e");
+}
+
+inline int in_interrupt_context(void)
+{
+ /* check INTL (Interrupt Stack Level) bits */
+ return get_psw() & PSW_INTL_MASK;
+}
+
+task_id_t task_get_current(void)
+{
+ return current_task - tasks;
+}
+
+uint32_t *task_get_event_bitmap(task_id_t tskid)
+{
+ task_ *tsk = __task_id_to_ptr(tskid);
+ return &tsk->events;
+}
+
+int task_start_called(void)
+{
+ return start_called;
+}
+
+/**
+ * Scheduling system call
+ *
+ * Also includes emulation of software triggering interrupt vector
+ */
+void syscall_handler(int desched, task_id_t resched, int swirq)
+{
+ /* are we emulating an interrupt ? */
+ if (swirq) {
+ void (*handler)(void) = __irqhandler[swirq + 1];
+ /* adjust IPC to return *after* the syscall instruction */
+ set_ipc(get_ipc() + 4);
+ /* call the regular IRQ handler */
+ handler();
+ return;
+ }
+
+ if (desched && !current_task->events) {
+ /*
+ * Remove our own ready bit (current - tasks is same as
+ * task_get_current())
+ */
+ tasks_ready &= ~(1 << (current_task - tasks));
+ }
+ tasks_ready |= 1 << resched;
+
+ /* trigger a re-scheduling on exit */
+ need_resched = 1;
+
+ /* adjust IPC to return *after* the syscall instruction */
+ set_ipc(get_ipc() + 4);
+}
+
+task_ *next_sched_task(void)
+{
+ return __task_id_to_ptr(31 - __builtin_clz(tasks_ready));
+}
+
+static inline void __schedule(int desched, int resched, int swirq)
+{
+ register int p0 asm("$r0") = desched;
+ register int p1 asm("$r1") = resched;
+ register int p2 asm("$r2") = swirq;
+
+ asm("syscall 0" : : "r"(p0), "r"(p1), "r"(p2));
+}
+
+static uint32_t __wait_evt(int timeout_us, task_id_t resched)
+{
+ task_ *tsk = current_task;
+ task_id_t me = tsk - tasks;
+ uint32_t evt;
+ int ret;
+
+ ASSERT(!in_interrupt_context());
+
+ if (timeout_us > 0) {
+ timestamp_t deadline = get_time();
+ deadline.val += timeout_us;
+ ret = timer_arm(deadline, me);
+ ASSERT(ret == EC_SUCCESS);
+ }
+ while (!(evt = atomic_read_clear(&tsk->events))) {
+ /* Remove ourself and get the next task in the scheduler */
+ __schedule(1, resched, 0);
+ resched = TASK_ID_IDLE;
+ }
+ if (timeout_us > 0)
+ timer_cancel(me);
+ return evt;
+}
+
+uint32_t task_set_event(task_id_t tskid, uint32_t event, int wait)
+{
+ task_ *receiver = __task_id_to_ptr(tskid);
+ ASSERT(receiver);
+
+ /* Set the event bit in the receiver message bitmap */
+ atomic_or(&receiver->events, event);
+
+ /* Re-schedule if priorities have changed */
+ if (in_interrupt_context()) {
+ /* The receiver might run again */
+ atomic_or(&tasks_ready, 1 << tskid);
+ need_resched = 1;
+ } else {
+ if (wait)
+ return __wait_evt(-1, tskid);
+ else
+ __schedule(0, tskid, 0);
+ }
+
+ return 0;
+}
+
+uint32_t task_wait_event(int timeout_us)
+{
+ return __wait_evt(timeout_us, TASK_ID_IDLE);
+}
+
+static uint32_t get_int_mask(void)
+{
+ uint32_t ret;
+ asm volatile ("mfsr %0, $INT_MASK" : "=r"(ret));
+ return ret;
+}
+
+static void set_int_mask(uint32_t val)
+{
+ asm volatile ("mtsr %0, $INT_MASK" : : "r"(val));
+}
+
+static void set_int_priority(uint32_t val)
+{
+ asm volatile ("mtsr %0, $INT_PRI" : : "r"(val));
+}
+
+void task_enable_irq(int irq)
+{
+ int cpu_int = chip_enable_irq(irq);
+ if (cpu_int >= 0)
+ set_int_mask(get_int_mask() | (1 << cpu_int));
+}
+
+void task_disable_irq(int irq)
+{
+ int cpu_int = chip_disable_irq(irq);
+ if (cpu_int >= 0)
+ set_int_mask(get_int_mask() & ~(1 << cpu_int));
+}
+
+void task_clear_pending_irq(int irq)
+{
+ chip_clear_pending_irq(irq);
+}
+
+void task_trigger_irq(int irq)
+{
+ int cpu_int = chip_trigger_irq(irq);
+ if (cpu_int > 0)
+ __schedule(0, 0, cpu_int);
+}
+
+/*
+ * Initialize IRQs in the IVIC and set their priorities as defined by the
+ * DECLARE_IRQ statements.
+ */
+static void ivic_init_irqs(void)
+{
+ /* Get the IRQ priorities section from the linker */
+ int exc_calls = __irqprio_end - __irqprio;
+ int i;
+ uint32_t all_priorities = 0;
+
+ /* chip-specific interrupt controller initialization */
+ chip_init_irqs();
+
+ /* Mask all interrupts, only keep division by zero exception */
+ set_int_mask(1 << 30 /* IDIVZ */);
+
+ /*
+ * Re-enable global interrupts in case they're disabled. On a reboot,
+ * they're already enabled; if we've jumped here from another image,
+ * they're not.
+ */
+ interrupt_enable();
+
+ /* Set priorities */
+ for (i = 0; i < exc_calls; i++) {
+ uint8_t irq = __irqprio[i].irq;
+ uint8_t prio = __irqprio[i].priority;
+ all_priorities |= (prio & 0x3) << (irq * 2);
+ }
+ set_int_priority(all_priorities);
+}
+
+void mutex_lock(struct mutex *mtx)
+{
+ uint32_t id = 1 << task_get_current();
+
+ ASSERT(id != TASK_ID_INVALID);
+
+ /* critical section with interrupts off */
+ asm volatile ("setgie.d ; dsb");
+ mtx->waiters |= id;
+ while (1) {
+ if (!mtx->lock) { /* we got it ! */
+ mtx->lock = 2;
+ mtx->waiters &= ~id;
+ /* end of critical section : re-enable interrupts */
+ asm volatile ("setgie.e");
+ return;
+ } else { /* Contention on the mutex */
+ /* end of critical section : re-enable interrupts */
+ asm volatile ("setgie.e");
+ /* Sleep waiting for our turn */
+ task_wait_event(0);
+ /* re-enter critical section */
+ asm volatile ("setgie.d ; dsb");
+ }
+ }
+}
+
+void mutex_unlock(struct mutex *mtx)
+{
+ uint32_t waiters;
+ task_ *tsk = current_task;
+
+ waiters = mtx->waiters;
+ /* give back the lock */
+ mtx->lock = 0;
+
+ while (waiters) {
+ task_id_t id = 31 - __builtin_clz(waiters);
+
+ /* Somebody is waiting on the mutex */
+ task_set_event(id, TASK_EVENT_MUTEX, 0);
+ waiters &= ~(1 << id);
+ }
+
+ /* Ensure no event is remaining from mutex wake-up */
+ atomic_clear(&tsk->events, TASK_EVENT_MUTEX);
+}
+
+void task_print_list(void)
+{
+ int i;
+
+ ccputs("Task Ready Name Events Time (s) StkUsed\n");
+
+ for (i = 0; i < TASK_ID_COUNT; i++) {
+ char is_ready = (tasks_ready & (1<<i)) ? 'R' : ' ';
+ uint32_t *sp;
+
+ int stackused = tasks_init[i].stack_size;
+
+ for (sp = tasks[i].stack;
+ sp < (uint32_t *)tasks[i].sp && *sp == STACK_UNUSED_VALUE;
+ sp++)
+ stackused -= sizeof(uint32_t);
+
+ ccprintf("%4d %c %-16s %08x %11.6ld %3d/%3d\n", i, is_ready,
+ task_names[i], tasks[i].events, tasks[i].runtime,
+ stackused, tasks_init[i].stack_size);
+ cflush();
+ }
+}
+
+int command_task_info(int argc, char **argv)
+{
+ task_print_list();
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(taskinfo, command_task_info,
+ NULL,
+ "Print task info",
+ NULL);
+
+static int command_task_ready(int argc, char **argv)
+{
+ if (argc < 2) {
+ ccprintf("tasks_ready: 0x%08x\n", tasks_ready);
+ } else {
+ tasks_ready = strtoi(argv[1], NULL, 16);
+ ccprintf("Setting tasks_ready to 0x%08x\n", tasks_ready);
+ __schedule(0, 0, 0);
+ }
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(taskready, command_task_ready,
+ "[setmask]",
+ "Print/set ready tasks",
+ NULL);
+
+void task_pre_init(void)
+{
+ uint32_t *stack_next = (uint32_t *)task_stacks;
+ int i;
+
+ /* Fill the task memory with initial values */
+ for (i = 0; i < TASK_ID_COUNT; i++) {
+ uint32_t *sp;
+ /* Stack size in words */
+ uint32_t ssize = tasks_init[i].stack_size / 4;
+
+ tasks[i].stack = stack_next;
+
+ /*
+ * Update stack used by first frame: 15 regs + PC + PSW
+ */
+ sp = stack_next + ssize - 17;
+ tasks[i].sp = (uint32_t)sp;
+
+ /* Initial context on stack (see __switchto()) */
+ sp[7] = tasks_init[i].r0; /* r0 */
+ sp[15] = (uint32_t)task_exit_trap; /* lr */
+ sp[1] = tasks_init[i].pc; /* pc */
+ sp[0] = 0x70009; /* psw */
+ sp[16] = (uint32_t)(sp + 17); /* sp */
+
+ /* Fill unused stack; also used to detect stack overflow. */
+ for (sp = stack_next; sp < (uint32_t *)tasks[i].sp; sp++)
+ *sp = STACK_UNUSED_VALUE;
+
+ stack_next += ssize;
+ }
+
+ /*
+ * Fill in guard value in scratchpad to prevent stack overflow
+ * detection failure on the first context switch. This works because
+ * the first word in the scratchpad is where the switcher will store
+ * sp, so it's ok to blow away.
+ */
+ ((task_ *)scratchpad)->stack = (uint32_t *)scratchpad;
+ *(uint32_t *)scratchpad = STACK_UNUSED_VALUE;
+
+ /* Initialize IRQs */
+ ivic_init_irqs();
+}
+
+int task_start(void)
+{
+ start_called = 1;
+
+ return __task_start();
+}
diff --git a/include/link_defs.h b/include/link_defs.h
index 52782760b1..3346ea8e5a 100644
--- a/include/link_defs.h
+++ b/include/link_defs.h
@@ -73,6 +73,7 @@ extern const struct host_command __hcmds_end[];
/* IRQs (interrupt handlers) */
extern const struct irq_priority __irqprio[];
extern const struct irq_priority __irqprio_end[];
+extern const void *__irqhandler[];
/* Shared memory buffer. Use via shared_mem.h interface. */
extern uint8_t __shared_mem_buf[];
diff --git a/include/task.h b/include/task.h
index edf278c066..1aeb1206b7 100644
--- a/include/task.h
+++ b/include/task.h
@@ -203,6 +203,14 @@ struct irq_priority {
* Macro to connect the interrupt handler "routine" to the irq number "irq" and
* ensure it is enabled in the interrupt controller with the right priority.
*/
+#ifdef __nds32__
+#define DECLARE_IRQ(irq, routine, priority) \
+ void IRQ_HANDLER(CPU_INT(irq))(void) \
+ __attribute__ ((alias(STRINGIFY(routine)))); \
+ const struct irq_priority IRQ_PRIORITY(CPU_INT(irq)) \
+ __attribute__((section(".rodata.irqprio"))) \
+ = {CPU_INT(irq), priority}
+#else
#define DECLARE_IRQ(irq, routine, priority) \
void IRQ_HANDLER(irq)(void) \
{ \
@@ -214,5 +222,6 @@ struct irq_priority {
const struct irq_priority IRQ_PRIORITY(irq) \
__attribute__((section(".rodata.irqprio"))) \
= {irq, priority}
+#endif
#endif /* __CROS_EC_TASK_H */