Add the TPL and SPL stages for the EN75xx. The TPL brings up the cache
and SoC straps, runs the vendor DDR calibration stage from flash via the
early SFC (serial-flash controller) reader, then the SPL loads U-Boot
proper.

The DDR calibration stage is a vendor artifact read from flash at boot
and is not shipped in-tree; see arch/mips/mach-en75xx/README.ddr-blob for
how to obtain it and its checksums.

Signed-off-by: AK Sharma <[email protected]>
---
 arch/mips/mach-en75xx/README.ddr-blob |  21 ++
 arch/mips/mach-en75xx/early_sfc.c     | 352 ++++++++++++++++++++++++++
 arch/mips/mach-en75xx/spl/Makefile    |   2 +
 arch/mips/mach-en75xx/spl/spl.c       |  47 ++++
 arch/mips/mach-en75xx/tpl/Makefile    |   3 +
 arch/mips/mach-en75xx/tpl/start.S     | 101 ++++++++
 arch/mips/mach-en75xx/tpl/tpl.c       | 158 ++++++++++++
 7 files changed, 684 insertions(+)
 create mode 100644 arch/mips/mach-en75xx/README.ddr-blob
 create mode 100644 arch/mips/mach-en75xx/early_sfc.c
 create mode 100644 arch/mips/mach-en75xx/spl/Makefile
 create mode 100644 arch/mips/mach-en75xx/spl/spl.c
 create mode 100644 arch/mips/mach-en75xx/tpl/Makefile
 create mode 100644 arch/mips/mach-en75xx/tpl/start.S
 create mode 100644 arch/mips/mach-en75xx/tpl/tpl.c

diff --git a/arch/mips/mach-en75xx/README.ddr-blob 
b/arch/mips/mach-en75xx/README.ddr-blob
new file mode 100644
index 00000000..b0074941
--- /dev/null
+++ b/arch/mips/mach-en75xx/README.ddr-blob
@@ -0,0 +1,21 @@
+EN75xx DDR calibration blob
+===========================
+
+The EN75xx TPL runs a small vendor DDR calibration stage that is NOT part
+of the U-Boot source tree. The TPL reads it from SPI-NAND at
+EN75XX_DDR_BLOB_OFFSET (0x8000) with the early SFC reader (see tpl/tpl.c)
+and executes it from FE-SRAM before DRAM is available.
+
+Obtain the blob by extracting the FE-SRAM DDR calibration stage
+("spram.bin") from the device's vendor bootloader and flashing it at
+EN75XX_DDR_BLOB_OFFSET.
+
+Expected images:
+
+  EN7512/EN7521  20336 bytes, linked/entered at 0x9fa32800
+                 sha256 
792068a4573b20cb8c39cb27df06c45683c11034e6fd07da24ddf7084828faa5
+  EN7528         23152 bytes
+                 sha256 
c2ff94b25100d46c0f2d8b42ebeb887bd6bfd0d49cd1ba5df61eea84ac227540
+
+Providing a source implementation of the DDR2/DDR3 calibration to remove
+this runtime dependency is a known TODO.
diff --git a/arch/mips/mach-en75xx/early_sfc.c 
b/arch/mips/mach-en75xx/early_sfc.c
new file mode 100644
index 00000000..39fc070d
--- /dev/null
+++ b/arch/mips/mach-en75xx/early_sfc.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Minimal EN7512/EN7521 serial-flash reader used before driver model.
+ * Reconstructed from the vendor TCBoot move_data stage.
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <mach/en75xx.h>
+
+#define SF_READ_IDLE_EN                0x004
+#define SF_MTX_MODE_TOG                0x014
+#define SF_RDCTL_FSM                   0x018
+#define SF_MACMUX_SEL                  0x01c
+#define SF_MANUAL_EN                   0x020
+#define SF_MANUAL_OPFIFO_EMPTY         0x024
+#define SF_MANUAL_OPFIFO_WDATA         0x028
+#define SF_MANUAL_OPFIFO_FULL          0x02c
+#define SF_MANUAL_OPFIFO_WR            0x030
+#define SF_MANUAL_DFIFO_FULL           0x034
+#define SF_MANUAL_DFIFO_WDATA          0x038
+#define SF_MANUAL_DFIFO_EMPTY          0x03c
+#define SF_MANUAL_DFIFO_RD             0x040
+#define SF_MANUAL_DFIFO_RDATA          0x044
+#define SF_SI_CK_SEL                   0x09c
+#define SF_STRAP                       0x114
+
+#define SF_STRAP_ADDR_4B               BIT(0)
+#define SF_STRAP_SPI_NAND              BIT(1)
+#define SF_STRAP_DUMMY_APPEND          BIT(2)
+
+#define OP_CSH                         0x00
+#define OP_CSL                         0x01
+#define OP_CK                          0x02
+#define OP_OUTS                                0x08
+#define OP_INS                         0x0c
+
+#define OP_SHIFT                       9
+#define OP_CMD_MASK                    0x1f
+#define OP_LEN_MASK                    0x1ff
+
+#define SPIN_LIMIT                     1000000
+#define NAND_PAGE_SIZE                 2048
+#define NOR_READ_CHUNK                 1024
+
+static inline void __iomem *sf_reg(u32 reg)
+{
+       return (void __iomem *)(EN75XX_SFC_BASE + reg);
+}
+
+static int sf_wait_eq(u32 reg, u32 expected)
+{
+       unsigned int timeout = SPIN_LIMIT;
+
+       while (timeout--) {
+               if (__raw_readl(sf_reg(reg)) == expected)
+                       return 0;
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int sf_op(u32 op, u32 len)
+{
+       u32 val = ((op & OP_CMD_MASK) << OP_SHIFT) | (len & OP_LEN_MASK);
+       int ret;
+
+       ret = sf_wait_eq(SF_MANUAL_OPFIFO_FULL, 0);
+       if (ret)
+               return ret;
+
+       __raw_writel(val, sf_reg(SF_MANUAL_OPFIFO_WDATA));
+       __raw_writel(1, sf_reg(SF_MANUAL_OPFIFO_WR));
+
+       return sf_wait_eq(SF_MANUAL_OPFIFO_EMPTY, 1);
+}
+
+static int sf_put_byte(u8 val)
+{
+       int ret = sf_wait_eq(SF_MANUAL_DFIFO_FULL, 0);
+
+       if (ret)
+               return ret;
+
+       __raw_writel(val, sf_reg(SF_MANUAL_DFIFO_WDATA));
+       return 0;
+}
+
+static int sf_put_bytes(const u8 *buf, size_t len)
+{
+       size_t i;
+       int ret;
+
+       for (i = 0; i < len; i++) {
+               ret = sf_put_byte(buf[i]);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int sf_get_byte(u8 *val)
+{
+       int ret = sf_wait_eq(SF_MANUAL_DFIFO_EMPTY, 0);
+
+       if (ret)
+               return ret;
+
+       *val = __raw_readl(sf_reg(SF_MANUAL_DFIFO_RDATA)) & 0xff;
+       __raw_writel(1, sf_reg(SF_MANUAL_DFIFO_RD));
+       return 0;
+}
+
+static int sf_finish(void)
+{
+       int ret;
+
+       ret = sf_op(OP_CSH, 1);
+       if (ret)
+               return ret;
+
+       return sf_op(OP_CK, 5);
+}
+
+int en75xx_sfc_init(void)
+{
+       int ret;
+
+       __raw_writel(0x9, sf_reg(SF_SI_CK_SEL));
+       __raw_writel(0, sf_reg(SF_READ_IDLE_EN));
+
+       ret = sf_wait_eq(SF_RDCTL_FSM, 0);
+       if (ret)
+               return ret;
+
+       __raw_writel(0x9, sf_reg(SF_MTX_MODE_TOG));
+       __raw_writel(1, sf_reg(SF_MACMUX_SEL));
+       __raw_writel(1, sf_reg(SF_MANUAL_EN));
+
+       return 0;
+}
+
+static int sf_nand_load_page(u32 page)
+{
+       u8 page_addr[] = { page >> 16, page >> 8, page };
+       u8 status;
+       unsigned int timeout = SPIN_LIMIT;
+       int ret;
+
+       ret = sf_op(OP_CSL, 1);
+       if (ret)
+               return ret;
+       ret = sf_op(OP_OUTS, 1);
+       if (ret)
+               return ret;
+       ret = sf_put_byte(0x13);
+       if (ret)
+               return ret;
+       ret = sf_op(OP_OUTS, ARRAY_SIZE(page_addr));
+       if (ret)
+               return ret;
+       ret = sf_put_bytes(page_addr, ARRAY_SIZE(page_addr));
+       if (ret)
+               return ret;
+       ret = sf_finish();
+       if (ret)
+               return ret;
+
+       while (timeout--) {
+               ret = sf_op(OP_CSL, 1);
+               if (ret)
+                       return ret;
+               ret = sf_op(OP_OUTS, 1);
+               if (ret)
+                       return ret;
+               ret = sf_put_byte(0x0f);
+               if (ret)
+                       return ret;
+               ret = sf_op(OP_OUTS, 1);
+               if (ret)
+                       return ret;
+               ret = sf_put_byte(0xc0);
+               if (ret)
+                       return ret;
+               ret = sf_op(OP_INS, 1);
+               if (ret)
+                       return ret;
+               ret = sf_get_byte(&status);
+               if (ret)
+                       return ret;
+               ret = sf_finish();
+               if (ret)
+                       return ret;
+               if (!(status & BIT(0)))
+                       return 0;
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int sf_nand_read_cache(u32 column, u8 *dst, size_t len,
+                             bool dummy_append)
+{
+       u8 address[3];
+       size_t i;
+       int ret;
+
+       if (dummy_append) {
+               address[0] = column >> 8;
+               address[1] = column;
+               address[2] = 0;
+       } else {
+               address[0] = 0;
+               address[1] = column >> 8;
+               address[2] = column;
+       }
+
+       ret = sf_op(OP_CSL, 1);
+       if (ret)
+               return ret;
+       ret = sf_op(OP_OUTS, 1);
+       if (ret)
+               return ret;
+       ret = sf_put_byte(0x03);
+       if (ret)
+               return ret;
+       ret = sf_op(OP_OUTS, ARRAY_SIZE(address));
+       if (ret)
+               return ret;
+       ret = sf_put_bytes(address, ARRAY_SIZE(address));
+       if (ret)
+               return ret;
+
+       for (i = 0; i < len; i++) {
+               ret = sf_op(OP_INS, 1);
+               if (ret)
+                       return ret;
+               ret = sf_get_byte(&dst[i]);
+               if (ret)
+                       return ret;
+       }
+
+       return sf_finish();
+}
+
+static int sf_nor_read_once(u32 offset, u8 *dst, size_t len, bool addr4b,
+                           bool dummy_append)
+{
+       u8 address[4];
+       size_t addr_len = 0;
+       size_t i;
+       int ret;
+
+       if (addr4b)
+               address[addr_len++] = offset >> 24;
+       if (dummy_append) {
+               address[addr_len++] = offset >> 8;
+               address[addr_len++] = offset;
+               address[addr_len++] = 0;
+       } else {
+               address[addr_len++] = offset >> 16;
+               address[addr_len++] = offset >> 8;
+               address[addr_len++] = offset;
+       }
+
+       ret = sf_op(OP_CSL, 1);
+       if (ret)
+               return ret;
+       ret = sf_op(OP_OUTS, 1);
+       if (ret)
+               return ret;
+       ret = sf_put_byte(0x03);
+       if (ret)
+               return ret;
+
+       ret = sf_op(OP_OUTS, addr_len);
+       if (ret)
+               return ret;
+       ret = sf_put_bytes(address, addr_len);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < len; i++) {
+               ret = sf_op(OP_INS, 1);
+               if (ret)
+                       return ret;
+               ret = sf_get_byte(&dst[i]);
+               if (ret)
+                       return ret;
+       }
+
+       return sf_finish();
+}
+
+static int sf_nor_read(u32 offset, u8 *dst, size_t len, bool addr4b,
+                      bool dummy_append)
+{
+       while (len) {
+               size_t chunk = NOR_READ_CHUNK - (offset % NOR_READ_CHUNK);
+               int ret;
+
+               if (chunk > len)
+                       chunk = len;
+               ret = sf_nor_read_once(offset, dst, chunk, addr4b,
+                                      dummy_append);
+               if (ret)
+                       return ret;
+               offset += chunk;
+               dst += chunk;
+               len -= chunk;
+       }
+
+       return 0;
+}
+
+int en75xx_sfc_read(u32 offset, void *dst, size_t len)
+{
+       u32 strap = __raw_readl(sf_reg(SF_STRAP));
+       u8 *buf = dst;
+       int ret;
+
+       if (!(strap & SF_STRAP_SPI_NAND))
+               return sf_nor_read(offset, buf, len, strap & SF_STRAP_ADDR_4B,
+                                  strap & SF_STRAP_DUMMY_APPEND);
+
+       while (len) {
+               u32 page = offset / NAND_PAGE_SIZE;
+               u32 column = offset % NAND_PAGE_SIZE;
+               size_t chunk = NAND_PAGE_SIZE - column;
+
+               if (chunk > len)
+                       chunk = len;
+
+               ret = sf_nand_load_page(page);
+               if (ret)
+                       return ret;
+               ret = sf_nand_read_cache(column, buf, chunk,
+                                        strap & SF_STRAP_DUMMY_APPEND);
+               if (ret)
+                       return ret;
+
+               offset += chunk;
+               buf += chunk;
+               len -= chunk;
+       }
+
+       return 0;
+}
diff --git a/arch/mips/mach-en75xx/spl/Makefile 
b/arch/mips/mach-en75xx/spl/Makefile
new file mode 100644
index 00000000..d19fc6e6
--- /dev/null
+++ b/arch/mips/mach-en75xx/spl/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0+
+obj-y += spl.o
diff --git a/arch/mips/mach-en75xx/spl/spl.c b/arch/mips/mach-en75xx/spl/spl.c
new file mode 100644
index 00000000..a42b2837
--- /dev/null
+++ b/arch/mips/mach-en75xx/spl/spl.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <errno.h>
+#include <init.h>
+#include <spl.h>
+#include <spl_load.h>
+#include <mach/en75xx.h>
+
+void __noreturn board_init_f(ulong dummy)
+{
+       spl_init();
+
+       if (IS_ENABLED(CONFIG_SPL_SERIAL))
+               preloader_console_init();
+
+       board_init_r(NULL, 0);
+}
+
+static ulong en75xx_spl_read(struct spl_load_info *load, ulong offset,
+                            ulong count, void *buf)
+{
+       if (en75xx_sfc_read(offset, buf, count))
+               return 0;
+
+       return count;
+}
+
+static int en75xx_spl_load_image(struct spl_image_info *spl_image,
+                                struct spl_boot_device *bootdev)
+{
+       struct spl_load_info load;
+
+       if (en75xx_sfc_init())
+               return -EIO;
+
+       spl_load_init(&load, en75xx_spl_read, NULL, 1);
+       return spl_load(spl_image, bootdev, &load, 0,
+                       EN75XX_UBOOT_IMAGE_OFFSET);
+}
+
+SPL_LOAD_IMAGE_METHOD("EN75XX SFC", 0, BOOT_DEVICE_BOARD,
+                     en75xx_spl_load_image);
+
+u32 spl_boot_device(void)
+{
+       return BOOT_DEVICE_BOARD;
+}
diff --git a/arch/mips/mach-en75xx/tpl/Makefile 
b/arch/mips/mach-en75xx/tpl/Makefile
new file mode 100644
index 00000000..da834c75
--- /dev/null
+++ b/arch/mips/mach-en75xx/tpl/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+extra-y += start.o
+obj-y += tpl.o
diff --git a/arch/mips/mach-en75xx/tpl/start.S 
b/arch/mips/mach-en75xx/tpl/start.S
new file mode 100644
index 00000000..1c192e2e
--- /dev/null
+++ b/arch/mips/mach-en75xx/tpl/start.S
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <mach/en75xx.h>
+
+       .set noreorder
+
+ENTRY(_start)
+       b       reset
+        mtc0   zero, CP0_COUNT
+
+       /*
+        * TCBoot image descriptor. The magic is byte-reversed in the vendor
+        * format, while section offsets are stored as big-endian words.
+        */
+       .org    0x0c
+       .byte   0x36, 0x35, 0x37, 0x38
+       .word   EN75XX_DDR_BLOB_OFFSET
+       .word   EN75XX_DDR_BLOB_OFFSET + EN75XX_DDR_BLOB_SIZE
+       .word   EN75XX_SPL_IMAGE_OFFSET
+       .word   EN75XX_UBOOT_IMAGE_OFFSET
+
+       /* The vendor reset code begins at offset 0x48. */
+       .org    0x48
+reset:
+       /* Disable interrupts and force KSEG0 uncached before DDR exists. */
+       mfc0    t0, CP0_STATUS
+       li      t1, ~(ST0_IE | ST0_KSU)
+       and     t0, t0, t1
+       li      t1, ST0_BEV | ST0_ERL
+       or      t0, t0, t1
+       mtc0    t0, CP0_STATUS
+       mtc0    zero, CP0_CAUSE
+       mtc0    zero, CP0_COMPARE
+
+       mfc0    t0, CP0_CONFIG
+       li      t1, ~7
+       and     t0, t0, t1
+       ori     t0, t0, 2
+       mtc0    t0, CP0_CONFIG
+       ehb
+
+       /* Arbiter and static-memory mappings copied from TCBoot. */
+       li      t0, EN75XX_SYSCTL_BASE
+       li      t1, 0x80071f1e
+       sw      t1, 0x20(t0)
+       li      t1, 0x00071f1f
+       sw      t1, 0x24(t0)
+       li      t1, 0x80050000
+       sw      t1, 0x34(t0)
+
+       li      t0, EN75XX_SMC_BASE
+       li      t1, 0x102d1040
+       sw      t1, 0x00(t0)
+       li      t1, 0x200028d0
+       sw      t1, 0x14(t0)
+
+       /* Disable the interrupt controller. */
+       li      t0, EN75XX_INTC_BASE
+       sw      zero, 0x00(t0)
+       sw      zero, 0x04(t0)
+
+       /* Enable PBUS access to the 48 KiB FE SRAM. */
+       li      t0, EN75XX_FE_SRAM_SEL
+       lw      t1, 0(t0)
+       ori     t1, t1, 1
+       sw      t1, 0(t0)
+       sync
+
+       li      sp, EN75XX_TPL_STACK_ADDR
+       li      fp, EN75XX_TPL_STACK_ADDR
+
+       la      t9, tpl_main
+       jalr    t9
+        nop
+
+1:     b       1b
+        nop
+END(_start)
+
+LEAF(en75xx_run_ddr_blob)
+       li      t0, EN75XX_TPL_SAVED_RA
+       sw      ra, 0(t0)
+
+       la      t1, 1f
+       li      t0, EN75XX_SCREG_WR0
+       sw      t1, 0(t0)
+       sync
+
+       li      t9, EN75XX_DDR_BLOB_ADDR
+       jr      t9
+        nop
+1:
+       li      t0, EN75XX_SCREG_WR0
+       sw      zero, 0(t0)
+       li      t0, EN75XX_TPL_SAVED_RA
+       lw      ra, 0(t0)
+       jr      ra
+        nop
+END(en75xx_run_ddr_blob)
diff --git a/arch/mips/mach-en75xx/tpl/tpl.c b/arch/mips/mach-en75xx/tpl/tpl.c
new file mode 100644
index 00000000..ce4ae6b6
--- /dev/null
+++ b/arch/mips/mach-en75xx/tpl/tpl.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <compiler.h>
+#include <linux/byteorder/generic.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <mach/en75xx.h>
+
+#define IH_MAGIC       0x27051956
+#define IH_HDR_SIZE    64
+
+struct en75xx_legacy_header {
+       u32 magic;
+       u32 hcrc;
+       u32 time;
+       u32 size;
+       u32 load;
+       u32 ep;
+       u32 dcrc;
+       u8 os;
+       u8 arch;
+       u8 type;
+       u8 comp;
+       u8 name[32];
+};
+
+static u32 get_be32(u32 val)
+{
+       return be32_to_cpu(val);
+}
+
+/*
+ * 16550 register access differs by SoC endianness:
+ *   EN7512 (big-endian): 8-bit registers live in the top byte of each 32-bit
+ *   word, i.e. at byte offset (reg << 2) + 3.
+ *   EN7528 (little-endian, reg-io-width=4, reg-shift=2): 32-bit word access at
+ *   (reg << 2); the 8-bit value sits in the low byte.
+ */
+#if defined(CONFIG_SOC_EN7528)
+static inline void tpl_uart_wr(u32 reg, u32 val)
+{
+       __raw_writel(val, (void __iomem *)(EN75XX_UART0_BASE + (reg << 2)));
+}
+
+static inline u32 tpl_uart_rd(u32 reg)
+{
+       return __raw_readl((void __iomem *)(EN75XX_UART0_BASE + (reg << 2)));
+}
+#else
+static inline void tpl_uart_wr(u32 reg, u32 val)
+{
+       __raw_writeb(val, (void __iomem *)(EN75XX_UART0_BASE + (reg << 2) + 3));
+}
+
+static inline u32 tpl_uart_rd(u32 reg)
+{
+       return __raw_readb((void __iomem *)(EN75XX_UART0_BASE + (reg << 2) + 
3));
+}
+#endif
+
+#define UART_RBR_THR   0       /* +0x00: receive/transmit, DLL when DLAB=1 */
+#define UART_IER_DLM   1       /* +0x04: interrupt enable, DLM when DLAB=1 */
+#define UART_IIR_FCR   2       /* +0x08: FIFO control */
+#define UART_LCR       3       /* +0x0c: line control (DLAB) */
+#define UART_MCR       4       /* +0x10: modem control */
+#define UART_LSR       5       /* +0x14: line status (bit5 = THR empty) */
+
+static void tpl_putc(u8 ch)
+{
+       while (!(tpl_uart_rd(UART_LSR) & 0x20))
+               ;
+       tpl_uart_wr(UART_RBR_THR, ch);
+}
+
+static void tpl_hang(u8 code)
+{
+       tpl_putc(code);
+       tpl_putc('\r');
+       tpl_putc('\n');
+
+       for (;;)
+               ;
+}
+
+static void tpl_uart_init(void)
+{
+#if defined(CONFIG_SOC_EN7528)
+       /*
+        * EN7528 UART fractional divider (RE'd from the stock bootbase, live-
+        * verified): 115200 8N1 = XYD 0xEA00FDE8 (X=round(baud*13/25)=0xEA00,
+        * Y=65000), DLL=1, DLM=0. XYD is reg 11 @ +0x2c.
+        */
+       tpl_uart_wr(UART_LCR, 0x80);            /* DLAB = 1 */
+       tpl_uart_wr(11, 0xea00fde8);            /* XYD fractional divider */
+       tpl_uart_wr(UART_RBR_THR, 0x01);        /* DLL = 1 */
+       tpl_uart_wr(UART_IER_DLM, 0x00);        /* DLM = 0 */
+       tpl_uart_wr(UART_LCR, 0x03);            /* 8N1, DLAB = 0 */
+       tpl_uart_wr(UART_IIR_FCR, 0x07);        /* enable + clear FIFOs */
+       tpl_uart_wr(UART_MCR, 0x00);
+       tpl_uart_wr(UART_IER_DLM, 0x00);
+#else
+       void __iomem *base = (void __iomem *)EN75XX_UART0_BASE;
+
+       __raw_writeb(0x80, base + 0x0f);
+       __raw_writel(0xea00fde8, base + 0x2c);
+       __raw_writeb(0x01, base + 0x03);
+       __raw_writeb(0x00, base + 0x07);
+       __raw_writeb(0x03, base + 0x0f);
+       __raw_writeb(0x0f, base + 0x0b);
+       __raw_writeb(0x00, base + 0x13);
+       __raw_writeb(0x00, base + 0x27);
+       __raw_writeb(0x00, base + 0x07);
+#endif
+}
+
+void __noreturn tpl_main(void)
+{
+       struct en75xx_legacy_header *hdr =
+               (struct en75xx_legacy_header *)EN75XX_SPL_HEADER_ADDR;
+       void (*entry)(void);
+       u32 load, size, ep;
+       int ret;
+
+       tpl_uart_init();
+       if (en75xx_sfc_init())
+               tpl_hang('I');
+
+       ret = en75xx_sfc_read(EN75XX_DDR_BLOB_OFFSET,
+                             (void *)EN75XX_DDR_BLOB_ADDR,
+                             EN75XX_DDR_BLOB_SIZE);
+       if (ret)
+               tpl_hang('F');
+
+       en75xx_run_ddr_blob();
+
+       ret = en75xx_sfc_read(EN75XX_SPL_IMAGE_OFFSET, hdr, IH_HDR_SIZE);
+       if (ret || get_be32(hdr->magic) != IH_MAGIC)
+               tpl_hang('H');
+
+       size = get_be32(hdr->size);
+       load = get_be32(hdr->load);
+       ep = get_be32(hdr->ep);
+
+       if (!size || (load & 0xe0000000) != 0x80000000 ||
+           (ep & 0xe0000000) != 0x80000000 || load + size < load)
+               tpl_hang('L');
+
+       /* Write through KSEG1 so no dirty cache lines hide the SPL image. */
+       ret = en75xx_sfc_read(EN75XX_SPL_IMAGE_OFFSET + IH_HDR_SIZE,
+                             (void *)(load | 0x20000000), size);
+       if (ret)
+               tpl_hang('S');
+
+       __asm__ volatile("sync" : : : "memory");
+       entry = (void (*)(void))ep;
+       entry();
+       __builtin_unreachable();
+}
-- 
2.53.0

Reply via email to