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>
diff --git a/arch/xtensa/lib/Makefile b/arch/xtensa/lib/Makefile
new file mode 100644
index 0000000..7c7d8d5
--- /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 0000000..1604bb9
--- /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 *)&params->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 0000000..2680839
--- /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 0000000..449a6db
--- /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 0000000..3f747ec
--- /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 0000000..1332072
--- /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