| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "os/mynewt.h" |
| #include "os_priv.h" |
| |
| #include <hal/hal_bsp.h> |
| #include <hal/hal_os_tick.h> |
| #include <env/encoding.h> |
| #include <env/freedom-e300-hifive1/platform.h> |
| #include <mcu/plic.h> |
| |
| extern void trap_entry(); |
| |
| struct context_switch_frame { |
| uint32_t pc; |
| /* Callee saved registers */ |
| uint32_t s0; |
| uint32_t s1; |
| uint32_t s2; |
| uint32_t s3; |
| uint32_t s4; |
| uint32_t s5; |
| uint32_t s6; |
| uint32_t s7; |
| uint32_t s8; |
| uint32_t s9; |
| uint32_t s10; |
| uint32_t s11; |
| /* Caller saved register */ |
| uint32_t ra; |
| uint32_t gp; |
| uint32_t tp; |
| uint32_t t0; |
| uint32_t t1; |
| uint32_t t2; |
| uint32_t t3; |
| uint32_t t4; |
| uint32_t t5; |
| uint32_t t6; |
| uint32_t a0; |
| uint32_t a1; |
| uint32_t a2; |
| uint32_t a3; |
| uint32_t a4; |
| uint32_t a5; |
| uint32_t a6; |
| uint32_t a7; |
| }; |
| |
| uint32_t |
| mtime_lo(void) |
| { |
| return CLINT_REG(CLINT_MTIME); |
| } |
| |
| uint32_t |
| mtime_hi(void) |
| { |
| return CLINT_REG(CLINT_MTIME + 4); |
| } |
| |
| uint64_t |
| get_timer_value(void) |
| { |
| while (1) { |
| uint32_t hi = mtime_hi(); |
| uint32_t lo = mtime_lo(); |
| if (hi == mtime_hi()) |
| return ((uint64_t)hi << 32) | lo; |
| } |
| } |
| |
| void |
| set_mtimecmp(uint64_t time) |
| { |
| CLINT_REG(CLINT_MTIMECMP + 4) = -1; |
| CLINT_REG(CLINT_MTIMECMP) = (uint32_t) time; |
| CLINT_REG(CLINT_MTIMECMP + 4) = (uint32_t) (time >> 32); |
| } |
| |
| unsigned long |
| get_timer_freq() |
| { |
| return 32768; |
| } |
| |
| /* XXX: determine how to deal with running un-privileged */ |
| /* only priv currently supported */ |
| uint32_t os_flags = OS_RUN_PRIV; |
| |
| extern struct os_task g_idle_task; |
| |
| #define OS_TICK_PRIO 0 |
| |
| static int |
| os_in_isr(void) |
| { |
| // TODO: |
| return 0; |
| } |
| |
| void |
| timer_handler(void) |
| { |
| os_time_advance(1); |
| } |
| |
| void |
| os_arch_ctx_sw(struct os_task *t) |
| { |
| if ((os_sched_get_current_task() != 0) && (t != 0)) { |
| os_sched_ctx_sw_hook(t); |
| } |
| |
| /* |
| * This request software interrupt that is used for contect switching |
| */ |
| CLINT_REG(CLINT_MSIP) = 1; |
| } |
| |
| os_sr_t |
| os_arch_save_sr(void) |
| { |
| uint32_t isr_ctx; |
| |
| isr_ctx = clear_csr(mstatus, MSTATUS_MIE) & MSTATUS_MIE; |
| |
| return isr_ctx; |
| } |
| |
| void |
| os_arch_restore_sr(os_sr_t isr_ctx) |
| { |
| if (isr_ctx) { |
| set_csr(mstatus, MSTATUS_MIE); |
| } |
| } |
| |
| int |
| os_arch_in_critical(void) |
| { |
| return !(read_csr(mstatus) & MSTATUS_MIE); |
| } |
| |
| /* assumes stack_top will be 8 aligned */ |
| |
| os_stack_t * |
| os_arch_task_stack_init(struct os_task *t, os_stack_t *stack_top, int size) |
| { |
| struct context_switch_frame *sf; |
| uint32_t *reg; |
| |
| /* Get stack frame pointer */ |
| sf = (struct context_switch_frame *) ((uint8_t *) stack_top - sizeof(*sf)); |
| reg = &sf->a7; |
| |
| /* Zero out registers except PC which will be set */ |
| while (reg != &sf->pc) { |
| *reg-- = 0; |
| } |
| |
| /* Set remaining portions of stack frame */ |
| sf->pc = (uint32_t) t->t_func; |
| sf->a0 = (uint32_t) t->t_arg; |
| |
| return (os_stack_t *) sf; |
| } |
| |
| void |
| os_arch_init(void) |
| { |
| os_init_idle_task(); |
| } |
| |
| os_error_t |
| os_arch_os_init(void) |
| { |
| os_error_t err = OS_OK; |
| int i; |
| |
| /* Set all external interrupts to default handler */ |
| for (i = 0; i < PLIC_NUM_INTERRUPTS; ++i) { |
| plic_interrupts[i] = plic_default_isr; |
| /* Default priority set to 0, never interrupt */ |
| PLIC_REG(PLIC_PRIORITY_OFFSET + i * 4) = 0; |
| } |
| |
| /* Disable all interrupts */ |
| for (i = 0; i < (31 + PLIC_NUM_INTERRUPTS) / 8; i += 4) { |
| PLIC_REG(PLIC_ENABLE_OFFSET + i) = 0; |
| } |
| |
| /* Enable interrupts at 0 level */ |
| PLIC_REG(PLIC_THRESHOLD_OFFSET) = 0; |
| |
| /* Set main trap handler */ |
| write_csr(mtvec, &trap_entry); |
| |
| os_arch_init(); |
| |
| return err; |
| } |
| |
| uint32_t |
| os_arch_start(void) |
| { |
| struct os_task *t; |
| struct os_task fake_task; |
| |
| /* Get the highest priority ready to run to set the current task */ |
| t = os_sched_next_task(); |
| /* |
| * First time setup fake os_task struct that only has one pointer for SP |
| * Having that will make context switch function work same for first |
| * and every other time. |
| * This fake SP will be used during initial context switch to store SP |
| * that will never be used. |
| */ |
| os_sched_set_current_task(&fake_task); |
| |
| /* Clean software interrupt, and enable it */ |
| CLINT_REG(CLINT_MSIP) = 0; |
| set_csr(mie, MIP_MSIP); |
| /* Enable external interrupts */ |
| set_csr(mie, MIP_MEIP); |
| |
| /* Intitialize and start system clock timer, this enable timer interrupt */ |
| os_tick_init(OS_TICKS_PER_SEC, OS_TICK_PRIO); |
| |
| /* Mark the OS as started, right before we run our first task */ |
| g_os_started = 1; |
| |
| /* Perform context switch */ |
| os_arch_ctx_sw(t); |
| |
| /* Enable interrupts */ |
| set_csr(mstatus, MSTATUS_MIE); |
| |
| /* This should not be reached */ |
| return (uint32_t) (t->t_arg); |
| } |
| |
| os_error_t |
| os_arch_os_start(void) |
| { |
| os_error_t err; |
| |
| err = OS_ERR_IN_ISR; |
| if (os_in_isr() == 0) { |
| err = OS_OK; |
| /* should be in kernel mode here */ |
| os_arch_start(); |
| } |
| |
| return err; |
| } |
| |
| void |
| software_interrupt_handler(uintptr_t mcause) |
| { |
| } |