From: Jiaxun Yang <[email protected]>

Add some common library routines for the architecture.

Signed-off-by: Jiaxun Yang <[email protected]>
Signed-off-by: Yao Zi <[email protected]>
---

Changed from v1
- Correctly implement dcache operations

 arch/loongarch/include/asm/cache.h       |  10 ++
 arch/loongarch/include/asm/global_data.h |   7 +
 arch/loongarch/lib/Makefile              |   7 +
 arch/loongarch/lib/asm-offsets.c         |  66 +++++++
 arch/loongarch/lib/boot.c                |  14 ++
 arch/loongarch/lib/cache.c               | 213 +++++++++++++++++++++++
 arch/loongarch/lib/reset.c               |  14 ++
 arch/loongarch/lib/setjmp.S              |  52 ++++++
 8 files changed, 383 insertions(+)
 create mode 100644 arch/loongarch/lib/asm-offsets.c
 create mode 100644 arch/loongarch/lib/boot.c
 create mode 100644 arch/loongarch/lib/cache.c
 create mode 100644 arch/loongarch/lib/reset.c
 create mode 100644 arch/loongarch/lib/setjmp.S

diff --git a/arch/loongarch/include/asm/cache.h 
b/arch/loongarch/include/asm/cache.h
index 854dd0c0a02e..2dd4e82340bb 100644
--- a/arch/loongarch/include/asm/cache.h
+++ b/arch/loongarch/include/asm/cache.h
@@ -6,8 +6,11 @@
 #ifndef _ASM_LOONGARCH_CACHE_H
 #define _ASM_LOONGARCH_CACHE_H
 
+#include <linux/bitops.h>
+
 /* cache */
 void cache_flush(void);
+void probe_caches(void);
 
 #define cache_op(op, addr)                                             \
        __asm__ __volatile__(                                           \
@@ -15,6 +18,13 @@ void cache_flush(void);
        :                                                               \
        : "i" (op), "ZC" (*(unsigned char *)(addr)))
 
+#define CACHE_MAX_LEVEL                3
+#define CACHE_MAX_INDEX                6
+#define CACHE_OP               GENMASK(4, 3)
+#define  CACHE_INDEX_INVWB     0x1
+#define  CACHE_HIT_INVWB       0x2
+#define CACHE_INDEX            GENMASK(2, 0)
+
 #ifdef CONFIG_SYS_CACHELINE_SIZE
 #define ARCH_DMA_MINALIGN      CONFIG_SYS_CACHELINE_SIZE
 #else
diff --git a/arch/loongarch/include/asm/global_data.h 
b/arch/loongarch/include/asm/global_data.h
index 2a9a525e2f37..7aab802ca04d 100644
--- a/arch/loongarch/include/asm/global_data.h
+++ b/arch/loongarch/include/asm/global_data.h
@@ -10,6 +10,7 @@
 #define __ASM_GBL_DATA_H
 
 #include <linux/types.h>
+#include <asm/cache.h>
 #include <asm/u-boot.h>
 #include <compiler.h>
 
@@ -24,6 +25,12 @@ struct arch_global_data {
 #ifdef CONFIG_SMBIOS
        ulong smbios_start;             /* Start address of SMBIOS table */
 #endif
+       ushort dcache_ways[CACHE_MAX_INDEX];
+       ushort dcache_sets[CACHE_MAX_INDEX];
+       ushort dcache_linesizes[CACHE_MAX_INDEX];
+       ushort dcache_index[CACHE_MAX_INDEX];
+       ushort dcache_levels;
+       bool dcache_inclusive;
 };
 
 #include <asm-generic/global_data.h>
diff --git a/arch/loongarch/lib/Makefile b/arch/loongarch/lib/Makefile
index 3dbed94cc624..3c17b9cd85af 100644
--- a/arch/loongarch/lib/Makefile
+++ b/arch/loongarch/lib/Makefile
@@ -3,3 +3,10 @@
 # Copyright (C) 2024 Jiaxun yang <[email protected]>
 #
 
+obj-$(CONFIG_CMD_GO) += boot.o
+obj-y  += cache.o
+obj-y  += interrupts.o
+ifeq ($(CONFIG_$(SPL_)SYSRESET),)
+obj-y  += reset.o
+endif
+obj-y  += setjmp.o
diff --git a/arch/loongarch/lib/asm-offsets.c b/arch/loongarch/lib/asm-offsets.c
new file mode 100644
index 000000000000..e3f4c629b63d
--- /dev/null
+++ b/arch/loongarch/lib/asm-offsets.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Jiaxun Yang <[email protected]>
+ *
+ * From arch/x86/lib/asm-offsets.c
+ *
+ * This program is used to generate definitions needed by
+ * assembly language modules.
+ */
+
+#include <asm/global_data.h>
+#include <asm/ptrace.h>
+#include <linux/kbuild.h>
+
+static void __used output_ptreg_defines(void)
+{
+       COMMENT("LoongArch pt_regs offsets.");
+       OFFSET(PT_R0, pt_regs, regs[0]);
+       OFFSET(PT_R1, pt_regs, regs[1]);
+       OFFSET(PT_R2, pt_regs, regs[2]);
+       OFFSET(PT_R3, pt_regs, regs[3]);
+       OFFSET(PT_R4, pt_regs, regs[4]);
+       OFFSET(PT_R5, pt_regs, regs[5]);
+       OFFSET(PT_R6, pt_regs, regs[6]);
+       OFFSET(PT_R7, pt_regs, regs[7]);
+       OFFSET(PT_R8, pt_regs, regs[8]);
+       OFFSET(PT_R9, pt_regs, regs[9]);
+       OFFSET(PT_R10, pt_regs, regs[10]);
+       OFFSET(PT_R11, pt_regs, regs[11]);
+       OFFSET(PT_R12, pt_regs, regs[12]);
+       OFFSET(PT_R13, pt_regs, regs[13]);
+       OFFSET(PT_R14, pt_regs, regs[14]);
+       OFFSET(PT_R15, pt_regs, regs[15]);
+       OFFSET(PT_R16, pt_regs, regs[16]);
+       OFFSET(PT_R17, pt_regs, regs[17]);
+       OFFSET(PT_R18, pt_regs, regs[18]);
+       OFFSET(PT_R19, pt_regs, regs[19]);
+       OFFSET(PT_R20, pt_regs, regs[20]);
+       OFFSET(PT_R21, pt_regs, regs[21]);
+       OFFSET(PT_R22, pt_regs, regs[22]);
+       OFFSET(PT_R23, pt_regs, regs[23]);
+       OFFSET(PT_R24, pt_regs, regs[24]);
+       OFFSET(PT_R25, pt_regs, regs[25]);
+       OFFSET(PT_R26, pt_regs, regs[26]);
+       OFFSET(PT_R27, pt_regs, regs[27]);
+       OFFSET(PT_R28, pt_regs, regs[28]);
+       OFFSET(PT_R29, pt_regs, regs[29]);
+       OFFSET(PT_R30, pt_regs, regs[30]);
+       OFFSET(PT_R31, pt_regs, regs[31]);
+       OFFSET(PT_CRMD, pt_regs, csr_crmd);
+       OFFSET(PT_PRMD, pt_regs, csr_prmd);
+       OFFSET(PT_EUEN, pt_regs, csr_euen);
+       OFFSET(PT_ECFG, pt_regs, csr_ecfg);
+       OFFSET(PT_ESTAT, pt_regs, csr_estat);
+       OFFSET(PT_ERA, pt_regs, csr_era);
+       OFFSET(PT_BVADDR, pt_regs, csr_badvaddr);
+       OFFSET(PT_ORIG_A0, pt_regs, orig_a0);
+       DEFINE(PT_SIZE, sizeof(struct pt_regs));
+       BLANK();
+}
+
+int main(void)
+{
+       output_ptreg_defines();
+       return 0;
+}
diff --git a/arch/loongarch/lib/boot.c b/arch/loongarch/lib/boot.c
new file mode 100644
index 000000000000..327be16bb59f
--- /dev/null
+++ b/arch/loongarch/lib/boot.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Jiaxun Yang <[email protected]>
+ */
+
+#include <asm/u-boot.h>
+
+unsigned long do_go_exec(ulong (*entry)(int, char * const []),
+                        int argc, char *const argv[])
+{
+       cleanup_before_linux();
+
+       return entry(argc, argv);
+}
diff --git a/arch/loongarch/lib/cache.c b/arch/loongarch/lib/cache.c
new file mode 100644
index 000000000000..ee27ec28b724
--- /dev/null
+++ b/arch/loongarch/lib/cache.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Jiaxun Yang <[email protected]>
+ * Copyright (C) 2026 Yao Zi <[email protected]>
+ */
+
+#include <asm/cache.h>
+#include <asm/global_data.h>
+#include <asm/loongarch.h>
+#include <cpu_func.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+
+#define CPUCFG_LX_IUPRE                BIT(0)
+#define CPUCFG_LX_IUUNIFY      BIT(1)
+#define CPUCFG_LX_IUINCL       BIT(3)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static inline void flush_cache_line_index(unsigned int index,
+                                         unsigned long addr)
+{
+#define do_flush(index)                                                        
\
+       case index:                                                     \
+               cache_op(FIELD_PREP(CACHE_OP, CACHE_INDEX_INVWB) |      \
+                        FIELD_PREP(CACHE_INDEX, index),                \
+                        index);                                        \
+               break;
+
+       switch (index) {
+               do_flush(0);
+               do_flush(1);
+               do_flush(2);
+               do_flush(3);
+               do_flush(4);
+               do_flush(5);
+       }
+
+#undef do_flush
+}
+
+static inline void flush_cache_line_hit(unsigned int index, unsigned long addr)
+{
+#define do_flush(index)                                                        
\
+       case index:                                                     \
+               cache_op(FIELD_PREP(CACHE_OP, CACHE_HIT_INVWB) |        \
+                        FIELD_PREP(CACHE_INDEX, index),                \
+                        addr);                                         \
+               break;
+
+       switch (index) {
+               do_flush(0);
+               do_flush(1);
+               do_flush(2);
+               do_flush(3);
+               do_flush(4);
+               do_flush(5);
+       }
+
+#undef do_flush
+}
+
+void invalidate_icache_all(void)
+{
+       asm volatile ("\tibar 0\n"::);
+}
+
+static void flush_dcache_level_all(unsigned int level)
+{
+       unsigned int way, set;
+       unsigned long index = 0;
+
+       for (set = 0; set < gd->arch.dcache_sets[level]; set++) {
+               for (way = 0; way < gd->arch.dcache_ways[level]; way++) {
+                       flush_cache_line_index(gd->arch.dcache_index[level],
+                                              index);
+                       index++;
+               }
+
+               index -= gd->arch.dcache_ways[level];
+               index += gd->arch.dcache_linesizes[level];
+       }
+}
+
+__weak void flush_dcache_all(void)
+{
+       unsigned int level;
+
+       if (gd->arch.dcache_inclusive) {
+               flush_dcache_level_all(gd->arch.dcache_levels - 1);
+               return;
+       }
+
+       for (level = 0; level < gd->arch.dcache_levels; level++)
+               flush_dcache_level_all(level);
+}
+
+static void flush_dcache_level(unsigned int level, unsigned long addr,
+                              unsigned long end)
+{
+       for (; addr < end; addr += gd->arch.dcache_linesizes[level])
+               flush_cache_line_hit(gd->arch.dcache_index[level], addr);
+}
+
+__weak void flush_dcache_range(unsigned long start, unsigned long end)
+{
+       unsigned int level;
+
+       if (gd->arch.dcache_inclusive) {
+               flush_dcache_level(gd->arch.dcache_levels - 1, start, end);
+               return;
+       }
+
+       for (level = 0; level < gd->arch.dcache_levels; level++)
+               flush_dcache_level(level, start, end);
+}
+
+__weak void invalidate_icache_range(unsigned long start, unsigned long end)
+{
+       /* LoongArch mandatory hardware I-Cache coherence */
+       invalidate_icache_all();
+}
+
+__weak void invalidate_dcache_range(unsigned long start, unsigned long end)
+{
+       flush_dcache_range(start, end);
+}
+
+__weak void cache_flush(void)
+{
+       flush_dcache_all();
+}
+
+__weak void cache_invalidate(void)
+{
+       flush_dcache_all();
+}
+
+__weak void flush_cache(unsigned long addr, unsigned long size)
+{
+       invalidate_icache_range(addr, addr + size);
+       flush_dcache_range(addr, addr + size);
+}
+
+__weak void dcache_enable(void)
+{
+}
+
+__weak void dcache_disable(void)
+{
+}
+
+__weak int dcache_status(void)
+{
+       return 0;
+}
+
+static void populate_dcache_properties(u32 cfg0, unsigned int level,
+                                      unsigned int cfgoff)
+{
+       u32 cfg1 = read_cpucfg(cfgoff);
+
+       if (level != 0)
+               gd->arch.dcache_inclusive = cfg0 & CPUCFG_LX_IUINCL;
+
+       gd->arch.dcache_ways[level] = FIELD_GET(CPUCFG_CACHE_WAYS_M, cfg1) + 1;
+       gd->arch.dcache_sets[level] = 1 << FIELD_GET(CPUCFG_CACHE_SETS_M, cfg1);
+       gd->arch.dcache_linesizes[level] = 1 << FIELD_GET(CPUCFG_CACHE_LSIZE_M, 
cfg1);
+}
+
+void probe_caches(void)
+{
+       unsigned int level = 0, index = 0;
+       u32 cfg = read_cpucfg(LOONGARCH_CPUCFG16);
+
+       if (cfg & CPUCFG16_L1_IUPRE) {
+               if (cfg & CPUCFG16_L1_IUUNIFY) {
+                       gd->arch.dcache_index[level] = index;
+                       populate_dcache_properties(cfg, level++,
+                                                  LOONGARCH_CPUCFG17);
+               }
+
+               index++;
+       }
+
+       if (cfg & CPUCFG16_L1_DPRE) {
+               gd->arch.dcache_index[level] = index++;
+               populate_dcache_properties(cfg, level++, LOONGARCH_CPUCFG18);
+       }
+
+       cfg >>= 3;
+
+       for (; level < 3; level++) {
+               if (cfg & CPUCFG_LX_IUPRE && cfg & CPUCFG_LX_IUUNIFY) {
+                       gd->arch.dcache_index[level] = index++;
+                       populate_dcache_properties(cfg, level,
+                                                  LOONGARCH_CPUCFG17 + level);
+               } else {
+                       break;
+               }
+
+               cfg >>= 7;
+       }
+
+       gd->arch.dcache_levels = level;
+}
+
+__weak void enable_caches(void)
+{
+       cache_invalidate();
+       /* Enable cache for direct address translation mode */
+       csr_xchg64(1 << CSR_CRMD_DACM_SHIFT, CSR_CRMD_DACM, LOONGARCH_CSR_CRMD);
+}
diff --git a/arch/loongarch/lib/reset.c b/arch/loongarch/lib/reset.c
new file mode 100644
index 000000000000..ddf29ee41d95
--- /dev/null
+++ b/arch/loongarch/lib/reset.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Jiaxun Yang <[email protected]>
+ */
+
+#include <command.h>
+#include <cpu_func.h>
+
+int do_reset(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+       reset_cpu();
+
+       return 0;
+}
diff --git a/arch/loongarch/lib/setjmp.S b/arch/loongarch/lib/setjmp.S
new file mode 100644
index 000000000000..12981d0013fa
--- /dev/null
+++ b/arch/loongarch/lib/setjmp.S
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2024 Jiaxun Yang <[email protected]>
+ */
+
+#include <config.h>
+#include <asm/asm.h>
+#include <linux/linkage.h>
+
+.pushsection .text.setjmp, "ax"
+ENTRY(setjmp)
+       LONG_S          s0, a0, 0
+       LONG_S          s1, a0, (1 * LONGSIZE)
+       LONG_S          s2, a0, (2 * LONGSIZE)
+       LONG_S          s3, a0, (3 * LONGSIZE)
+       LONG_S          s4, a0, (4 * LONGSIZE)
+       LONG_S          s5, a0, (5 * LONGSIZE)
+       LONG_S          s6, a0, (6 * LONGSIZE)
+       LONG_S          s7, a0, (7 * LONGSIZE)
+       LONG_S          s8, a0, (8 * LONGSIZE)
+       LONG_S          fp, a0, (9 * LONGSIZE)
+       LONG_S          sp, a0, (10 * LONGSIZE)
+       LONG_S          ra, a0, (11 * LONGSIZE)
+
+       move            a0, zero
+       ret
+ENDPROC(setjmp)
+.popsection
+
+.pushsection .text.longjmp, "ax"
+ENTRY(longjmp)
+       LONG_L          s0, a0, 0
+       LONG_L          s1, a0, (1 * LONGSIZE)
+       LONG_L          s2, a0, (2 * LONGSIZE)
+       LONG_L          s3, a0, (3 * LONGSIZE)
+       LONG_L          s4, a0, (4 * LONGSIZE)
+       LONG_L          s5, a0, (5 * LONGSIZE)
+       LONG_L          s6, a0, (6 * LONGSIZE)
+       LONG_L          s7, a0, (7 * LONGSIZE)
+       LONG_L          s8, a0, (8 * LONGSIZE)
+       LONG_L          fp, a0, (9 * LONGSIZE)
+       LONG_L          sp, a0, (10 * LONGSIZE)
+       LONG_L          ra, a0, (11 * LONGSIZE)
+
+       /* Move the return value in place, but return 1 if passed 0. */
+       li.w            a0, 1
+       beqz            a1, 1f
+       move            a0, a1
+1:
+       jr              ra
+ENDPROC(longjmp)
+.popsection
-- 
2.54.0

Reply via email to