diff options
author | Chris Zankel <chris@zankel.net> | 2016-08-10 18:36:44 +0300 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2016-08-15 18:46:38 -0400 |
commit | c978b52410016b0ab5a213f235596340af8d45f7 (patch) | |
tree | b01e9a8ea9a92fe962a545974339677b87dcc1ba /arch/xtensa/lib | |
parent | de5e5cea022ab44006ff1edf45a39f0943fb9dff (diff) | |
download | u-boot-c978b52410016b0ab5a213f235596340af8d45f7.tar.gz |
xtensa: add support for the xtensa processor architecture [2/2]
The Xtensa processor architecture is a configurable, extensible,
and synthesizable 32-bit RISC processor core provided by Tensilica, inc.
This is the second part of the basic architecture port, adding the
'arch/xtensa' directory and a readme file.
Signed-off-by: Chris Zankel <chris@zankel.net>
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Tom Rini <trini@konsulko.com>
Diffstat (limited to 'arch/xtensa/lib')
-rw-r--r-- | arch/xtensa/lib/Makefile | 10 | ||||
-rw-r--r-- | arch/xtensa/lib/bootm.c | 197 | ||||
-rw-r--r-- | arch/xtensa/lib/cache.c | 60 | ||||
-rw-r--r-- | arch/xtensa/lib/misc.S | 179 | ||||
-rw-r--r-- | arch/xtensa/lib/relocate.c | 18 | ||||
-rw-r--r-- | arch/xtensa/lib/time.c | 121 |
6 files changed, 585 insertions, 0 deletions
diff --git a/arch/xtensa/lib/Makefile b/arch/xtensa/lib/Makefile new file mode 100644 index 0000000000..7c7d8d59bc --- /dev/null +++ b/arch/xtensa/lib/Makefile @@ -0,0 +1,10 @@ +# +# (C) Copyright 2007 - 2013 Tensilica Inc. +# (C) Copyright 2014 - 2016 Cadence Design Systems Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_CMD_BOOTM) += bootm.o + +obj-y += cache.o misc.o relocate.o time.o diff --git a/arch/xtensa/lib/bootm.c b/arch/xtensa/lib/bootm.c new file mode 100644 index 0000000000..1604bb9536 --- /dev/null +++ b/arch/xtensa/lib/bootm.c @@ -0,0 +1,197 @@ +/* + * (C) Copyright 2008 - 2013 Tensilica Inc. + * (C) Copyright 2014 Cadence Design Systems Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <u-boot/zlib.h> +#include <asm/byteorder.h> +#include <asm/addrspace.h> +#include <asm/bootparam.h> +#include <asm/cache.h> +#include <image.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Setup boot-parameters. + */ + +static struct bp_tag *setup_first_tag(struct bp_tag *params) +{ + params->id = BP_TAG_FIRST; + params->size = sizeof(long); + *(unsigned long *)¶ms->data = BP_VERSION; + + return bp_tag_next(params); +} + +static struct bp_tag *setup_last_tag(struct bp_tag *params) +{ + params->id = BP_TAG_LAST; + params->size = 0; + + return bp_tag_next(params); +} + +static struct bp_tag *setup_memory_tag(struct bp_tag *params) +{ + struct bd_info *bd = gd->bd; + struct meminfo *mem; + + params->id = BP_TAG_MEMORY; + params->size = sizeof(struct meminfo); + mem = (struct meminfo *)params->data; + mem->type = MEMORY_TYPE_CONVENTIONAL; + mem->start = bd->bi_memstart; + mem->end = bd->bi_memstart + bd->bi_memsize; + + printf(" MEMORY: tag:0x%04x, type:0X%lx, start:0X%lx, end:0X%lx\n", + BP_TAG_MEMORY, mem->type, mem->start, mem->end); + + return bp_tag_next(params); +} + +static struct bp_tag *setup_commandline_tag(struct bp_tag *params, + char *cmdline) +{ + int len; + + if (!cmdline) + return params; + + len = strlen(cmdline); + + params->id = BP_TAG_COMMAND_LINE; + params->size = (len + 3) & -4; + strcpy((char *)params->data, cmdline); + + printf(" COMMAND_LINE: tag:0x%04x, size:%u, data:'%s'\n", + BP_TAG_COMMAND_LINE, params->size, cmdline); + + return bp_tag_next(params); +} + +static struct bp_tag *setup_ramdisk_tag(struct bp_tag *params, + unsigned long rd_start, + unsigned long rd_end) +{ + struct meminfo *mem; + + if (rd_start == rd_end) + return params; + + /* Add a single banked memory */ + + params->id = BP_TAG_INITRD; + params->size = sizeof(struct meminfo); + + mem = (struct meminfo *)params->data; + mem->type = MEMORY_TYPE_CONVENTIONAL; + mem->start = PHYSADDR(rd_start); + mem->end = PHYSADDR(rd_end); + + printf(" INITRD: tag:0x%x, type:0X%04lx, start:0X%lx, end:0X%lx\n", + BP_TAG_INITRD, mem->type, mem->start, mem->end); + + return bp_tag_next(params); +} + +static struct bp_tag *setup_serial_tag(struct bp_tag *params) +{ + params->id = BP_TAG_SERIAL_BAUDRATE; + params->size = sizeof(unsigned long); + params->data[0] = gd->baudrate; + + printf(" SERIAL_BAUDRATE: tag:0x%04x, size:%u, baudrate:%lu\n", + BP_TAG_SERIAL_BAUDRATE, params->size, params->data[0]); + + return bp_tag_next(params); +} + +#ifdef CONFIG_OF_LIBFDT + +static struct bp_tag *setup_fdt_tag(struct bp_tag *params, void *fdt_start) +{ + params->id = BP_TAG_FDT; + params->size = sizeof(unsigned long); + params->data[0] = (unsigned long)fdt_start; + + printf(" FDT: tag:0x%04x, size:%u, start:0x%lx\n", + BP_TAG_FDT, params->size, params->data[0]); + + return bp_tag_next(params); +} + +#endif + +/* + * Boot Linux. + */ + +int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) +{ + struct bp_tag *params, *params_start; + ulong initrd_start, initrd_end; + char *commandline = getenv("bootargs"); + + if (!(flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO))) + return 0; + + show_boot_progress(15); + + if (images->rd_start) { + initrd_start = images->rd_start; + initrd_end = images->rd_end; + } else { + initrd_start = 0; + initrd_end = 0; + } + + params_start = (struct bp_tag *)gd->bd->bi_boot_params; + params = params_start; + params = setup_first_tag(params); + params = setup_memory_tag(params); + params = setup_commandline_tag(params, commandline); + params = setup_serial_tag(params); + + if (initrd_start) + params = setup_ramdisk_tag(params, initrd_start, initrd_end); + +#ifdef CONFIG_OF_LIBFDT + if (images->ft_addr) + params = setup_fdt_tag(params, images->ft_addr); +#endif + + printf("\n"); + + params = setup_last_tag(params); + + show_boot_progress(15); + + printf("Transferring Control to Linux @0x%08lx ...\n\n", + (ulong)images->ep); + + flush_dcache_range((unsigned long)params_start, (unsigned long)params); + + if (flag & BOOTM_STATE_OS_FAKE_GO) + return 0; + + /* + * _start() in vmlinux expects boot params in register a2. + * NOTE: + * Disable/delete your u-boot breakpoints before stepping into linux. + */ + asm volatile ("mov a2, %0\n\t" + "jx %1\n\t" + : : "a" (params_start), "a" (images->ep) + : "a2"); + + /* Does not return */ + + return 1; +} + diff --git a/arch/xtensa/lib/cache.c b/arch/xtensa/lib/cache.c new file mode 100644 index 0000000000..2680839092 --- /dev/null +++ b/arch/xtensa/lib/cache.c @@ -0,0 +1,60 @@ +/* + * (C) Copyright 2008 - 2013 Tensilica Inc. + * (C) Copyright 2014 - 2016 Cadence Design Systems Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/cache.h> + +/* + * We currently run always with caches enabled when running from memory. + * Xtensa version D or later will support changing cache behavior, so + * we could implement it if necessary. + */ + +int dcache_status(void) +{ + return 1; +} + +void dcache_enable(void) +{ +} + +void dcache_disable(void) +{ +} + +void flush_cache(ulong start_addr, ulong size) +{ + __flush_invalidate_dcache_range(start_addr, size); + __invalidate_icache_range(start_addr, size); +} + +void flush_dcache_all(void) +{ + __flush_dcache_all(); + __invalidate_icache_all(); +} + +void flush_dcache_range(ulong start_addr, ulong end_addr) +{ + __flush_invalidate_dcache_range(start_addr, end_addr - start_addr); +} + +void invalidate_dcache_range(ulong start, ulong stop) +{ + __invalidate_dcache_range(start, stop - start); +} + +void invalidate_dcache_all(void) +{ + __invalidate_dcache_all(); +} + +void invalidate_icache_all(void) +{ + __invalidate_icache_all(); +} diff --git a/arch/xtensa/lib/misc.S b/arch/xtensa/lib/misc.S new file mode 100644 index 0000000000..449a6db8fd --- /dev/null +++ b/arch/xtensa/lib/misc.S @@ -0,0 +1,179 @@ +/* + * Miscellaneous assembly functions. + * + * Copyright (C) 2001 - 2007 Tensilica Inc. + * Copyright (C) 2014 - 2016 Cadence Design Systems Inc. + * + * Chris Zankel <chris@zankel.net> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#include <linux/linkage.h> +#include <asm/asmmacro.h> +#include <asm/cacheasm.h> + +/* + * void __invalidate_icache_page(ulong start) + */ + +ENTRY(__invalidate_icache_page) + + abi_entry + + ___invalidate_icache_page a2 a3 + isync + + abi_ret + +ENDPROC(__invalidate_icache_page) + +/* + * void __invalidate_dcache_page(ulong start) + */ + +ENTRY(__invalidate_dcache_page) + + abi_entry + + ___invalidate_dcache_page a2 a3 + dsync + + abi_ret + +ENDPROC(__invalidate_dcache_page) + +/* + * void __flush_invalidate_dcache_page(ulong start) + */ + +ENTRY(__flush_invalidate_dcache_page) + + abi_entry + + ___flush_invalidate_dcache_page a2 a3 + + dsync + abi_ret + +ENDPROC(__flush_invalidate_dcache_page) + +/* + * void __flush_dcache_page(ulong start) + */ + +ENTRY(__flush_dcache_page) + + abi_entry + + ___flush_dcache_page a2 a3 + + dsync + abi_ret + +ENDPROC(__flush_dcache_page) + +/* + * void __invalidate_icache_range(ulong start, ulong size) + */ + +ENTRY(__invalidate_icache_range) + + abi_entry + + ___invalidate_icache_range a2 a3 a4 + isync + + abi_ret + +ENDPROC(__invalidate_icache_range) + +/* + * void __flush_invalidate_dcache_range(ulong start, ulong size) + */ + +ENTRY(__flush_invalidate_dcache_range) + + abi_entry + + ___flush_invalidate_dcache_range a2 a3 a4 + dsync + + abi_ret + +ENDPROC(__flush_invalidate_dcache_range) + +/* + * void _flush_dcache_range(ulong start, ulong size) + */ + +ENTRY(__flush_dcache_range) + + abi_entry + + ___flush_dcache_range a2 a3 a4 + dsync + + abi_ret + +ENDPROC(__flush_dcache_range) + +/* + * void _invalidate_dcache_range(ulong start, ulong size) + */ + +ENTRY(__invalidate_dcache_range) + + abi_entry + + ___invalidate_dcache_range a2 a3 a4 + + abi_ret + +ENDPROC(__invalidate_dcache_range) + +/* + * void _invalidate_icache_all(void) + */ + +ENTRY(__invalidate_icache_all) + + abi_entry + + ___invalidate_icache_all a2 a3 + isync + + abi_ret + +ENDPROC(__invalidate_icache_all) + +/* + * void _flush_invalidate_dcache_all(void) + */ + +ENTRY(__flush_invalidate_dcache_all) + + abi_entry + + ___flush_invalidate_dcache_all a2 a3 + dsync + + abi_ret + +ENDPROC(__flush_invalidate_dcache_all) + +/* + * void _invalidate_dcache_all(void) + */ + +ENTRY(__invalidate_dcache_all) + + abi_entry + + ___invalidate_dcache_all a2 a3 + dsync + + abi_ret + +ENDPROC(__invalidate_dcache_all) diff --git a/arch/xtensa/lib/relocate.c b/arch/xtensa/lib/relocate.c new file mode 100644 index 0000000000..3f747ec55a --- /dev/null +++ b/arch/xtensa/lib/relocate.c @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2016 Cadence Design Systems Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/relocate.h> +#include <asm/sections.h> +#include <asm/string.h> + +int clear_bss(void) +{ + size_t len = (size_t)&__bss_end - (size_t)&__bss_start; + + memset((void *)&__bss_start, 0x00, len); + return 0; +} + diff --git a/arch/xtensa/lib/time.c b/arch/xtensa/lib/time.c new file mode 100644 index 0000000000..1332072ffe --- /dev/null +++ b/arch/xtensa/lib/time.c @@ -0,0 +1,121 @@ +/* + * (C) Copyright 2008 - 2013 Tensilica Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/global_data.h> +#include <linux/stringify.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if XCHAL_HAVE_CCOUNT +static ulong get_ccount(void) +{ + ulong ccount; + asm volatile ("rsr %0,"__stringify(CCOUNT) : "=a" (ccount)); + return ccount; +} +#else +static ulong fake_ccount; +#define get_ccount() fake_ccount +#endif + +static void delay_cycles(unsigned cycles) +{ +#if XCHAL_HAVE_CCOUNT + unsigned expiry = get_ccount() + cycles; + while ((signed)(expiry - get_ccount()) > 0) + ; +#else +#warning "Without Xtensa timer option, timing will not be accurate." + + /* + * Approximate the cycle count by a loop iteration count. + * This is highly dependent on config and optimization. + */ + + volatile unsigned i; + for (i = cycles >> 4U; i > 0; --i) + ; + fake_ccount += cycles; +#endif +} + +/* + * Delay (busy-wait) for a number of microseconds. + */ + +void __udelay(unsigned long usec) +{ + ulong lo, hi, i; + ulong mhz = CONFIG_SYS_CLK_FREQ / 1000000; + + /* Scale to support full 32-bit usec range */ + + lo = usec & ((1<<22)-1); + hi = usec >> 22UL; + for (i = 0; i < hi; ++i) + delay_cycles(mhz << 22); + delay_cycles(mhz * lo); +} + + +/* + * Return the elapsed time (ticks) since 'base'. + */ + +ulong get_timer(ulong base) +{ + /* Don't tie up a timer; use cycle counter if available (or fake it) */ + +#if XCHAL_HAVE_CCOUNT + register ulong ccount; + __asm__ volatile ("rsr %0, CCOUNT" : "=a"(ccount)); + return ccount / (CONFIG_SYS_CLK_FREQ / CONFIG_SYS_HZ) - base; +#else + /* + * Add at least the overhead of this call (in cycles). + * Avoids hanging in case caller doesn't use udelay(). + * Note that functions that don't call udelay() (such as + * the "sleep" command) will not get a significant delay + * because there is no time reference. + */ + + fake_ccount += 20; + return fake_ccount / (CONFIG_SYS_CLK_FREQ / CONFIG_SYS_HZ) - base; +#endif +} + + +/* + * This function is derived from ARM/PowerPC code (read timebase as long long). + * On Xtensa it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from ARM/PowerPC code (timebase clock frequency). + * On Xtensa it returns the number of timer ticks per second. + */ +ulong get_tbclk(void) +{ + ulong tbclk; + + tbclk = CONFIG_SYS_HZ; + return tbclk; +} + +#if XCHAL_HAVE_CCOUNT +unsigned long timer_get_us(void) +{ + unsigned long ccount; + + __asm__ volatile ("rsr %0, CCOUNT" : "=a"(ccount)); + return ccount / (CONFIG_SYS_CLK_FREQ / 1000000); +} +#endif |