/* * Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved. * * SPDX-License-Identifier: MIT */ #include #include #include #include #include "n200_func.h" #include "register.h" #include "common.h" #include "n200_timer.h" #include "gcc_compiler_attributes.h" #include "riscv_encoding.h" #include "lscript.h" #define PMP_PRINT(...) printf(__VA_ARGS__) #define PMP_PRINT_DEBUG(...) //printf(__VA_ARGS__) /* * -- Configure PMP -- * For User mode only. * To make all the address space accessible and executable */ void pmp_open_all_space(void) { // Config entry0 addr to all 1s to make the range cover all space asm volatile ("li x6, 0xffffffff":::"x6"); asm volatile ("csrw pmpaddr0, x6":::); // Config entry0 cfg to make it NAPOT address mode, and R/W/X okay asm volatile ("li x6, 0x7f":::"x6"); asm volatile ("csrw pmpcfg0, x6":::); } #ifndef CONFIG_N200_REVA static uint32_t pmpcfg_order_get(uint32_t pmp_size) { int pmpcfg_order = 0; if ((pmp_size & (pmp_size - 1)) != 0) { PMP_PRINT("err: pmp_size is not 2^n\n"); return 0; } while (pmp_size > 1) { pmp_size >>= 1; pmpcfg_order++; } return pmpcfg_order; } static uint32_t pmpaddr_encode(uint32_t pmp_base_addr, uint32_t pmp_cfg_order) { uint32_t addrmask, pmpaddr; addrmask = (1UL << (pmp_cfg_order - PMP_SHIFT)) - 1; pmpaddr = ((pmp_base_addr >> PMP_SHIFT) & ~addrmask); pmpaddr |= (addrmask >> 1); return pmpaddr; } /* * -- Configure PMP -- * For both Machine and User mode. * To make aocpu text/ro data space readable and executable. * Note: * If config_pmp is called, pmp_open_all_space is invalid. */ uint32_t config_pmp(void) { uint32_t start_text_addr = (uint32_t)&_text; uint32_t end_text_addr = (uint32_t)&_etext; uint32_t text_len_left = end_text_addr - start_text_addr; uint32_t next_seg_len = 0; uint32_t pmp_cfg[8] = {0}, pmp_addr[8] = {0}; int pmp_entry_used = 0; uint32_t pmp_region_base = start_text_addr; PMP_PRINT("PMP config range %x ~ %x\n", start_text_addr, end_text_addr); while (text_len_left > 0) { if ((text_len_left >= SIZE_64K) && ((pmp_region_base & (SIZE_64K - 1)) == 0)) next_seg_len = SIZE_64K; else if ((text_len_left >= SIZE_32K) && ((pmp_region_base & (SIZE_32K - 1)) == 0)) next_seg_len = SIZE_32K; else if ((text_len_left >= SIZE_16K) && ((pmp_region_base & (SIZE_16K - 1)) == 0)) next_seg_len = SIZE_16K; else if ((text_len_left >= SIZE_8K) && ((pmp_region_base & (SIZE_8K - 1)) == 0)) next_seg_len = SIZE_8K; else if ((text_len_left >= SIZE_4K) && ((pmp_region_base & (SIZE_4K - 1)) == 0)) next_seg_len = SIZE_4K; else if ((text_len_left >= SIZE_2K) && ((pmp_region_base & (SIZE_2K - 1)) == 0)) next_seg_len = SIZE_2K; else if ((text_len_left >= SIZE_1K) && ((pmp_region_base & (SIZE_1K - 1)) == 0)) next_seg_len = SIZE_1K; if (next_seg_len == 0) { PMP_PRINT("PMP config err: not align.\n"); PMP_PRINT_DEBUG("pmp base: 0x%x, segment left: 0x%x\n", pmp_region_base, text_len_left); break; } text_len_left -= next_seg_len; PMP_PRINT_DEBUG("pm pregion base: %x\n", pmp_region_base); PMP_PRINT_DEBUG("segment length : %x\n", next_seg_len); PMP_PRINT_DEBUG("length left : %x\n", text_len_left); pmp_addr[pmp_entry_used] = pmpaddr_encode(pmp_region_base, pmpcfg_order_get(next_seg_len)); pmp_cfg[pmp_entry_used] = (PMP_CFG_R_EN | PMP_CFG_X_EN | PMP_CFG_A_NAPOT | PMP_CFG_L_EN); pmp_entry_used++; pmp_region_base += next_seg_len; PMP_PRINT_DEBUG("pmp_entry_used : %d\n", pmp_entry_used); next_seg_len = 0; if (pmp_entry_used >= 8) { PMP_PRINT_DEBUG("Note: pmp entry has been full used\n"); break; } } write_csr(pmpaddr0, pmp_addr[0]); write_csr(pmpaddr1, pmp_addr[1]); write_csr(pmpaddr2, pmp_addr[2]); write_csr(pmpaddr3, pmp_addr[3]); write_csr(pmpaddr4, pmp_addr[4]); write_csr(pmpaddr5, pmp_addr[5]); write_csr(pmpaddr6, pmp_addr[6]); write_csr(pmpaddr7, pmp_addr[7]); PMP_PRINT_DEBUG("pmpaddr0 : %lx\n", read_csr(pmpaddr0)); PMP_PRINT_DEBUG("pmpaddr1 : %lx\n", read_csr(pmpaddr1)); PMP_PRINT_DEBUG("pmpaddr2 : %lx\n", read_csr(pmpaddr2)); PMP_PRINT_DEBUG("pmpaddr3 : %lx\n", read_csr(pmpaddr3)); PMP_PRINT_DEBUG("pmpaddr4 : %lx\n", read_csr(pmpaddr4)); PMP_PRINT_DEBUG("pmpaddr5 : %lx\n", read_csr(pmpaddr5)); PMP_PRINT_DEBUG("pmpaddr6 : %lx\n", read_csr(pmpaddr6)); PMP_PRINT_DEBUG("pmpaddr7 : %lx\n", read_csr(pmpaddr7)); write_csr(pmpcfg0, (pmp_cfg[3] << 24) | (pmp_cfg[2] << 16) | (pmp_cfg[1] << 8) | (pmp_cfg[0] << 0)); PMP_PRINT_DEBUG("pmpcfg0 : %lx\n", read_csr(pmpcfg0)); write_csr(pmpcfg1, (pmp_cfg[7] << 24) | (pmp_cfg[6] << 16) | (pmp_cfg[5] << 8) | (pmp_cfg[4] << 0)); PMP_PRINT_DEBUG("pmpcfg1 : %lx\n", read_csr(pmpcfg1)); PMP_PRINT("PMP config end\n"); return 0; } #ifdef CONFIG_AOCPU_BUSRESPERR_DETECTION void config_eclic_busresperr_irq(void) { /* enable bus response error interrupt for no access permission */ uint8_t bus_err_intattr; bus_err_intattr = eclic_get_intattr(ECLIC_INT_BUS_RESP_ERR); bus_err_intattr |= ECLIC_INT_ATTR_SHV | ECLIC_INT_ATTR_TRIG_EDGE; eclic_set_intattr(ECLIC_INT_BUS_RESP_ERR, bus_err_intattr); eclic_set_intctrl(ECLIC_INT_BUS_RESP_ERR, 15 << 4); eclic_enable_interrupt(ECLIC_INT_BUS_RESP_ERR); } #endif #endif void switch_m2u_mode(void) { clear_csr(mstatus, MSTATUS_MPP); //printf("\nIn the m2u function, the mstatus is 0x%x\n", read_csr(mstatus)); //printf("\nIn the m2u function, the mepc is 0x%x\n", read_csr(mepc)); asm volatile("la x6, 1f " ::: "x6"); asm volatile("csrw mepc, x6" :::); asm volatile("mret" :::); asm volatile("1:" :::); } uint32_t mtime_lo(void) { #if ((defined configSOC_TIMER_AS_TICK) && !(defined CONFIG_TIMEBASE_MTIME)) return *(volatile uint32_t *)TIMERE_LOW_REG; #else return *(volatile uint32_t *)(TIMER_CTRL_ADDR + TIMER_MTIME); #endif } uint32_t mtime_hi(void) { #if ((defined configSOC_TIMER_AS_TICK) && !(defined CONFIG_TIMEBASE_MTIME)) return *(volatile uint32_t *)TIMERE_HIG_REG; #else return *(volatile uint32_t *)(TIMER_CTRL_ADDR + TIMER_MTIME + 4); #endif } 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; } } inline void mtime_start(void) { #ifdef CONFIG_N200_REVA set_csr_mcounterstop(1); #else *(volatile uint8_t *)(TIMER_CTRL_ADDR + TIMER_MSTOP) = 0; #endif } inline void mtime_stop(void) { #ifdef CONFIG_N200_REVA clear_csr_mcounterstop(1); #else *(volatile uint8_t *)(TIMER_CTRL_ADDR + TIMER_MSTOP) = 1; #endif } uint32_t get_timer_freq(void) { return TIMER_FREQ; } uint64_t get_instret_value(void) { while (1) { uint32_t hi = read_csr(minstreth); uint32_t lo = read_csr(minstret); if (hi == read_csr(minstreth)) return ((uint64_t)hi << 32) | lo; } } uint64_t get_cycle_value(void) { while (1) { uint32_t hi = read_csr(mcycleh); uint32_t lo = read_csr(mcycle); if (hi == read_csr(mcycleh)) return ((uint64_t)hi << 32) | lo; } } unsigned long interrupt_status_get(void) { return read_csr(mstatus) >> 0x3; } void interrupt_disable(void) { clear_csr(mstatus, MSTATUS_MIE); } void interrupt_enable(void) { set_csr(mstatus, MSTATUS_MIE); } #ifndef CONFIG_N200_REVA uint32_t __noinline measure_cpu_freq(size_t n) { uint32_t start_mtime, delta_mtime; uint32_t mtime_freq = get_timer_freq(); // Don't start measuruing until we see an mtime tick uint32_t tmp = mtime_lo(); do { start_mtime = mtime_lo(); } while (start_mtime == tmp); uint32_t start_mcycle = read_csr(mcycle); do { delta_mtime = mtime_lo() - start_mtime; } while (delta_mtime < n); uint32_t delta_mcycle = read_csr(mcycle) - start_mcycle; return (delta_mcycle / delta_mtime) * mtime_freq + ((delta_mcycle % delta_mtime) * mtime_freq) / delta_mtime; } uint32_t get_cpu_freq(void) { uint32_t cpu_freq; // warm up measure_cpu_freq(1); // measure for real cpu_freq = measure_cpu_freq(100); return cpu_freq; } unsigned int xPortIsIsrContext(void) { return (read_csr_msubmode & 0xff); } #else unsigned int xPortIsIsrContext(void) { return read_csr_msubmode; } #endif #ifdef AOCPU_TIMER_DIV inline void config_ao_timer_freq(uint32_t src, uint32_t freq) { /* AO internal timer frequency: (src/(div+1)*2) */ *(volatile uint32_t *)AOCPU_TIMER_DIV = src / freq * 2 - 1; } #endif