Add SPL initialization support for SpacemiT K3. Enable instruction and data caches and cache snooping during early hart initialization, and provide the timer configuration needed for M-mode execution.
Initialize I2C and read the DDR part number from the board's TLV EEPROM. Populate the RAM driver's platform data before probing it to initialize DRAM. Set up the DRAM PMA boundary and record the RAM base and size in global data. Loading the next boot stage is not yet supported. Signed-off-by: Junhui Liu <[email protected]> --- arch/riscv/cpu/k3/Kconfig | 6 +- arch/riscv/cpu/k3/Makefile | 1 + arch/riscv/cpu/k3/spl.c | 52 ++++++++++++++ arch/riscv/include/asm/arch-k3/csr.h | 20 ++++++ arch/riscv/include/asm/arch-k3/spl.h | 11 +++ board/spacemit/k3/Makefile | 5 ++ board/spacemit/k3/spl.c | 127 +++++++++++++++++++++++++++++++++++ include/configs/spacemit-k3.h | 3 + 8 files changed, 224 insertions(+), 1 deletion(-) diff --git a/arch/riscv/cpu/k3/Kconfig b/arch/riscv/cpu/k3/Kconfig index 5474ac11243..dee79644b87 100644 --- a/arch/riscv/cpu/k3/Kconfig +++ b/arch/riscv/cpu/k3/Kconfig @@ -9,10 +9,14 @@ config SPACEMIT_K3 select BINMAN select ARCH_EARLY_INIT_R select SYS_CACHE_SHIFT_6 + select SUPPORT_SPL imply CPU imply CPU_RISCV - imply RISCV_TIMER if RISCV_SMODE + imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE) + imply RISCV_ACLINT if RISCV_MMODE + imply SPL_RISCV_ACLINT if SPL_RISCV_MMODE imply CMD_CPU + imply SPL_CPU imply OF_UPSTREAM endif diff --git a/arch/riscv/cpu/k3/Makefile b/arch/riscv/cpu/k3/Makefile index fba4e4b0a3f..6afe6caac69 100644 --- a/arch/riscv/cpu/k3/Makefile +++ b/arch/riscv/cpu/k3/Makefile @@ -3,3 +3,4 @@ # Copyright (C) 2026 Yixun Lan <[email protected]> obj-y += dram.o +obj-$(CONFIG_XPL_BUILD) += spl.o diff --git a/arch/riscv/cpu/k3/spl.c b/arch/riscv/cpu/k3/spl.c new file mode 100644 index 00000000000..e8ee32e37fd --- /dev/null +++ b/arch/riscv/cpu/k3/spl.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 Junhui Liu <[email protected]> + */ + +#include <asm/arch/csr.h> +#include <asm/arch/spl.h> +#include <asm/csr.h> +#include <asm/global_data.h> +#include <dm.h> +#include <ram.h> + +#define K3_DRAM_UPPER_LIMIT_ADDR 0x1100000000ULL + +DECLARE_GLOBAL_DATA_PTR; + +static void setup_dram_pma(void) +{ + csr_write(CSR_X100_PMAADDR8, K3_DRAM_UPPER_LIMIT_ADDR >> 2); + asm volatile("sfence.vma zero, zero" ::: "memory"); +} + +int spl_dram_init(void) +{ + struct ram_info info; + struct udevice *dev; + int ret; + + ret = uclass_get_device(UCLASS_RAM, 0, &dev); + if (ret) { + printf("DRAM init failed: %d\n", ret); + return ret; + } + + setup_dram_pma(); + + ret = ram_get_info(dev, &info); + if (ret) + return ret; + + gd->ram_base = info.base; + gd->ram_size = info.size; + + return 0; +} + +void harts_early_init(void) +{ + csr_set(CSR_X100_CACHE_CTL, CSR_X100_CACHE_CTL_ICACHE_EN | + CSR_X100_CACHE_CTL_DCACHE_EN); + csr_set(CSR_X100_SNOOP_CTL, CSR_X100_SNOOP_CTL_EN); +} diff --git a/arch/riscv/include/asm/arch-k3/csr.h b/arch/riscv/include/asm/arch-k3/csr.h new file mode 100644 index 00000000000..a6c3e6af9f5 --- /dev/null +++ b/arch/riscv/include/asm/arch-k3/csr.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 Junhui Liu <[email protected]> + */ + +#ifndef _ASM_ARCH_K3_CSR_H_ +#define _ASM_ARCH_K3_CSR_H_ + +#include <linux/bitops.h> + +#define CSR_X100_CACHE_CTL 0x7c0 +#define CSR_X100_CACHE_CTL_ICACHE_EN BIT(1) +#define CSR_X100_CACHE_CTL_DCACHE_EN BIT(0) + +#define CSR_X100_SNOOP_CTL 0x7f0 +#define CSR_X100_SNOOP_CTL_EN BIT(0) + +#define CSR_X100_PMAADDR8 0x7e8 + +#endif /* _ASM_ARCH_K3_CSR_H_ */ diff --git a/arch/riscv/include/asm/arch-k3/spl.h b/arch/riscv/include/asm/arch-k3/spl.h new file mode 100644 index 00000000000..7dda93cfda8 --- /dev/null +++ b/arch/riscv/include/asm/arch-k3/spl.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 Junhui Liu <[email protected]> + */ + +#ifndef _ASM_ARCH_K3_SPL_H_ +#define _ASM_ARCH_K3_SPL_H_ + +int spl_dram_init(void); + +#endif /* _ASM_ARCH_K3_SPL_H_ */ diff --git a/board/spacemit/k3/Makefile b/board/spacemit/k3/Makefile new file mode 100644 index 00000000000..67f30a40e6c --- /dev/null +++ b/board/spacemit/k3/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2026 Junhui Liu <[email protected]> + +obj-$(CONFIG_SPL_BUILD) += spl.o diff --git a/board/spacemit/k3/spl.c b/board/spacemit/k3/spl.c new file mode 100644 index 00000000000..4798e5c9ebc --- /dev/null +++ b/board/spacemit/k3/spl.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 Junhui Liu <[email protected]> + */ + +#include <asm/arch/spl.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <display_options.h> +#include <dm/device.h> +#include <dm/uclass.h> +#include <errno.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <soc/spacemit/k3-ddr.h> +#include <spl.h> +#include <tlv_eeprom.h> + +#define MUX_MODE5 5 +#define EDGE_NONE BIT(6) +#define PULL_UP (6 << 13) +#define I2C_PIN_CONFIG(x) ((x) | EDGE_NONE | PULL_UP) + +#define MFP_GPIO_46 0xd401e0b8 +#define MFP_GPIO_47 0xd401e0bc + +#define K3_TLV_CODE_DDR_PART_NUMBER 0x45 + +DECLARE_GLOBAL_DATA_PTR; + +static void i2c_early_init(void) +{ + struct udevice *bus; + + /* EEPROM uses I2C2 on GPIO46 and GPIO47. */ + writel(I2C_PIN_CONFIG(MUX_MODE5), (void __iomem *)MFP_GPIO_46); + writel(I2C_PIN_CONFIG(MUX_MODE5), (void __iomem *)MFP_GPIO_47); + + udelay(100); + + uclass_first_device(UCLASS_I2C, &bus); + while (bus) { + uclass_next_device(&bus); + if (!bus) + break; + } +} + +static int read_ddr_part_number(char *part, size_t part_size) +{ + struct tlvinfo_header *header; + u8 contents[TLV_INFO_MAX_LEN]; + struct tlvinfo_tlv *entry; + u8 *end; + int ret; + + ret = read_tlvinfo_tlv_eeprom(contents, &header, &entry, 0); + if (ret) + return ret; + + end = (u8 *)(header + 1) + be16_to_cpu(header->totallen); + + while ((u8 *)entry + sizeof(*entry) <= end) { + if ((u8 *)entry + sizeof(*entry) + entry->length > end) + return -EINVAL; + + if (entry->type == TLV_CODE_CRC_32) + break; + + if (entry->type == K3_TLV_CODE_DDR_PART_NUMBER) { + if (!entry->length || entry->length >= part_size) + return -EINVAL; + + memcpy(part, entry->value, entry->length); + part[entry->length] = '\0'; + + return 0; + } + + entry = (void *)(entry->value + entry->length); + } + + return -ENOENT; +} + +static int setup_ddr_plat(void) +{ + struct k3_ddr_plat *plat; + struct udevice *dev; + + dev = uclass_try_first_device(UCLASS_RAM); + if (!dev) + return -ENODEV; + + if (!device_is_compatible(dev, "spacemit,k3-ddrc")) + return -ENODEV; + + plat = dev_get_plat(dev); + + return read_ddr_part_number(plat->part_number, sizeof(plat->part_number)); +} + +int spl_board_init_f(void) +{ + int ret; + + i2c_early_init(); + + ret = setup_ddr_plat(); + if (ret) { + printf("DDR identification failed: %d\n", ret); + return ret; + } + + puts("DRAM: "); + ret = spl_dram_init(); + if (ret) + return ret; + print_size(gd->ram_size, "\n"); + + return 0; +} + +u32 spl_boot_device(void) +{ + return BOOT_DEVICE_NONE; +} diff --git a/include/configs/spacemit-k3.h b/include/configs/spacemit-k3.h index 9dd545e2a3b..ccd900ce91c 100644 --- a/include/configs/spacemit-k3.h +++ b/include/configs/spacemit-k3.h @@ -8,6 +8,9 @@ #define CFG_SYS_NS16550_IER 0x40 +#define RISCV_MMODE_TIMERBASE 0xf1810000 +#define RISCV_MMODE_TIMEROFF 0xbff8 +#define RISCV_MMODE_TIMER_FREQ 24000000 #define RISCV_SMODE_TIMER_FREQ 24000000 #endif /* __SPACEMIT_K3_H */ -- 2.56.0
