diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2012-01-25 21:47:20 +0000 |
---|---|---|
committer | Vincent Palatin <vpalatin@chromium.org> | 2012-01-25 22:50:07 +0000 |
commit | cf9fcef328900ae4460755cca01ec46e98b01732 (patch) | |
tree | 19e56762b6daa2b3abb2a2d2235d72dd417b3ed3 /core | |
parent | 645dad5d3f658d7c5e0d54453964e91afe7b43c0 (diff) | |
download | chrome-ec-cf9fcef328900ae4460755cca01ec46e98b01732.tar.gz |
Move OS files to a CPU specific directory
Preparatory work to introduce a second SoC : 3/5
We split the drivers files which contain SoC specific drivers from the
OS files which only depend the actual CPU core.
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
BUG=None
TEST=run EC firmware on BDS and test a few commands on the console.
Change-Id: I598f8b23e074da9bd6b0e2ce6689c1075fe854f0
Diffstat (limited to 'core')
-rw-r--r-- | core/cortex-m/build.mk | 11 | ||||
-rw-r--r-- | core/cortex-m/ec.lds.S | 63 | ||||
-rw-r--r-- | core/cortex-m/init.S | 378 | ||||
-rw-r--r-- | core/cortex-m/panic.S | 111 | ||||
-rw-r--r-- | core/cortex-m/switch.S | 63 | ||||
-rw-r--r-- | core/cortex-m/task.c | 394 | ||||
-rw-r--r-- | core/cortex-m/timer.c | 206 |
7 files changed, 1226 insertions, 0 deletions
diff --git a/core/cortex-m/build.mk b/core/cortex-m/build.mk new file mode 100644 index 0000000000..8da6792154 --- /dev/null +++ b/core/cortex-m/build.mk @@ -0,0 +1,11 @@ +# Copyright (c) 2012 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. +# +# Cortex-M4 core OS files build +# + +# CPU specific compilation flags +CFLAGS_CPU=-mcpu=cortex-m4 -mthumb -Os -mno-sched-prolog + +core-y=init.o panic.o switch.o task.o timer.o diff --git a/core/cortex-m/ec.lds.S b/core/cortex-m/ec.lds.S new file mode 100644 index 0000000000..35006ae77f --- /dev/null +++ b/core/cortex-m/ec.lds.S @@ -0,0 +1,63 @@ +/* Copyright (c) 2012 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 CONFIG_FW_SECT_OFF(section) CONFIG_FW_##section##_OFF +#define CONFIG_FW_BASE(section) (CONFIG_FLASH_BASE + CONFIG_FW_SECT_OFF(section)) + +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(reset) +MEMORY +{ + FLASH (rx) : ORIGIN = CONFIG_FW_BASE(SECTION), LENGTH = CONFIG_FW_IMAGE_SIZE + IRAM (rw) : ORIGIN = CONFIG_RAM_BASE, LENGTH = CONFIG_RAM_SIZE +} +SECTIONS +{ + .text : { + OUTDIR/core/CORE/init.o (.text) + *(.text*) +#ifdef COMPILE_FOR_RAM + } > IRAM +#else + } > FLASH +#endif + . = ALIGN(4); + .rodata : { + __irqprio = .; + *(.rodata.irqprio) + __irqprio_end = .; + . = ALIGN(4); + __cmds = .; + *(.rodata.cmds) + __cmds_end = .; + *(.rodata*) + . = ALIGN(4); +#ifdef COMPILE_FOR_RAM + } > IRAM + __ro_end = . ; + .data : { +#else + } > FLASH + __ro_end = . ; + .data : AT(ADDR(.rodata) + SIZEOF(.rodata)) { +#endif + . = ALIGN(4); + __data_start = .; + *(.data.tasks) + *(.data) + . = ALIGN(4); + __data_end = .; + } > IRAM + .bss : { + . = ALIGN(4); + __bss_start = .; + *(.bss) + . = ALIGN(4); + __bss_end = .; + } > IRAM + /DISCARD/ : { *(.ARM.*) } +} diff --git a/core/cortex-m/init.S b/core/cortex-m/init.S new file mode 100644 index 0000000000..6e3f3489e6 --- /dev/null +++ b/core/cortex-m/init.S @@ -0,0 +1,378 @@ +/* Copyright (c) 2011 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. + * + * Cortex-M CPU initialization + */ + +#include "config.h" + +.text + +.syntax unified +.code 16 + +.macro vector name +.long \name\()_handler +.weak \name\()_handler +.set \name\()_handler, default_handler +.endm + +.macro vector_irq number +.if \number < CONFIG_IRQ_COUNT +vector irq_\()\number +.endif +.endm + +/* Exceptions vector */ +vectors: +.long stack_end @ initial stack pointer +.long reset @ reset handler +vector nmi @ NMI handler +vector hard_fault @ HardFault handler +vector mpu_fault @ MPU fault handler +vector bus_fault @ Bus fault handler +vector usage_fault @ Usage fault handler +.long 0 @ reserved +.long 0 @ reserved +.long 0 @ reserved +.long 0 @ reserved +vector svc @ SWI +vector debug @ Debug handler +.long 0 @ reserved +vector pendsv @ PendSV handler +vector sys_tick @ SysTick handler +vector_irq 0 @ IRQ 0 handler +vector_irq 1 @ IRQ 1 handler +vector_irq 2 @ IRQ 2 handler +vector_irq 3 @ IRQ 3 handler +vector_irq 4 @ IRQ 4 handler +vector_irq 5 @ IRQ 5 handler +vector_irq 6 @ IRQ 6 handler +vector_irq 7 @ IRQ 7 handler +vector_irq 8 @ IRQ 8 handler +vector_irq 9 @ IRQ 9 handler +vector_irq 10 @ IRQ 10 handler +vector_irq 11 @ IRQ 11 handler +vector_irq 12 @ IRQ 12 handler +vector_irq 13 @ IRQ 13 handler +vector_irq 14 @ IRQ 14 handler +vector_irq 15 @ IRQ 15 handler +vector_irq 16 @ IRQ 16 handler +vector_irq 17 @ IRQ 17 handler +vector_irq 18 @ IRQ 18 handler +vector_irq 19 @ IRQ 19 handler +vector_irq 20 @ IRQ 20 handler +vector_irq 21 @ IRQ 21 handler +vector_irq 22 @ IRQ 22 handler +vector_irq 23 @ IRQ 23 handler +vector_irq 24 @ IRQ 24 handler +vector_irq 25 @ IRQ 25 handler +vector_irq 26 @ IRQ 26 handler +vector_irq 27 @ IRQ 27 handler +vector_irq 28 @ IRQ 28 handler +vector_irq 29 @ IRQ 29 handler +vector_irq 30 @ IRQ 30 handler +vector_irq 31 @ IRQ 31 handler +vector_irq 32 @ IRQ 32 handler +vector_irq 33 @ IRQ 33 handler +vector_irq 34 @ IRQ 34 handler +vector_irq 35 @ IRQ 35 handler +vector_irq 36 @ IRQ 36 handler +vector_irq 37 @ IRQ 37 handler +vector_irq 38 @ IRQ 38 handler +vector_irq 39 @ IRQ 39 handler +vector_irq 40 @ IRQ 40 handler +vector_irq 41 @ IRQ 41 handler +vector_irq 42 @ IRQ 42 handler +vector_irq 43 @ IRQ 43 handler +vector_irq 44 @ IRQ 44 handler +vector_irq 45 @ IRQ 45 handler +vector_irq 46 @ IRQ 46 handler +vector_irq 47 @ IRQ 47 handler +vector_irq 48 @ IRQ 48 handler +vector_irq 49 @ IRQ 49 handler +vector_irq 50 @ IRQ 50 handler +vector_irq 51 @ IRQ 51 handler +vector_irq 52 @ IRQ 52 handler +vector_irq 53 @ IRQ 53 handler +vector_irq 54 @ IRQ 54 handler +vector_irq 55 @ IRQ 55 handler +vector_irq 56 @ IRQ 56 handler +vector_irq 57 @ IRQ 57 handler +vector_irq 58 @ IRQ 58 handler +vector_irq 59 @ IRQ 59 handler +vector_irq 60 @ IRQ 60 handler +vector_irq 61 @ IRQ 61 handler +vector_irq 62 @ IRQ 62 handler +vector_irq 63 @ IRQ 63 handler +vector_irq 64 @ IRQ 64 handler +vector_irq 65 @ IRQ 65 handler +vector_irq 66 @ IRQ 66 handler +vector_irq 67 @ IRQ 67 handler +vector_irq 68 @ IRQ 68 handler +vector_irq 69 @ IRQ 69 handler +vector_irq 70 @ IRQ 70 handler +vector_irq 71 @ IRQ 71 handler +vector_irq 72 @ IRQ 72 handler +vector_irq 73 @ IRQ 73 handler +vector_irq 74 @ IRQ 74 handler +vector_irq 75 @ IRQ 75 handler +vector_irq 76 @ IRQ 76 handler +vector_irq 77 @ IRQ 77 handler +vector_irq 78 @ IRQ 78 handler +vector_irq 79 @ IRQ 79 handler +vector_irq 80 @ IRQ 80 handler +vector_irq 81 @ IRQ 81 handler +vector_irq 82 @ IRQ 82 handler +vector_irq 83 @ IRQ 83 handler +vector_irq 84 @ IRQ 84 handler +vector_irq 85 @ IRQ 85 handler +vector_irq 86 @ IRQ 86 handler +vector_irq 87 @ IRQ 87 handler +vector_irq 88 @ IRQ 88 handler +vector_irq 89 @ IRQ 89 handler +vector_irq 90 @ IRQ 90 handler +vector_irq 91 @ IRQ 91 handler +vector_irq 92 @ IRQ 92 handler +vector_irq 93 @ IRQ 93 handler +vector_irq 94 @ IRQ 94 handler +vector_irq 95 @ IRQ 95 handler +vector_irq 96 @ IRQ 96 handler +vector_irq 97 @ IRQ 97 handler +vector_irq 98 @ IRQ 98 handler +vector_irq 99 @ IRQ 99 handler +vector_irq 100 @ IRQ 100 handler +vector_irq 101 @ IRQ 101 handler +vector_irq 102 @ IRQ 102 handler +vector_irq 103 @ IRQ 103 handler +vector_irq 104 @ IRQ 104 handler +vector_irq 105 @ IRQ 105 handler +vector_irq 106 @ IRQ 106 handler +vector_irq 107 @ IRQ 107 handler +vector_irq 108 @ IRQ 108 handler +vector_irq 109 @ IRQ 109 handler +vector_irq 110 @ IRQ 110 handler +vector_irq 111 @ IRQ 111 handler +vector_irq 112 @ IRQ 112 handler +vector_irq 113 @ IRQ 113 handler +vector_irq 114 @ IRQ 114 handler +vector_irq 115 @ IRQ 115 handler +vector_irq 116 @ IRQ 116 handler +vector_irq 117 @ IRQ 117 handler +vector_irq 118 @ IRQ 118 handler +vector_irq 119 @ IRQ 119 handler +vector_irq 120 @ IRQ 120 handler +vector_irq 121 @ IRQ 121 handler +vector_irq 122 @ IRQ 122 handler +vector_irq 123 @ IRQ 123 handler +vector_irq 124 @ IRQ 124 handler +vector_irq 125 @ IRQ 125 handler +vector_irq 126 @ IRQ 126 handler +vector_irq 127 @ IRQ 127 handler +vector_irq 128 @ IRQ 128 handler +vector_irq 129 @ IRQ 129 handler +vector_irq 130 @ IRQ 130 handler +vector_irq 131 @ IRQ 131 handler +vector_irq 132 @ IRQ 132 handler +vector_irq 133 @ IRQ 133 handler +vector_irq 134 @ IRQ 134 handler +vector_irq 135 @ IRQ 135 handler +vector_irq 136 @ IRQ 136 handler +vector_irq 137 @ IRQ 137 handler +vector_irq 138 @ IRQ 138 handler +vector_irq 139 @ IRQ 139 handler +vector_irq 140 @ IRQ 140 handler +vector_irq 141 @ IRQ 141 handler +vector_irq 142 @ IRQ 142 handler +vector_irq 143 @ IRQ 143 handler +vector_irq 144 @ IRQ 144 handler +vector_irq 145 @ IRQ 145 handler +vector_irq 146 @ IRQ 146 handler +vector_irq 147 @ IRQ 147 handler +vector_irq 148 @ IRQ 148 handler +vector_irq 149 @ IRQ 149 handler +vector_irq 150 @ IRQ 150 handler +vector_irq 151 @ IRQ 151 handler +vector_irq 152 @ IRQ 152 handler +vector_irq 153 @ IRQ 153 handler +vector_irq 154 @ IRQ 154 handler +vector_irq 155 @ IRQ 155 handler +vector_irq 156 @ IRQ 156 handler +vector_irq 157 @ IRQ 157 handler +vector_irq 158 @ IRQ 158 handler +vector_irq 159 @ IRQ 159 handler +vector_irq 160 @ IRQ 160 handler +vector_irq 161 @ IRQ 161 handler +vector_irq 162 @ IRQ 162 handler +vector_irq 163 @ IRQ 163 handler +vector_irq 164 @ IRQ 164 handler +vector_irq 165 @ IRQ 165 handler +vector_irq 166 @ IRQ 166 handler +vector_irq 167 @ IRQ 167 handler +vector_irq 168 @ IRQ 168 handler +vector_irq 169 @ IRQ 169 handler +vector_irq 170 @ IRQ 170 handler +vector_irq 171 @ IRQ 171 handler +vector_irq 172 @ IRQ 172 handler +vector_irq 173 @ IRQ 173 handler +vector_irq 174 @ IRQ 174 handler +vector_irq 175 @ IRQ 175 handler +vector_irq 176 @ IRQ 176 handler +vector_irq 177 @ IRQ 177 handler +vector_irq 178 @ IRQ 178 handler +vector_irq 179 @ IRQ 179 handler +vector_irq 180 @ IRQ 180 handler +vector_irq 181 @ IRQ 181 handler +vector_irq 182 @ IRQ 182 handler +vector_irq 183 @ IRQ 183 handler +vector_irq 184 @ IRQ 184 handler +vector_irq 185 @ IRQ 185 handler +vector_irq 186 @ IRQ 186 handler +vector_irq 187 @ IRQ 187 handler +vector_irq 188 @ IRQ 188 handler +vector_irq 189 @ IRQ 189 handler +vector_irq 190 @ IRQ 190 handler +vector_irq 191 @ IRQ 191 handler +vector_irq 192 @ IRQ 192 handler +vector_irq 193 @ IRQ 193 handler +vector_irq 194 @ IRQ 194 handler +vector_irq 195 @ IRQ 195 handler +vector_irq 196 @ IRQ 196 handler +vector_irq 197 @ IRQ 197 handler +vector_irq 198 @ IRQ 198 handler +vector_irq 199 @ IRQ 199 handler +vector_irq 200 @ IRQ 200 handler +vector_irq 201 @ IRQ 201 handler +vector_irq 202 @ IRQ 202 handler +vector_irq 203 @ IRQ 203 handler +vector_irq 204 @ IRQ 204 handler +vector_irq 205 @ IRQ 205 handler +vector_irq 206 @ IRQ 206 handler +vector_irq 207 @ IRQ 207 handler +vector_irq 208 @ IRQ 208 handler +vector_irq 209 @ IRQ 209 handler +vector_irq 210 @ IRQ 210 handler +vector_irq 211 @ IRQ 211 handler +vector_irq 212 @ IRQ 212 handler +vector_irq 213 @ IRQ 213 handler +vector_irq 214 @ IRQ 214 handler +vector_irq 215 @ IRQ 215 handler +vector_irq 216 @ IRQ 216 handler +vector_irq 217 @ IRQ 217 handler +vector_irq 218 @ IRQ 218 handler +vector_irq 219 @ IRQ 219 handler +vector_irq 220 @ IRQ 220 handler +vector_irq 221 @ IRQ 221 handler +vector_irq 222 @ IRQ 222 handler +vector_irq 223 @ IRQ 223 handler +vector_irq 224 @ IRQ 224 handler +vector_irq 225 @ IRQ 225 handler +vector_irq 226 @ IRQ 226 handler +vector_irq 227 @ IRQ 227 handler +vector_irq 228 @ IRQ 228 handler +vector_irq 229 @ IRQ 229 handler +vector_irq 230 @ IRQ 230 handler +vector_irq 231 @ IRQ 231 handler +vector_irq 232 @ IRQ 232 handler +vector_irq 233 @ IRQ 233 handler +vector_irq 234 @ IRQ 234 handler +vector_irq 235 @ IRQ 235 handler +vector_irq 236 @ IRQ 236 handler +vector_irq 237 @ IRQ 237 handler +vector_irq 238 @ IRQ 238 handler +vector_irq 239 @ IRQ 239 handler +vector_irq 240 @ IRQ 240 handler +vector_irq 241 @ IRQ 241 handler +vector_irq 242 @ IRQ 242 handler +vector_irq 243 @ IRQ 243 handler +vector_irq 244 @ IRQ 244 handler +vector_irq 245 @ IRQ 245 handler +vector_irq 246 @ IRQ 246 handler +vector_irq 247 @ IRQ 247 handler +vector_irq 248 @ IRQ 248 handler +vector_irq 249 @ IRQ 249 handler +vector_irq 250 @ IRQ 250 handler +vector_irq 251 @ IRQ 251 handler +vector_irq 252 @ IRQ 252 handler +vector_irq 253 @ IRQ 253 handler +vector_irq 254 @ IRQ 254 handler + +.global reset +.thumb_func +reset: + /* set the vector table on our current code */ + adr r1, vectors + ldr r2, =0xE000ED08 /* VTABLE register in SCB*/ + str r1, [r2] + /* Clear BSS */ + mov r0, #0 + ldr r1,_bss_start + ldr r2,_bss_end +bss_loop: + cmp r1, r2 + it lt + strlt r0, [r1], #4 + blt bss_loop + +#ifndef COMPILE_FOR_RAM + /* Copy initialized data to Internal RAM */ + ldr r0,_ro_end + ldr r1,_data_start + ldr r2,_data_end +data_loop: + ldr r3, [r0], #4 + cmp r1, r2 + it lt + strlt r3, [r1], #4 + blt data_loop +#endif + + /** + * Set stack pointer + * already done my Cortex-M hardware but this allows software to + * jump directly to reset function or to run on other ARM + */ + ldr r0, =stack_end + mov sp, r0 + + /* jump to C code */ + bl main + /* we should not return here */ + /* TODO check error code ? */ +fini_loop: + b fini_loop + +/* default exception handler */ +.thumb_func +default_handler: + b panic + +_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 + +/* Dummy functions to avoid linker complaints */ +.global __aeabi_unwind_cpp_pr0 +.global __aeabi_unwind_cpp_pr1 +.global __aeabi_unwind_cpp_pr2 +__aeabi_unwind_cpp_pr0: +__aeabi_unwind_cpp_pr1: +__aeabi_unwind_cpp_pr2: + bx lr + +.section .bss + +/* Reserve space for system stack */ +stack_start: + .space CONFIG_STACK_SIZE, 0 +stack_end: + .globl stack_end + diff --git a/core/cortex-m/panic.S b/core/cortex-m/panic.S new file mode 100644 index 0000000000..4c87cac511 --- /dev/null +++ b/core/cortex-m/panic.S @@ -0,0 +1,111 @@ +/* Copyright (c) 2011 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. + * + * Fatal exception handling and debug tracing + */ + +#include "config.h" + +.text + +.syntax unified +.code 16 + +.macro hex_reg r, offset @ prepare to build + add r1, r3, #\offset @ .. hexadecimal string + mov r0, \r @ .. from the reg value + bl buildhex +.endm + +/* fatal exception handler */ +.global panic +.thumb_func +panic: +#ifndef CONFIG_DEBUG + b EcSystemReset @ Reboot the system +#else /* CONFIG_DEBUG */ + /* check that the exception stack pointer is valid */ + ldr r0, =CONFIG_RAM_BASE @ start of RAM + ldr r1, =CONFIG_RAM_BASE+CONFIG_RAM_SIZE @ end of RAM + mrs r12, psp @ process stack pointer + cmp r12, r0 @ is sp >= RAM start ? + it ge + cmpge r1, r12 @ is sp < RAM end ? + blt panic_print @ no => no values to read + /* output registers value */ + ldr r3, =msg_excep @ pointer to the text buffer + mrs r2, ipsr @ get exception num from IPSR + bfc r2, #9, #23 @ the exception is the 3 LSB + hex_reg r2, 18 @ prepare hexa for excep number + hex_reg r4, 119 @ prepare hexa for R4 + hex_reg r5, 132 @ prepare hexa for R5 + hex_reg r6, 145 @ prepare hexa for R6 + hex_reg r7, 156 @ prepare hexa for R7 + hex_reg r8, 171 @ prepare hexa for R8 + hex_reg r9, 184 @ prepare hexa for R9 + hex_reg r10, 197 @ prepare hexa for R10 + hex_reg r11, 210 @ prepare hexa for R11 + ldmia r12!, {r4-r11} @ load saved r0-r3,r12,lr,pc,psr + hex_reg r4, 66 @ prepare hexa for R0 + hex_reg r5, 79 @ prepare hexa for R1 + hex_reg r6, 92 @ prepare hexa for R2 + hex_reg r7, 105 @ prepare hexa for R3 + hex_reg r8, 225 @ prepare hexa for R12 + hex_reg r12, 238 @ prepare hexa for SP + hex_reg r9, 251 @ prepare hexa for LR + hex_reg r10, 264 @ prepare hexa for PC + hex_reg r11, 40 @ prepare hexa for xPSR + /* print exception trace */ +panic_print: + ldr r0, =msg_excep @ pointer to the text buffer + bl emergency_puts @ print the banner + b system_reset @ Reboot the system + +/* Helpers for exception trace */ +/* print a string on the UART + * r0: asciiZ string pointer + */ +emergency_puts: + ldr r1, =0x4000c000 @ UART0 first register +1: + ldrb r3, [r0], #1 @ read one character + cmp r3, #0 @ end of the string ? + beq 3f @ if yes, return +2: /* putchar */ + ldr r2, [r1, #0x18] @ read LM4_UART0_UARTFR + tst r2, #0x20 @ TX FIFO full ? + bne 2b @ if yes, wait + str r3, [r1] @ send character to UART DR + b 1b @ goto next character +3: /* flush */ + ldr r2, [r1, #0x18] @ read LM4_UART0_UARTFR + tst r2, #0x8 @ TX on-going ? + bne 3b @ if yes, wait + bx lr + +/* write a number in hexadecimal in a text buffer + * r0: number to print + * r1: pointer to *end* of the buffer (filled with '0') + */ +buildhex: + cmp r0, #0 + it eq + bxeq lr + and r2, r0, #0xf + cmp r2, #10 + ite lt + addlt r2, #'0' + addge r2, #'A'-10 + strb r2, [r1],#-1 + lsr r0, #4 + b buildhex + +.data +msg_excep: .ascii "\r\n=== EXCEPTION: 00 ====== xPSR: 00000000 ===========\r\n" +msg_reg0: .ascii "R0 :00000000 R1 :00000000 R2 :00000000 R3 :00000000\r\n" +msg_reg1: .ascii "R4 :00000000 R5 :00000000 R6 :00000000 R7 :00000000\r\n" +msg_reg2: .ascii "R8 :00000000 R9 :00000000 R10:00000000 R11:00000000\r\n" +msg_reg3: .ascii "R12:00000000 SP :00000000 LR :00000000 PC :00000000\r\n\0" +.align 4 +#endif /* CONFIG_DEBUG */ diff --git a/core/cortex-m/switch.S b/core/cortex-m/switch.S new file mode 100644 index 0000000000..4d974251cd --- /dev/null +++ b/core/cortex-m/switch.S @@ -0,0 +1,63 @@ +/* Copyright (c) 2011 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 swtching + */ + +#include "config.h" + +.text + +.syntax unified +.code 16 + +/** + * Task context switching + * + * Change the task scheduled after returning from the exception. + * + * Save the registers of the current task below the exception 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 + * + * must be called from interrupt context + * + * the structure of tje saved context on the stack is : + * r0, r1, r2, r3, r12, lr, pc, psr, r4, r5, r6, r7, r8, r9, r10, r11 + * exception frame <|> additional registers + */ +.global __switchto +.thumb_func +__switchto: + mrs r3, psp @ get the task stack where the context has been saved + ldr r2, [r1] @ get the new scheduled task stack pointer + stmdb r3!, {r4-r11} @ save additional r4-r7 in the task stack + ldmia r2!, {r4-r11} @ restore r4-r7 for the next task context + str r3, [r0] @ save the task stack pointer in its context + msr psp, r2 @ set the process stack pointer to exception context + bx lr @ return from exception + +/** + * Start the task scheduling + */ +.global task_start +.thumb_func +task_start: + ldr r2,=scratchpad @ area used as dummy thread stack for the first switch + mov r3, #2 + mov r0, #0 @ __Schedule parameter : de-schedule nothing + mov r1, #0 @ __Schedule parameter : re-schedule nothing + add r2, #17*4 @ put the pointer at the top of the stack + msr psp, r2 @ setup a thread stack up to the first context switch + isb @ ensure the write is done + msr control, r3 @ use : priv. mode / thread stack / no floating point + isb @ ensure the write is done + bl __schedule @ execute the task with the highest priority + /* we should never return here */ + mov r0, #1 @ set to EC_ERROR_UNKNOWN + bx lr + diff --git a/core/cortex-m/task.c b/core/cortex-m/task.c new file mode 100644 index 0000000000..2e0821e80f --- /dev/null +++ b/core/cortex-m/task.c @@ -0,0 +1,394 @@ +/* Copyright (c) 2011 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 <stdint.h> + +#include "config.h" +#include "atomic.h" +#include "console.h" +#include "task.h" +#include "timer.h" +#include "uart.h" +#include "registers.h" +#include "util.h" + +/** + * Global memory size for a task : 512 bytes + * including its contexts and its stack + */ +#define TASK_SIZE_LOG2 9 +#define TASK_SIZE (1<<TASK_SIZE_LOG2) + +typedef union { + struct { + uint32_t sp; /* saved stack pointer for context switch */ + uint32_t events; /* bitmaps of received events */ + uint8_t stack[0]; /* task stack */ + }; + uint32_t context[TASK_SIZE/4]; +} task_; + +/* declare task routine prototypes */ +#define TASK(n, r, d) int r(void *); +#include TASK_LIST +void __idle(void); +CONFIG_TASK_LIST +#undef TASK + + +extern void __switchto(task_ *from, task_ *to); + + +/* declare and fill the contexts for all the tasks */ +#define TASK(n, r, d) { \ + .context[0] = (uint32_t)(tasks + TASK_ID_##n + 1) - 64, \ + .context[TASK_SIZE/4 - 8/*r0*/] = (uint32_t)d, \ + /* TODO set a LR to a trap */ \ + .context[TASK_SIZE/4 - 2/*pc*/] = (uint32_t)r, \ + .context[TASK_SIZE/4 - 1/*psr*/] = 0x01000000 }, +#include TASK_LIST +static task_ tasks[] __attribute__((section(".data.tasks"))) + __attribute__((aligned(TASK_SIZE))) = { + TASK(IDLE, __idle, 0) + CONFIG_TASK_LIST +}; +#undef TASK +/* reserve space to discard context on first context switch */ +uint32_t scratchpad[17] __attribute__((section(".data.tasks"))); + +/* context switch at the next exception exit if needed */ +/* TODO: who sets this back to 0 after it's set to 1? */ +static int need_resched = 0; + +/** + * bitmap of all tasks ready to be run + * + * Currently all tasks are enabled at startup. + */ +static unsigned tasks_ready = (1<<TASK_ID_COUNT) - 1; + + +static task_ *__get_current(void) +{ + unsigned sp; + + asm("mov %0, sp":"=r"(sp)); + return (task_ *)((sp - 4) & ~(TASK_SIZE-1)); +} + + +/** + * Return a pointer to the task preempted by the current exception + * + * designed to be called from interrupt context. + */ +static task_ *__get_task_scheduled(void) +{ + unsigned sp; + + asm("mrs %0, psp":"=r"(sp)); + return (task_ *)((sp - 16) & ~(TASK_SIZE-1)); +} + + +static inline task_ *__task_id_to_ptr(task_id_t id) +{ + return tasks + id; +} + + +inline int in_interrupt_context(void) +{ + int ret; + asm("mrs %0, ipsr \n" /* read exception number */ + "lsl %0, #23 \n":"=r"(ret)); /* exception bits are the 9 LSB */ + return ret; +} + + +task_id_t task_get_current(void) +{ + task_id_t id = __get_current() - tasks; + if (id >= TASK_ID_COUNT) + id = TASK_ID_INVALID; + + return id; +} + + +uint32_t *task_get_event_bitmap(task_id_t tskid) +{ + task_ *tsk = __task_id_to_ptr(tskid); + return &tsk->events; +} + + +/** + * scheduling system call + */ +void svc_handler(int desched, task_id_t resched) +{ + task_ *current, *next; + uint32_t reg; + + /* push the priority to -1 until the return, to avoid being + * interrupted */ + asm volatile("mov %0, #1\n" + "msr faultmask, %0" :"=r"(reg)); + current = __get_task_scheduled(); + if (desched && !current->events) { + /* Remove our own ready bit */ + tasks_ready &= ~(1 << (current-tasks)); + } + tasks_ready |= 1 << resched; + + ASSERT(tasks_ready); + next = __task_id_to_ptr(31 - __builtin_clz(tasks_ready)); + + /* Nothing to do */ + if (next == current) + return; + + __switchto(current, next); +} + + +void __schedule(int desched, int resched) +{ + register int p0 asm("r0") = desched; + register int p1 asm("r1") = resched; + /* TODO remove hardcoded opcode + * SWI not compiled properly for ARMv7-M on our current chroot toolchain + */ + asm(".hword 0xdf00 @swi 0"::"r"(p0),"r"(p1)); +} + + +/** + * Change the task scheduled after returning from the exception. + * + * If task_send_msg has been called and has set need_resched flag, + * we re-compute which task is running and eventually swap the context + * saved on the process stack to restore the new one at exception exit. + * + * it must be called from interrupt context ! + */ +void task_resched_if_needed(void *excep_return) +{ + /** + * continue iff a rescheduling event happened and + * we are not called from another exception + */ + if (!need_resched || (((uint32_t)excep_return & 0xf) == 1)) + return; + + svc_handler(0, 0); +} + + +static uint32_t __wait_msg(int timeout_us, task_id_t resched) +{ + task_ *tsk = __get_current(); + 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); + resched = TASK_ID_IDLE; + } + if (timeout_us > 0) + timer_cancel(me); + return evt; +} + + +uint32_t task_send_msg(task_id_t tskid, task_id_t from, int wait) +{ + task_ *receiver = __task_id_to_ptr(tskid); + ASSERT(receiver); + + if (from == TASK_ID_CURRENT) { + from = task_get_current(); + } + + /* set the event bit in the receiver message bitmap */ + atomic_or(&receiver->events, 1 << from); + + /* Re-schedule if priorities have changed */ + if (in_interrupt_context()) { + /* the receiver might run again */ + tasks_ready |= 1 << tskid; + need_resched = 1; + } else { + if (wait) + return __wait_msg(-1, tskid); + else + __schedule(0, tskid); + } + + return 0; +} + + +uint32_t task_wait_msg(int timeout_us) +{ + return __wait_msg(timeout_us, TASK_ID_IDLE); +} + + +void task_enable_irq(int irq) +{ + LM4_NVIC_EN(irq / 32) = 1 << (irq % 32); +} + + +void task_disable_irq(int irq) +{ + LM4_NVIC_DIS(irq / 32) = 1 << (irq % 32); +} + + +void task_trigger_irq(int irq) +{ + LM4_NVIC_SWTRIG = irq; +} + + +/** + * Enable all used IRQ in the NVIC and set their priorities + * as defined by the DECLARE_IRQ statements + */ +static void __nvic_init_irqs(void) +{ + /* get the IRQ priorities section from the linker */ + extern struct irq_priority __irqprio[]; + extern struct irq_priority __irqprio_end[]; + int irq_count = __irqprio_end - __irqprio; + int i; + + for (i = 0; i < irq_count; i++) { + uint8_t irq = __irqprio[i].irq; + uint8_t prio = __irqprio[i].priority; + uint32_t prio_shift = irq % 4 * 8 + 5; + LM4_NVIC_PRI(irq / 4) = + (LM4_NVIC_PRI(irq / 4) & + ~(0x7 << prio_shift)) | + (prio << prio_shift); + } +} + + +void mutex_lock(struct mutex *mtx) +{ + uint32_t value; + uint32_t id = 1 << task_get_current(); + + ASSERT(id != TASK_ID_INVALID); + atomic_or(&mtx->waiters, id); + + do { + /* try to get the lock (set 1 into the lock field) */ + __asm__ __volatile__(" ldrex %0, [%1]\n" + " teq %0, #0\n" + " it eq\n" + " strexeq %0, %2, [%1]\n" + : "=&r" (value) + : "r" (&mtx->lock), "r" (1) : "cc"); + if (value) { + /* contention on the mutex */ + task_wait_msg(0); + } + } while (value); + + atomic_clear(&mtx->waiters, id); +} + +void mutex_unlock(struct mutex *mtx) +{ + uint32_t waiters; + task_ *tsk = __get_current(); + + __asm__ __volatile__(" ldr %0, [%2]\n" + " str %3, [%1]\n" + : "=&r" (waiters) + : "r" (&mtx->lock), "r" (&mtx->waiters), "r" (0) + : "cc"); + while (waiters) { + task_id_t id = 31 - __builtin_clz(waiters); + /* somebody is waiting on the mutex */ + task_send_msg(id, TASK_ID_MUTEX, 0); + waiters &= ~(1 << id); + } + /* Ensure no event is remaining from mutex wake-up */ + atomic_clear(&tsk->events, 1 << TASK_ID_MUTEX); +} + + +#ifdef CONFIG_DEBUG + +/* store the task names for easier debugging */ +#define TASK(n, r, d) #n, +#include TASK_LIST +static const char * const task_names[] = { + "<< idle >>", + CONFIG_TASK_LIST +}; +#undef TASK + + +int command_task_info(int argc, char **argv) +{ + int i; + + for (i = 0; i < TASK_ID_COUNT; i++) { + char is_ready = (tasks_ready & (1<<i)) ? 'R' : ' '; + uart_printf("%2d %c %-16s events %08x\n", i, is_ready, + task_names[i], tasks[i].events); + } + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(taskinfo, command_task_info); + + +static int command_task_ready(int argc, char **argv) +{ + if (argc < 2) { + uart_printf("tasks_ready: 0x%08x\n", tasks_ready); + } else { + tasks_ready = strtoi(argv[1], NULL, 16); + uart_printf("Setting tasks_ready to 0x%08x\n", tasks_ready); + __schedule(0, 0); + } + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(taskready, command_task_ready); +#endif + + +int task_init(void) +{ + /* 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))); + + /* Initialize IRQs */ + __nvic_init_irqs(); + + return EC_SUCCESS; +} diff --git a/core/cortex-m/timer.c b/core/cortex-m/timer.c new file mode 100644 index 0000000000..56b67a9d2f --- /dev/null +++ b/core/cortex-m/timer.c @@ -0,0 +1,206 @@ +/* Copyright (c) 2012 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. + */ + +/* Timer module for Chrome EC operating system */ + +#include <stdint.h> + +#include "task.h" +#include "timer.h" +#include "hwtimer.h" +#include "atomic.h" +#include "board.h" +#include "console.h" +#include "uart.h" +#include "util.h" + +/* high word of the 64-bit timestamp counter */ +static volatile uint32_t clksrc_high; + +/* bitmap of currently running timers */ +static uint32_t timer_running = 0; + +/* deadlines of all timers */ +static timestamp_t timer_deadline[TASK_ID_COUNT]; + +static uint32_t next_deadline = 0xffffffff; + +/* Hardware timer routine IRQ number */ +static int timer_irq; + +static void expire_timer(task_id_t tskid) +{ + /* we are done with this timer */ + atomic_clear(&timer_running, 1<<tskid); + /* wake up the taks waiting for this timer */ + task_send_msg(tskid, TASK_ID_TIMER, 0); +} + +/** + * Search the next deadline and program it in the timer hardware + * + * overflow: if true, the 32-bit counter as overflowed since the last call. + */ +void process_timers(int overflow) +{ + uint32_t check_timer, running_t0; + timestamp_t next; + timestamp_t now; + + if (overflow) + clksrc_high++; + +reprocess_timers: + next.val = 0xffffffffffffffff; + now = get_time(); + do { + /* read atomically the current state of timer running */ + check_timer = running_t0 = timer_running; + while (check_timer) { + int tskid = 31 - __builtin_clz(check_timer); + + /* timer has expired ? */ + if (timer_deadline[tskid].val < now.val) + expire_timer(tskid); + else if ((timer_deadline[tskid].le.hi == now.le.hi) && + (timer_deadline[tskid].le.lo < next.le.lo)) + next.val = timer_deadline[tskid].val; + + check_timer &= ~(1 << tskid); + } + /* if there is a new timer, let's retry */ + } while (timer_running & ~running_t0); + + if (next.le.hi == 0xffffffff) { + /* no deadline to set */ + __hw_clock_event_clear(); + next_deadline = 0xffffffff; + return; + } + + if (next.val <= get_time().val) + goto reprocess_timers; + __hw_clock_event_set(next.le.lo); + next_deadline = next.le.lo; + //TODO narrow race: deadline might have been reached before +} + +void udelay(unsigned us) +{ + timestamp_t deadline = get_time(); + + deadline.val += us; + while (get_time().val < deadline.val) {} +} + +int timer_arm(timestamp_t tstamp, task_id_t tskid) +{ + ASSERT(tskid < TASK_ID_COUNT); + + if (timer_running & (1<<tskid)) + return EC_ERROR_BUSY; + + timer_deadline[tskid] = tstamp; + atomic_or(&timer_running, 1<<tskid); + + /* modify the next event if needed */ + if ((tstamp.le.hi < clksrc_high) || + ((tstamp.le.hi == clksrc_high) && (tstamp.le.lo <= next_deadline))) + task_trigger_irq(timer_irq); + + return EC_SUCCESS; +} + +int timer_cancel(task_id_t tskid) +{ + ASSERT(tskid < TASK_ID_COUNT); + + atomic_clear(&timer_running, 1<<tskid); + /* don't bother about canceling the interrupt: + * it would be slow, just do it on the next IT + */ + + return EC_SUCCESS; +} + + +void usleep(unsigned us) +{ + uint32_t evt = 0; + ASSERT(us); + do { + evt |= task_wait_msg(us); + } while (!(evt & (1 << TASK_ID_TIMER))); + /* re-queue other events which happened in the meanwhile */ + if (evt) + atomic_or(task_get_event_bitmap(task_get_current()), + evt & ~(1 << TASK_ID_TIMER)); +} + + +timestamp_t get_time(void) +{ + timestamp_t ts; + ts.le.hi = clksrc_high; + ts.le.lo = __hw_clock_source_read(); + if (ts.le.hi != clksrc_high) { + ts.le.hi = clksrc_high; + ts.le.lo = __hw_clock_source_read(); + } + return ts; +} + + +static int command_wait(int argc, char **argv) +{ + if (argc < 2) + return EC_ERROR_INVAL; + + udelay(atoi(argv[1]) * 1000); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(waitms, command_wait); + + +static int command_get_time(int argc, char **argv) +{ + timestamp_t ts = get_time(); + uart_printf("Time: 0x%08x%08x us\n", ts.le.hi, ts.le.lo); + return EC_SUCCESS; + +} +DECLARE_CONSOLE_COMMAND(gettime, command_get_time); + + +int command_timer_info(int argc, char **argv) +{ + timestamp_t ts = get_time(); + int tskid; + + uart_printf("Time: 0x%08x%08x us\n" + "Deadline: 0x%08x%08x us\n" + "Active timers:\n", + ts.le.hi, ts.le.lo, clksrc_high, + __hw_clock_event_get()); + for (tskid = 0; tskid < TASK_ID_COUNT; tskid++) { + if (timer_running & (1<<tskid)) + uart_printf("Tsk %d tmr 0x%08x%08x\n", tskid, + timer_deadline[tskid].le.hi, + timer_deadline[tskid].le.lo); + } + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(timerinfo, command_timer_info); + + +int timer_init(void) +{ + BUILD_ASSERT(TASK_ID_COUNT < sizeof(timer_running) * 8); + + timer_irq = __hw_clock_source_init(); + + return EC_SUCCESS; +} |