mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-27 18:25:05 +00:00
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:
committed by
chrome-internal-fetch
parent
6f348ecf08
commit
93cc00fde1
56
core/nds32/atomic.h
Normal file
56
core/nds32/atomic.h
Normal 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
15
core/nds32/build.mk
Normal 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
13
core/nds32/config_core.h
Normal 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
52
core/nds32/cpu.c
Normal 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
57
core/nds32/cpu.h
Normal 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
213
core/nds32/ec.lds.S
Normal 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
194
core/nds32/init.S
Normal 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
54
core/nds32/irq_chip.h
Normal 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
42
core/nds32/panic.c
Normal 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
99
core/nds32/switch.S
Normal 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
515
core/nds32/task.c
Normal 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();
|
||||
}
|
||||
@@ -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[];
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user