From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id E9651C61DB9 for ; Tue, 25 Aug 2026 22:50:45 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id B00D94064F; Tue, 25 Aug 2026 22:50:45 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id iCAYlMSrq09X; Tue, 25 Aug 2026 22:50:41 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.166.142; helo=lists1.osuosl.org; envelope-from=u-boot-bounces@lists.u-boot-project.org; receiver= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lists.u-boot-project.org; s=default; t=1787698223; bh=5Gk6Lf68tCfSNmgBzL7dGazS6cOZfdvlAfXcFUJS3Fw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=z1GqN2UYwLsUK8mGJWmWMoedmQ9a0b9iVF+UPiq4w3zaiesMsLHQK4Q/Bt15xaKrO Wn2ycEqDm3j6TnQ34KK9B9IMk7HWbQkq/rlVAboIS0rHsQAJIytLc5jTnEHQn7l0Oa D9cJYK7vlJAXUHh11Zvex7+iARgb1bbZS/+cyKgGvH63q1jsGnBJSDiiefFoyLt3lq 5tz0eVgbciugjWnM3I1eU2SSsfXuzmpYRDubxOAJsQ35GCPmb5h/1oePRpwPe6w413 Zn0vJ72Oy8v4+gQXsWGix+Uk4Kft3dC8ZaklVsF7xF/NBmN0qARihCeXN44F2vfzG2 W2CvYPguefpHA== Received: from lists1.osuosl.org (lists1.osuosl.org [140.211.166.142]) by smtp4.osuosl.org (Postfix) with ESMTP id BF8B640711; Tue, 25 Aug 2026 22:50:23 +0000 (UTC) Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists1.osuosl.org (Postfix) with ESMTP id 8FA572FD for ; Tue, 25 Aug 2026 22:44:03 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 762E4606F6 for ; Tue, 25 Aug 2026 22:44:03 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Jl0Eou22q9hc for ; Tue, 25 Aug 2026 22:44:02 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=34.2.90.19; helo=sonic.asd.mail.yahoo.com; envelope-from=maskachoska@yahoo.com; receiver= Authentication-Results: smtp3.osuosl.org; dmarc=pass (p=reject dis=none) header.from=yahoo.com Authentication-Results: smtp3.osuosl.org; dkim=pass (2048-bit key) header.d=yahoo.com header.i=@yahoo.com header.a=rsa-sha256 header.s=s2048 header.b=H29PQ6cI Received: from sonic.asd.mail.yahoo.com (sonic-asso1-0020.asd.mail.yahoo.com [34.2.90.19]) by smtp3.osuosl.org (Postfix) with ESMTPS id 7C444606AE for ; Tue, 25 Aug 2026 22:44:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1787697837; bh=5Gk6Lf68tCfSNmgBzL7dGazS6cOZfdvlAfXcFUJS3Fw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Subject:Reply-To; b=H29PQ6cI68afxE5EVP+rqpRKlknSA/ojWe2ng2xyXneNgg/h6+80YHogfkJHZxeRLyQBhP+gZg2YJTLWq2shylMlxU62SaD901fofhfiSKrp8vFirgm8mlv/7V2oH8C6oSqBlhONqDXXf23HFW1Jug6mGiTKP2G12X6ijiMwIMwWaG6yM/W33WTkJ+unzItlblwCbHMZPl1W1Z60wHF/02EAlvXzLDsVROra6sGwE3gchOLT4sHGW9QoPrQ6xt446m2sijmwMh/5eJYnLtw6Uk583ycB1YVGKGiwuPizsy1KRig7ttT8E94bdFWD+KoZaJUbsKwoJDqMl8B6HHZCeQ== X-SONIC-DKIM-SIGN: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1787697837; bh=tvILe9+07DVQK12p24pu0BZHr08dTE31hZordIOwhT4=; h=X-Sonic-MF:From:To:Subject:Date:From:Subject; b=AbD5R7Bm9axuh3l9MhDcjrnx0NYK8N9CocnLOqbCedqCR0820nX14YtP7S1cleGAPZOTmgB/XuYaJnGiBi3hyGY+FTgS0MXhAA++uePUP1jR4lJtWo3sSniIZ7i+v8zUWl60sAq2D9hM5JFvSfmxuppRkhljHeN0g3SroknQ4nTJ8fAfz76RekT+YPUFun8KydsJhp9d/b4GF+SsqTYmqQzN3+AP20Rsimv/ElCj/1dWE3hTvfkAKI4+jZtJAMKsZzqmQpPad2tCt4tT/HbFeh5n984onfwqVc7cmFqnmkY15AC8ewOHtNA04B7wIlGKlcOBmddCbxqMZE6qFAB9cQ== X-YMail-OSG: j3kcF2kVM1n7J_scR2r_r0hIjiBZ3T1cQTpPuHdnDG19J.P1OjNAd1GPGdibb44 rWb.JPBm6WEpD5ynP9Jq__EefdHG4KG4GV4FhfiKjTc3d4nW4JKGvn9fIXp5OS77tlRzeT8CM3Or BpmeBfS0ItD98xO_VKTsZNIbnEBUANvgAufIi8FTfyr6Nsu9EXtuakwxutKQVDlwJXpP2efKmvgJ bY1O6ZzxQzIBM6ga2JJRZ0YSAaB4iXoAPHcdRtOEnNQFAk9H1NYjTfbZAMKJe5XqJE5d6pCMLJVF dAWnrAcR5Po3za8d5iXmpANsCpZVZI14xItxkDQZlEdGK64JrM6roD7iEbX8bylra4ukgM8kqXyW N0ltn6pHtx56YB22NrHoIFYPgTXuB_aoPiCPUsBVj5Uvoo3h.t38VOXcosuglqyce5VkzoVvcq62 PNLuEJG8EruKtV3eLAmBco4VOGIWEUIZflycETQbgvhJce2OFX8qGCU40hfpTdcF2DbL5i8adiAs 5_KIg3NjEAPMug2vkTKIFrmSAVdFgDqQN95qd.OFP1txy6A5gV3ipF4fub4EctfOUAxpPNzJdfFb foo31BI8EtKdWn.JVyuKkxk0b7O5caC.hQ94vMUkk.9_iYQ6MQBF2ajKS3JzBBIBpsr4ven0hub_ qRinxw1lv01VwMKZIr.E70RLx5uM8NTcT6jZowlSa.P3qkjZ96OrANEJRmTOjQdpsi5XCP2HeItI MVAKJJRBScAEo35sVmIVmPX_Vix1WsFK3x7_SLHwGTEeBUZeH0Leunor30hF9pv_GrdwPwW77cxA c.D_rflci8x67yN_fQsPjYbJXmHCl9krKW3hp_.1YQ3HszJc0agrPZ6T5bfS5RmFL3SCbsj2bwD. g7pwQNuXtfvE1jN1O.OvavQkR77pZVKNjXIpdo5zXshpGZck9we1VEYhPh1wY8wGAEhlZ1Gm.iRg 1OlnbjxKpG1wIsdweSKXAFdIf9_56WMQBnoDU0hs4IY.AfiDk6S_U95w1xD1AXt3Lh1Xs0EMjTQ2 31WPV_bdfzcSFaT7tZ4bGgdFUxhvjgIzOKqK3Rt9wgzc3EDZZapS.LtFxzY3NurpDN9tG4SVzokI 5guyHIAYXP7XBcfq0mMheC9YawldJctI68SxiPudAs6G34XFY2JuZ.R15M6p.Q2UjtgpGpTiK_Cb mUeCHP6IGIFMcGwCQs6hw85Apqid.OysgJmkLKQBNz0KJcHkmBKLRFxSAXn2wLOgqc3Zlpuf8xEl fulPQPD6uJUL42.4mgf1oH2tcFHMS2KN9RJPYIdRPFr44fjJ2EA_.BEp2I8JrkyPxWpU0qYtMxqP UX_2W._Q_jVDchiPvjWdmsUblFIdMjAIVheUuBwX1GglA2Mm3FIhHpWpQxEqeqhqFmYYQDFRNglW dcnS3OAVKYLHE6uDY.efS80Cg9LtW9b_wy16D5.XHQayzU9T.ghUIDFlmz86S1TrlWwokN.Jt0SV D8lisVL7Qbs2dB.B9wb4i9h5T.gmSL_uGjOjkT34o1wSOWpNeEuTjfnhyhJJ87fUcxF7akA676bg PoQkQizdn1I7BV7AJrBM.Oj41vUD13TrY0yZbbrzIeLl3l1eG9w2VFu.3n8nqDUN8BbnaelzUZS7 eF11m_qg0_z2SYXW5kqrwV6LrR5RNoLxHKk6vik0b0C6vZrgRBW52oIWN0pc5S9MbN9PKg3xSKHc bcf8yIAcCkMIYpzRpfHFqx_HQg7oXqGefH6oEILj7MtyG8sTFoJpRv3ltL_UDqKIPU456_1Kpxx8 67lQoqEoe1GLGogWIRHHY2GFGAd1JioNd7TpbSyxBIqGcNwVFifiPe.f.nzC.K05HdIDNK2ZEs3g gXI7BCWvyVBFcR37OURZcS9rnFbZnVtH0gh1PuWJEXHJmLtoymV.qBZD2sgI.06dAAc_Z4eSLt1L HV5IA0xXOSAi5Yw2BBurySpuaj1FKDP51KcfMpIoCVAKTwTFlyV2YnS1lyBEnYCWdIy5RaW8wpGS w_d1Bgkpz5h7Y8ynqK5GQdeal2t9fmI8cds1doUkKgT6D_BdpTZzUXFpzmEnhBusm.M4hNj1588X 7BB.zFHEWh04Zlj2gt2USV8A- X-Sonic-MF: X-Sonic-ID: 4c402f9c-31d4-476f-b955-7fb6a3c05e8f Received: from sonic.gate.mail.ne1.yahoo.com by mail-asdoutdeli-p-cin-asso1-prod-sonicconsumer-svc-101 with HTTP; Tue, 25 Aug 2026 22:43:57 +0000 Received: from dip125.lsn.sg3.yahoo.com ([106.10.223.134]) by hello (SMTP) with SMTP ID 692ceff9cbf31ebf3ce1df27c5387b11; Tue, 25 Aug 2026 22:43:56 +0000 (UTC) From: AK Sharma To: u-boot@lists.u-boot-project.org Cc: Christian Marangi , Daniel Schwierzeck , Dario Binacchi , Frieder Schrempf , Jerome Forissier , Marek Vasut , Michael Trimarchi , Tom Rini , AK Sharma Subject: [PATCH v1 02/12] mips: en75xx: add TPL and SPL early boot stages Date: Wed, 26 Aug 2026 04:11:57 +0530 Message-ID: <20260825224330.713151-3-maskachoska@yahoo.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260825224330.713151-1-maskachoska@yahoo.com> References: <20260825224330.713151-1-maskachoska@yahoo.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Mailman-Approved-At: Tue, 25 Aug 2026 22:49:58 +0000 X-BeenThere: u-boot@lists.u-boot-project.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.u-boot-project.org 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 --- 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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +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 +#include +#include +#include + + .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 +#include +#include +#include +#include + +#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