spacemit: k1: Add DDR firmware support to SPL

Include DDR initialization firmware in the SPL image. The firmware
path can be specified via the DDR_FW_FILE environment variable. If
the firmware is not found, an empty placeholder file is created to
allow the build to proceed without DDR initialization support.

Signed-off-by: Raymond Mao <raymond.mao@riscstar.com>
Signed-off-by: Guodong Xu <guodong@riscstar.com>
Tested-by: Songsong Zhang <sszhang@vsit.ai>
This commit is contained in:
Raymond Mao
2026-07-21 16:57:14 +08:00
committed by Tim Ouyang
parent b3ec88a7f3
commit 83026cf8f9
3 changed files with 171 additions and 0 deletions
+23
View File
@@ -56,3 +56,26 @@
bootph-pre-ram;
};
};
&binman {
u-boot-spl-ddr {
type = "section";
filename = "u-boot-spl-ddr.bin";
pad-byte = <0xff>;
u-boot-spl {
};
ddr-fw {
type = "blob";
filename = "ddr_fw.bin";
align = <64>;
};
u-boot-any {
type = "section";
size = <0>;
offset = <0>;
};
};
};
+21
View File
@@ -5,3 +5,24 @@
obj-y := board.o
obj-$(CONFIG_SPL_BUILD) += spl.o
DDR_FW_SRC ?= $(DDR_FW_FILE)
FW_TARGET = $(objtree)/ddr_fw.bin
$(obj)/spl.o: $(FW_TARGET)
$(FW_TARGET): FORCE
@if [ -n "$(DDR_FW_SRC)" ] && [ -f "$(DDR_FW_SRC)" ]; then \
if ! cmp -s "$(DDR_FW_SRC)" $@ 2>/dev/null; then \
echo " Copying DDR firmware from: $(DDR_FW_SRC)"; \
cp "$(DDR_FW_SRC)" $@; \
fi; \
elif [ -s $@ ]; then \
: ; \
else \
echo " Note: No DDR firmware found, creating empty placeholder"; \
echo " (Set DDR_FW_FILE to specify firmware location)"; \
touch $@; \
fi
clean-files += $(FW_TARGET)
+127
View File
@@ -4,8 +4,11 @@
*/
#include <asm/io.h>
#include <binman.h>
#include <binman_sym.h>
#include <clk.h>
#include <clk-uclass.h>
#include <cpu_func.h>
#include <configs/k1.h>
#include <dm/device.h>
#include <dm/uclass.h>
@@ -14,6 +17,7 @@
#include <log.h>
#include <spl.h>
#include <tlv_eeprom.h>
#include "tlv_codes.h"
#define MUX_MODE4 4
#define EDGE_NONE BIT(6)
@@ -26,6 +30,29 @@
#define MFP_GPIO_84 0xd401e154
#define MFP_GPIO_85 0xd401e158
#define DDR_FIRMWARE_BASE 0xc082d000
#define DDR_DEFAULT_CS_NUM 2
#define DDR_DEFAULT_TYPE "LPDDR4X"
#define DDR_DEFAULT_TX_ODT 80
#define DDR_DEFAULT_DATA_RATE 2400
#define MAGIC_NUM 0xaa55aa55
typedef void (*puts_func_t)(const char *s);
typedef int (*ddr_init_func_t)(u64 ddr_base, u32 cs_num, u32 data_rate,
puts_func_t puts);
struct ddr_cfg {
u32 data_rate;
u32 cs_num;
u32 tx_odt;
u8 type[I2C_BUF_SIZE];
};
binman_sym_declare(ulong, ddr_fw, image_pos);
binman_sym_declare(ulong, ddr_fw, size);
static void i2c_early_init(void)
{
struct udevice *bus;
@@ -109,6 +136,105 @@ void serial_early_init(void)
panic("Serial uclass init failed: %d\n", ret);
}
/* Set default value for DDR chips */
static void ddr_cfg_init(struct ddr_cfg *cfg)
{
memset(cfg, 0, sizeof(struct ddr_cfg));
cfg->data_rate = DDR_DEFAULT_DATA_RATE;
cfg->cs_num = DDR_DEFAULT_CS_NUM;
cfg->tx_odt = DDR_DEFAULT_TX_ODT;
strcpy(cfg->type, DDR_DEFAULT_TYPE);
}
int read_ddr_info(struct ddr_cfg *cfg)
{
u8 eeprom_data[TLV_TOTAL_LEN_MAX], *p;
struct tlvinfo_header *tlv_hdr;
struct tlvinfo_tlv *tlv_entry;
u32 size, entry_size;
int ret, i;
bool found = false;
if (!cfg)
return -EINVAL;
ddr_cfg_init(cfg);
ret = read_tlvinfo_tlv_eeprom(eeprom_data, &tlv_hdr,
&tlv_entry, i);
if (ret)
return ret;
p = (u8 *)tlv_entry;
for (i = 0; i < tlv_hdr->totallen; ) {
switch (tlv_entry->type) {
case TLV_CODE_DDR_CSNUM:
memcpy(&cfg->cs_num, &tlv_entry->value[0], 1);
found = true;
break;
case TLV_CODE_DDR_TYPE:
size = min((u32)tlv_entry->length, (u32)I2C_BUF_SIZE);
memcpy(&cfg->type[0], &tlv_entry->value[0], size);
found = true;
break;
case TLV_CODE_DDR_DATARATE:
memcpy(&cfg->data_rate, &tlv_entry->value[0], 2);
found = true;
break;
case TLV_CODE_DDR_TX_ODT:
memcpy(&cfg->tx_odt, &tlv_entry->value[0], 1);
found = true;
break;
case TLV_CODE_CRC_32:
if (!found)
return -ENOENT;
return 0;
}
entry_size = tlv_entry->length + sizeof(struct tlvinfo_tlv);
i += entry_size;
p += entry_size;
tlv_entry = (struct tlvinfo_tlv *)p;
}
if (!found)
return -ENOENT;
return 0;
}
void ddr_early_init(void)
{
void __iomem *src, *dst;
ulong pos, size;
struct ddr_cfg cfg;
ddr_init_func_t ddr_init;
pos = binman_sym(ulong, ddr_fw, image_pos);
size = binman_sym(ulong, ddr_fw, size);
src = (void __iomem *)pos;
dst = (void __iomem *)(DDR_FIRMWARE_BASE);
log_info("DDR firmware: [0x%lx]:0x%x, size:0x%lx\n", pos, readl(src), size);
memcpy((u8 *)dst, (u8 *)src, size);
size = round_up(size, 64);
/*
* Ensure the just-written DDR firmware bytes are observable in the
* instruction stream. RISC-V's fence.i alone is sufficient for the
* single-hart SPL case; skip flush_dcache_range as cbo.flush may
* trap on our current privilege state.
*/
asm volatile ("fence.i" ::: "memory");
read_ddr_info(&cfg);
ddr_init = (ddr_init_func_t)DDR_FIRMWARE_BASE;
#ifdef DEBUG
ddr_init(0xc0000000, cfg.cs_num, cfg.data_rate, puts);
#else
ddr_init(0xc0000000, cfg.cs_num, cfg.data_rate, NULL);
#endif
writel(MAGIC_NUM, (void __iomem *)0x00000000);
flush_dcache_range(0, 64);
invalidate_dcache_range(0, 64);
if (readl((void __iomem *)0x00000000) == MAGIC_NUM)
log_info("DDR is ready\n");
else
log_info("DDR is not ready\n");
}
void board_init_f(ulong dummy)
{
u8 i2c_buf[I2C_BUF_SIZE] = { 0 };
@@ -131,6 +257,7 @@ void board_init_f(ulong dummy)
log_info("Fail to detect board:%d\n", ret);
else
log_info("Get board name:%s\n", (char *)i2c_buf);
ddr_early_init();
}
u32 spl_boot_device(void)