diff options
Diffstat (limited to 'roms/u-boot/arch/arc/lib')
-rw-r--r-- | roms/u-boot/arch/arc/lib/Makefile | 17 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/_millicodethunk.S | 225 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/bootm.c | 125 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/cache.c | 822 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/cpu.c | 225 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/init_helpers.c | 15 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/interrupts.c | 169 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/ints_low.S | 178 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/libgcc2.c | 235 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/libgcc2.h | 131 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/relocate.c | 137 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/reset.c | 23 | ||||
-rw-r--r-- | roms/u-boot/arch/arc/lib/start.S | 139 |
13 files changed, 2441 insertions, 0 deletions
diff --git a/roms/u-boot/arch/arc/lib/Makefile b/roms/u-boot/arch/arc/lib/Makefile new file mode 100644 index 000000000..0eb44bcf3 --- /dev/null +++ b/roms/u-boot/arch/arc/lib/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. + +extra-y = start.o +head-y := start.o +obj-y += cache.o +obj-y += cpu.o +obj-y += interrupts.o +obj-y += relocate.o +obj-y += reset.o +obj-y += ints_low.o +obj-y += init_helpers.o + +obj-$(CONFIG_CMD_BOOTM) += bootm.o + +lib-$(CONFIG_USE_PRIVATE_LIBGCC) += _millicodethunk.o libgcc2.o diff --git a/roms/u-boot/arch/arc/lib/_millicodethunk.S b/roms/u-boot/arch/arc/lib/_millicodethunk.S new file mode 100644 index 000000000..4ad16ae3b --- /dev/null +++ b/roms/u-boot/arch/arc/lib/_millicodethunk.S @@ -0,0 +1,225 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 1995, 1997, 2007-2013 Free Software Foundation, Inc. + */ + + /* ANSI concatenation macros. */ + + #define CONCAT1(a, b) CONCAT2(a, b) + #define CONCAT2(a, b) a ## b + + /* Use the right prefix for global labels. */ + + #define SYM(x) CONCAT1 (__USER_LABEL_PREFIX__, x) + +#ifndef WORKING_ASSEMBLER +#define abs_l abs +#define asl_l asl +#define mov_l mov +#endif + +#define FUNC(X) .type SYM(X),@function +#define HIDDEN_FUNC(X) FUNC(X)` .hidden X +#define ENDFUNC0(X) .Lfe_##X: .size X,.Lfe_##X-X +#define ENDFUNC(X) ENDFUNC0(X) + + .section .text + .align 4 + .global SYM(__st_r13_to_r15) + .global SYM(__st_r13_to_r16) + .global SYM(__st_r13_to_r17) + .global SYM(__st_r13_to_r18) + .global SYM(__st_r13_to_r19) + .global SYM(__st_r13_to_r20) + .global SYM(__st_r13_to_r21) + .global SYM(__st_r13_to_r22) + .global SYM(__st_r13_to_r23) + .global SYM(__st_r13_to_r24) + .global SYM(__st_r13_to_r25) + HIDDEN_FUNC(__st_r13_to_r15) + HIDDEN_FUNC(__st_r13_to_r16) + HIDDEN_FUNC(__st_r13_to_r17) + HIDDEN_FUNC(__st_r13_to_r18) + HIDDEN_FUNC(__st_r13_to_r19) + HIDDEN_FUNC(__st_r13_to_r20) + HIDDEN_FUNC(__st_r13_to_r21) + HIDDEN_FUNC(__st_r13_to_r22) + HIDDEN_FUNC(__st_r13_to_r23) + HIDDEN_FUNC(__st_r13_to_r24) + HIDDEN_FUNC(__st_r13_to_r25) + .align 4 +SYM(__st_r13_to_r25): + st r25, [sp,48] +SYM(__st_r13_to_r24): + st r24, [sp,44] +SYM(__st_r13_to_r23): + st r23, [sp,40] +SYM(__st_r13_to_r22): + st r22, [sp,36] +SYM(__st_r13_to_r21): + st r21, [sp,32] +SYM(__st_r13_to_r20): + st r20, [sp,28] +SYM(__st_r13_to_r19): + st r19, [sp,24] +SYM(__st_r13_to_r18): + st r18, [sp,20] +SYM(__st_r13_to_r17): + st r17, [sp,16] +SYM(__st_r13_to_r16): + st r16, [sp,12] +SYM(__st_r13_to_r15): +#ifdef __ARC700__ + st r15, [sp,8] ; minimum function size to avoid stall: 6 bytes. +#else + st_s r15, [sp,8] +#endif + st_s r14, [sp,4] + j_s.d [%blink] + st_s r13, [sp,0] + ENDFUNC(__st_r13_to_r15) + ENDFUNC(__st_r13_to_r16) + ENDFUNC(__st_r13_to_r17) + ENDFUNC(__st_r13_to_r18) + ENDFUNC(__st_r13_to_r19) + ENDFUNC(__st_r13_to_r20) + ENDFUNC(__st_r13_to_r21) + ENDFUNC(__st_r13_to_r22) + ENDFUNC(__st_r13_to_r23) + ENDFUNC(__st_r13_to_r24) + ENDFUNC(__st_r13_to_r25) + + .section .text + .align 4 +; ================================== +; the loads + + .global SYM(__ld_r13_to_r15) + .global SYM(__ld_r13_to_r16) + .global SYM(__ld_r13_to_r17) + .global SYM(__ld_r13_to_r18) + .global SYM(__ld_r13_to_r19) + .global SYM(__ld_r13_to_r20) + .global SYM(__ld_r13_to_r21) + .global SYM(__ld_r13_to_r22) + .global SYM(__ld_r13_to_r23) + .global SYM(__ld_r13_to_r24) + .global SYM(__ld_r13_to_r25) + HIDDEN_FUNC(__ld_r13_to_r15) + HIDDEN_FUNC(__ld_r13_to_r16) + HIDDEN_FUNC(__ld_r13_to_r17) + HIDDEN_FUNC(__ld_r13_to_r18) + HIDDEN_FUNC(__ld_r13_to_r19) + HIDDEN_FUNC(__ld_r13_to_r20) + HIDDEN_FUNC(__ld_r13_to_r21) + HIDDEN_FUNC(__ld_r13_to_r22) + HIDDEN_FUNC(__ld_r13_to_r23) + HIDDEN_FUNC(__ld_r13_to_r24) + HIDDEN_FUNC(__ld_r13_to_r25) +SYM(__ld_r13_to_r25): + ld r25, [sp,48] +SYM(__ld_r13_to_r24): + ld r24, [sp,44] +SYM(__ld_r13_to_r23): + ld r23, [sp,40] +SYM(__ld_r13_to_r22): + ld r22, [sp,36] +SYM(__ld_r13_to_r21): + ld r21, [sp,32] +SYM(__ld_r13_to_r20): + ld r20, [sp,28] +SYM(__ld_r13_to_r19): + ld r19, [sp,24] +SYM(__ld_r13_to_r18): + ld r18, [sp,20] +SYM(__ld_r13_to_r17): + ld r17, [sp,16] +SYM(__ld_r13_to_r16): + ld r16, [sp,12] +SYM(__ld_r13_to_r15): +#ifdef __ARC700__ + ld r15, [sp,8] ; minimum function size to avoid stall: 6 bytes. +#else + ld_s r15, [sp,8] +#endif + ld_s r14, [sp,4] + j_s.d [%blink] + ld_s r13, [sp,0] + ENDFUNC(__ld_r13_to_r15) + ENDFUNC(__ld_r13_to_r16) + ENDFUNC(__ld_r13_to_r17) + ENDFUNC(__ld_r13_to_r18) + ENDFUNC(__ld_r13_to_r19) + ENDFUNC(__ld_r13_to_r20) + ENDFUNC(__ld_r13_to_r21) + ENDFUNC(__ld_r13_to_r22) + ENDFUNC(__ld_r13_to_r23) + ENDFUNC(__ld_r13_to_r24) + ENDFUNC(__ld_r13_to_r25) + + .global SYM(__ld_r13_to_r14_ret) + .global SYM(__ld_r13_to_r15_ret) + .global SYM(__ld_r13_to_r16_ret) + .global SYM(__ld_r13_to_r17_ret) + .global SYM(__ld_r13_to_r18_ret) + .global SYM(__ld_r13_to_r19_ret) + .global SYM(__ld_r13_to_r20_ret) + .global SYM(__ld_r13_to_r21_ret) + .global SYM(__ld_r13_to_r22_ret) + .global SYM(__ld_r13_to_r23_ret) + .global SYM(__ld_r13_to_r24_ret) + .global SYM(__ld_r13_to_r25_ret) + HIDDEN_FUNC(__ld_r13_to_r14_ret) + HIDDEN_FUNC(__ld_r13_to_r15_ret) + HIDDEN_FUNC(__ld_r13_to_r16_ret) + HIDDEN_FUNC(__ld_r13_to_r17_ret) + HIDDEN_FUNC(__ld_r13_to_r18_ret) + HIDDEN_FUNC(__ld_r13_to_r19_ret) + HIDDEN_FUNC(__ld_r13_to_r20_ret) + HIDDEN_FUNC(__ld_r13_to_r21_ret) + HIDDEN_FUNC(__ld_r13_to_r22_ret) + HIDDEN_FUNC(__ld_r13_to_r23_ret) + HIDDEN_FUNC(__ld_r13_to_r24_ret) + HIDDEN_FUNC(__ld_r13_to_r25_ret) + .section .text + .align 4 +SYM(__ld_r13_to_r25_ret): + ld r25, [sp,48] +SYM(__ld_r13_to_r24_ret): + ld r24, [sp,44] +SYM(__ld_r13_to_r23_ret): + ld r23, [sp,40] +SYM(__ld_r13_to_r22_ret): + ld r22, [sp,36] +SYM(__ld_r13_to_r21_ret): + ld r21, [sp,32] +SYM(__ld_r13_to_r20_ret): + ld r20, [sp,28] +SYM(__ld_r13_to_r19_ret): + ld r19, [sp,24] +SYM(__ld_r13_to_r18_ret): + ld r18, [sp,20] +SYM(__ld_r13_to_r17_ret): + ld r17, [sp,16] +SYM(__ld_r13_to_r16_ret): + ld r16, [sp,12] +SYM(__ld_r13_to_r15_ret): + ld r15, [sp,8] +SYM(__ld_r13_to_r14_ret): + ld blink,[sp,r12] + ld_s r14, [sp,4] + ld.ab r13, [sp,r12] + j_s.d [%blink] + add_s sp,sp,4 + ENDFUNC(__ld_r13_to_r14_ret) + ENDFUNC(__ld_r13_to_r15_ret) + ENDFUNC(__ld_r13_to_r16_ret) + ENDFUNC(__ld_r13_to_r17_ret) + ENDFUNC(__ld_r13_to_r18_ret) + ENDFUNC(__ld_r13_to_r19_ret) + ENDFUNC(__ld_r13_to_r20_ret) + ENDFUNC(__ld_r13_to_r21_ret) + ENDFUNC(__ld_r13_to_r22_ret) + ENDFUNC(__ld_r13_to_r23_ret) + ENDFUNC(__ld_r13_to_r24_ret) + ENDFUNC(__ld_r13_to_r25_ret) diff --git a/roms/u-boot/arch/arc/lib/bootm.c b/roms/u-boot/arch/arc/lib/bootm.c new file mode 100644 index 000000000..8a8d394a5 --- /dev/null +++ b/roms/u-boot/arch/arc/lib/bootm.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. + */ + +#include <common.h> +#include <bootstage.h> +#include <env.h> +#include <image.h> +#include <irq_func.h> +#include <lmb.h> +#include <log.h> +#include <asm/cache.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +static ulong get_sp(void) +{ + ulong ret; + + asm("mov %0, sp" : "=r"(ret) : ); + return ret; +} + +void arch_lmb_reserve(struct lmb *lmb) +{ + ulong sp; + + /* + * Booting a (Linux) kernel image + * + * Allocate space for command line and board info - the + * address should be as high as possible within the reach of + * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused + * memory, which means far enough below the current stack + * pointer. + */ + sp = get_sp(); + debug("## Current stack ends at 0x%08lx ", sp); + + /* adjust sp by 4K to be safe */ + sp -= 4096; + lmb_reserve(lmb, sp, (CONFIG_SYS_SDRAM_BASE + gd->ram_size - sp)); +} + +static int cleanup_before_linux(void) +{ + disable_interrupts(); + sync_n_cleanup_cache_all(); + + return 0; +} + +__weak int board_prep_linux(bootm_headers_t *images) { return 0; } + +/* Subcommand: PREP */ +static int boot_prep_linux(bootm_headers_t *images) +{ + int ret; + + ret = image_setup_linux(images); + if (ret) + return ret; + + return board_prep_linux(images); +} + +/* Generic implementation for single core CPU */ +__weak void board_jump_and_run(ulong entry, int zero, int arch, uint params) +{ + void (*kernel_entry)(int zero, int arch, uint params); + + kernel_entry = (void (*)(int, int, uint))entry; + + kernel_entry(zero, arch, params); +} + +/* Subcommand: GO */ +static void boot_jump_linux(bootm_headers_t *images, int flag) +{ + ulong kernel_entry; + unsigned int r0, r2; + int fake = (flag & BOOTM_STATE_OS_FAKE_GO); + + kernel_entry = images->ep; + + debug("## Transferring control to Linux (at address %08lx)...\n", + kernel_entry); + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + printf("\nStarting kernel ...%s\n\n", fake ? + "(fake run for tracing)" : ""); + bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); + + if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) { + r0 = 2; + r2 = (unsigned int)images->ft_addr; + } else { + r0 = 1; + r2 = (unsigned int)env_get("bootargs"); + } + + cleanup_before_linux(); + + if (!fake) + board_jump_and_run(kernel_entry, r0, 0, r2); +} + +int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) +{ + /* No need for those on ARC */ + if ((flag & BOOTM_STATE_OS_BD_T) || (flag & BOOTM_STATE_OS_CMDLINE)) + return -1; + + if (flag & BOOTM_STATE_OS_PREP) + return boot_prep_linux(images); + + if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) { + boot_jump_linux(images, flag); + return 0; + } + + return -1; +} diff --git a/roms/u-boot/arch/arc/lib/cache.c b/roms/u-boot/arch/arc/lib/cache.c new file mode 100644 index 000000000..f807cd83d --- /dev/null +++ b/roms/u-boot/arch/arc/lib/cache.c @@ -0,0 +1,822 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. + */ + +#include <config.h> +#include <common.h> +#include <cpu_func.h> +#include <asm/global_data.h> +#include <linux/bitops.h> +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/log2.h> +#include <asm/arcregs.h> +#include <asm/arc-bcr.h> +#include <asm/cache.h> + +/* + * [ NOTE 1 ]: + * Data cache (L1 D$ or SL$) entire invalidate operation or data cache disable + * operation may result in unexpected behavior and data loss even if we flush + * data cache right before invalidation. That may happens if we store any context + * on stack (like we store BLINK register on stack before function call). + * BLINK register is the register where return address is automatically saved + * when we do function call with instructions like 'bl'. + * + * There is the real example: + * We may hang in the next code as we store any BLINK register on stack in + * invalidate_dcache_all() function. + * + * void flush_dcache_all() { + * __dc_entire_op(OP_FLUSH); + * // Other code // + * } + * + * void invalidate_dcache_all() { + * __dc_entire_op(OP_INV); + * // Other code // + * } + * + * void foo(void) { + * flush_dcache_all(); + * invalidate_dcache_all(); + * } + * + * Now let's see what really happens during that code execution: + * + * foo() + * |->> call flush_dcache_all + * [return address is saved to BLINK register] + * [push BLINK] (save to stack) ![point 1] + * |->> call __dc_entire_op(OP_FLUSH) + * [return address is saved to BLINK register] + * [flush L1 D$] + * return [jump to BLINK] + * <<------ + * [other flush_dcache_all code] + * [pop BLINK] (get from stack) + * return [jump to BLINK] + * <<------ + * |->> call invalidate_dcache_all + * [return address is saved to BLINK register] + * [push BLINK] (save to stack) ![point 2] + * |->> call __dc_entire_op(OP_FLUSH) + * [return address is saved to BLINK register] + * [invalidate L1 D$] ![point 3] + * // Oops!!! + * // We lose return address from invalidate_dcache_all function: + * // we save it to stack and invalidate L1 D$ after that! + * return [jump to BLINK] + * <<------ + * [other invalidate_dcache_all code] + * [pop BLINK] (get from stack) + * // we don't have this data in L1 dcache as we invalidated it in [point 3] + * // so we get it from next memory level (for example DDR memory) + * // but in the memory we have value which we save in [point 1], which + * // is return address from flush_dcache_all function (instead of + * // address from current invalidate_dcache_all function which we + * // saved in [point 2] !) + * return [jump to BLINK] + * <<------ + * // As BLINK points to invalidate_dcache_all, we call it again and + * // loop forever. + * + * Fortunately we may fix that by using flush & invalidation of D$ with a single + * one instruction (instead of flush and invalidation instructions pair) and + * enabling force function inline with '__attribute__((always_inline))' gcc + * attribute to avoid any function call (and BLINK store) between cache flush + * and disable. + * + * + * [ NOTE 2 ]: + * As of today we only support the following cache configurations on ARC. + * Other configurations may exist in HW but we don't support it in SW. + * Configuration 1: + * ______________________ + * | | + * | ARC CPU | + * |______________________| + * ___|___ ___|___ + * | | | | + * | L1 I$ | | L1 D$ | + * |_______| |_______| + * on/off on/off + * ___|______________|____ + * | | + * | main memory | + * |______________________| + * + * Configuration 2: + * ______________________ + * | | + * | ARC CPU | + * |______________________| + * ___|___ ___|___ + * | | | | + * | L1 I$ | | L1 D$ | + * |_______| |_______| + * on/off on/off + * ___|______________|____ + * | | + * | L2 (SL$) | + * |______________________| + * always on (ARCv2, HS < 3.0) + * on/off (ARCv2, HS >= 3.0) + * ___|______________|____ + * | | + * | main memory | + * |______________________| + * + * Configuration 3: + * ______________________ + * | | + * | ARC CPU | + * |______________________| + * ___|___ ___|___ + * | | | | + * | L1 I$ | | L1 D$ | + * |_______| |_______| + * on/off must be on + * ___|______________|____ _______ + * | | | | + * | L2 (SL$) |-----| IOC | + * |______________________| |_______| + * always must be on on/off + * ___|______________|____ + * | | + * | main memory | + * |______________________| + */ + +DECLARE_GLOBAL_DATA_PTR; + +/* Bit values in IC_CTRL */ +#define IC_CTRL_CACHE_DISABLE BIT(0) + +/* Bit values in DC_CTRL */ +#define DC_CTRL_CACHE_DISABLE BIT(0) +#define DC_CTRL_INV_MODE_FLUSH BIT(6) +#define DC_CTRL_FLUSH_STATUS BIT(8) + +#define OP_INV BIT(0) +#define OP_FLUSH BIT(1) +#define OP_FLUSH_N_INV (OP_FLUSH | OP_INV) + +/* Bit val in SLC_CONTROL */ +#define SLC_CTRL_DIS 0x001 +#define SLC_CTRL_IM 0x040 +#define SLC_CTRL_BUSY 0x100 +#define SLC_CTRL_RGN_OP_INV 0x200 + +#define CACHE_LINE_MASK (~(gd->arch.l1_line_sz - 1)) + +/* + * We don't want to use '__always_inline' macro here as it can be redefined + * to simple 'inline' in some cases which breaks stuff. See [ NOTE 1 ] for more + * details about the reasons we need to use always_inline functions. + */ +#define inlined_cachefunc inline __attribute__((always_inline)) + +static inlined_cachefunc void __ic_entire_invalidate(void); +static inlined_cachefunc void __dc_entire_op(const int cacheop); +static inlined_cachefunc void __slc_entire_op(const int op); +static inlined_cachefunc bool ioc_enabled(void); + +static inline bool pae_exists(void) +{ + /* TODO: should we compare mmu version from BCR and from CONFIG? */ +#if (CONFIG_ARC_MMU_VER >= 4) + union bcr_mmu_4 mmu4; + + mmu4.word = read_aux_reg(ARC_AUX_MMU_BCR); + + if (mmu4.fields.pae) + return true; +#endif /* (CONFIG_ARC_MMU_VER >= 4) */ + + return false; +} + +static inlined_cachefunc bool icache_exists(void) +{ + union bcr_di_cache ibcr; + + ibcr.word = read_aux_reg(ARC_BCR_IC_BUILD); + return !!ibcr.fields.ver; +} + +static inlined_cachefunc bool icache_enabled(void) +{ + if (!icache_exists()) + return false; + + return !(read_aux_reg(ARC_AUX_IC_CTRL) & IC_CTRL_CACHE_DISABLE); +} + +static inlined_cachefunc bool dcache_exists(void) +{ + union bcr_di_cache dbcr; + + dbcr.word = read_aux_reg(ARC_BCR_DC_BUILD); + return !!dbcr.fields.ver; +} + +static inlined_cachefunc bool dcache_enabled(void) +{ + if (!dcache_exists()) + return false; + + return !(read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_CACHE_DISABLE); +} + +static inlined_cachefunc bool slc_exists(void) +{ + if (is_isa_arcv2()) { + union bcr_generic sbcr; + + sbcr.word = read_aux_reg(ARC_BCR_SLC); + return !!sbcr.fields.ver; + } + + return false; +} + +enum slc_dis_status { + ST_SLC_MISSING = 0, + ST_SLC_NO_DISABLE_CTRL, + ST_SLC_DISABLE_CTRL +}; + +/* + * ARCv1 -> ST_SLC_MISSING + * ARCv2 && SLC absent -> ST_SLC_MISSING + * ARCv2 && SLC exists && SLC version <= 2 -> ST_SLC_NO_DISABLE_CTRL + * ARCv2 && SLC exists && SLC version > 2 -> ST_SLC_DISABLE_CTRL + */ +static inlined_cachefunc enum slc_dis_status slc_disable_supported(void) +{ + if (is_isa_arcv2()) { + union bcr_generic sbcr; + + sbcr.word = read_aux_reg(ARC_BCR_SLC); + if (sbcr.fields.ver == 0) + return ST_SLC_MISSING; + else if (sbcr.fields.ver <= 2) + return ST_SLC_NO_DISABLE_CTRL; + else + return ST_SLC_DISABLE_CTRL; + } + + return ST_SLC_MISSING; +} + +static inlined_cachefunc bool __slc_enabled(void) +{ + return !(read_aux_reg(ARC_AUX_SLC_CTRL) & SLC_CTRL_DIS); +} + +static inlined_cachefunc void __slc_enable(void) +{ + unsigned int ctrl; + + ctrl = read_aux_reg(ARC_AUX_SLC_CTRL); + ctrl &= ~SLC_CTRL_DIS; + write_aux_reg(ARC_AUX_SLC_CTRL, ctrl); +} + +static inlined_cachefunc void __slc_disable(void) +{ + unsigned int ctrl; + + ctrl = read_aux_reg(ARC_AUX_SLC_CTRL); + ctrl |= SLC_CTRL_DIS; + write_aux_reg(ARC_AUX_SLC_CTRL, ctrl); +} + +static inlined_cachefunc bool slc_enabled(void) +{ + enum slc_dis_status slc_status = slc_disable_supported(); + + if (slc_status == ST_SLC_MISSING) + return false; + else if (slc_status == ST_SLC_NO_DISABLE_CTRL) + return true; + else + return __slc_enabled(); +} + +static inlined_cachefunc bool slc_data_bypass(void) +{ + /* + * If L1 data cache is disabled SL$ is bypassed and all load/store + * requests are sent directly to main memory. + */ + return !dcache_enabled(); +} + +void slc_enable(void) +{ + if (slc_disable_supported() != ST_SLC_DISABLE_CTRL) + return; + + if (__slc_enabled()) + return; + + __slc_enable(); +} + +/* TODO: warn if we are not able to disable SLC */ +void slc_disable(void) +{ + if (slc_disable_supported() != ST_SLC_DISABLE_CTRL) + return; + + /* we don't support SLC disabling if we use IOC */ + if (ioc_enabled()) + return; + + if (!__slc_enabled()) + return; + + /* + * We need to flush L1D$ to guarantee that we won't have any + * writeback operations during SLC disabling. + */ + __dc_entire_op(OP_FLUSH); + __slc_entire_op(OP_FLUSH_N_INV); + __slc_disable(); +} + +static inlined_cachefunc bool ioc_exists(void) +{ + if (is_isa_arcv2()) { + union bcr_clust_cfg cbcr; + + cbcr.word = read_aux_reg(ARC_BCR_CLUSTER); + return cbcr.fields.c; + } + + return false; +} + +static inlined_cachefunc bool ioc_enabled(void) +{ + /* + * We check only CONFIG option instead of IOC HW state check as IOC + * must be disabled by default. + */ + if (is_ioc_enabled()) + return ioc_exists(); + + return false; +} + +static inlined_cachefunc void __slc_entire_op(const int op) +{ + unsigned int ctrl; + + if (!slc_enabled()) + return; + + ctrl = read_aux_reg(ARC_AUX_SLC_CTRL); + + if (!(op & OP_FLUSH)) /* i.e. OP_INV */ + ctrl &= ~SLC_CTRL_IM; /* clear IM: Disable flush before Inv */ + else + ctrl |= SLC_CTRL_IM; + + write_aux_reg(ARC_AUX_SLC_CTRL, ctrl); + + if (op & OP_INV) /* Inv or flush-n-inv use same cmd reg */ + write_aux_reg(ARC_AUX_SLC_INVALIDATE, 0x1); + else + write_aux_reg(ARC_AUX_SLC_FLUSH, 0x1); + + /* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */ + read_aux_reg(ARC_AUX_SLC_CTRL); + + /* Important to wait for flush to complete */ + while (read_aux_reg(ARC_AUX_SLC_CTRL) & SLC_CTRL_BUSY); +} + +static void slc_upper_region_init(void) +{ + /* + * ARC_AUX_SLC_RGN_START1 and ARC_AUX_SLC_RGN_END1 register exist + * only if PAE exists in current HW. So we had to check pae_exist + * before using them. + */ + if (!pae_exists()) + return; + + /* + * ARC_AUX_SLC_RGN_END1 and ARC_AUX_SLC_RGN_START1 are always == 0 + * as we don't use PAE40. + */ + write_aux_reg(ARC_AUX_SLC_RGN_END1, 0); + write_aux_reg(ARC_AUX_SLC_RGN_START1, 0); +} + +static void __slc_rgn_op(unsigned long paddr, unsigned long sz, const int op) +{ +#ifdef CONFIG_ISA_ARCV2 + + unsigned int ctrl; + unsigned long end; + + if (!slc_enabled()) + return; + + /* + * The Region Flush operation is specified by CTRL.RGN_OP[11..9] + * - b'000 (default) is Flush, + * - b'001 is Invalidate if CTRL.IM == 0 + * - b'001 is Flush-n-Invalidate if CTRL.IM == 1 + */ + ctrl = read_aux_reg(ARC_AUX_SLC_CTRL); + + /* Don't rely on default value of IM bit */ + if (!(op & OP_FLUSH)) /* i.e. OP_INV */ + ctrl &= ~SLC_CTRL_IM; /* clear IM: Disable flush before Inv */ + else + ctrl |= SLC_CTRL_IM; + + if (op & OP_INV) + ctrl |= SLC_CTRL_RGN_OP_INV; /* Inv or flush-n-inv */ + else + ctrl &= ~SLC_CTRL_RGN_OP_INV; + + write_aux_reg(ARC_AUX_SLC_CTRL, ctrl); + + /* + * Lower bits are ignored, no need to clip + * END needs to be setup before START (latter triggers the operation) + * END can't be same as START, so add (l2_line_sz - 1) to sz + */ + end = paddr + sz + gd->arch.slc_line_sz - 1; + + /* + * Upper addresses (ARC_AUX_SLC_RGN_END1 and ARC_AUX_SLC_RGN_START1) + * are always == 0 as we don't use PAE40, so we only setup lower ones + * (ARC_AUX_SLC_RGN_END and ARC_AUX_SLC_RGN_START) + */ + write_aux_reg(ARC_AUX_SLC_RGN_END, end); + write_aux_reg(ARC_AUX_SLC_RGN_START, paddr); + + /* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */ + read_aux_reg(ARC_AUX_SLC_CTRL); + + while (read_aux_reg(ARC_AUX_SLC_CTRL) & SLC_CTRL_BUSY); + +#endif /* CONFIG_ISA_ARCV2 */ +} + +static void arc_ioc_setup(void) +{ + /* IOC Aperture start is equal to DDR start */ + unsigned int ap_base = CONFIG_SYS_SDRAM_BASE; + /* IOC Aperture size is equal to DDR size */ + long ap_size = CONFIG_SYS_SDRAM_SIZE; + + /* Unsupported configuration. See [ NOTE 2 ] for more details. */ + if (!slc_exists()) + panic("Try to enable IOC but SLC is not present"); + + if (!slc_enabled()) + panic("Try to enable IOC but SLC is disabled"); + + /* Unsupported configuration. See [ NOTE 2 ] for more details. */ + if (!dcache_enabled()) + panic("Try to enable IOC but L1 D$ is disabled"); + + if (!is_power_of_2(ap_size) || ap_size < 4096) + panic("IOC Aperture size must be power of 2 and bigger 4Kib"); + + /* IOC Aperture start must be aligned to the size of the aperture */ + if (ap_base % ap_size != 0) + panic("IOC Aperture start must be aligned to the size of the aperture"); + + flush_n_invalidate_dcache_all(); + + /* + * IOC Aperture size decoded as 2 ^ (SIZE + 2) KB, + * so setting 0x11 implies 512M, 0x12 implies 1G... + */ + write_aux_reg(ARC_AUX_IO_COH_AP0_SIZE, + order_base_2(ap_size / 1024) - 2); + + write_aux_reg(ARC_AUX_IO_COH_AP0_BASE, ap_base >> 12); + write_aux_reg(ARC_AUX_IO_COH_PARTIAL, 1); + write_aux_reg(ARC_AUX_IO_COH_ENABLE, 1); +} + +static void read_decode_cache_bcr_arcv2(void) +{ +#ifdef CONFIG_ISA_ARCV2 + + union bcr_slc_cfg slc_cfg; + + if (slc_exists()) { + slc_cfg.word = read_aux_reg(ARC_AUX_SLC_CONFIG); + gd->arch.slc_line_sz = (slc_cfg.fields.lsz == 0) ? 128 : 64; + + /* + * We don't support configuration where L1 I$ or L1 D$ is + * absent but SL$ exists. See [ NOTE 2 ] for more details. + */ + if (!icache_exists() || !dcache_exists()) + panic("Unsupported cache configuration: SLC exists but one of L1 caches is absent"); + } + +#endif /* CONFIG_ISA_ARCV2 */ +} + +void read_decode_cache_bcr(void) +{ + int dc_line_sz = 0, ic_line_sz = 0; + union bcr_di_cache ibcr, dbcr; + + /* + * We don't care much about I$ line length really as there're + * no per-line ops on I$ instead we only do full invalidation of it + * on occasion of relocation and right before jumping to the OS. + * Still we check insane config with zero-encoded line length in + * presense of version field in I$ BCR. Just in case. + */ + ibcr.word = read_aux_reg(ARC_BCR_IC_BUILD); + if (ibcr.fields.ver) { + ic_line_sz = 8 << ibcr.fields.line_len; + if (!ic_line_sz) + panic("Instruction exists but line length is 0\n"); + } + + dbcr.word = read_aux_reg(ARC_BCR_DC_BUILD); + if (dbcr.fields.ver) { + gd->arch.l1_line_sz = dc_line_sz = 16 << dbcr.fields.line_len; + if (!dc_line_sz) + panic("Data cache exists but line length is 0\n"); + } +} + +void cache_init(void) +{ + read_decode_cache_bcr(); + + if (is_isa_arcv2()) + read_decode_cache_bcr_arcv2(); + + if (is_isa_arcv2() && ioc_enabled()) + arc_ioc_setup(); + + if (is_isa_arcv2() && slc_exists()) + slc_upper_region_init(); +} + +int icache_status(void) +{ + return icache_enabled(); +} + +void icache_enable(void) +{ + if (icache_exists()) + write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) & + ~IC_CTRL_CACHE_DISABLE); +} + +void icache_disable(void) +{ + if (!icache_exists()) + return; + + __ic_entire_invalidate(); + + write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) | + IC_CTRL_CACHE_DISABLE); +} + +/* IC supports only invalidation */ +static inlined_cachefunc void __ic_entire_invalidate(void) +{ + if (!icache_enabled()) + return; + + /* Any write to IC_IVIC register triggers invalidation of entire I$ */ + write_aux_reg(ARC_AUX_IC_IVIC, 1); + /* + * As per ARC HS databook (see chapter 5.3.3.2) + * it is required to add 3 NOPs after each write to IC_IVIC. + */ + __builtin_arc_nop(); + __builtin_arc_nop(); + __builtin_arc_nop(); + read_aux_reg(ARC_AUX_IC_CTRL); /* blocks */ +} + +void invalidate_icache_all(void) +{ + __ic_entire_invalidate(); + + /* + * If SL$ is bypassed for data it is used only for instructions, + * so we need to invalidate it too. + */ + if (is_isa_arcv2() && slc_data_bypass()) + __slc_entire_op(OP_INV); +} + +int dcache_status(void) +{ + return dcache_enabled(); +} + +void dcache_enable(void) +{ + if (!dcache_exists()) + return; + + write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) & + ~(DC_CTRL_INV_MODE_FLUSH | DC_CTRL_CACHE_DISABLE)); +} + +void dcache_disable(void) +{ + if (!dcache_exists()) + return; + + __dc_entire_op(OP_FLUSH_N_INV); + + /* + * As SLC will be bypassed for data after L1 D$ disable we need to + * flush it first before L1 D$ disable. Also we invalidate SLC to + * avoid any inconsistent data problems after enabling L1 D$ again with + * dcache_enable function. + */ + if (is_isa_arcv2()) + __slc_entire_op(OP_FLUSH_N_INV); + + write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) | + DC_CTRL_CACHE_DISABLE); +} + +/* Common Helper for Line Operations on D-cache */ +static inline void __dcache_line_loop(unsigned long paddr, unsigned long sz, + const int cacheop) +{ + unsigned int aux_cmd; + int num_lines; + + /* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */ + aux_cmd = cacheop & OP_INV ? ARC_AUX_DC_IVDL : ARC_AUX_DC_FLDL; + + sz += paddr & ~CACHE_LINE_MASK; + paddr &= CACHE_LINE_MASK; + + num_lines = DIV_ROUND_UP(sz, gd->arch.l1_line_sz); + + while (num_lines-- > 0) { +#if (CONFIG_ARC_MMU_VER == 3) + write_aux_reg(ARC_AUX_DC_PTAG, paddr); +#endif + write_aux_reg(aux_cmd, paddr); + paddr += gd->arch.l1_line_sz; + } +} + +static inlined_cachefunc void __before_dc_op(const int op) +{ + unsigned int ctrl; + + ctrl = read_aux_reg(ARC_AUX_DC_CTRL); + + /* IM bit implies flush-n-inv, instead of vanilla inv */ + if (op == OP_INV) + ctrl &= ~DC_CTRL_INV_MODE_FLUSH; + else + ctrl |= DC_CTRL_INV_MODE_FLUSH; + + write_aux_reg(ARC_AUX_DC_CTRL, ctrl); +} + +static inlined_cachefunc void __after_dc_op(const int op) +{ + if (op & OP_FLUSH) /* flush / flush-n-inv both wait */ + while (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS); +} + +static inlined_cachefunc void __dc_entire_op(const int cacheop) +{ + int aux; + + if (!dcache_enabled()) + return; + + __before_dc_op(cacheop); + + if (cacheop & OP_INV) /* Inv or flush-n-inv use same cmd reg */ + aux = ARC_AUX_DC_IVDC; + else + aux = ARC_AUX_DC_FLSH; + + write_aux_reg(aux, 0x1); + + __after_dc_op(cacheop); +} + +static inline void __dc_line_op(unsigned long paddr, unsigned long sz, + const int cacheop) +{ + if (!dcache_enabled()) + return; + + __before_dc_op(cacheop); + __dcache_line_loop(paddr, sz, cacheop); + __after_dc_op(cacheop); +} + +void invalidate_dcache_range(unsigned long start, unsigned long end) +{ + if (start >= end) + return; + + /* + * ARCv1 -> call __dc_line_op + * ARCv2 && L1 D$ disabled -> nothing + * ARCv2 && L1 D$ enabled && IOC enabled -> nothing + * ARCv2 && L1 D$ enabled && no IOC -> call __dc_line_op; call __slc_rgn_op + */ + if (!is_isa_arcv2() || !ioc_enabled()) + __dc_line_op(start, end - start, OP_INV); + + if (is_isa_arcv2() && !ioc_enabled() && !slc_data_bypass()) + __slc_rgn_op(start, end - start, OP_INV); +} + +void flush_dcache_range(unsigned long start, unsigned long end) +{ + if (start >= end) + return; + + /* + * ARCv1 -> call __dc_line_op + * ARCv2 && L1 D$ disabled -> nothing + * ARCv2 && L1 D$ enabled && IOC enabled -> nothing + * ARCv2 && L1 D$ enabled && no IOC -> call __dc_line_op; call __slc_rgn_op + */ + if (!is_isa_arcv2() || !ioc_enabled()) + __dc_line_op(start, end - start, OP_FLUSH); + + if (is_isa_arcv2() && !ioc_enabled() && !slc_data_bypass()) + __slc_rgn_op(start, end - start, OP_FLUSH); +} + +void flush_cache(unsigned long start, unsigned long size) +{ + flush_dcache_range(start, start + size); +} + +/* + * As invalidate_dcache_all() is not used in generic U-Boot code and as we + * don't need it in arch/arc code alone (invalidate without flush) we implement + * flush_n_invalidate_dcache_all (flush and invalidate in 1 operation) because + * it's much safer. See [ NOTE 1 ] for more details. + */ +void flush_n_invalidate_dcache_all(void) +{ + __dc_entire_op(OP_FLUSH_N_INV); + + if (is_isa_arcv2() && !slc_data_bypass()) + __slc_entire_op(OP_FLUSH_N_INV); +} + +void flush_dcache_all(void) +{ + __dc_entire_op(OP_FLUSH); + + if (is_isa_arcv2() && !slc_data_bypass()) + __slc_entire_op(OP_FLUSH); +} + +/* + * This is function to cleanup all caches (and therefore sync I/D caches) which + * can be used for cleanup before linux launch or to sync caches during + * relocation. + */ +void sync_n_cleanup_cache_all(void) +{ + __dc_entire_op(OP_FLUSH_N_INV); + + /* + * If SL$ is bypassed for data it is used only for instructions, + * and we shouldn't flush it. So invalidate it instead of flush_n_inv. + */ + if (is_isa_arcv2()) { + if (slc_data_bypass()) + __slc_entire_op(OP_INV); + else + __slc_entire_op(OP_FLUSH_N_INV); + } + + __ic_entire_invalidate(); +} diff --git a/roms/u-boot/arch/arc/lib/cpu.c b/roms/u-boot/arch/arc/lib/cpu.c new file mode 100644 index 000000000..07f57878e --- /dev/null +++ b/roms/u-boot/arch/arc/lib/cpu.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013-2014, 2018 Synopsys, Inc. All rights reserved. + */ + +#include <common.h> +#include <init.h> +#include <malloc.h> +#include <vsprintf.h> +#include <asm/arcregs.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <linux/bitops.h> + +DECLARE_GLOBAL_DATA_PTR; + +int arch_cpu_init(void) +{ + timer_init(); + + gd->cpu_clk = CONFIG_SYS_CLK_FREQ; + gd->ram_size = CONFIG_SYS_SDRAM_SIZE; + + cache_init(); + + return 0; +} + +/* This is a dummy function on arc */ +int dram_init(void) +{ + return 0; +} + +#ifdef CONFIG_DISPLAY_CPUINFO +const char *arc_700_version(int arcver, char *name, int name_len) +{ + const char *arc_ver; + + switch (arcver) { + case 0x32: + arc_ver = "v4.4-4.5"; + break; + case 0x33: + arc_ver = "v4.6-v4.9"; + break; + case 0x34: + arc_ver = "v4.10"; + break; + case 0x35: + arc_ver = "v4.11"; + break; + default: + arc_ver = "unknown version"; + } + + snprintf(name, name_len, "ARC 700 %s", arc_ver); + + return name; +} + +struct em_template_t { + const bool cache; + const bool dsp; + const bool xymem; + const char name[8]; +}; + +static const struct em_template_t em_versions[] = { + {false, false, false, "EM4"}, + {true, false, false, "EM6"}, + {false, true, false, "EM5D"}, + {true, true, false, "EM7D"}, + {false, true, true, "EM9D"}, + {true, true, true, "EM11D"}, +}; + +const char *arc_em_version(int arcver, char *name, int name_len) +{ + const char *arc_name = "EM"; + const char *arc_ver; + bool cache = ARC_FEATURE_EXISTS(ARC_BCR_IC_BUILD); + bool dsp = ARC_FEATURE_EXISTS(ARC_AUX_DSP_BUILD); + bool xymem = ARC_FEATURE_EXISTS(ARC_AUX_XY_BUILD); + int i; + + for (i = 0; i < sizeof(em_versions) / sizeof(struct em_template_t); i++) { + if (em_versions[i].cache == cache && + em_versions[i].dsp == dsp && + em_versions[i].xymem == xymem) { + arc_name = em_versions[i].name; + break; + } + } + + switch (arcver) { + case 0x41: + arc_ver = "v1.1a"; + break; + case 0x42: + arc_ver = "v3.0"; + break; + case 0x43: + arc_ver = "v4.0"; + break; + case 0x44: + arc_ver = "v5.0"; + break; + default: + arc_ver = "unknown version"; + } + + snprintf(name, name_len, "ARC %s %s", arc_name, arc_ver); + + return name; +} + +struct hs_template_t { + const bool cache; + const bool mmu; + const bool dual_issue; + const bool dsp; + const char name[8]; +}; + +static const struct hs_template_t hs_versions[] = { + {false, false, false, false, "HS34"}, + {true, false, false, false, "HS36"}, + {true, true, false, false, "HS38"}, + {false, false, true, false, "HS44"}, + {true, false, true, false, "HS46"}, + {true, true, true, false, "HS48"}, + {false, false, true, true, "HS45D"}, + {true, false, true, true, "HS47D"}, +}; + +const char *arc_hs_version(int arcver, char *name, int name_len) +{ + const char *arc_name = "HS"; + const char *arc_ver; + bool cache = ARC_FEATURE_EXISTS(ARC_BCR_IC_BUILD); + bool dsp = ARC_FEATURE_EXISTS(ARC_AUX_DSP_BUILD); + bool mmu = !!read_aux_reg(ARC_AUX_MMU_BCR); + bool dual_issue = arcver == 0x54 ? true : false; + int i; + + for (i = 0; i < sizeof(hs_versions) / sizeof(struct hs_template_t); i++) { + if (hs_versions[i].cache == cache && + hs_versions[i].mmu == mmu && + hs_versions[i].dual_issue == dual_issue && + hs_versions[i].dsp == dsp) { + arc_name = hs_versions[i].name; + break; + } + } + + switch (arcver) { + case 0x50: + arc_ver = "v1.0"; + break; + case 0x51: + arc_ver = "v2.0"; + break; + case 0x52: + arc_ver = "v2.1c"; + break; + case 0x53: + arc_ver = "v3.0"; + break; + case 0x54: + arc_ver = "v4.0"; + break; + default: + arc_ver = "unknown version"; + } + + snprintf(name, name_len, "ARC %s %s", arc_name, arc_ver); + + return name; +} + +const char *decode_identity(void) +{ +#define MAX_CPU_NAME_LEN 64 + + int arcver = read_aux_reg(ARC_AUX_IDENTITY) & 0xff; + char *name = malloc(MAX_CPU_NAME_LEN); + + if (arcver >= 0x50) + return arc_hs_version(arcver, name, MAX_CPU_NAME_LEN); + else if (arcver >= 0x40) + return arc_em_version(arcver, name, MAX_CPU_NAME_LEN); + else if (arcver >= 0x30) + return arc_700_version(arcver, name, MAX_CPU_NAME_LEN); + else + return "Unknown ARC core"; +} + +const char *decode_subsystem(void) +{ + int subsys_type = read_aux_reg(ARC_AUX_SUBSYS_BUILD) & GENMASK(3, 0); + + switch (subsys_type) { + case 0: return NULL; + case 2: return "ARC Sensor & Control IP Subsystem"; + case 3: return "ARC Data Fusion IP Subsystem"; + case 4: return "ARC Secure Subsystem"; + default: return "Unknown subsystem"; + }; +} + +__weak int print_cpuinfo(void) +{ + const char *subsys_name = decode_subsystem(); + char mhz[8]; + + printf("CPU: %s at %s MHz\n", decode_identity(), + strmhz(mhz, gd->cpu_clk)); + + if (subsys_name) + printf("Subsys:%s\n", subsys_name); + + return 0; +} +#endif /* CONFIG_DISPLAY_CPUINFO */ diff --git a/roms/u-boot/arch/arc/lib/init_helpers.c b/roms/u-boot/arch/arc/lib/init_helpers.c new file mode 100644 index 000000000..023eae190 --- /dev/null +++ b/roms/u-boot/arch/arc/lib/init_helpers.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013-2015 Synopsys, Inc. All rights reserved. + */ + +#include <init.h> +#include <asm/cache.h> +#include <common.h> + +int init_cache_f_r(void) +{ + sync_n_cleanup_cache_all(); + + return 0; +} diff --git a/roms/u-boot/arch/arc/lib/interrupts.c b/roms/u-boot/arch/arc/lib/interrupts.c new file mode 100644 index 000000000..db21fbb11 --- /dev/null +++ b/roms/u-boot/arch/arc/lib/interrupts.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. + */ + +#include <common.h> +#include <irq_func.h> +#include <asm/arcregs.h> +#include <asm/ptrace.h> + +/* Bit values in STATUS32 */ +#define E1_MASK (1 << 1) /* Level 1 interrupts enable */ +#define E2_MASK (1 << 2) /* Level 2 interrupts enable */ + +int interrupt_init(void) +{ + return 0; +} + +/* + * returns true if interrupts had been enabled before we disabled them + */ +int disable_interrupts(void) +{ + int status = read_aux_reg(ARC_AUX_STATUS32); + int state = (status & (E1_MASK | E2_MASK)) ? 1 : 0; + + status &= ~(E1_MASK | E2_MASK); + /* STATUS32 register is updated indirectly with "FLAG" instruction */ + __asm__("flag %0" : : "r" (status)); + return state; +} + +void enable_interrupts(void) +{ + unsigned int status = read_aux_reg(ARC_AUX_STATUS32); + + status |= E1_MASK | E2_MASK; + /* STATUS32 register is updated indirectly with "FLAG" instruction */ + __asm__("flag %0" : : "r" (status)); +} + +static void print_reg_file(long *reg_rev, int start_num) +{ + unsigned int i; + + /* Print 3 registers per line */ + for (i = start_num; i < start_num + 25; i++) { + printf("r%02u: 0x%08lx\t", i, (unsigned long)*reg_rev); + if (((i + 1) % 3) == 0) + printf("\n"); + + /* Because pt_regs has registers reversed */ + reg_rev--; + } + + /* Add new-line if none was inserted in the end of loop above */ + if (((i + 1) % 3) != 0) + printf("\n"); +} + +void show_regs(struct pt_regs *regs) +{ + printf("ECR:\t0x%08lx\n", regs->ecr); + printf("RET:\t0x%08lx\nBLINK:\t0x%08lx\nSTAT32:\t0x%08lx\n", + regs->ret, regs->blink, regs->status32); + printf("GP: 0x%08lx\t r25: 0x%08lx\t\n", regs->r26, regs->r25); + printf("BTA: 0x%08lx\t SP: 0x%08lx\t FP: 0x%08lx\n", regs->bta, + regs->sp, regs->fp); + printf("LPS: 0x%08lx\tLPE: 0x%08lx\tLPC: 0x%08lx\n", regs->lp_start, + regs->lp_end, regs->lp_count); + + print_reg_file(&(regs->r0), 0); +} + +void bad_mode(struct pt_regs *regs) +{ + if (regs) + show_regs(regs); + + panic("Resetting CPU ...\n"); +} + +void do_memory_error(unsigned long address, struct pt_regs *regs) +{ + printf("Memory error exception @ 0x%lx\n", address); + bad_mode(regs); +} + +void do_instruction_error(unsigned long address, struct pt_regs *regs) +{ + printf("Instruction error exception @ 0x%lx\n", address); + bad_mode(regs); +} + +void do_machine_check_fault(unsigned long address, struct pt_regs *regs) +{ + printf("Machine check exception @ 0x%lx\n", address); + bad_mode(regs); +} + +void do_interrupt_handler(void) +{ + printf("Interrupt fired\n"); + bad_mode(0); +} + +void do_itlb_miss(struct pt_regs *regs) +{ + printf("I TLB miss exception\n"); + bad_mode(regs); +} + +void do_dtlb_miss(struct pt_regs *regs) +{ + printf("D TLB miss exception\n"); + bad_mode(regs); +} + +void do_tlb_prot_violation(unsigned long address, struct pt_regs *regs) +{ + printf("TLB protection violation or misaligned access @ 0x%lx\n", + address); + bad_mode(regs); +} + +void do_privilege_violation(struct pt_regs *regs) +{ + printf("Privilege violation exception\n"); + bad_mode(regs); +} + +void do_trap(struct pt_regs *regs) +{ + printf("Trap exception\n"); + bad_mode(regs); +} + +void do_extension(struct pt_regs *regs) +{ + printf("Extension instruction exception\n"); + bad_mode(regs); +} + +#ifdef CONFIG_ISA_ARCV2 +void do_swi(struct pt_regs *regs) +{ + printf("Software Interrupt exception\n"); + bad_mode(regs); +} + +void do_divzero(unsigned long address, struct pt_regs *regs) +{ + printf("Division by zero exception @ 0x%lx\n", address); + bad_mode(regs); +} + +void do_dcerror(struct pt_regs *regs) +{ + printf("Data cache consistency error exception\n"); + bad_mode(regs); +} + +void do_maligned(unsigned long address, struct pt_regs *regs) +{ + printf("Misaligned data access exception @ 0x%lx\n", address); + bad_mode(regs); +} +#endif diff --git a/roms/u-boot/arch/arc/lib/ints_low.S b/roms/u-boot/arch/arc/lib/ints_low.S new file mode 100644 index 000000000..38c45c60a --- /dev/null +++ b/roms/u-boot/arch/arc/lib/ints_low.S @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2013-2015 Synopsys, Inc. All rights reserved. + */ + +#include <linux/linkage.h> + +/* + * Note on the LD/ST addressing modes with address register write-back + * + * LD.a same as LD.aw + * + * LD.a reg1, [reg2, x] => Pre Incr + * Eff Addr for load = [reg2 + x] + * + * LD.ab reg1, [reg2, x] => Post Incr + * Eff Addr for load = [reg2] + */ + +.macro PUSH reg + st.a \reg, [%sp, -4] +.endm + +.macro PUSHAX aux + lr %r9, [\aux] + PUSH %r9 +.endm + +.macro SAVE_R1_TO_R24 + PUSH %r1 + PUSH %r2 + PUSH %r3 + PUSH %r4 + PUSH %r5 + PUSH %r6 + PUSH %r7 + PUSH %r8 + PUSH %r9 + PUSH %r10 + PUSH %r11 + PUSH %r12 + PUSH %r13 + PUSH %r14 + PUSH %r15 + PUSH %r16 + PUSH %r17 + PUSH %r18 + PUSH %r19 + PUSH %r20 + PUSH %r21 + PUSH %r22 + PUSH %r23 + PUSH %r24 +.endm + +.macro SAVE_ALL_SYS + /* saving %r0 to reg->r0 in advance since we read %ecr into it */ + st %r0, [%sp, -8] + lr %r0, [%ecr] /* all stack addressing is manual so far */ + st %r0, [%sp] + st %sp, [%sp, -4] + /* now move %sp to reg->r0 position so we can do "push" automatically */ + sub %sp, %sp, 8 + + SAVE_R1_TO_R24 + PUSH %r25 + PUSH %gp + PUSH %fp + PUSH %blink + PUSHAX %eret + PUSHAX %erstatus + PUSH %lp_count + PUSHAX %lp_end + PUSHAX %lp_start + PUSHAX %erbta +.endm + +.macro SAVE_EXCEPTION_SOURCE +#ifdef CONFIG_MMU + /* If MMU exists exception faulting address is loaded in EFA reg */ + lr %r0, [%efa] +#else + /* Otherwise in ERET (exception return) reg */ + lr %r0, [%eret] +#endif +.endm + +ENTRY(memory_error) + SAVE_ALL_SYS + SAVE_EXCEPTION_SOURCE + mov %r1, %sp + j do_memory_error +ENDPROC(memory_error) + +ENTRY(instruction_error) + SAVE_ALL_SYS + SAVE_EXCEPTION_SOURCE + mov %r1, %sp + j do_instruction_error +ENDPROC(instruction_error) + +ENTRY(interrupt_handler) + /* Todo - save and restore CPU context when interrupts will be in use */ + bl do_interrupt_handler + rtie +ENDPROC(interrupt_handler) + +ENTRY(EV_MachineCheck) + SAVE_ALL_SYS + SAVE_EXCEPTION_SOURCE + mov %r1, %sp + j do_machine_check_fault +ENDPROC(EV_MachineCheck) + +ENTRY(EV_TLBMissI) + SAVE_ALL_SYS + mov %r0, %sp + j do_itlb_miss +ENDPROC(EV_TLBMissI) + +ENTRY(EV_TLBMissD) + SAVE_ALL_SYS + mov %r0, %sp + j do_dtlb_miss +ENDPROC(EV_TLBMissD) + +ENTRY(EV_TLBProtV) + SAVE_ALL_SYS + SAVE_EXCEPTION_SOURCE + mov %r1, %sp + j do_tlb_prot_violation +ENDPROC(EV_TLBProtV) + +ENTRY(EV_PrivilegeV) + SAVE_ALL_SYS + mov %r0, %sp + j do_privilege_violation +ENDPROC(EV_PrivilegeV) + +ENTRY(EV_Trap) + SAVE_ALL_SYS + mov %r0, %sp + j do_trap +ENDPROC(EV_Trap) + +ENTRY(EV_Extension) + SAVE_ALL_SYS + mov %r0, %sp + j do_extension +ENDPROC(EV_Extension) + +#ifdef CONFIG_ISA_ARCV2 +ENTRY(EV_SWI) + SAVE_ALL_SYS + mov %r0, %sp + j do_swi +ENDPROC(EV_SWI) + +ENTRY(EV_DivZero) + SAVE_ALL_SYS + SAVE_EXCEPTION_SOURCE + mov %r1, %sp + j do_divzero +ENDPROC(EV_DivZero) + +ENTRY(EV_DCError) + SAVE_ALL_SYS + mov %r0, %sp + j do_dcerror +ENDPROC(EV_DCError) + +ENTRY(EV_Maligned) + SAVE_ALL_SYS + SAVE_EXCEPTION_SOURCE + mov %r1, %sp + j do_maligned +ENDPROC(EV_Maligned) +#endif diff --git a/roms/u-boot/arch/arc/lib/libgcc2.c b/roms/u-boot/arch/arc/lib/libgcc2.c new file mode 100644 index 000000000..ab1dbe1c1 --- /dev/null +++ b/roms/u-boot/arch/arc/lib/libgcc2.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 1989-2013 Free Software Foundation, Inc. + */ + +#include "libgcc2.h" + +DWtype +__ashldi3(DWtype u, shift_count_type b) +{ + if (b == 0) + return u; + + const DWunion uu = {.ll = u}; + const shift_count_type bm = W_TYPE_SIZE - b; + DWunion w; + + if (bm <= 0) { + w.s.low = 0; + w.s.high = (UWtype)uu.s.low << -bm; + } else { + const UWtype carries = (UWtype) uu.s.low >> bm; + + w.s.low = (UWtype)uu.s.low << b; + w.s.high = ((UWtype)uu.s.high << b) | carries; + } + + return w.ll; +} + +DWtype +__ashrdi3(DWtype u, shift_count_type b) +{ + if (b == 0) + return u; + + const DWunion uu = {.ll = u}; + const shift_count_type bm = W_TYPE_SIZE - b; + DWunion w; + + if (bm <= 0) { + /* w.s.high = 1..1 or 0..0 */ + w.s.high = uu.s.high >> (W_TYPE_SIZE - 1); + w.s.low = uu.s.high >> -bm; + } else { + const UWtype carries = (UWtype) uu.s.high << bm; + + w.s.high = uu.s.high >> b; + w.s.low = ((UWtype)uu.s.low >> b) | carries; + } + + return w.ll; +} + +DWtype +__lshrdi3(DWtype u, shift_count_type b) +{ + if (b == 0) + return u; + + const DWunion uu = {.ll = u}; + const shift_count_type bm = W_TYPE_SIZE - b; + DWunion w; + + if (bm <= 0) { + w.s.high = 0; + w.s.low = (UWtype)uu.s.high >> -bm; + } else { + const UWtype carries = (UWtype)uu.s.high << bm; + + w.s.high = (UWtype)uu.s.high >> b; + w.s.low = ((UWtype)uu.s.low >> b) | carries; + } + + return w.ll; +} + +unsigned long +udivmodsi4(unsigned long num, unsigned long den, int modwanted) +{ + unsigned long bit = 1; + unsigned long res = 0; + + while (den < num && bit && !(den & (1L<<31))) { + den <<= 1; + bit <<= 1; + } + + while (bit) { + if (num >= den) { + num -= den; + res |= bit; + } + bit >>= 1; + den >>= 1; + } + + if (modwanted) + return num; + + return res; +} + +long +__divsi3(long a, long b) +{ + int neg = 0; + long res; + + if (a < 0) { + a = -a; + neg = !neg; + } + + if (b < 0) { + b = -b; + neg = !neg; + } + + res = udivmodsi4(a, b, 0); + + if (neg) + res = -res; + + return res; +} + +long +__modsi3(long a, long b) +{ + int neg = 0; + long res; + + if (a < 0) { + a = -a; + neg = 1; + } + + if (b < 0) + b = -b; + + res = udivmodsi4(a, b, 1); + + if (neg) + res = -res; + + return res; +} + +long +__udivsi3(long a, long b) +{ + return udivmodsi4(a, b, 0); +} + +long +__umodsi3(long a, long b) +{ + return udivmodsi4(a, b, 1); +} + +UDWtype +__udivmoddi4(UDWtype n, UDWtype d, UDWtype *rp) +{ + UDWtype q = 0, r = n, y = d; + UWtype lz1, lz2, i, k; + + /* + * Implements align divisor shift dividend method. This algorithm + * aligns the divisor under the dividend and then perform number of + * test-subtract iterations which shift the dividend left. Number of + * iterations is k + 1 where k is the number of bit positions the + * divisor must be shifted left to align it under the dividend. + * quotient bits can be saved in the rightmost positions of the + * dividend as it shifts left on each test-subtract iteration. + */ + + if (y <= r) { + lz1 = __builtin_clzll(d); + lz2 = __builtin_clzll(n); + + k = lz1 - lz2; + y = (y << k); + + /* + * Dividend can exceed 2 ^ (width - 1) - 1 but still be less + * than the aligned divisor. Normal iteration can drops the + * high order bit of the dividend. Therefore, first + * test-subtract iteration is a special case, saving its + * quotient bit in a separate location and not shifting + * the dividend. + */ + + if (r >= y) { + r = r - y; + q = (1ULL << k); + } + + if (k > 0) { + y = y >> 1; + + /* + * k additional iterations where k regular test + * subtract shift dividend iterations are done. + */ + i = k; + do { + if (r >= y) + r = ((r - y) << 1) + 1; + else + r = (r << 1); + i = i - 1; + } while (i != 0); + + /* + * First quotient bit is combined with the quotient + * bits resulting from the k regular iterations. + */ + q = q + r; + r = r >> k; + q = q - (r << k); + } + } + + if (rp) + *rp = r; + + return q; +} + +UDWtype +__udivdi3(UDWtype n, UDWtype d) +{ + return __udivmoddi4(n, d, (UDWtype *)0); +} diff --git a/roms/u-boot/arch/arc/lib/libgcc2.h b/roms/u-boot/arch/arc/lib/libgcc2.h new file mode 100644 index 000000000..9c3ce9989 --- /dev/null +++ b/roms/u-boot/arch/arc/lib/libgcc2.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 1989-2013 Free Software Foundation, Inc. + */ + +#ifndef __ASM_LIBGCC_H +#define __ASM_LIBGCC_H + +#define UNITS_PER_WORD 4 /* for ARC */ +#define BITS_PER_UNIT 8 /* for ARC */ + +#define W_TYPE_SIZE (4 * BITS_PER_UNIT) + +#define MIN_UNITS_PER_WORD UNITS_PER_WORD + +/* Work out the largest "word" size that we can deal with on this target. */ +#if MIN_UNITS_PER_WORD > 4 +# define LIBGCC2_MAX_UNITS_PER_WORD 8 +#elif (MIN_UNITS_PER_WORD > 2 \ + || (MIN_UNITS_PER_WORD > 1 && __SIZEOF_LONG_LONG__ > 4)) +# define LIBGCC2_MAX_UNITS_PER_WORD 4 +#else +# define LIBGCC2_MAX_UNITS_PER_WORD MIN_UNITS_PER_WORD +#endif + +/* Work out what word size we are using for this compilation. + The value can be set on the command line. */ +#ifndef LIBGCC2_UNITS_PER_WORD +#define LIBGCC2_UNITS_PER_WORD LIBGCC2_MAX_UNITS_PER_WORD +#endif + +typedef int QItype __attribute__ ((mode (QI))); +typedef unsigned int UQItype __attribute__ ((mode (QI))); +typedef int HItype __attribute__ ((mode (HI))); +typedef unsigned int UHItype __attribute__ ((mode (HI))); +#if MIN_UNITS_PER_WORD > 1 +/* These typedefs are usually forbidden on dsp's with UNITS_PER_WORD 1. */ +typedef int SItype __attribute__ ((mode (SI))); +typedef unsigned int USItype __attribute__ ((mode (SI))); +#if __SIZEOF_LONG_LONG__ > 4 +/* These typedefs are usually forbidden on archs with UNITS_PER_WORD 2. */ +typedef int DItype __attribute__ ((mode (DI))); +typedef unsigned int UDItype __attribute__ ((mode (DI))); +#if MIN_UNITS_PER_WORD > 4 +/* These typedefs are usually forbidden on archs with UNITS_PER_WORD 4. */ +typedef int TItype __attribute__ ((mode (TI))); +typedef unsigned int UTItype __attribute__ ((mode (TI))); +#endif +#endif +#endif + +#if LIBGCC2_UNITS_PER_WORD == 8 +#define W_TYPE_SIZE (8 * BITS_PER_UNIT) +#define Wtype DItype +#define UWtype UDItype +#define HWtype DItype +#define UHWtype UDItype +#define DWtype TItype +#define UDWtype UTItype +#ifdef LIBGCC2_GNU_PREFIX +#define __NW(a,b) __gnu_ ## a ## di ## b +#define __NDW(a,b) __gnu_ ## a ## ti ## b +#else +#define __NW(a,b) __ ## a ## di ## b +#define __NDW(a,b) __ ## a ## ti ## b +#endif +#elif LIBGCC2_UNITS_PER_WORD == 4 +#define W_TYPE_SIZE (4 * BITS_PER_UNIT) +#define Wtype SItype +#define UWtype USItype +#define HWtype SItype +#define UHWtype USItype +#define DWtype DItype +#define UDWtype UDItype +#ifdef LIBGCC2_GNU_PREFIX +#define __NW(a,b) __gnu_ ## a ## si ## b +#define __NDW(a,b) __gnu_ ## a ## di ## b +#else +#define __NW(a,b) __ ## a ## si ## b +#define __NDW(a,b) __ ## a ## di ## b +#endif +#elif LIBGCC2_UNITS_PER_WORD == 2 +#define W_TYPE_SIZE (2 * BITS_PER_UNIT) +#define Wtype HItype +#define UWtype UHItype +#define HWtype HItype +#define UHWtype UHItype +#define DWtype SItype +#define UDWtype USItype +#ifdef LIBGCC2_GNU_PREFIX +#define __NW(a,b) __gnu_ ## a ## hi ## b +#define __NDW(a,b) __gnu_ ## a ## si ## b +#else +#define __NW(a,b) __ ## a ## hi ## b +#define __NDW(a,b) __ ## a ## si ## b +#endif +#else +#define W_TYPE_SIZE BITS_PER_UNIT +#define Wtype QItype +#define UWtype UQItype +#define HWtype QItype +#define UHWtype UQItype +#define DWtype HItype +#define UDWtype UHItype +#ifdef LIBGCC2_GNU_PREFIX +#define __NW(a,b) __gnu_ ## a ## qi ## b +#define __NDW(a,b) __gnu_ ## a ## hi ## b +#else +#define __NW(a,b) __ ## a ## qi ## b +#define __NDW(a,b) __ ## a ## hi ## b +#endif +#endif + +typedef int shift_count_type __attribute__((mode (__libgcc_shift_count__))); + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + struct DWstruct {Wtype high, low;}; +#else + struct DWstruct {Wtype low, high;}; +#endif + +/* We need this union to unpack/pack DImode values, since we don't have + any arithmetic yet. Incoming DImode parameters are stored into the + `ll' field, and the unpacked result is read from the struct `s'. */ + +typedef union { + struct DWstruct s; + DWtype ll; +} DWunion; + +#endif /* __ASM_LIBGCC_H */ diff --git a/roms/u-boot/arch/arc/lib/relocate.c b/roms/u-boot/arch/arc/lib/relocate.c new file mode 100644 index 000000000..7f531c95e --- /dev/null +++ b/roms/u-boot/arch/arc/lib/relocate.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. + */ + +#include <common.h> +#include <elf.h> +#include <log.h> +#include <asm-generic/sections.h> +#include <asm/global_data.h> + +extern ulong __image_copy_start; +extern ulong __ivt_start; +extern ulong __ivt_end; +extern ulong __text_end; + +DECLARE_GLOBAL_DATA_PTR; + +int copy_uboot_to_ram(void) +{ + size_t len = (size_t)&__image_copy_end - (size_t)&__image_copy_start; + + if (gd->flags & GD_FLG_SKIP_RELOC) + return 0; + + memcpy((void *)gd->relocaddr, (void *)&__image_copy_start, len); + + return 0; +} + +int clear_bss(void) +{ + ulong dst_addr = (ulong)&__bss_start + gd->reloc_off; + size_t len = (size_t)&__bss_end - (size_t)&__bss_start; + + memset((void *)dst_addr, 0x00, len); + + return 0; +} + +/* + * Base functionality is taken from x86 version with added ARC-specifics + */ +int do_elf_reloc_fixups(void) +{ + Elf32_Rela *re_src = (Elf32_Rela *)(&__rel_dyn_start); + Elf32_Rela *re_end = (Elf32_Rela *)(&__rel_dyn_end); + + if (gd->flags & GD_FLG_SKIP_RELOC) + return 0; + + debug("Section .rela.dyn is located at %08x-%08x\n", + (unsigned int)re_src, (unsigned int)re_end); + + Elf32_Addr *offset_ptr_rom; + Elf32_Addr *offset_ptr_ram; + + do { + /* Get the location from the relocation entry */ + offset_ptr_rom = (Elf32_Addr *)re_src->r_offset; + + /* Check that the location of the relocation is in .text */ + if (offset_ptr_rom >= (Elf32_Addr *)&__image_copy_start && + offset_ptr_rom < (Elf32_Addr *)&__image_copy_end) { + unsigned int val, do_swap = 0; + /* Switch to the in-RAM version */ + offset_ptr_ram = (Elf32_Addr *)((ulong)offset_ptr_rom + + gd->reloc_off); + +#ifdef __LITTLE_ENDIAN__ + /* If location in ".text" section swap value */ + if (((u32)offset_ptr_rom >= (u32)&__text_start && + (u32)offset_ptr_rom <= (u32)&__text_end) +#if defined(__ARC700__) || defined(__ARC600__) + || ((u32)offset_ptr_rom >= (u32)&__ivt_start && + (u32)offset_ptr_rom <= (u32)&__ivt_end) +#endif + ) + do_swap = 1; +#endif + + debug("Patching value @ %08x (relocated to %08x)%s\n", + (unsigned int)offset_ptr_rom, + (unsigned int)offset_ptr_ram, + do_swap ? ", middle-endian encoded" : ""); + + /* + * Use "memcpy" because target location might be + * 16-bit aligned on ARC so we may need to read + * byte-by-byte. On attempt to read entire word by + * CPU throws an exception + */ + memcpy(&val, offset_ptr_ram, sizeof(int)); + + if (do_swap) + val = (val << 16) | (val >> 16); + + /* Check that the target points into executable */ + if (val < (unsigned int)&__image_copy_start || + val > (unsigned int)&__image_copy_end) { + /* TODO: Use panic() instead of debug() + * + * For some reason GCC might generate + * fake relocation even for LD/SC of constant + * inderectly. See an example below: + * ----------------------->8-------------------- + * static int setup_mon_len(void) + * { + * gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE; + * return 0; + * } + * ----------------------->8-------------------- + * + * And that's what we get in the binary: + * ----------------------->8-------------------- + * 10005cb4 <setup_mon_len>: + * 10005cb4: 193c 3f80 0003 2f80 st 0x32f80,[r25,60] + * 10005cb8: R_ARC_RELATIVE *ABS*-0x10000000 + * 10005cbc: 7fe0 j_s.d [blink] + * 10005cbe: 700c mov_s r0,0 + * ----------------------->8-------------------- + */ + debug("Relocation target %08x points outside of image\n", + val); + } + + val += gd->reloc_off; + + if (do_swap) + val = (val << 16) | (val >> 16); + + memcpy(offset_ptr_ram, &val, sizeof(int)); + } + } while (++re_src < re_end); + + return 0; +} diff --git a/roms/u-boot/arch/arc/lib/reset.c b/roms/u-boot/arch/arc/lib/reset.c new file mode 100644 index 000000000..b8589d0f0 --- /dev/null +++ b/roms/u-boot/arch/arc/lib/reset.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. + */ + +#include <command.h> +#include <common.h> +#include <cpu_func.h> + +__weak void reset_cpu(void) +{ + /* Stop debug session here */ + __builtin_arc_brk(); +} + +int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + printf("Resetting the board...\n"); + + reset_cpu(); + + return 0; +} diff --git a/roms/u-boot/arch/arc/lib/start.S b/roms/u-boot/arch/arc/lib/start.S new file mode 100644 index 000000000..016ae85be --- /dev/null +++ b/roms/u-boot/arch/arc/lib/start.S @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. + */ + +#include <asm-offsets.h> +#include <config.h> +#include <linux/linkage.h> +#include <asm/arcregs.h> + +ENTRY(_start) + /* Setup interrupt vector base that matches "__text_start" */ + sr __ivt_start, [ARC_AUX_INTR_VEC_BASE] + + ; Disable/enable I-cache according to configuration + lr r5, [ARC_BCR_IC_BUILD] + breq r5, 0, 1f ; I$ doesn't exist + lr r5, [ARC_AUX_IC_CTRL] +#if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF) + bclr r5, r5, 0 ; 0 - Enable, 1 is Disable +#else + bset r5, r5, 0 ; I$ exists, but is not used +#endif + sr r5, [ARC_AUX_IC_CTRL] + + mov r5, 1 + sr r5, [ARC_AUX_IC_IVIC] + ; As per ARC HS databook (see chapter 5.3.3.2) + ; it is required to add 3 NOPs after each write to IC_IVIC. + nop + nop + nop + +1: + ; Disable/enable D-cache according to configuration + lr r5, [ARC_BCR_DC_BUILD] + breq r5, 0, 1f ; D$ doesn't exist + lr r5, [ARC_AUX_DC_CTRL] + bclr r5, r5, 6 ; Invalidate (discard w/o wback) +#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) + bclr r5, r5, 0 ; Enable (+Inv) +#else + bset r5, r5, 0 ; Disable (+Inv) +#endif + sr r5, [ARC_AUX_DC_CTRL] + + mov r5, 1 + sr r5, [ARC_AUX_DC_IVDC] + + +1: +#ifdef CONFIG_ISA_ARCV2 + ; Disable System-Level Cache (SLC) + lr r5, [ARC_BCR_SLC] + breq r5, 0, 1f ; SLC doesn't exist + lr r5, [ARC_AUX_SLC_CTRL] + bclr r5, r5, 6 ; Invalidate (discard w/o wback) + bclr r5, r5, 0 ; Enable (+Inv) + sr r5, [ARC_AUX_SLC_CTRL] + +1: +#endif + +#ifdef CONFIG_ISA_ARCV2 + ; In case of DSP extension presence in HW some instructions + ; (related to integer multiply, multiply-accumulate, and divide + ; operation) executes on this DSP execution unit. So their + ; execution will depend on dsp configuration register (DSP_CTRL) + ; As we want these instructions to execute the same way regardless + ; of DSP presence we need to set DSP_CTRL properly. + lr r5, [ARC_AUX_DSP_BUILD] + bmsk r5, r5, 7 + breq r5, 0, 1f + mov r5, 0 + sr r5, [ARC_AUX_DSP_CTRL] +1: +#endif + +#ifdef __ARC_UNALIGNED__ + /* + * Enable handling of unaligned access in the CPU as by default + * this HW feature is disabled while GCC starting from 8.1.0 + * unconditionally uses it for ARC HS cores. + */ + flag 1 << STATUS_AD_BIT +#endif + + /* Establish C runtime stack and frame */ + mov %sp, CONFIG_SYS_INIT_SP_ADDR + mov %fp, %sp + + /* Allocate reserved area from current top of stack */ + mov %r0, %sp + bl board_init_f_alloc_reserve + /* Set stack below reserved area, adjust frame pointer accordingly */ + mov %sp, %r0 + mov %fp, %sp + + /* Initialize reserved area - note: r0 already contains address */ + bl board_init_f_init_reserve + +#ifdef CONFIG_DEBUG_UART + /* Earliest point to set up early debug uart */ + bl debug_uart_init +#endif + + /* Zero the one and only argument of "board_init_f" */ + mov_s %r0, 0 + bl board_init_f + + /* We only get here if relocation is disabled by GD_FLG_SKIP_RELOC */ + /* Make sure we don't lose GD overwritten by zero new GD */ + mov %r0, %r25 + mov %r1, 0 + bl board_init_r +ENDPROC(_start) + +/* + * void board_init_f_r_trampoline(stack-pointer address) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * r0 = new stack-pointer + */ +ENTRY(board_init_f_r_trampoline) + /* Set up the stack- and frame-pointers */ + mov %sp, %r0 + mov %fp, %sp + + /* Update position of intterupt vector table */ + lr %r0, [ARC_AUX_INTR_VEC_BASE] + ld %r1, [%r25, GD_RELOC_OFF] + add %r0, %r0, %r1 + sr %r0, [ARC_AUX_INTR_VEC_BASE] + + /* Re-enter U-Boot by calling board_init_f_r */ + j board_init_f_r +ENDPROC(board_init_f_r_trampoline) |