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>
This commit is contained in:
Vincent Palatin
2013-10-25 15:37:11 -07:00
committed by chrome-internal-fetch
parent 6f348ecf08
commit 93cc00fde1
13 changed files with 1320 additions and 0 deletions

56
core/nds32/atomic.h Normal file
View File

@@ -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 */

15
core/nds32/build.mk Normal file
View File

@@ -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

13
core/nds32/config_core.h Normal file
View File

@@ -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 */

52
core/nds32/cpu.c Normal file
View File

@@ -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;
}

57
core/nds32/cpu.h Normal file
View File

@@ -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 */

213
core/nds32/ec.lds.S Normal file
View File

@@ -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.*) }
}

194
core/nds32/init.S Normal file
View File

@@ -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

54
core/nds32/irq_chip.h Normal file
View File

@@ -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 */

42
core/nds32/panic.c Normal file
View File

@@ -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)
{
}

99
core/nds32/switch.S Normal file
View File

@@ -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

515
core/nds32/task.c Normal file
View File

@@ -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();
}

View File

@@ -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[];

View File

@@ -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 */