summaryrefslogtreecommitdiffstats
path: root/arch/kvx/lib
diff options
context:
space:
mode:
Diffstat (limited to 'arch/kvx/lib')
-rw-r--r--arch/kvx/lib/Makefile6
-rw-r--r--arch/kvx/lib/asm-offsets.c11
-rw-r--r--arch/kvx/lib/board.c158
-rw-r--r--arch/kvx/lib/bootm.c168
-rw-r--r--arch/kvx/lib/cache.c25
-rw-r--r--arch/kvx/lib/cpuinfo.c20
-rw-r--r--arch/kvx/lib/dma-default.c60
-rw-r--r--arch/kvx/lib/dtb.c14
-rw-r--r--arch/kvx/lib/poweroff.c44
-rw-r--r--arch/kvx/lib/setjmp.S85
10 files changed, 591 insertions, 0 deletions
diff --git a/arch/kvx/lib/Makefile b/arch/kvx/lib/Makefile
new file mode 100644
index 0000000000..c730e1c23f
--- /dev/null
+++ b/arch/kvx/lib/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Kalray Inc.
+#
+
+obj-y += cpuinfo.o board.o dtb.o poweroff.o bootm.o setjmp.o cache.o dma-default.o
diff --git a/arch/kvx/lib/asm-offsets.c b/arch/kvx/lib/asm-offsets.c
new file mode 100644
index 0000000000..9ab4fc4d4d
--- /dev/null
+++ b/arch/kvx/lib/asm-offsets.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Kalray Inc.
+ */
+
+#include <linux/kbuild.h>
+
+int main(void)
+{
+ return 0;
+}
diff --git a/arch/kvx/lib/board.c b/arch/kvx/lib/board.c
new file mode 100644
index 0000000000..78fa83e02b
--- /dev/null
+++ b/arch/kvx/lib/board.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Kalray Inc.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <memory.h>
+#include <asm-generic/memory_layout.h>
+
+static int find_memory_malloc(u32 na, u32 ns, u64 addr, u64 *membase,
+ u64 *memsize, const __be32 *reg)
+{
+ int i;
+ u64 memsize64 = 0, membase64 = 0;
+
+ for (i = 0; i < na; i++)
+ membase64 = (membase64 << 32) | fdt32_to_cpu(*reg++);
+
+ for (i = 0; i < ns; i++)
+ memsize64 = (memsize64 << 32) | fdt32_to_cpu(*reg++);
+
+ if (addr > membase64 && addr < (membase64 + memsize64)) {
+ *membase = membase64;
+ *memsize = memsize64;
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * of_find_mem - Find the first memory range (from fdt) in which an address is
+ * contained
+ * @fdt: fdt blob containing memory nodes
+ * @addr: Address to search in available memories
+ * @membase: Returned memory base address
+ * @memsize: Returned memory size
+ */
+static void of_find_mem(void *fdt, u64 addr, u64 *membase, u64 *memsize)
+{
+ const __be32 *nap, *nsp, *reg;
+ u32 na, ns, reg_size;
+ int node, size, i, ret;
+
+ /* Make sure FDT blob is sane */
+ if (fdt_check_header(fdt) != 0) {
+ pr_err("Invalid device tree blob\n");
+ goto err;
+ }
+
+ /* Find the #address-cells and #size-cells properties */
+ node = fdt_path_offset(fdt, "/");
+ if (node < 0) {
+ pr_err("Cannot find root node\n");
+ goto err;
+ }
+
+ nap = fdt_getprop(fdt, node, "#address-cells", &size);
+ if (!nap || (size != 4)) {
+ pr_err("Cannot find #address-cells property");
+ goto err;
+ }
+ na = fdt32_to_cpu(*nap);
+
+ nsp = fdt_getprop(fdt, node, "#size-cells", &size);
+ if (!nsp || (size != 4)) {
+ pr_err("Cannot find #size-cells property");
+ goto err;
+ }
+ ns = fdt32_to_cpu(*nap);
+
+ node = -1;
+ /* Iterate on the memory devices */
+ while (1) {
+ /* Find the memory node */
+ node = fdt_node_offset_by_prop_value(fdt, node, "device_type",
+ "memory",
+ sizeof("memory"));
+ if (node < 0) {
+ pr_err("Cannot find memory node\n");
+ goto err;
+ }
+
+ reg_size = na + ns * sizeof(u32);
+ reg = fdt_getprop(fdt, node, "reg", &size);
+ if (size < reg_size) {
+ pr_err("cannot get memory range\n");
+ goto err;
+ }
+
+ /* Iterate on reg content */
+ for (i = 0; i < size; i += reg_size) {
+ ret = find_memory_malloc(na, ns, addr, membase, memsize,
+ reg);
+ if (ret)
+ return;
+ reg += na + ns;
+ }
+ }
+err:
+ pr_err("No memory, cannot continue\n");
+ while (1);
+}
+
+/**
+ * exclude_dtb_from_alloc - Find best zone to allocate without overwriting dtb
+ *
+ * @fdt: fdt blob
+ * @alloc_start: start of allocation zone
+ * @alloc_end: end of allocation zone
+ */
+static void exclude_dtb_from_alloc(void *fdt, u64 *alloc_start, u64 *alloc_end)
+{
+ const struct fdt_header *fdth = fdt;
+ u64 fdt_start = (u64) fdt;
+ u64 fdt_end = fdt_start + be32_to_cpu(fdth->totalsize);
+ u64 start_size = 0, end_size = 0;
+
+ /*
+ * If the device tree is inside the malloc zone, we must exclude it to
+ * avoid allocating memory over it while unflattening it
+ */
+ if (fdt_end < *alloc_start || fdt_start > (*alloc_end))
+ return;
+
+ /* Compute the largest remaining chunk when removing the dtb */
+ if (fdt_start >= *alloc_start)
+ start_size = (fdt_start - *alloc_start);
+
+ if (fdt_end <= *alloc_end)
+ end_size = *alloc_end - fdt_end;
+
+ /* Modify start/end to reflect the maximum area we found */
+ if (start_size >= end_size)
+ *alloc_end = fdt_start;
+ else
+ *alloc_start = fdt_end;
+}
+
+void __noreturn kvx_start_barebox(void)
+{
+ u64 memsize = 0, membase = 0;
+ u64 barebox_text_end = (u64) &__end;
+ u64 alloc_start, alloc_end;
+
+ of_find_mem(boot_dtb, barebox_text_end, &membase, &memsize);
+
+ alloc_start = barebox_text_end;
+ alloc_end = (membase + memsize);
+ exclude_dtb_from_alloc(boot_dtb, &alloc_start, &alloc_end);
+
+ mem_malloc_init((void *) alloc_start, (void *) alloc_end);
+
+ start_barebox();
+
+ hang();
+}
diff --git a/arch/kvx/lib/bootm.c b/arch/kvx/lib/bootm.c
new file mode 100644
index 0000000000..4c77f676ec
--- /dev/null
+++ b/arch/kvx/lib/bootm.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2019 Kalray Inc.
+ */
+
+#include <elf.h>
+#include <boot.h>
+#include <init.h>
+#include <bootm.h>
+#include <binfmt.h>
+#include <common.h>
+#include <libfile.h>
+#include <linux/kernel.h>
+
+#include <asm/cache.h>
+
+typedef void __noreturn (*boot_func_entry)(unsigned long, void *);
+
+static int do_boot_entry(struct image_data *data, boot_func_entry entry,
+ void *fdt_load_addr)
+{
+ int ret;
+
+ printf("starting elf (entry at %p)\n", entry);
+
+ if (data->dryrun)
+ return 0;
+
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
+ shutdown_barebox();
+
+ /* Synchronize I-cache with D-cache */
+ sync_caches_for_execution();
+
+ /**
+ * Parameters passing
+ * r0: boot magic
+ * r1: device tree pointer
+ */
+ entry(LINUX_BOOT_PARAM_MAGIC, (void *) fdt_load_addr);
+
+ /* should never return ! */
+ panic("Returned from boot program !\n");
+
+ return -EINVAL;
+}
+
+static int do_boot_elf(struct image_data *data, struct elf_image *elf)
+{
+ int ret;
+ void *fdt;
+ boot_func_entry entry;
+ unsigned long load_addr, initrd_address;
+
+ /* load initrd after the elf */
+ load_addr = PAGE_ALIGN((unsigned long) elf->high_addr);
+ if (bootm_has_initrd(data)) {
+ if (data->initrd_address != UIMAGE_INVALID_ADDRESS)
+ initrd_address = data->initrd_address;
+ else
+ initrd_address = load_addr;
+
+ printf("Loading initrd at 0x%lx\n", initrd_address);
+ ret = bootm_load_initrd(data, initrd_address);
+ if (ret) {
+ printf("Failed to load initrd\n");
+ return ret;
+ }
+
+ if (data->initrd_address == UIMAGE_INVALID_ADDRESS) {
+ load_addr += resource_size(data->initrd_res);
+ load_addr = PAGE_ALIGN(load_addr);
+ }
+ }
+
+ fdt = bootm_get_devicetree(data);
+ if (IS_ERR(fdt)) {
+ printf("Failed to load dtb\n");
+ return PTR_ERR(fdt);
+ }
+
+ printf("Loading device tree at %lx\n", load_addr);
+ /* load device tree after the initrd if any */
+ ret = bootm_load_devicetree(data, fdt, load_addr);
+ if (ret) {
+ printf("Failed to load device tree: %d\n", ret);
+ goto err_free_fdt;
+ }
+
+ entry = (boot_func_entry) elf->entry;
+
+ ret = do_boot_entry(data, entry, fdt);
+
+err_free_fdt:
+ free(fdt);
+
+ return ret;
+}
+
+static int do_bootm_fit(struct image_data *data)
+{
+ int ret;
+ struct elf_image *elf;
+
+ elf = elf_open_binary((void *) data->fit_kernel);
+ if (IS_ERR(elf))
+ return PTR_ERR(data->elf);
+
+ ret = elf_load(elf);
+ if (ret)
+ goto close_elf;
+
+ ret = do_boot_elf(data, elf);
+
+close_elf:
+ elf_close(elf);
+
+ return ret;
+}
+
+static int do_bootm_elf(struct image_data *data)
+{
+ int ret;
+
+ ret = bootm_load_os(data, data->os_address);
+ if (ret)
+ return ret;
+
+ return do_boot_elf(data, data->elf);
+}
+
+static struct image_handler elf_handler = {
+ .name = "ELF",
+ .bootm = do_bootm_elf,
+ .filetype = filetype_elf,
+};
+
+static struct image_handler fit_handler = {
+ .name = "FIT",
+ .bootm = do_bootm_fit,
+ .filetype = filetype_oftree,
+};
+
+static struct binfmt_hook binfmt_elf_hook = {
+ .type = filetype_elf,
+ .exec = "bootm",
+};
+
+static int kvx_register_image_handler(void)
+{
+ register_image_handler(&elf_handler);
+
+ if (IS_ENABLED(CONFIG_FITIMAGE))
+ register_image_handler(&fit_handler);
+
+ binfmt_register(&binfmt_elf_hook);
+
+ return 0;
+}
+
+late_initcall(kvx_register_image_handler);
diff --git a/arch/kvx/lib/cache.c b/arch/kvx/lib/cache.c
new file mode 100644
index 0000000000..bd074de79b
--- /dev/null
+++ b/arch/kvx/lib/cache.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2021 Yann Sionneau <ysionneau@kalray.eu>, Kalray Inc.
+
+#include <asm/cache.h>
+#include <linux/kernel.h>
+
+#define KVX_DCACHE_LINE_SIZE (64)
+#define KVX_DCACHE_SIZE (128*1024)
+
+void invalidate_dcache_range(unsigned long addr, unsigned long stop)
+{
+ long size;
+
+ addr = ALIGN_DOWN(addr, KVX_DCACHE_LINE_SIZE);
+ size = stop - addr;
+
+ if (size < KVX_DCACHE_SIZE) {
+ while (addr < stop) {
+ __builtin_kvx_dinvall((void *)addr);
+ addr += KVX_DCACHE_LINE_SIZE;
+ }
+ } else {
+ __builtin_kvx_dinval();
+ }
+}
diff --git a/arch/kvx/lib/cpuinfo.c b/arch/kvx/lib/cpuinfo.c
new file mode 100644
index 0000000000..f17a0dc6b9
--- /dev/null
+++ b/arch/kvx/lib/cpuinfo.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Kalray Inc.
+ */
+
+#include <common.h>
+#include <command.h>
+
+static int do_cpuinfo(int argc, char *argv[])
+{
+ printf("Kalray Coolidge\n");
+
+ return 0;
+}
+
+BAREBOX_CMD_START(cpuinfo)
+ .cmd = do_cpuinfo,
+ BAREBOX_CMD_DESC("show CPU information")
+ BAREBOX_CMD_GROUP(CMD_GRP_INFO)
+BAREBOX_CMD_END
diff --git a/arch/kvx/lib/dma-default.c b/arch/kvx/lib/dma-default.c
new file mode 100644
index 0000000000..1d37fb27a6
--- /dev/null
+++ b/arch/kvx/lib/dma-default.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2021 Yann Sionneau <ysionneau@kalray.eu>, Kalray Inc.
+
+#include <dma.h>
+#include <asm/barrier.h>
+#include <asm/io.h>
+#include <asm/cache.h>
+#include <asm/sfr.h>
+#include <asm/sys_arch.h>
+
+/*
+ * The implementation of arch should follow the following rules:
+ * map for_cpu for_device unmap
+ * TO_DEV writeback none writeback none
+ * FROM_DEV invalidate invalidate(*) invalidate invalidate(*)
+ * BIDIR writeback invalidate writeback invalidate
+ *
+ * (*) - only necessary if the CPU speculatively prefetches.
+ *
+ * (see https://lkml.org/lkml/2018/5/18/979)
+ */
+
+void arch_sync_dma_for_device(void *vaddr, size_t size,
+ enum dma_data_direction dir)
+{
+ unsigned long start = (unsigned long)vaddr;
+
+ /* dcache is Write-Through: no need to flush to force writeback */
+ switch (dir) {
+ case DMA_FROM_DEVICE:
+ invalidate_dcache_range(start, start + size);
+ break;
+ case DMA_TO_DEVICE:
+ case DMA_BIDIRECTIONAL:
+ /* allow device to read buffer written by CPU */
+ wmb();
+ break;
+ default:
+ BUG();
+ }
+}
+
+void arch_sync_dma_for_cpu(void *vaddr, size_t size,
+ enum dma_data_direction dir)
+{
+ unsigned long start = (unsigned long)vaddr;
+
+ /* CPU does not speculatively prefetches */
+ switch (dir) {
+ case DMA_FROM_DEVICE:
+ /* invalidate has been done during map/for_device */
+ case DMA_TO_DEVICE:
+ break;
+ case DMA_BIDIRECTIONAL:
+ invalidate_dcache_range(start, start + size);
+ break;
+ default:
+ BUG();
+ }
+}
diff --git a/arch/kvx/lib/dtb.c b/arch/kvx/lib/dtb.c
new file mode 100644
index 0000000000..3d65bd7bd4
--- /dev/null
+++ b/arch/kvx/lib/dtb.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Kalray Inc.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <of.h>
+
+static int of_kvx_init(void)
+{
+ return barebox_register_fdt(boot_dtb);
+}
+core_initcall(of_kvx_init);
diff --git a/arch/kvx/lib/poweroff.c b/arch/kvx/lib/poweroff.c
new file mode 100644
index 0000000000..3ffda026b3
--- /dev/null
+++ b/arch/kvx/lib/poweroff.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Kalray Inc.
+ */
+
+#include <init.h>
+#include <common.h>
+#include <poweroff.h>
+
+static void __noreturn kvx_poweroff(struct poweroff_handler *handler)
+{
+ register int status asm("r0") = 0;
+
+ shutdown_barebox();
+
+ asm volatile ("scall 0xfff\n\t;;"
+ : : "r"(status)
+ : "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "memory");
+ hang();
+}
+
+static int kvx_scall_poweroff_probe(struct device *dev)
+{
+ poweroff_handler_register_fn(kvx_poweroff);
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id kvx_scall_poweroff_id[] = {
+ {
+ .compatible = "kalray,kvx-scall-poweroff",
+ }, {
+ }
+};
+MODULE_DEVICE_TABLE(of, kvx_scall_poweroff_id);
+
+static struct driver kvx_scall_poweroff = {
+ .name = "kvx_scall_poweroff",
+ .probe = kvx_scall_poweroff_probe,
+ .of_compatible = DRV_OF_COMPAT(kvx_scall_poweroff_id),
+};
+
+device_platform_driver(kvx_scall_poweroff);
diff --git a/arch/kvx/lib/setjmp.S b/arch/kvx/lib/setjmp.S
new file mode 100644
index 0000000000..829299711d
--- /dev/null
+++ b/arch/kvx/lib/setjmp.S
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/* SPDX-FileCopyrightText: 2021 Jules Maselbas <jmaselbas@kalray.eu>, Kalray Inc. */
+
+#define REG_SIZE 8
+
+#include <linux/linkage.h>
+
+/* jmp_buf layout:
+ * [0] = $ra, $sp, $cs, $r14,
+ * [4] = $r20, $r21, $r22, $r23,
+ * [8] = $r24, $r25, $r26, $r27,
+ * [12] = $r28, $r29, $r30, $r31,
+ * [16] = $r18, $r19,
+ * [18] = $lc, $le, $ls, xxxx
+ */
+
+/**
+ * int initjmp(jmp_buf jmp, void __noreturn (*func)(void), void *stack_top);
+ */
+ENTRY(initjmp)
+ /* store $ra */
+ sd (0 * REG_SIZE)[$r0] = $r1
+ ;;
+ /* store $sp */
+ sd (1 * REG_SIZE)[$r0] = $r2
+ make $r0 = 0
+ ret
+ ;;
+ENDPROC(initjmp)
+
+/**
+ * int setjmp(jmp_buf jmp);
+ */
+ENTRY(setjmp)
+ sq (16 * REG_SIZE)[$r0] = $r18r19
+ get $r40 = $ra
+ copyd $r41 = $sp
+ ;;
+ so (4 * REG_SIZE)[$r0] = $r20r21r22r23
+ get $r42 = $cs
+ copyd $r43 = $r14
+ ;;
+ so (0 * REG_SIZE)[$r0] = $r40r41r42r43
+ get $r40 = $lc
+ ;;
+ so (8 * REG_SIZE)[$r0] = $r24r25r26r27
+ get $r41 = $le
+ ;;
+ so (12 * REG_SIZE)[$r0] = $r28r29r30r31
+ get $r42 = $ls
+ ;;
+ so (18 * REG_SIZE)[$r0] = $r40r41r42r43
+ make $r0 = 0
+ ret
+ ;;
+ENDPROC(setjmp)
+
+/**
+ * void longjmp(jmp_buf jmp, int ret);
+ */
+ENTRY(longjmp)
+ lo $r40r41r42r43 = (0 * REG_SIZE)[$r0]
+ ;;
+ lo $r44r45r46r47 = (18 * REG_SIZE)[$r0]
+ set $ra = $r40
+ copyd $sp = $r41
+ ;;
+ lo $r20r21r22r23 = (4 * REG_SIZE)[$r0]
+ set $cs = $r42
+ copyd $r14 = $r43
+ ;;
+ lo $r24r25r26r27 = (8 * REG_SIZE)[$r0]
+ set $lc = $r44
+ ;;
+ lo $r28r29r30r31 = (12 * REG_SIZE)[$r0]
+ set $le = $r45
+ ;;
+ lq $r18r19 = (16 * REG_SIZE)[$r0]
+ set $ls = $r46
+ ;;
+ /* According to man, if retval is equal to 0, then we should return 1 */
+ maxud $r0 = $r1, 1
+ ret
+ ;;
+ENDPROC(longjmp)