All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB
@ 2026-10-02 13:02 Benoît Monin
  2026-10-02 13:02 ` [PATCH 01/10] iopoll: Add readw_poll_sleep_timeout Benoît Monin
                   ` (9 more replies)
  0 siblings, 10 replies; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

The MT2824 is a RISC-V SoC integrating a dual-core Andes AX45MP with
256 KB of L2 cache, Andes PLIC/PLICSW/PLMT interrupt and timer
controllers, an ATCSPI200 QSPI controller, a DesignWare-derived SDHCI
controller and a DDR controller that is initialized by firmware loaded
by the SPL from the boot device.

The SoC is used together with two MT3812 5G transceivers to form the
MT5824 reference design [1]. The evaluation board allows testing this
reference design and provides a 32 MiB SPI-NOR flash as well as an SD
card port for booting.

This series adds support for the platform: the SoC drivers (SDHCI, DDR
controller and a simple reset controller), the SPL boot flow (GPT
partition lookup with A/B slot selection driven by the ROM one-shot
boot registers), the environment backend selected at runtime from the
boot source, and SPI-NOR support (an nvmxip memory-mapped device plus
spi-mem operations for the Metanoia variant of the ATCSPI200
controller).

The series is organized as follows:

- patches 1-5: generic fixes and helpers (16-bit iopoll variant,
  simple reset controller driver, GPT parsing in SPL, NVM XIP devices
  in SPL, Andes erratum #35102 Kconfig option)
- patches 6-8: spi-mem support for the ATCSPI200 QSPI controller and
  the MT2824 DDR and SDHCI drivers
- patches 9-10: device trees and MT5824 EVB platform support

The series was built and tested on the MT5824 EVB:

- boot from SD card and from QSPI NOR
- environment loaded from the boot device (MMC and SPI flash)
- SPL A/B boot from the GPT partitions
- sf probe/read/erase on the Macronix NOR flash

[1]: https://metanoia-comm.com/products/5g/mt5824/

Base commit: a06e89ab05e ("Prepare v2026.10-rc5")

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
Benoît Monin (9):
      iopoll: Add readw_poll_sleep_timeout
      reset: Add a driver for simple controller
      disk: efi: Reuse the GPT entries buffer with simple malloc
      nvmxip: Make NVM XIP devices available in SPL
      mmc: metanoia_sdhci: Add SDHCI driver for Metanoia MT2824 SoC
      ram: metanoia: Add driver for MT2824 DDR controller
      spi: atcspi200_spi: Add spi-mem support for the Metanoia ATCSPI200 variant
      riscv: dts: add MT2824 SoC and MT5824 EVB device trees
      riscv: metanoia: Add support for the MT5824 EVB platform

Jun Chang (1):
      riscv: andes: Add Kconfig option for Andes erratum #35102

 MAINTAINERS                              |   5 +
 arch/riscv/Kconfig                       |   4 +
 arch/riscv/cpu/andes/Kconfig             |   3 +
 arch/riscv/cpu/andes/cpu.c               |   7 +-
 arch/riscv/dts/Makefile                  |   1 +
 arch/riscv/dts/mt2824-u-boot.dtsi        |  66 +++
 arch/riscv/dts/mt2824.dtsi               | 219 ++++++++++
 arch/riscv/dts/mt5824-evb.dts            |  34 ++
 board/metanoia/mt5824-evb/Kconfig        |  45 ++
 board/metanoia/mt5824-evb/MAINTAINERS    |  11 +
 board/metanoia/mt5824-evb/Makefile       |   7 +
 board/metanoia/mt5824-evb/board.c        |  61 +++
 board/metanoia/mt5824-evb/board.h        |  35 ++
 board/metanoia/mt5824-evb/mt5824-evb.env |   6 +
 board/metanoia/mt5824-evb/spl.c          | 388 +++++++++++++++++
 common/spl/Kconfig                       |   8 +
 configs/metanoia-mt5824-evb_defconfig    |  73 ++++
 disk/part_efi.c                          |  25 +-
 doc/board/index.rst                      |   1 +
 doc/board/metanoia/index.rst             |   9 +
 doc/board/metanoia/mt5824-evb.rst        | 232 ++++++++++
 drivers/mmc/Kconfig                      |  11 +
 drivers/mmc/Makefile                     |   1 +
 drivers/mmc/metanoia_sdhci.c             | 494 +++++++++++++++++++++
 drivers/mtd/Makefile                     |   1 +
 drivers/ram/Kconfig                      |   7 +
 drivers/ram/Makefile                     |   2 +
 drivers/ram/mt2824-ddr.c                 | 706 +++++++++++++++++++++++++++++++
 drivers/reset/Kconfig                    |   8 +
 drivers/reset/Makefile                   |   1 +
 drivers/reset/reset-simple.c             | 115 +++++
 drivers/spi/atcspi200_spi.c              | 176 +++++++-
 include/configs/metanoia-mt5824-evb.h    |  28 ++
 include/linux/iopoll.h                   |   3 +
 34 files changed, 2780 insertions(+), 13 deletions(-)
---
base-commit: a06e89ab05eaa2b8344d521319399333cd760ae5
change-id: 20260622-mt2824-97193f1fbe29

Best regards,
--  
Benoît Monin, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 01/10] iopoll: Add readw_poll_sleep_timeout
  2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
@ 2026-10-02 13:02 ` Benoît Monin
  2026-10-02 15:25   ` Tom Rini
  2026-10-02 13:02 ` [PATCH 02/10] reset: Add a driver for simple controller Benoît Monin
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

The readl poll API with sleep and timeout support was introduced by
commit ce786ae3913f ("iopoll: Add readl_poll_sleep_timeout"), add the
16-bit variant.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 include/linux/iopoll.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h
index 0ee2bddaa83..ab2f363f277 100644
--- a/include/linux/iopoll.h
+++ b/include/linux/iopoll.h
@@ -46,6 +46,9 @@
 #define readx_poll_sleep_timeout(op, addr, val, cond, sleep_us, timeout_us) \
 	read_poll_timeout(op, val, cond, sleep_us, timeout_us, addr)
 
+#define readw_poll_sleep_timeout(addr, val, cond, sleep_us, timeout_us) \
+	readx_poll_sleep_timeout(readw, addr, val, cond, sleep_us, timeout_us)
+
 #define readl_poll_sleep_timeout(addr, val, cond, sleep_us, timeout_us) \
 	readx_poll_sleep_timeout(readl, addr, val, cond, sleep_us, timeout_us)
 

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 02/10] reset: Add a driver for simple controller
  2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
  2026-10-02 13:02 ` [PATCH 01/10] iopoll: Add readw_poll_sleep_timeout Benoît Monin
@ 2026-10-02 13:02 ` Benoît Monin
  2026-10-02 13:02 ` [PATCH 03/10] disk: efi: Reuse the GPT entries buffer with simple malloc Benoît Monin
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

Add a driver for simple reset controllers, where each reset line is
controlled by a single bit in a memory-mapped register range.

The implementation is derived from the Linux reset-simple driver. The
controller logic itself is generic; the driver currently matches the
Metanoia MT2824 instance and further controllers can be supported by
adding their compatible to the match table.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 MAINTAINERS                  |   5 ++
 drivers/reset/Kconfig        |   8 +++
 drivers/reset/Makefile       |   1 +
 drivers/reset/reset-simple.c | 115 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 129 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index eb4209bdea6..aed0dcc515a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1570,6 +1570,11 @@ S:	Maintained
 T:	git https://git.u-boot-project.org/u-boot/custodians/u-boot-nand-flash.git
 F:	drivers/mtd/nand/raw/
 
+RESET SIMPLE DRIVER
+M:	Benoît Monin <benoit.monin@bootlin.com>
+S:	Maintained
+F:	drivers/reset/reset-simple.c
+
 RISC-V
 M:	Tim Ouyang <tim609@andestech.com>
 M:	Leo Liang <leo.liang@sifive.com>
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index db764ed5ace..be178125be6 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -149,6 +149,14 @@ config RESET_MESON
 	help
 	  Support for reset controller on Amlogic Meson SoC.
 
+config RESET_SIMPLE
+	bool "Generic driver for simple reset controller"
+	depends on DM_RESET
+	help
+	  This enables the reset driver for simple controllers where
+	  each reset line is controlled by a single bit in a memory
+	  mapped region.
+
 config RESET_SOCFPGA
 	bool "Reset controller driver for SoCFPGA"
 	depends on DM_RESET && ARCH_SOCFPGA
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 384f4336331..553260a71e8 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_RESET_AST2600) += reset-ast2600.o
 obj-$(CONFIG_RESET_AST2700) += reset-ast2700.o
 obj-$(CONFIG_$(PHASE_)RESET_ROCKCHIP) += reset-rockchip.o rst-rk3506.o rst-rk3528.o rst-rk3576.o rst-rk3588.o
 obj-$(CONFIG_RESET_MESON) += reset-meson.o
+obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
 obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
 obj-$(CONFIG_RESET_MEDIATEK) += reset-mediatek.o
 obj-$(CONFIG_RESET_MTMIPS) += reset-mtmips.o
diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c
new file mode 100644
index 00000000000..5df16a341d4
--- /dev/null
+++ b/drivers/reset/reset-simple.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Simple Reset Controller Driver
+ *
+ * Based on Linux reset-simple driver
+ */
+
+#include <dm.h>
+#include <reset-uclass.h>
+#include <linux/io.h>
+
+struct reset_simple_priv {
+	void __iomem *membase;
+	int nr_resets;
+};
+
+static int reset_simple_update(struct reset_ctl *rst, bool assert)
+{
+	struct reset_simple_priv *priv = dev_get_priv(rst->dev);
+	int reg_width = sizeof(u32);
+	int bank, offset;
+	u32 reg;
+
+	if (rst->id >= priv->nr_resets)
+		return -EINVAL;
+
+	bank = rst->id / (reg_width * BITS_PER_BYTE);
+	offset = rst->id % (reg_width * BITS_PER_BYTE);
+
+	reg = readl(priv->membase + (bank * reg_width));
+	if (assert)
+		writel(reg | BIT(offset), priv->membase + (bank * reg_width));
+	else
+		writel(reg & ~BIT(offset), priv->membase + (bank * reg_width));
+
+	return 0;
+}
+
+static int reset_simple_request(struct reset_ctl *rst)
+{
+	struct reset_simple_priv *priv = dev_get_priv(rst->dev);
+
+	if (rst->id >= priv->nr_resets)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int reset_simple_assert(struct reset_ctl *rst)
+{
+	return reset_simple_update(rst, true);
+}
+
+static int reset_simple_deassert(struct reset_ctl *rst)
+{
+	return reset_simple_update(rst, false);
+}
+
+static int reset_simple_status(struct reset_ctl *rst)
+{
+	struct reset_simple_priv *priv = dev_get_priv(rst->dev);
+	int reg_width = sizeof(u32);
+	int bank, offset;
+	u32 reg;
+
+	if (rst->id >= priv->nr_resets)
+		return -EINVAL;
+
+	bank = rst->id / (reg_width * BITS_PER_BYTE);
+	offset = rst->id % (reg_width * BITS_PER_BYTE);
+
+	reg = readl(priv->membase + (bank * reg_width));
+
+	return !!(reg & BIT(offset));
+}
+
+static const struct reset_ops reset_simple_ops = {
+	.request = reset_simple_request,
+	.rst_assert = reset_simple_assert,
+	.rst_deassert = reset_simple_deassert,
+	.rst_status = reset_simple_status,
+};
+
+static const struct udevice_id reset_simple_ids[] = {
+	{ .compatible = "metanoia,mt2824-reset" },
+	{ }
+};
+
+static int reset_simple_probe(struct udevice *dev)
+{
+	struct reset_simple_priv *priv = dev_get_priv(dev);
+	fdt_addr_t addr;
+	fdt_size_t size;
+
+	addr = dev_read_addr_size_index(dev, 0, &size);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->nr_resets = size * BITS_PER_BYTE;
+
+	priv->membase = devm_ioremap(dev, addr, size);
+	if (!priv->membase)
+		return -EFAULT;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(reset_simple) = {
+	.name = "simple-reset",
+	.id = UCLASS_RESET,
+	.of_match = reset_simple_ids,
+	.ops = &reset_simple_ops,
+	.probe = reset_simple_probe,
+	.priv_auto = sizeof(struct reset_simple_priv),
+};

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 03/10] disk: efi: Reuse the GPT entries buffer with simple malloc
  2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
  2026-10-02 13:02 ` [PATCH 01/10] iopoll: Add readw_poll_sleep_timeout Benoît Monin
  2026-10-02 13:02 ` [PATCH 02/10] reset: Add a driver for simple controller Benoît Monin
@ 2026-10-02 13:02 ` Benoît Monin
  2026-10-02 15:26   ` Tom Rini
  2026-10-02 13:02 ` [PATCH 04/10] nvmxip: Make NVM XIP devices available in SPL Benoît Monin
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

The simple malloc used in SPL has a no-op free(), so allocating the
GPT partition entries buffer on every partition table read leaks
memory. With repeated lookups, SPL can exhaust its small heap.

Keep a single cached buffer and reuse it while the allocation size
does not change. The entries are re-read on every call, so the buffer
is device- and LBA-agnostic.

It is safe to reuse the same buffer in the single-threaded xPL context
as the returned pointer is only ever handed out within a single
partition-table lookup and every caller consumes or copies the entries
before the next lookup.

The change is gated on CONFIG_IS_ENABLED(SYS_MALLOC_SIMPLE), so builds
with a real free() (U-Boot proper) keep the original malloc/free behavior.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 disk/part_efi.c | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/disk/part_efi.c b/disk/part_efi.c
index d8b17ec2e91..8a57fe911c4 100644
--- a/disk/part_efi.c
+++ b/disk/part_efi.c
@@ -1141,7 +1141,7 @@ static int find_valid_gpt(struct blk_desc *desc, gpt_header *gpt_head,
 static gpt_entry *alloc_read_gpt_entries(struct blk_desc *desc,
 					 gpt_header *pgpt_head)
 {
-	size_t count = 0, blk_cnt;
+	size_t count = 0, blk_cnt, size;
 	lbaint_t blk;
 	gpt_entry *pte = NULL;
 
@@ -1158,10 +1158,27 @@ static gpt_entry *alloc_read_gpt_entries(struct blk_desc *desc,
 		  (u32)le32_to_cpu(pgpt_head->sizeof_partition_entry),
 		  (ulong)count);
 
+	size = PAD_TO_BLOCKSIZE(count, desc);
+
 	/* Allocate memory for PTE, remember to FREE */
-	if (count != 0) {
-		pte = memalign(ARCH_DMA_MINALIGN,
-			       PAD_TO_BLOCKSIZE(count, desc));
+	if (CONFIG_IS_ENABLED(SYS_MALLOC_SIMPLE)) {
+		/*
+		 * The simple malloc has a no-op free(): keep one
+		 * buffer and reuse it. The entries are re-read on
+		 * every call, so the buffer is device- and LBA-agnostic.
+		 */
+		static gpt_entry *cache;
+		static size_t cache_size;
+
+		if (cache && cache_size == size) {
+			pte = cache;
+		} else {
+			pte = memalign(ARCH_DMA_MINALIGN, size);
+			cache = pte;
+			cache_size = size;
+		}
+	} else {
+		pte = memalign(ARCH_DMA_MINALIGN, size);
 	}
 
 	if (count == 0 || pte == NULL) {

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 04/10] nvmxip: Make NVM XIP devices available in SPL
  2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
                   ` (2 preceding siblings ...)
  2026-10-02 13:02 ` [PATCH 03/10] disk: efi: Reuse the GPT entries buffer with simple malloc Benoît Monin
@ 2026-10-02 13:02 ` Benoît Monin
  2026-10-02 15:28   ` Tom Rini
  2026-10-02 13:02 ` [PATCH 05/10] riscv: andes: Add Kconfig option for Andes erratum #35102 Benoît Monin
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

Add the SPL variant of the NVMXIP option and build the driver when it
is enabled for the current phase. This allows the SPL to use the block
devices emulated on top of a directly mapped non-volatile memory (XIP)
to read data, such as the next stage image or a firmware, from the
memory-mapped flash.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 common/spl/Kconfig   | 8 ++++++++
 drivers/mtd/Makefile | 1 +
 2 files changed, 9 insertions(+)

diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 0618f42c941..1c0628b2350 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -1605,6 +1605,14 @@ config SYS_SPI_ARGS_SIZE
 	hex "Falcon mode: size of args payload in SPI flash"
 	depends on SPL_SPI_FLASH_SUPPORT && SPL_OS_BOOT_ARGS
 
+config SPL_NVMXIP
+	bool "NVM XIP devices support"
+	select SPL_BLK
+	help
+	  This option allows the emulation of a block storage device
+	  on top of a direct access non volatile memory XIP flash devices.
+	  This support provides the read operation.
+
 config SPL_THERMAL
 	bool "Driver support for thermal devices"
 	help
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index ce05e206073..7ccc5da6d19 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -38,5 +38,6 @@ obj-$(CONFIG_$(PHASE_)NAND_SUPPORT) += nand/
 obj-$(CONFIG_SPL_ONENAND_SUPPORT) += onenand/
 obj-$(CONFIG_$(PHASE_)SPI_FLASH_SUPPORT) += spi/
 obj-$(CONFIG_SPL_UBI) += ubispl/
+obj-$(CONFIG_$(PHASE_)NVMXIP) += nvmxip/
 
 endif

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 05/10] riscv: andes: Add Kconfig option for Andes erratum #35102
  2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
                   ` (3 preceding siblings ...)
  2026-10-02 13:02 ` [PATCH 04/10] nvmxip: Make NVM XIP devices available in SPL Benoît Monin
@ 2026-10-02 13:02 ` Benoît Monin
  2026-10-02 13:02 ` [PATCH 06/10] mmc: metanoia_sdhci: Add SDHCI driver for Metanoia MT2824 SoC Benoît Monin
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

From: Jun Chang <jun.chang@metanoia-comm.com>

Add the ANDES_ERRATA_35102 config option to control the workaround for
Andes CPU erratum #35102. When enabled, non-blocking load is disabled in
the MMISC_CTL register during harts_early_init() to prevent potential
data corruption.

Signed-off-by: Jun Chang <jun.chang@metanoia-comm.com>
Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 arch/riscv/cpu/andes/Kconfig | 3 +++
 arch/riscv/cpu/andes/cpu.c   | 7 ++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/cpu/andes/Kconfig b/arch/riscv/cpu/andes/Kconfig
index 120fec5e540..a2e68b6ad9c 100644
--- a/arch/riscv/cpu/andes/Kconfig
+++ b/arch/riscv/cpu/andes/Kconfig
@@ -15,3 +15,6 @@ config RISCV_ANDES
 	help
 	  Run U-Boot on AndeStar V5 platforms and use some specific features
 	  which are provided by Andes Technology AndeStar V5 families.
+
+config ANDES_ERRATA_35102
+	bool
diff --git a/arch/riscv/cpu/andes/cpu.c b/arch/riscv/cpu/andes/cpu.c
index d25ecba0e88..44704a2359e 100644
--- a/arch/riscv/cpu/andes/cpu.c
+++ b/arch/riscv/cpu/andes/cpu.c
@@ -57,7 +57,12 @@ void harts_early_init(void)
 			}
 		}
 
-		mmisc_ctl_val |= MMISC_CTL_NON_BLOCKING_EN;
+		if (IS_ENABLED(CONFIG_ANDES_ERRATA_35102)) {
+			/* Errata #35102: Disable non-blocking load */
+			mmisc_ctl_val &= ~MMISC_CTL_NON_BLOCKING_EN;
+		} else {
+			mmisc_ctl_val |= MMISC_CTL_NON_BLOCKING_EN;
+		}
 
 		csr_write(CSR_MMISC_CTL, mmisc_ctl_val);
 	}

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 06/10] mmc: metanoia_sdhci: Add SDHCI driver for Metanoia MT2824 SoC
  2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
                   ` (4 preceding siblings ...)
  2026-10-02 13:02 ` [PATCH 05/10] riscv: andes: Add Kconfig option for Andes erratum #35102 Benoît Monin
@ 2026-10-02 13:02 ` Benoît Monin
  2026-10-02 15:29   ` Tom Rini
  2026-10-02 13:02 ` [PATCH 07/10] ram: metanoia: Add driver for MT2824 DDR controller Benoît Monin
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

Add an SDHCI driver for the DesignWare Mobile Storage Host Controller
(DWC MSHC) integrated in the Metanoia MT2824 SoC. It supports eMMC up to
HS400/HS400ES and SD cards, and implements the MMC driver model.

The controller is a DWC MSHC instance, but its PHY programming,
voltage/speed-mode handling and tuning model are silicon-specific
and differ from the existing DWC MSHC drivers.

The PHY pad drive strength, receiver select and weak-pull settings
depend on the card type (eMMC vs SD) and are re-applied on every ios,
whereas snps_sdhci.c uses fixed pad values.

High-speed modes force 1.8V signalling and HS400 uses an explicit PHY
DLL lock sequence (set_control_reg()/set_ios_post()), which snps_sdhci.c
does not implement.

Tuning relies on the generic SDHCI core tuner, whereas snps_sdhci.c uses
the hardware auto-tuning block.

Keeping a dedicated driver avoids per-variant branches in the shared
DWC MSHC drivers, matching how snps_sdhci.c, adi_sdhci.c and
cv1800b_sdhci.c are already split in U-Boot.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 drivers/mmc/Kconfig          |  11 +
 drivers/mmc/Makefile         |   1 +
 drivers/mmc/metanoia_sdhci.c | 494 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 506 insertions(+)

diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index f9f7aa5cf97..b4ac68c04c3 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -654,6 +654,17 @@ config MMC_SDHCI_F_SDH30
 	  If you have a controller with this interface, say Y here.
 	  If unsure, say N.
 
+config MMC_SDHCI_METANOIA
+	bool "SDHCI support for Metanoia MT2824 SoC"
+	depends on MMC_SDHCI
+	depends on OF_CONTROL
+	help
+	  Support for SDHCI host controller on Metanoia MT2824 SoC platform.
+
+	  If you have a controller with this interface, say Y here.
+
+	  If unsure, say N.
+
 config MMC_SDHCI_MSM
 	bool "Qualcomm SDHCI controller"
 	depends on MMC_SDHCI && ARCH_SNAPDRAGON
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index a23336d7d8d..f9e60370dd2 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_MMC_SDHCI_CADENCE)		+= sdhci-cadence6.o
 obj-$(CONFIG_MMC_SDHCI_CV1800B)		+= cv1800b_sdhci.o
 obj-$(CONFIG_MMC_SDHCI_AM654)		+= am654_sdhci.o
 obj-$(CONFIG_MMC_SDHCI_IPROC)		+= iproc_sdhci.o
+obj-$(CONFIG_MMC_SDHCI_METANOIA)	+= metanoia_sdhci.o
 obj-$(CONFIG_MMC_SDHCI_MSM)		+= msm_sdhci.o
 obj-$(CONFIG_MMC_SDHCI_MV)		+= mv_sdhci.o
 obj-$(CONFIG_MMC_SDHCI_NPCM)            += npcm_sdhci.o
diff --git a/drivers/mmc/metanoia_sdhci.c b/drivers/mmc/metanoia_sdhci.c
new file mode 100644
index 00000000000..e109eebd0db
--- /dev/null
+++ b/drivers/mmc/metanoia_sdhci.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang@metanoia-comm.com>
+ * Wilson Kang, Metanoia Communications Inc. <wilson.kang@metanoia-comm.com>
+ */
+
+#include <config.h>
+#include <clk.h>
+#include <dm.h>
+#include <linux/delay.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+#include <sdhci.h>
+
+#define DWCMSHC_CTRL_HS400 0x7
+#define EMMC_MIN_FREQ 400000
+
+/* PHY register area pointer */
+#define DWC_MSHC_PHY_REGS 0x300
+
+#define DWC_MSHC_PHY_CNFG_REG (DWC_MSHC_PHY_REGS + 0x00)
+#define PHY_CNFG_RSTN_DEASSERT 0x1 /* Deassert PHY reset */
+#define PHY_PAD_SN_MASK GENMASK(23, 20)
+#define PHY_PAD_SN_SD 0x8
+#define PHY_PAD_SN_EMMC 0x8
+#define PHY_PAD_SP_MASK GENMASK(19, 16)
+#define PHY_PAD_SP_SD 0x9
+#define PHY_PAD_SP_EMMC 0x8
+#define PHY_PWRGOOD BIT(1)
+#define PHY_RSTN BIT(0)
+
+#define PHY_CMDPAD_CNFG_REG (DWC_MSHC_PHY_REGS + 0x04)
+#define PHY_DATPAD_CNFG_REG (DWC_MSHC_PHY_REGS + 0x06)
+#define PHY_CLKPAD_CNFG_REG (DWC_MSHC_PHY_REGS + 0x08)
+#define PHY_STBPAD_CNFG_REG (DWC_MSHC_PHY_REGS + 0x0a)
+#define PHY_RSTNPAD_CNFG_REG (DWC_MSHC_PHY_REGS + 0x0c)
+#define PHY_SMPLDL_CNFG_REG (DWC_MSHC_PHY_REGS + 0x20)
+#define PHY_ATDL_CNFG_REG (DWC_MSHC_PHY_REGS + 0x21)
+#define PHY_DLL_CTRL_REG (DWC_MSHC_PHY_REGS + 0x24)
+#define PHY_DLL_CNFG1_REG (DWC_MSHC_PHY_REGS + 0x25)
+#define PHY_DLLDL_CNFG_REG (DWC_MSHC_PHY_REGS + 0x28)
+#define PHY_DLL_STATUS_REG (DWC_MSHC_PHY_REGS + 0x2e)
+
+#define PHY_RXSEL_MASK GENMASK(2, 0)
+#define PHY_RXSEL_180V 0x01
+#define PHY_RXSEL_330V 0x02
+
+#define PHY_TXSLEW_CTRL_N_MASK GENMASK(12, 9)
+#define PHY_TXSLEW_CTRL_N_EMMC 0x2
+#define PHY_TXSLEW_CTRL_N_SD 0x1
+#define PHY_TXSLEW_CTRL_P_MASK GENMASK(8, 5)
+#define PHY_TXSLEW_CTRL_P_EMMC 0x2
+#define PHY_TXSLEW_CTRL_P_SD 0x1
+#define PHY_TXSLEW_CTRL_P_DS 0x3
+
+#define PHY_WEAKPULL_EN_PULLUP 0x08
+#define PHY_WEAKPULL_EN_PULLDOWN 0x10
+
+/* Vendor specific Registers */
+#define VENDOR_SPECIFIC_AREA_BASE 0x500
+
+#define DWCMSHC_HOST_CTRL3_REG (VENDOR_SPECIFIC_AREA_BASE + 0x08)
+#define DWCMSHC_CMD_CONFLICT_CHECK BIT(0)
+#define DWCMSHC_NEGEDGE_DATAOUT_EN BIT(1)
+#define NEDGE_SMPL_EN BIT(7)
+
+#define DWCMSHC_EMMC_CTRL_REG (VENDOR_SPECIFIC_AREA_BASE + 0x2c)
+#define ENH_STROBE_ENABLE BIT(8)
+#define CARD_IS_EMMC BIT(0)
+
+#define DWCMSHC_AT_CTRL_REG (VENDOR_SPECIFIC_AREA_BASE + 0x40)
+#define TUNE_CLK_STOP_EN BIT(16)
+
+static inline int metanoia_boundary_ok(dma_addr_t addr, int len)
+{
+	return ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)));
+}
+
+struct metanoia_sdhc_plat {
+	struct mmc_config cfg;
+	struct mmc mmc;
+};
+
+struct metanoia_sdhc {
+	struct sdhci_host host;
+};
+
+static void metanoia_sdhci_sw_reset(struct sdhci_host *host, u8 mask)
+{
+	u8 val;
+	int retries = 100;
+
+	sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+	do {
+		udelay(1000);
+		val = sdhci_readb(host, SDHCI_SOFTWARE_RESET);
+		if (!(val & mask))
+			break;
+	} while (--retries > 0);
+
+	if (retries <= 0)
+		pr_warn("%s: Reset 0x%x never completed.\n", __func__,
+			(int)mask);
+}
+
+static bool metanoia_sdhci_is_emmc(struct mmc *mmc)
+{
+	u32 bus_width;
+
+	if (IS_MMC(mmc))
+		return true;
+	if (IS_SD(mmc))
+		return false;
+
+	/*
+	 * Card type may be unknown early (before op-cond completes). Fall back
+	 * to board description (e.g. non-removable in DT -> eMMC).
+	 */
+	if (mmc->dev) {
+		bus_width = dev_read_u32_default(mmc->dev, "bus-width", 0);
+		if (bus_width == 8)
+			return true;
+		if (dev_read_bool(mmc->dev, "non-removable"))
+			return true;
+	}
+
+	return false;
+}
+
+void metanoia_sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
+				    dma_addr_t addr, int len, bool end)
+{
+	int tmplen, offset;
+
+	if (likely(!len || metanoia_boundary_ok(addr, len))) {
+		sdhci_adma_write_desc(host, desc, addr, len, end);
+		return;
+	}
+
+	offset = addr & (SZ_128M - 1);
+	tmplen = SZ_128M - offset;
+	sdhci_adma_write_desc(host, desc, addr, tmplen, false);
+
+	addr += tmplen;
+	len -= tmplen;
+	sdhci_adma_write_desc(host, desc, addr, len, end);
+}
+
+static int metanoia_sdhci_of_to_plat(struct udevice *dev)
+{
+	struct metanoia_sdhc_plat *plat = dev_get_plat(dev);
+	struct sdhci_host *host = dev_get_priv(dev);
+	struct mmc_config *cfg = &plat->cfg;
+	int ret;
+
+	host->name = dev->name;
+	host->ioaddr = dev_read_addr_ptr(dev);
+	ret = mmc_of_parse(dev, cfg);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int metanoia_sdhci_bind(struct udevice *dev)
+{
+	struct metanoia_sdhc_plat *plat = dev_get_plat(dev);
+
+	return sdhci_bind(dev, &plat->mmc, &plat->cfg);
+}
+
+static void metanoia_emmc_phy_pad_cfg(struct sdhci_host *host, bool is_emmc)
+{
+	u16 val;
+	u16 txslew_n = 0;
+	u16 txslew_p = 0;
+	u16 rx_sel = PHY_RXSEL_330V;
+
+	if (host->mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+		rx_sel = PHY_RXSEL_180V;
+
+	if (is_emmc) {
+		txslew_n = PHY_TXSLEW_CTRL_N_EMMC;
+		txslew_p = PHY_TXSLEW_CTRL_P_EMMC;
+	} else {
+		txslew_n = PHY_TXSLEW_CTRL_N_SD;
+		txslew_p = PHY_TXSLEW_CTRL_P_SD;
+	}
+
+	val = PHY_WEAKPULL_EN_PULLUP;
+	val |= FIELD_PREP(PHY_TXSLEW_CTRL_N_MASK, txslew_n);
+	val |= FIELD_PREP(PHY_TXSLEW_CTRL_P_MASK, txslew_p);
+	val |= FIELD_PREP(PHY_RXSEL_MASK, rx_sel);
+
+	sdhci_writew(host, val, PHY_CMDPAD_CNFG_REG);
+	sdhci_writew(host, val, PHY_DATPAD_CNFG_REG);
+	sdhci_writew(host, val, PHY_RSTNPAD_CNFG_REG);
+
+	val = FIELD_PREP(PHY_TXSLEW_CTRL_N_MASK, txslew_n);
+	val |= FIELD_PREP(PHY_TXSLEW_CTRL_P_MASK, txslew_p);
+	sdhci_writew(host, val, PHY_CLKPAD_CNFG_REG);
+
+	if (CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)) {
+		val = PHY_WEAKPULL_EN_PULLDOWN;
+		val |= FIELD_PREP(PHY_TXSLEW_CTRL_N_MASK, PHY_TXSLEW_CTRL_N_SD);
+		val |= FIELD_PREP(PHY_TXSLEW_CTRL_P_MASK, PHY_TXSLEW_CTRL_P_DS);
+		val |= FIELD_PREP(PHY_RXSEL_MASK, PHY_RXSEL_180V);
+		sdhci_writew(host, val, PHY_STBPAD_CNFG_REG);
+	}
+}
+
+static void metanoia_emmc_phy_dl_cfg(struct sdhci_host *host)
+{
+	sdhci_writeb(host, 0x0C, PHY_SMPLDL_CNFG_REG);
+	sdhci_writeb(host, 0x0C, PHY_ATDL_CNFG_REG);
+}
+
+static void metanoia_sdhci_apply_phy_type(struct sdhci_host *host, bool is_emmc)
+{
+	u32 val;
+
+	u32 phy_pad_sn = is_emmc ? PHY_PAD_SN_EMMC : PHY_PAD_SN_SD;
+	u32 phy_pad_sp = is_emmc ? PHY_PAD_SP_EMMC : PHY_PAD_SP_SD;
+
+	val = sdhci_readl(host, DWC_MSHC_PHY_CNFG_REG);
+	val &= ~(PHY_PAD_SN_MASK | PHY_PAD_SP_MASK);
+	val |= FIELD_PREP(PHY_PAD_SN_MASK, phy_pad_sn);
+	val |= FIELD_PREP(PHY_PAD_SP_MASK, phy_pad_sp);
+	sdhci_writel(host, val, DWC_MSHC_PHY_CNFG_REG);
+
+	metanoia_emmc_phy_pad_cfg(host, is_emmc);
+}
+
+static void metanoia_sdhci_apply_hs400_phy(struct sdhci_host *host)
+{
+	u16 clk;
+	u8 val;
+	int retries = 10;
+
+	/* configure DLL as user guide suggest */
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+	clk &= ~SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	sdhci_writeb(host, 0x20, PHY_DLL_CNFG1_REG);
+	sdhci_writeb(host, 0x60, PHY_DLLDL_CNFG_REG);
+
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+	clk |= SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+	udelay(10);
+
+	sdhci_writeb(host, 0x1, PHY_DLL_CTRL_REG);
+
+	do {
+		udelay(1);
+		val = sdhci_readb(host, PHY_DLL_STATUS_REG);
+		if ((val & 0x1) == 0x1)
+			break;
+	} while (--retries);
+
+	if (!retries)
+		pr_err("eMMC dll has not locked\n");
+
+	val = sdhci_readb(host, PHY_DLL_STATUS_REG);
+	if (val & 0x2)
+		pr_err("eMMC dll has locked and has errors\n");
+
+	metanoia_sdhci_sw_reset(host, 0x3 << 1);
+}
+
+static int metanoia_sdhci_phy_init(struct sdhci_host *host)
+{
+	u32 val;
+	int ret;
+
+	/* Wait powergood before deassert phy reset */
+	ret = readl_poll_timeout(host->ioaddr + DWC_MSHC_PHY_CNFG_REG, val,
+				 val & PHY_PWRGOOD, 20000);
+	if (ret) {
+		pr_warn("%s: Internal clock never stabilised.\n", __func__);
+		return ret;
+	}
+
+	val = sdhci_readl(host, DWC_MSHC_PHY_CNFG_REG);
+	if (!(val & PHY_RSTN)) {
+		val |= PHY_RSTN;
+		sdhci_writel(host, val, DWC_MSHC_PHY_CNFG_REG);
+	}
+
+	/* Set TUNE_CLK_STOP_EN to prevent glitching */
+	val = sdhci_readl(host, DWCMSHC_AT_CTRL_REG);
+	val |= TUNE_CLK_STOP_EN;
+	sdhci_writel(host, val, DWCMSHC_AT_CTRL_REG);
+
+	return 0;
+}
+
+static int metanoia_sdhci_apply_phy(struct sdhci_host *host)
+{
+	struct mmc *mmc = host->mmc;
+	bool is_emmc = metanoia_sdhci_is_emmc(mmc);
+	bool is_hs400 = is_emmc && (mmc->selected_mode == MMC_HS_400 ||
+				    mmc->selected_mode == MMC_HS_400_ES);
+	u32 emmc_ctrl;
+	int ret;
+
+	metanoia_emmc_phy_dl_cfg(host);
+	metanoia_sdhci_apply_phy_type(host, is_emmc);
+
+	emmc_ctrl = sdhci_readl(host, DWCMSHC_EMMC_CTRL_REG);
+	if (is_emmc)
+		emmc_ctrl |= CARD_IS_EMMC;
+	else
+		emmc_ctrl &= ~CARD_IS_EMMC;
+	sdhci_writel(host, emmc_ctrl, DWCMSHC_EMMC_CTRL_REG);
+
+	/*
+	 * PHY pad/voltage settings are programmed above based on the current
+	 * ios. Ensure the PHY is out of reset afterwards; otherwise voltage
+	 * switching (e.g. SD 3.3V -> 1.8V) can fail.
+	 */
+	ret = metanoia_sdhci_phy_init(host);
+	if (ret)
+		return ret;
+
+	if (is_hs400)
+		metanoia_sdhci_apply_hs400_phy(host);
+
+	return 0;
+}
+
+static int metanoia_sdhci_set_ios_post(struct sdhci_host *host)
+{
+	struct mmc *mmc = host->mmc;
+	u32 reg;
+	int ret;
+
+	/* Disable cmd conflict check, especially for clk 200MHz */
+	reg = sdhci_readb(host, DWCMSHC_HOST_CTRL3_REG);
+	reg &= ~DWCMSHC_CMD_CONFLICT_CHECK;
+	if (host->mmc->ddr_mode && (mmc->selected_mode == MMC_DDR_52 ||
+				    mmc->selected_mode == UHS_DDR50)) {
+		/*
+		 * Databook claims this bit only for HS400, however, it does
+		 * not work on real silicon.
+		 */
+		reg |= DWCMSHC_NEGEDGE_DATAOUT_EN;
+	} else {
+		reg &= ~DWCMSHC_NEGEDGE_DATAOUT_EN;
+	}
+
+	if (mmc->selected_mode == MMC_HS_400 ||
+	    mmc->selected_mode == MMC_HS_400_ES) {
+		/* Samples CMD/DATA with respect to negative edge of cclk_rx */
+		reg |= NEDGE_SMPL_EN;
+	} else {
+		reg &= ~NEDGE_SMPL_EN;
+	}
+
+	sdhci_writeb(host, reg, DWCMSHC_HOST_CTRL3_REG);
+
+	ret = metanoia_sdhci_apply_phy(host);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int metanoia_sdhci_set_enhanced_strobe(struct sdhci_host *host)
+{
+	struct mmc *mmc = host->mmc;
+	u32 val;
+
+	val = sdhci_readl(host, DWCMSHC_EMMC_CTRL_REG);
+	if (mmc->selected_mode == MMC_HS_400_ES)
+		val |= ENH_STROBE_ENABLE;
+	else
+		val &= ~ENH_STROBE_ENABLE;
+	sdhci_writel(host, val, DWCMSHC_EMMC_CTRL_REG);
+
+	return 0;
+}
+
+static void metanoia_sdhci_set_control_reg(struct sdhci_host *host)
+{
+	struct mmc *mmc = host->mmc;
+	u32 val;
+
+	if (mmc->selected_mode == MMC_HS || mmc->selected_mode == MMC_HS_52) {
+		pr_debug("force 1.8v for MMC\n");
+		mmc->signal_voltage = MMC_SIGNAL_VOLTAGE_180;
+	}
+
+	sdhci_set_control_reg(host);
+
+	if (mmc->selected_mode == MMC_HS_400 ||
+	    mmc->selected_mode == MMC_HS_400_ES) {
+		val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+		val &= ~SDHCI_CTRL_UHS_MASK;
+		val |= DWCMSHC_CTRL_HS400;
+		sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
+	}
+
+	/*
+	 * Reinitializing the device tries to set it to lower-speed modes
+	 * first, which fails if the Enhanced Strobe bit is set, making
+	 * the device impossible to use. Set the correct value here to
+	 * let reinitialization attempts succeed.
+	 */
+	if (CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT))
+		metanoia_sdhci_set_enhanced_strobe(host);
+}
+
+static const struct sdhci_ops metanoia_sdhci_ops = {
+	.set_ios_post = metanoia_sdhci_set_ios_post,
+	.set_control_reg = metanoia_sdhci_set_control_reg,
+	.set_enhanced_strobe = metanoia_sdhci_set_enhanced_strobe,
+#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA_HELPERS)
+	.adma_write_desc = metanoia_sdhci_adma_write_desc,
+#endif
+};
+
+static int metanoia_sdhci_probe(struct udevice *dev)
+{
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+	struct metanoia_sdhc_plat *plat = dev_get_plat(dev);
+	struct metanoia_sdhc *priv = dev_get_priv(dev);
+	struct mmc_config *cfg = &plat->cfg;
+	struct sdhci_host *host = &priv->host;
+	struct clk clk = {};
+	ulong clk_rate = 0;
+	int ret;
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (!ret) {
+		clk_rate = clk_get_rate(&clk);
+		if (IS_ERR_VALUE(clk_rate))
+			clk_rate = 0;
+	}
+
+	if (!clk_rate) {
+		u32 max_freq;
+
+		dev_read_u32(dev, "max-frequency", &max_freq);
+		clk_rate = max_freq;
+	}
+
+	if (clk_rate) {
+		host->max_clk = clk_rate;
+		if (!cfg->f_max || cfg->f_max > clk_rate)
+			cfg->f_max = clk_rate;
+	} else {
+		host->max_clk = cfg->f_max;
+	}
+
+	host->ops = &metanoia_sdhci_ops;
+	host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8;
+	host->mmc = &plat->mmc;
+	host->mmc->priv = &priv->host;
+	host->mmc->dev = dev;
+	upriv->mmc = host->mmc;
+
+	ret = metanoia_sdhci_apply_phy(host);
+	if (ret)
+		return ret;
+
+	ret = sdhci_setup_cfg(cfg, host, cfg->f_max, EMMC_MIN_FREQ);
+	if (ret)
+		return ret;
+
+	cfg->host_caps |= MMC_MODE_8BIT | MMC_CAP(MMC_DDR_52);
+
+	return sdhci_probe(dev);
+}
+
+static const struct udevice_id metanoia_sdhci_ids[] = {
+	{ .compatible = "metanoia,mt2824-dwcmshc" },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(metanoia_sdhci_drv) = {
+	.name = "metanoia_sdhci",
+	.id = UCLASS_MMC,
+	.of_match = metanoia_sdhci_ids,
+	.of_to_plat = metanoia_sdhci_of_to_plat,
+	.ops = &sdhci_ops,
+	.bind = metanoia_sdhci_bind,
+	.probe = metanoia_sdhci_probe,
+	.priv_auto = sizeof(struct metanoia_sdhc),
+	.plat_auto = sizeof(struct metanoia_sdhc_plat),
+};

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 07/10] ram: metanoia: Add driver for MT2824 DDR controller
  2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
                   ` (5 preceding siblings ...)
  2026-10-02 13:02 ` [PATCH 06/10] mmc: metanoia_sdhci: Add SDHCI driver for Metanoia MT2824 SoC Benoît Monin
@ 2026-10-02 13:02 ` Benoît Monin
  2026-10-02 13:02 ` [PATCH 08/10] spi: atcspi200_spi: Add spi-mem support for the Metanoia ATCSPI200 variant Benoît Monin
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

Add a DDR controller driver for the Metanoia MT2824 SoC, supporting
initialization and training of the DDR subsystem in SPL, and implementing
the driver-model for the RAM uclass.

The driver relies on a firmware to configure the DDR controller, and
expects the firmware to be found via a device tree reg entry named "fw".

The driver expects the DDR firmware to be packaged inside a FIT image,
as the firmware node of the default configuration, and it supports signed
FIT image.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 drivers/ram/Kconfig      |   7 +
 drivers/ram/Makefile     |   2 +
 drivers/ram/mt2824-ddr.c | 706 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 715 insertions(+)

diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig
index acd6d9cfd6a..feb1754e40e 100644
--- a/drivers/ram/Kconfig
+++ b/drivers/ram/Kconfig
@@ -139,6 +139,13 @@ config K3_MULTI_DDR
 	  responsible for interleaving between the controllers.
 	default y if SOC_K3_J721S2 || SOC_K3_J784S4
 
+config SPL_METANOIA_MT2824_DDR
+	bool "Metanoia MT2824 DDR driver in SPL"
+	depends on SPL_RAM
+	help
+	   Enable support for the internal DDR Memory Controller
+	   of the Metanoia MT2824 SoC in the SPL.
+
 source "drivers/ram/aspeed/Kconfig"
 source "drivers/ram/cadence/Kconfig"
 source "drivers/ram/octeon/Kconfig"
diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile
index 82afd5fcbcc..6e1b86127ce 100644
--- a/drivers/ram/Makefile
+++ b/drivers/ram/Makefile
@@ -19,6 +19,8 @@ obj-$(CONFIG_K3_DDRSS) += k3-ddrss/
 
 obj-$(CONFIG_IMXRT_SDRAM) += imxrt_sdram.o
 
+obj-$(CONFIG_SPL_METANOIA_MT2824_DDR) += mt2824-ddr.o
+
 obj-$(CONFIG_RAM_SIFIVE) += sifive/
 ifdef CONFIG_XPL_BUILD
 obj-$(CONFIG_SPL_STARFIVE_DDR) += starfive/
diff --git a/drivers/ram/mt2824-ddr.c b/drivers/ram/mt2824-ddr.c
new file mode 100644
index 00000000000..c01c30089ca
--- /dev/null
+++ b/drivers/ram/mt2824-ddr.c
@@ -0,0 +1,706 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <dm.h>
+#include <image.h>
+#include <ram.h>
+#include <reset.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <u-boot/crc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* controller registers */
+#define CTRL_STAT				0x00000004
+#define CTRL_PWRCTL				0x00000030
+#define CTRL_RFSHCTL3				0x00000060
+#define CTRL_INIT7				0x000000ec
+#define CTRL_DFITMG0				0x00000190
+#define CTRL_DFITMG1				0x00000194
+#define CTRL_DFIMISC				0x000001b0
+#define CTRL_DFISTAT				0x000001bc
+#define CTRL_SWCTL				0x00000320
+#define CTRL_SWSTAT				0x00000324
+
+#define CTRL_STAT_MODE				GENMASK(2, 0)
+#define CTRL_STAT_NORMAL			1
+
+#define CTRL_PWRCTL_SELFREF			BIT(5)
+#define CTRL_PWRCTL_CLKDIS			BIT(3)
+#define CTRL_PWRCTL_PWRDN			BIT(1)
+
+#define CTRL_RFSHCTL3_DIS_AUTO			BIT(0)
+
+#define CTRL_INIT7_MR22				GENMASK(31, 16)
+#define CTRL_INIT7_MR14				GENMASK(15, 0)
+
+#define CTRL_DFITMG0_CTRL_DELAY			GENMASK(28, 24)
+#define CTRL_DFITMG0_RDDATA_USE_DFI_PHY_CLK	BIT(23)
+#define CTRL_DFITMG0_RDDATA_EN			GENMASK(22, 16)
+#define CTRL_DFITMG0_WRDATA_USE_DFI_PHY_CLK	BIT(15)
+#define CTRL_DFITMG0_TPHY_WRDATA		GENMASK(13, 8)
+#define CTRL_DFITMG0_TPHY_WRLAT			GENMASK(5, 0)
+
+#define CTRL_DFITMG1_WRDATA_DELAY		GENMASK(20, 16)
+#define CTRL_DFITMG1_CLK_DIS			GENMASK(12, 8)
+#define CTRL_DFITMG1_CLK_EN			GENMASK(4, 0)
+
+#define CTRL_DFIMISC_START			BIT(5)
+#define CTRL_DFIMISC_COMPLETE			BIT(0)
+
+#define CTRL_DFISTAT_COMPLETE			BIT(0)
+
+#define CTRL_SWCTL_DONE				BIT(0)
+#define CTRL_SWSTAT_DONE			BIT(0)
+
+/* PHY registers */
+#define PHY_DQS_DELAY_GRP0			0x000201a0
+#define PHY_RDPTR_INITVAL			0x0004005c
+#define PHY_ICCM_BASE				0x000a0000
+#define PHY_DCCM_BASE				0x000a8000
+#define PHY_DCCM_V26				0x000a804c
+#define PHY_DCCM_V27				0x000a804e
+#define PHY_DCCM_V40				0x000a8080
+#define PHY_PMU_MUX_SEL				0x001a0000
+#define PHY_UCTSHADOWREGS			0x001a0008
+#define PHY_DCTWRITEPROT			0x001a0062
+#define PHY_UCTWRITEONLYSHADOW			0x001a0064
+#define PHY_UCTDATWRITEONLYSHADOW		0x001a0068
+#define PHY_UC_CTRL				0x001a0132
+
+#define PHY_DQS_DELAY_COARSE			GENMASK(9, 6)
+#define PHY_DQS_DELAY_FINE			GENMASK(4, 0)
+
+#define PHY_ICCM_SIZE				0x00008000
+#define PHY_DCCM_SIZE				0x00002000
+
+#define PHY_PMU_MUX_SEL_UC			BIT(0)
+
+#define PHY_UC_CTRL_RESET			BIT(3)
+#define PHY_UC_CTRL_STALL			BIT(0)
+
+/* DDR parameters */
+#define PARAM_BL				16
+#define PARAM_CLK_DIS				2
+#define PARAM_CLK_EN				2
+#define PARAM_MISC				0
+#define PARAM_MR22				5
+#define PARAM_RD				0
+#define PARAM_RL				28
+#define PARAM_TDQSS				1
+#define PARAM_WL				14
+#define PARAM_WR				0
+#define PARAM_WRDATA				2
+
+#define TRAINING_STAGES				2
+
+#define POLL_TIMEOUT				1000000
+
+#define MSG_TRAINING_DONE			0x07
+#define MSG_TRAINING_FAIL			0xff
+
+#define FW_MAGIC				0x44445246 /* "DDRF" */
+#define FW_VERSION				0x01000000
+
+/* Base address used in the firmware for the controller */
+#define CTRL_FW_BASE				0x10060000
+
+enum {
+	FW_COMP_CTRL_CFG = 0,
+	FW_COMP_PHY_CFG,
+	FW_COMP_PMU_1D_IMEM,
+	FW_COMP_PMU_1D_DMEM,
+	FW_COMP_PMU_2D_IMEM,
+	FW_COMP_PMU_2D_DMEM,
+	FW_COMP_PIE_IMAGE,
+	FW_COMP_MAX
+};
+
+struct fw_comp {
+	u32 offset;
+	u32 size;
+	u32 crc32;
+	u32 reserved;
+};
+
+struct fw_header {
+	u32 magic;
+	u32 version;
+	u32 header_size;
+	u32 total_size;
+	u32 flags;
+	u32 timestamp;
+	u32 reserved1;
+	u32 reserved2;
+	struct fw_comp comps[FW_COMP_MAX];
+	u32 header_crc32;
+	u32 reserved[27]; /* Padding to 256 bytes */
+};
+
+struct fw_reg {
+	u32 addr;
+	u32 value;
+};
+
+struct mt2824_ddr_priv {
+	void __iomem *ctrl;
+	resource_size_t ctrl_size;
+	void __iomem *phy;
+	resource_size_t phy_size;
+	void *fw;
+	resource_size_t fw_size;
+	struct reset_ctl rst;
+	const void *fw_img;
+};
+
+static int get_fw_img_from_fit(const void *fit, size_t fit_size,
+			       const void **fw_img, size_t *fw_size)
+{
+	int conf_noffset;
+	const char *img_name;
+	int img_noffset;
+
+	if (fit_check_format(fit, fit_size))
+		return -EINVAL;
+
+	conf_noffset = fit_conf_get_node(fit, NULL);
+	if (conf_noffset < 0)
+		return -EINVAL;
+
+	printf("## Checking hash(es) for config %s ... ",
+	       fit_get_name(fit, conf_noffset, NULL));
+	if (fit_config_verify(fit, conf_noffset))
+		return -EPERM;
+	puts("OK\n");
+
+	img_name = fdt_getprop(fit, conf_noffset, FIT_FIRMWARE_PROP, NULL);
+	if (!img_name)
+		return -EINVAL;
+
+	img_noffset = fit_image_get_node(fit, img_name);
+	if (img_noffset < 0)
+		return -EINVAL;
+
+	if (fit_image_get_emb_data(fit, img_noffset, fw_img, fw_size))
+		return -EINVAL;
+
+	printf("## Checking hash(es) for image %s ... ", img_name);
+	if (!fit_image_verify_with_data(fit, img_noffset, gd_fdt_blob(), *fw_img, *fw_size))
+		return -EPERM;
+	puts("OK\n");
+
+	return 0;
+}
+
+static int check_firmware(struct udevice *dev)
+{
+	struct mt2824_ddr_priv *priv = dev_get_priv(dev);
+	const struct fw_header *header;
+	u32 calculated_crc;
+	int ret = 0;
+	size_t fw_size;
+
+	ret = get_fw_img_from_fit(priv->fw, priv->fw_size, &priv->fw_img, &fw_size);
+	if (ret)
+		return ret;
+
+	header = priv->fw_img;
+
+	if (header->magic != FW_MAGIC) {
+		dev_err(dev, "Invalid firmware magic: 0x%08x (expected 0x%08x)\n",
+			header->magic, FW_MAGIC);
+		return -EINVAL;
+	}
+
+	if (header->version != FW_VERSION) {
+		dev_err(dev, "Invalid firmware version %u\n", header->version);
+		return -EINVAL;
+	}
+
+	if (header->header_size != sizeof(struct fw_header)) {
+		dev_err(dev, "Invalid firmware header size %u\n", header->header_size);
+		return -EINVAL;
+	}
+
+	if (header->total_size > fw_size) {
+		dev_err(dev, "firmware too big\n");
+		return -EINVAL;
+	}
+
+	calculated_crc = crc32(0, priv->fw_img, offsetof(struct fw_header, header_crc32));
+	if (calculated_crc != header->header_crc32) {
+		dev_err(dev, "Header CRC mismatch: calculated=0x%08x, expected=0x%08x\n",
+			calculated_crc, header->header_crc32);
+		return -EINVAL;
+	}
+
+	for (int i = 0; i < FW_COMP_MAX; i++) {
+		u32 offset = header->comps[i].offset;
+		u32 size = header->comps[i].size;
+		u32 expected_crc = header->comps[i].crc32;
+
+		if (size == 0) {
+			dev_err(dev, "Component #%d is empty\n", i);
+			ret = -EINVAL;
+			continue;
+		}
+
+		if ((resource_size_t)offset + size > fw_size) {
+			dev_err(dev, "Component #%d out of range\n", i);
+			ret = -EINVAL;
+			continue;
+		}
+
+		calculated_crc = crc32(0, priv->fw_img + offset, size);
+		if (calculated_crc != expected_crc) {
+			dev_err(dev, "Component #%d CRC verification failed\n", i);
+			ret = -EINVAL;
+		}
+	}
+
+	return ret;
+}
+
+static int ddr_ctrl_load_config(struct mt2824_ddr_priv *priv)
+{
+	const struct fw_header *header = priv->fw_img;
+	const struct fw_reg *reg;
+	unsigned int nb_regs;
+	u32 addr;
+
+	/* address + value per register */
+	nb_regs = header->comps[FW_COMP_CTRL_CFG].size / sizeof(struct fw_reg);
+	reg = priv->fw_img + header->comps[FW_COMP_CTRL_CFG].offset;
+
+	/* Apply each register configuration */
+	for (unsigned int i = 0; i < nb_regs; i++, reg++) {
+		addr = reg->addr;
+
+		/*
+		 * Some firmware version use an absolute address,
+		 * convert to a relative offset
+		 */
+		if (addr >= CTRL_FW_BASE)
+			addr -= CTRL_FW_BASE;
+
+		if (addr >= priv->ctrl_size)
+			return -EINVAL;
+
+		writel(reg->value, priv->ctrl + addr);
+	}
+
+	return 0;
+}
+
+static int ddr_ctrl_init(struct mt2824_ddr_priv *priv)
+{
+	u32 val;
+
+	setbits_le32(priv->ctrl + CTRL_RFSHCTL3, CTRL_RFSHCTL3_DIS_AUTO);
+	clrbits_le32(priv->ctrl + CTRL_PWRCTL, CTRL_PWRCTL_PWRDN | CTRL_PWRCTL_CLKDIS);
+	writel(0, priv->ctrl + CTRL_SWCTL);
+	clrbits_le32(priv->ctrl + CTRL_DFIMISC, CTRL_DFIMISC_COMPLETE);
+	setbits_le32(priv->ctrl + CTRL_SWCTL, CTRL_SWCTL_DONE);
+
+	return readl_poll_timeout(priv->ctrl + CTRL_SWSTAT, val,
+				  val == CTRL_SWSTAT_DONE, POLL_TIMEOUT);
+}
+
+static int ddr_phy_load_comp(struct mt2824_ddr_priv *priv, unsigned int comp)
+{
+	const struct fw_header *header = priv->fw_img;
+	const struct fw_reg *reg;
+	unsigned int nb_regs;
+
+	/* address + value per register */
+	nb_regs = header->comps[comp].size / sizeof(struct fw_reg);
+	reg = priv->fw_img + header->comps[comp].offset;
+
+	/* Apply each register configuration */
+	for (unsigned int i = 0; i < nb_regs; i++, reg++) {
+		if (reg->addr >= priv->phy_size / 2)
+			return -EINVAL;
+		writew((u16)(reg->value & 0xFFFF), priv->phy + reg->addr * 2);
+	}
+
+	return 0;
+}
+
+static int phy_load_training_data(struct mt2824_ddr_priv *priv, unsigned int comp,
+				  void __iomem *base, resource_size_t size)
+{
+	const struct fw_header *header = priv->fw_img;
+	unsigned int nb_vals;
+	const u16 *val;
+
+	if (header->comps[comp].size > size)
+		return -EINVAL;
+
+	nb_vals = header->comps[comp].size / sizeof(u16);
+	val = priv->fw_img + header->comps[comp].offset;
+
+	for (unsigned int i = 0; i < nb_vals; i++) {
+		writew(*val, base + i * 2);
+		val++;
+	}
+
+	return 0;
+}
+
+static u32 get_mail(struct mt2824_ddr_priv *priv, int mode)
+{
+	u32 mail;
+	u16 val;
+
+	readw_poll_sleep_timeout(priv->phy + PHY_UCTSHADOWREGS, val,
+				 !(val & BIT(0)), mode ? 0 : 100, 0);
+
+	mail = readw(priv->phy + PHY_UCTWRITEONLYSHADOW);
+	if (mode == 1)
+		mail |= (readw(priv->phy + PHY_UCTDATWRITEONLYSHADOW) << 16);
+
+	writew(0, priv->phy + PHY_DCTWRITEPROT);
+	readw_poll_timeout(priv->phy + PHY_UCTSHADOWREGS, val, val, 0);
+	writew(1, priv->phy + PHY_DCTWRITEPROT);
+
+	return mail;
+}
+
+static void decode_streaming_message(struct mt2824_ddr_priv *priv)
+{
+	u32 msg_len = get_mail(priv, 1) & 0xffff;
+
+	for (unsigned int i = 0; i < msg_len; i++)
+		get_mail(priv, 1);
+}
+
+static void decode_major_message(struct mt2824_ddr_priv *priv, u32 mail)
+{
+	switch (mail) {
+	case MSG_TRAINING_DONE:
+		debug("DDRPHY: Training has run successfully\n");
+		break;
+	case MSG_TRAINING_FAIL:
+		debug("DDRPHY: Training has failed\n");
+		break;
+	case 0x00:
+		debug("DDRPHY: End of initialization\n");
+		break;
+	case 0x01:
+		debug("DDRPHY: End of fine write leveling\n");
+		break;
+	case 0x02:
+		debug("DDRPHY: End of read enable training\n");
+		break;
+	case 0x03:
+		debug("DDRPHY: End of read delay center optimization\n");
+		break;
+	case 0x04:
+		debug("DDRPHY: End of write delay center optimization\n");
+		break;
+	case 0x05:
+		debug("DDRPHY: End of 2D read delay/voltage center optimization\n");
+		break;
+	case 0x06:
+		debug("DDRPHY: End of 2D write delay/voltage center optimization\n");
+		break;
+	case 0x09:
+		debug("DDRPHY: End of max read latency training\n");
+		break;
+	case 0x0a:
+		debug("DDRPHY: End of read dq deskew training\n");
+		break;
+	case 0x0b:
+		debug("DDRPHY: Reserved\n");
+		break;
+	case 0x0c:
+		debug("DDRPHY: End of all DB training (MREP/DWL/MRD/MWD complete)\n");
+		break;
+	case 0x0d:
+		debug("DDRPHY: End of CA training\n");
+		break;
+	case 0xfd:
+		debug("DDRPHY: End of MPR read delay center optimization\n");
+		break;
+	case 0xfe:
+		debug("DDRPHY: End of Write leveling coarse delay\n");
+		break;
+	case 0x08:
+		debug("DDRPHY: Start streaming message mode\n");
+		decode_streaming_message(priv);
+		break;
+	default:
+		debug("DDRPHY: Error, unknown major_message %x\n", mail);
+	}
+}
+
+static int ddr_phy_wait_training_done(struct mt2824_ddr_priv *priv)
+{
+	u32 mail = 0;
+
+	do {
+		mail = get_mail(priv, 0);
+		decode_major_message(priv, mail);
+	} while (mail != MSG_TRAINING_DONE && mail != MSG_TRAINING_FAIL);
+
+	return (mail == MSG_TRAINING_DONE) ? 0 : -EIO;
+}
+
+static void write_mr14_to_register(struct mt2824_ddr_priv *priv)
+{
+	u16 v26 = readw(priv->phy + PHY_DCCM_V26);
+	u16 v27 = readw(priv->phy + PHY_DCCM_V27);
+	u16 v40 = readw(priv->phy + PHY_DCCM_V40);
+
+	u8 b0 = (v26 >> 8) & 0xff;
+	u8 b1 = v27 & 0xff;
+	u8 b2 = v40 & 0xff;
+	u8 b3 = (v40 >> 8) & 0xff;
+
+	u32 sum = (u32)b0 + b1 + b2 + b3;
+	u8 avg = (sum + 2) / 4;
+
+	writel(FIELD_PREP(CTRL_INIT7_MR22, PARAM_MR22) | FIELD_PREP(CTRL_INIT7_MR14, avg),
+	       priv->ctrl + CTRL_INIT7);
+}
+
+static int ddr_phy_training(struct mt2824_ddr_priv *priv)
+{
+	struct {
+		unsigned int imem_comp;
+		unsigned int dmem_comp;
+	} fw_info[TRAINING_STAGES] = {
+		{FW_COMP_PMU_1D_IMEM, FW_COMP_PMU_1D_DMEM},
+		{FW_COMP_PMU_2D_IMEM, FW_COMP_PMU_2D_DMEM}
+	};
+	int ret;
+
+	for (unsigned int stage = 0; stage < TRAINING_STAGES; stage++) {
+		ret = phy_load_training_data(priv, fw_info[stage].imem_comp,
+					     priv->phy + PHY_ICCM_BASE, PHY_ICCM_SIZE);
+		if (ret)
+			return ret;
+
+		writew(PHY_PMU_MUX_SEL_UC, priv->phy + PHY_PMU_MUX_SEL);
+		writew(0, priv->phy + PHY_PMU_MUX_SEL);
+
+		ret = phy_load_training_data(priv, fw_info[stage].dmem_comp,
+					     priv->phy + PHY_DCCM_BASE, PHY_DCCM_SIZE);
+		if (ret)
+			return ret;
+
+		/*
+		 * MicroContMuxSel=1, written twice to match the PhyInit
+		 * sequence (end of DMEM load and start of the firmware).
+		 */
+		writew(PHY_PMU_MUX_SEL_UC, priv->phy + PHY_PMU_MUX_SEL);
+		writew(PHY_PMU_MUX_SEL_UC, priv->phy + PHY_PMU_MUX_SEL);
+
+		writew(PHY_UC_CTRL_RESET | PHY_UC_CTRL_STALL, priv->phy + PHY_UC_CTRL);
+		writew(PHY_UC_CTRL_STALL, priv->phy + PHY_UC_CTRL);
+		writew(0, priv->phy + PHY_UC_CTRL);
+
+		ret = ddr_phy_wait_training_done(priv);
+		if (ret)
+			return ret;
+
+		writew(PHY_UC_CTRL_STALL, priv->phy + PHY_UC_CTRL);
+		writew(0, priv->phy + PHY_PMU_MUX_SEL);
+
+		if (stage == 1)
+			write_mr14_to_register(priv);
+
+		writew(PHY_PMU_MUX_SEL_UC, priv->phy + PHY_PMU_MUX_SEL);
+		writew(0, priv->phy + PHY_PMU_MUX_SEL);
+	}
+
+	return 0;
+}
+
+static int ddr_ctrl_configure(struct mt2824_ddr_priv *priv)
+{
+	u8 trddata_en = PARAM_RL - 5 + 2 * (PARAM_MISC - PARAM_RD);
+	u8 tphy_wrlat = PARAM_WL - 5 + 2 * (PARAM_MISC - PARAM_WR) + PARAM_TDQSS;
+	u8 twrdata_delay;
+	u8 tctrl_delay;
+	u8 ardptrinitval;
+	u16 dqs_dly;
+	u8 trained_txdqsdly;
+	u32 val;
+	int ret;
+
+	clrbits_le32(priv->ctrl + CTRL_SWCTL, CTRL_SWCTL_DONE);
+	setbits_le32(priv->ctrl + CTRL_DFIMISC, CTRL_DFIMISC_START);
+	setbits_le32(priv->ctrl + CTRL_SWCTL, CTRL_SWCTL_DONE);
+
+	ret = readl_poll_timeout(priv->ctrl + CTRL_SWSTAT, val,
+				 val == CTRL_SWSTAT_DONE, POLL_TIMEOUT);
+	if (ret)
+		return ret;
+
+	ret = readl_poll_timeout(priv->ctrl + CTRL_DFISTAT, val,
+				 FIELD_GET(CTRL_DFISTAT_COMPLETE, val), POLL_TIMEOUT);
+	if (ret)
+		return ret;
+
+	clrbits_le32(priv->ctrl + CTRL_SWCTL, CTRL_SWCTL_DONE);
+	clrbits_le32(priv->ctrl + CTRL_DFIMISC, CTRL_DFIMISC_START);
+	setbits_le32(priv->ctrl + CTRL_DFIMISC, CTRL_DFIMISC_COMPLETE);
+	clrbits_le32(priv->ctrl + CTRL_PWRCTL, CTRL_PWRCTL_SELFREF);
+
+	writew(0, priv->phy + PHY_PMU_MUX_SEL);
+
+	/* read the trained DQS delay and round it up */
+	dqs_dly = readw(priv->phy + PHY_DQS_DELAY_GRP0);
+	trained_txdqsdly = FIELD_GET(PHY_DQS_DELAY_COARSE, dqs_dly) +
+			   (FIELD_GET(PHY_DQS_DELAY_FINE, dqs_dly) +
+			    FIELD_MAX(PHY_DQS_DELAY_FINE)) /
+			   (FIELD_MAX(PHY_DQS_DELAY_FINE) + 1);
+
+	ardptrinitval = readw(priv->phy + PHY_RDPTR_INITVAL);
+	tctrl_delay = ((ardptrinitval / 2) + (PARAM_MISC * 2) * 1 + 3);
+	twrdata_delay = tctrl_delay + (6 + PARAM_BL / 2) + trained_txdqsdly;
+
+	writew(PHY_PMU_MUX_SEL_UC, priv->phy + PHY_PMU_MUX_SEL);
+
+	writel(FIELD_PREP(CTRL_DFITMG0_CTRL_DELAY, tctrl_delay) |
+	       CTRL_DFITMG0_RDDATA_USE_DFI_PHY_CLK |
+	       FIELD_PREP(CTRL_DFITMG0_RDDATA_EN, trddata_en) |
+	       CTRL_DFITMG0_WRDATA_USE_DFI_PHY_CLK |
+	       FIELD_PREP(CTRL_DFITMG0_TPHY_WRDATA, PARAM_WRDATA) |
+	       FIELD_PREP(CTRL_DFITMG0_TPHY_WRLAT, tphy_wrlat),
+	       priv->ctrl + CTRL_DFITMG0);
+
+	writel(FIELD_PREP(CTRL_DFITMG1_WRDATA_DELAY, twrdata_delay) |
+	       FIELD_PREP(CTRL_DFITMG1_CLK_DIS, PARAM_CLK_DIS) |
+	       FIELD_PREP(CTRL_DFITMG1_CLK_EN, PARAM_CLK_EN),
+	       priv->ctrl + CTRL_DFITMG1);
+
+	setbits_le32(priv->ctrl + CTRL_DFIMISC, CTRL_DFIMISC_COMPLETE);
+
+	clrbits_le32(priv->ctrl + CTRL_PWRCTL, CTRL_PWRCTL_SELFREF);
+
+	setbits_le32(priv->ctrl + CTRL_SWCTL, CTRL_SWCTL_DONE);
+	ret = readl_poll_timeout(priv->ctrl + CTRL_SWSTAT, val,
+				 val == CTRL_SWSTAT_DONE, POLL_TIMEOUT);
+	if (ret)
+		return ret;
+
+	ret = readl_poll_timeout(priv->ctrl + CTRL_STAT, val,
+				 FIELD_GET(CTRL_STAT_MODE, val) == CTRL_STAT_NORMAL,
+				 POLL_TIMEOUT);
+	if (ret)
+		return ret;
+
+	clrbits_le32(priv->ctrl + CTRL_RFSHCTL3, CTRL_RFSHCTL3_DIS_AUTO);
+
+	return 0;
+}
+
+static int mt2824_ddr_init(struct udevice *dev)
+{
+	struct mt2824_ddr_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = check_firmware(dev);
+	if (ret)
+		return ret;
+
+	ret = reset_assert(&priv->rst);
+	if (ret)
+		return ret;
+
+	ret = ddr_ctrl_load_config(priv);
+	if (ret)
+		return ret;
+
+	ret = reset_deassert(&priv->rst);
+	if (ret)
+		return ret;
+
+	ret = ddr_ctrl_init(priv);
+	if (ret)
+		return ret;
+
+	ret = ddr_phy_load_comp(priv, FW_COMP_PHY_CFG);
+	if (ret)
+		return ret;
+
+	ret = ddr_phy_training(priv);
+	if (ret)
+		return ret;
+
+	ret = ddr_phy_load_comp(priv, FW_COMP_PIE_IMAGE);
+	if (ret)
+		return ret;
+
+	ret = ddr_ctrl_configure(priv);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int mt2824_ddr_probe(struct udevice *dev)
+{
+	struct mt2824_ddr_priv *priv = dev_get_priv(dev);
+	struct resource res;
+	int ret;
+
+	ret = dev_read_resource_byname(dev, "ctrl", &res);
+	if (ret)
+		return ret;
+
+	priv->ctrl_size = resource_size(&res);
+	priv->ctrl = devm_ioremap(dev, res.start, priv->ctrl_size);
+
+	ret = dev_read_resource_byname(dev, "phy", &res);
+	if (ret)
+		return ret;
+
+	priv->phy_size = resource_size(&res);
+	priv->phy = devm_ioremap(dev, res.start, priv->phy_size);
+
+	ret = dev_read_resource_byname(dev, "fw", &res);
+	if (ret)
+		return ret;
+
+	priv->fw_size = resource_size(&res);
+	priv->fw = devm_ioremap(dev, res.start, priv->fw_size);
+
+	ret = reset_get_by_index(dev, 0, &priv->rst);
+	if (ret)
+		return ret;
+
+	return mt2824_ddr_init(dev);
+}
+
+static int mt2824_ddr_get_info(struct udevice *dev, struct ram_info *info)
+{
+	info->base = gd->ram_base;
+	info->size = gd->ram_size;
+
+	return 0;
+}
+
+static const struct ram_ops mt2824_ddr_ops = {
+	.get_info = mt2824_ddr_get_info,
+};
+
+static const struct udevice_id mt2824_ddr_ids[] = {
+	{ .compatible = "metanoia,mt2824-ddrc" },
+	{ }
+};
+
+U_BOOT_DRIVER(mt2824_ddr) = {
+	.name = "mt2824_ddr",
+	.id = UCLASS_RAM,
+	.ops = &mt2824_ddr_ops,
+	.of_match = mt2824_ddr_ids,
+	.probe = mt2824_ddr_probe,
+	.priv_auto = sizeof(struct mt2824_ddr_priv),
+};

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 08/10] spi: atcspi200_spi: Add spi-mem support for the Metanoia ATCSPI200 variant
  2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
                   ` (6 preceding siblings ...)
  2026-10-02 13:02 ` [PATCH 07/10] ram: metanoia: Add driver for MT2824 DDR controller Benoît Monin
@ 2026-10-02 13:02 ` Benoît Monin
  2026-10-02 13:02 ` [PATCH 09/10] riscv: dts: add MT2824 SoC and MT5824 EVB device trees Benoît Monin
  2026-10-02 13:02 ` [PATCH 10/10] riscv: metanoia: Add support for the MT5824 EVB platform Benoît Monin
  9 siblings, 0 replies; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

The Metanoia variant of the ATCSPI200 core adds hardware command and
address phases along with the dual and quad I/O data formats. Expose
them through the spi-mem operations, which is required to use SPI NOR
flash devices at full speed.

The Metanoia variant is detected at probe time with
device_is_compatible() and gets the mem_ops assigned, following the
same pattern as the Octeon driver. The Andes instances keep the
generic spi-mem fallback built on top of the .xfer operation,
unchanged.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 drivers/spi/atcspi200_spi.c | 176 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 168 insertions(+), 8 deletions(-)

diff --git a/drivers/spi/atcspi200_spi.c b/drivers/spi/atcspi200_spi.c
index 72b612c6560..a229b196de1 100644
--- a/drivers/spi/atcspi200_spi.c
+++ b/drivers/spi/atcspi200_spi.c
@@ -10,6 +10,7 @@
 #include <log.h>
 #include <malloc.h>
 #include <spi.h>
+#include <spi-mem.h>
 #include <asm/global_data.h>
 #include <asm/io.h>
 #include <dm.h>
@@ -30,6 +31,7 @@ struct atcspi200_spi_regs {
 	u32	reserve1[3];
 	u32	format;		/* 0x10 */
 #define DATA_LENGTH(x)	((x-1)<<8)
+#define ADDR_LENGTH(x)	((x-1)<<16)
 	u32	pio;
 	u32	reserve2[2];
 	u32	tctrl;		/* 0x20 */
@@ -49,6 +51,10 @@ struct atcspi200_spi_regs {
 #define WCNT_MASK	(0x1FF<<WCNT_OFFSET)
 #define RCNT_OFFSET	0
 #define RCNT_MASK	(0x1FF<<RCNT_OFFSET)
+#define CMD_EN		(1<<30)
+#define ADDR_EN		(1<<29)
+#define DUAL_QUAD(x)	((x)<<22)
+#define DUMMY_CNT(x)	(((x)-1)<<9)
 	u32	cmd;
 	u32	addr;
 	u32	data;
@@ -364,6 +370,164 @@ static int atcspi200_spi_get_clk(struct udevice *bus)
 	return 0;
 }
 
+/*
+ * The dummy phase is generated in units of 8 bits: one unit takes
+ * (8 / data buswidth) SCLK cycles. The unit count is programmed in the
+ * 2-bit DummyCnt field of the TransCtrl register.
+ */
+static unsigned int atcspi200_mem_dummy_units(const struct spi_mem_op *op)
+{
+	unsigned int cycles;
+
+	cycles = op->dummy.nbytes * 8 /
+		 max_t(unsigned int, op->dummy.buswidth, 1U);
+
+	return cycles * op->data.buswidth / 8;
+}
+
+static int atcspi200_mem_adjust_op_size(struct spi_slave *slave,
+					struct spi_mem_op *op)
+{
+	op->data.nbytes = min(op->data.nbytes, (unsigned int)MAX_TRANSFER_LEN);
+
+	return 0;
+}
+
+static bool atcspi200_mem_supports_op(struct spi_slave *slave,
+				      const struct spi_mem_op *op)
+{
+	unsigned int cycles, units;
+
+	if (!spi_mem_default_supports_op(slave, op))
+		return false;
+
+	/* the address phase is always transferred on a single line */
+	if (op->addr.nbytes > 4 || (op->addr.nbytes && op->addr.buswidth != 1))
+		return false;
+
+	/* a dummy phase without data cannot be expressed by the controller */
+	if (op->dummy.nbytes && !op->data.nbytes)
+		return false;
+
+	if (!op->data.nbytes)
+		return true;
+
+	if (op->data.buswidth != 1 && op->data.buswidth != 2 &&
+	    op->data.buswidth != 4)
+		return false;
+
+	/* the write and read counters are 9-bit fields */
+	if (op->data.nbytes > MAX_TRANSFER_LEN)
+		return false;
+
+	/* reject dummy cycles which cannot be represented by DummyCnt */
+	cycles = op->dummy.nbytes * 8 /
+		 max_t(unsigned int, op->dummy.buswidth, 1U);
+	if (cycles * op->data.buswidth % 8)
+		return false;
+
+	units = cycles * op->data.buswidth / 8;
+
+	return units <= 4;
+}
+
+static int atcspi200_mem_exec_op(struct spi_slave *slave,
+				 const struct spi_mem_op *op)
+{
+	struct udevice *bus = slave->dev->parent;
+	struct nds_spi_slave *ns = dev_get_priv(bus);
+	unsigned int format, tc = 0, units;
+	unsigned int len = op->data.nbytes;
+	const u8 *dout = op->data.buf.out;
+	u8 *din = op->data.buf.in;
+	int timeout = SPI_TIMEOUT;
+
+	/* reprogram the address length for this operation */
+	format = in_le32(&ns->regs->format) & ~(0x3 << 16);
+	if (op->addr.nbytes)
+		format |= ADDR_LENGTH(op->addr.nbytes);
+	out_le32(&ns->regs->format, format);
+
+	if (op->cmd.nbytes)
+		tc |= CMD_EN;
+	if (op->addr.nbytes) {
+		tc |= ADDR_EN;
+		out_le32(&ns->regs->addr, op->addr.val);
+	}
+
+	if (op->data.nbytes) {
+		if (op->data.buswidth == 2)
+			tc |= DUAL_QUAD(1);
+		else if (op->data.buswidth == 4)
+			tc |= DUAL_QUAD(2);
+
+		if (op->data.dir == SPI_MEM_DATA_IN) {
+			units = atcspi200_mem_dummy_units(op);
+			tc |= units ? TRAMODE_DR : TRAMODE_RO;
+			if (units)
+				tc |= DUMMY_CNT(units);
+			tc |= (op->data.nbytes - 1) & 0x1FF;
+		} else {
+			tc |= TRAMODE_WO;
+			tc |= ((op->data.nbytes - 1) & 0x1FF) << WCNT_OFFSET;
+		}
+	} else {
+		tc |= TRAMODE_NONE;
+	}
+
+	out_le32(&ns->regs->tctrl, tc);
+	out_le32(&ns->regs->cmd, op->cmd.opcode);
+
+	while (len) {
+		unsigned int event = in_le32(&ns->regs->status);
+
+		if (op->data.dir == SPI_MEM_DATA_OUT) {
+			if (!(event & TXFFL)) {
+				out_le32(&ns->regs->data, *dout++);
+				len--;
+			}
+		} else {
+			if (event & RXFVE_MASK) {
+				*din++ = in_le32(&ns->regs->data);
+				len--;
+			}
+		}
+
+		if (!timeout--)
+			return -ETIMEDOUT;
+	}
+
+	while (in_le32(&ns->regs->status) & SPIBSY) {
+		if (!timeout--)
+			return -ETIMEDOUT;
+	}
+
+	/* restore the timing register like the legacy path does */
+	out_le32(&ns->regs->timing, ns->mtiming);
+
+	return 0;
+}
+
+/*
+ * The Metanoia variant of the ATCSPI200 core adds the command and address
+ * phases along with the dual and quad I/O data formats, exposed through the
+ * spi-mem operations assigned at probe time below. The other instances keep
+ * using the generic spi-mem fallback on top of .xfer.
+ */
+static struct dm_spi_ops atcspi200_spi_ops = {
+	.claim_bus	= atcspi200_spi_claim_bus,
+	.release_bus	= atcspi200_spi_release_bus,
+	.xfer		= atcspi200_spi_xfer,
+	.set_speed	= atcspi200_spi_set_speed,
+	.set_mode	= atcspi200_spi_set_mode,
+};
+
+static const struct spi_controller_mem_ops atcspi200_mem_ops = {
+	.adjust_op_size = atcspi200_mem_adjust_op_size,
+	.supports_op = atcspi200_mem_supports_op,
+	.exec_op = atcspi200_mem_exec_op,
+};
+
 static int atcspi200_spi_probe(struct udevice *bus)
 {
 	struct nds_spi_slave *ns = dev_get_priv(bus);
@@ -373,6 +537,9 @@ static int atcspi200_spi_probe(struct udevice *bus)
 	ns->mtiming = ns->regs->timing;
 	atcspi200_spi_get_clk(bus);
 
+	if (device_is_compatible(bus, "metanoia,mt2824-spi"))
+		atcspi200_spi_ops.mem_ops = &atcspi200_mem_ops;
+
 	return 0;
 }
 
@@ -394,16 +561,9 @@ static int atcspi200_ofdata_to_platadata(struct udevice *bus)
 	return 0;
 }
 
-static const struct dm_spi_ops atcspi200_spi_ops = {
-	.claim_bus	= atcspi200_spi_claim_bus,
-	.release_bus	= atcspi200_spi_release_bus,
-	.xfer		= atcspi200_spi_xfer,
-	.set_speed	= atcspi200_spi_set_speed,
-	.set_mode	= atcspi200_spi_set_mode,
-};
-
 static const struct udevice_id atcspi200_spi_ids[] = {
 	{ .compatible = "andestech,atcspi200" },
+	{ .compatible = "metanoia,mt2824-spi" },
 	{ }
 };
 

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 09/10] riscv: dts: add MT2824 SoC and MT5824 EVB device trees
  2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
                   ` (7 preceding siblings ...)
  2026-10-02 13:02 ` [PATCH 08/10] spi: atcspi200_spi: Add spi-mem support for the Metanoia ATCSPI200 variant Benoît Monin
@ 2026-10-02 13:02 ` Benoît Monin
  2026-10-02 13:02 ` [PATCH 10/10] riscv: metanoia: Add support for the MT5824 EVB platform Benoît Monin
  9 siblings, 0 replies; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

Add the device trees for the MT2824 SoC (mt2824.dtsi) and the MT5824
EVB board (mt5824-evb.dts). The SoC integrates:

- a dual-core Andes AX45MP with 256 KB unified L2 cache
- PLIC, PLIC SW and PLMT interrupt and timer controllers
- a DesignWare-derived SDHCI controller
- an ATCSPI200 QSPI controller with a 32 MiB Macronix NOR flash
  exposed as a memory-mapped device through nvmxip
- a DDR controller with its firmware mapped through a "fw" reg entry

The u-boot.dtsi overlay marks the nodes used by the SPL with
bootph-pre-ram and describes the binman FIT image packing OpenSBI,
U-Boot and the device tree.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 arch/riscv/dts/Makefile           |   1 +
 arch/riscv/dts/mt2824-u-boot.dtsi |  66 ++++++++++++
 arch/riscv/dts/mt2824.dtsi        | 219 ++++++++++++++++++++++++++++++++++++++
 arch/riscv/dts/mt5824-evb.dts     |  34 ++++++
 4 files changed, 320 insertions(+)

diff --git a/arch/riscv/dts/Makefile b/arch/riscv/dts/Makefile
index 8ba75fdb9f5..b13e0436b98 100644
--- a/arch/riscv/dts/Makefile
+++ b/arch/riscv/dts/Makefile
@@ -3,6 +3,7 @@
 dtb-$(CONFIG_TARGET_ANDES_AE350) += ae350_32.dtb ae350_64.dtb
 dtb-$(CONFIG_TARGET_ANDES_VOYAGER) += qilai-voyager.dtb
 dtb-$(CONFIG_TARGET_K230_CANMV) += k230-canmv.dtb
+dtb-$(CONFIG_TARGET_METANOIA_MT5824_EVB) += mt5824-evb.dtb
 dtb-$(CONFIG_TARGET_MICROCHIP_ICICLE) += mpfs-icicle-kit.dtb
 dtb-$(CONFIG_TARGET_MILKV_DUO) += cv1800b-milkv-duo.dtb
 dtb-$(CONFIG_TARGET_MILKV_DUO_256M) += sg2002-milkv-duo256m.dtb
diff --git a/arch/riscv/dts/mt2824-u-boot.dtsi b/arch/riscv/dts/mt2824-u-boot.dtsi
new file mode 100644
index 00000000000..8155fe580eb
--- /dev/null
+++ b/arch/riscv/dts/mt2824-u-boot.dtsi
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2026 Metanoia Communications Inc. All rights reserved.
+ */
+
+#include "binman.dtsi"
+
+/ {
+	cpus {
+		bootph-pre-ram;
+
+		cpu0: cpu@0 {
+			bootph-pre-ram;
+		};
+
+		cpu1: cpu@1 {
+			bootph-pre-ram;
+		};
+	};
+
+	l2: l2-cache@a000000 {
+		bootph-pre-ram;
+	};
+
+	soc {
+		bootph-pre-ram;
+
+		plicsw: interrupt-controller@c800000 {
+			bootph-pre-ram;
+		};
+
+		plmt0@c400000 {
+			bootph-pre-ram;
+		};
+
+		rst: reset-controller@1000000c {
+			bootph-pre-ram;
+		};
+
+		ddrc: ddrc@10060000 {
+			compatible = "metanoia,mt2824-ddrc";
+			reg = <0x0 0x10060000 0x0 0x1000>,
+			      <0x0 0x14000000 0x0 0x800000>,
+			      <0x0 0x70080000 0x0 0x20000>;
+			reg-names = "ctrl", "phy", "fw";
+			resets = <&rst 17>;
+			bootph-pre-ram;
+		};
+
+		serial0: serial@100e0000 {
+			bootph-pre-ram;
+		};
+
+		nvmxip@20000000 {
+			compatible = "nvmxip,qspi";
+			reg = <0x0 0x20000000 0x0 0x2000000>;
+			lba_shift = <9>;
+			lba = <65536>;
+			bootph-pre-ram;
+		};
+
+		mmcsd: mmc@4100000 {
+			bootph-pre-ram;
+		};
+	};
+};
diff --git a/arch/riscv/dts/mt2824.dtsi b/arch/riscv/dts/mt2824.dtsi
new file mode 100644
index 00000000000..cd11a949f22
--- /dev/null
+++ b/arch/riscv/dts/mt2824.dtsi
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2026 Metanoia Communications Inc. All rights reserved.
+ */
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+/ {
+	#address-cells = <0x02>;
+	#size-cells = <0x02>;
+	model = "Metanoia MT2824";
+	compatible = "metanoia,mt2824";
+
+	aliases {
+		uart0 = &serial0;
+	};
+
+	cpus: cpus {
+		#address-cells = <0x01>;
+		#size-cells = <0x00>;
+		timebase-frequency = <20000000>;
+
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			reg = <0x00>;
+			status = "okay";
+			compatible = "andestech,ax45mp", "riscv";
+			riscv,isa-base = "rv64i";
+			riscv,isa-extensions = "i", "m", "a", "f", "d", "c",
+					       "zicntr", "zicsr", "zifencei",
+					       "zihpm", "xandespmu";
+			mmu-type = "riscv,sv39";
+			clock-frequency = <900000000>;
+			i-cache-size = <0x8000>;
+			i-cache-sets = <0x100>;
+			i-cache-line-size = <0x40>;
+			i-cache-block-size = <0x40>;
+			d-cache-size = <0x8000>;
+			d-cache-sets = <0x80>;
+			d-cache-line-size = <0x40>;
+			d-cache-block-size = <0x40>;
+			next-level-cache = <&l2>;
+
+			cpu0intc: interrupt-controller {
+				#interrupt-cells = <0x01>;
+				interrupt-controller;
+				compatible = "andestech,cpu-intc", "riscv,cpu-intc";
+			};
+		};
+
+		cpu1: cpu@1 {
+			device_type = "cpu";
+			reg = <0x01>;
+			status = "okay";
+			compatible = "andestech,ax45mp", "riscv";
+			riscv,isa-base = "rv64i";
+			riscv,isa-extensions = "i", "m", "a", "f", "d", "c",
+					       "zicntr", "zicsr", "zifencei",
+					       "zihpm", "xandespmu";
+			mmu-type = "riscv,sv39";
+			clock-frequency = <900000000>;
+			i-cache-size = <0x8000>;
+			i-cache-sets = <0x100>;
+			i-cache-line-size = <0x40>;
+			i-cache-block-size = <0x40>;
+			d-cache-size = <0x8000>;
+			d-cache-sets = <0x80>;
+			d-cache-line-size = <0x40>;
+			d-cache-block-size = <0x40>;
+			next-level-cache = <&l2>;
+
+			cpu1intc: interrupt-controller {
+				#interrupt-cells = <0x01>;
+				interrupt-controller;
+				compatible = "andestech,cpu-intc", "riscv,cpu-intc";
+			};
+		};
+	};
+
+	l2: l2-cache@a000000 {
+		compatible = "metanoia,mt2824-ax45mp-cache",
+			     "andestech,ax45mp-cache",
+			     "cache";
+		reg = <0x00 0xa000000 0x00 0x40000>;
+		cache-line-size = <64>;
+		cache-level = <2>;
+		cache-size = <0x40000>;
+		cache-sets = <1024>;
+		cache-unified;
+		interrupts = <1 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-parent = <&plic>;
+	};
+
+	clocks {
+		ext_clk: ext-clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <20000000>;
+			clock-output-names = "ext-clk";
+		};
+
+		pclk: platform-clock {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <900000000>;
+			clock-output-names = "platform-clock";
+		};
+
+		pclk_div2: platform-clock-div2 {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <450000000>;
+			clock-output-names = "platform-clock-div2";
+		};
+
+		pclk_div4: platform-clock-div4 {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <225000000>;
+			clock-output-names = "platform-clock-div4";
+		};
+
+		pll_soc_out3: pll-soc-out3 {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <180000000>;
+			clock-output-names = "pll-soc-out3";
+		};
+
+		usb_otg_ahb_clk: clock-usb-otg-ahb {
+			#clock-cells = <0>;
+			compatible = "fixed-clock";
+			clock-frequency = <60000000>;
+			clock-output-names = "otg";
+		};
+	};
+
+	soc: soc {
+		#address-cells = <0x02>;
+		#size-cells = <0x02>;
+		compatible = "simple-bus";
+		interrupt-parent = <&plic>;
+		ranges;
+
+		plic: interrupt-controller@c000000 {
+			compatible = "metanoia,mt2824-plic", "andestech,nceplic100";
+			reg = <0x0 0x0c000000 0x0 0x400000>;
+			interrupts-extended = <&cpu0intc 0x0b>, <&cpu0intc 0x09>,
+					      <&cpu1intc 0x0b>, <&cpu1intc 0x09>;
+			interrupt-controller;
+			#address-cells = <0x00>;
+			#interrupt-cells = <0x02>;
+			riscv,ndev = <0x47>;
+		};
+
+		plicsw: interrupt-controller@c800000 {
+			compatible = "metanoia,mt2824-plicsw", "andestech,plicsw";
+			reg = <0x00 0x0c800000 0x00 0x400000>;
+			interrupts-extended =
+			<&cpu0intc IRQ_TYPE_EDGE_BOTH>,
+			<&cpu1intc IRQ_TYPE_EDGE_BOTH>;
+		};
+
+		plmt0@c400000 {
+			compatible = "metanoia,mt2824-plmt", "andestech,plmt0";
+			reg = <0x0 0x0c400000 0x0 0x00400000>;
+			interrupts-extended = <&cpu0intc 0x07>,
+					      <&cpu1intc 0x07>;
+		};
+
+		reboot: reboot@10000004 {
+			compatible = "metanoia,mt2824-reboot";
+			reg = <0x0 0x10000004 0x0 0x08>;
+		};
+
+		rst: reset-controller@1000000c {
+			#reset-cells = <1>;
+			compatible = "metanoia,mt2824-reset";
+			reg = <0x0 0x1000000c 0x0 0x04>;
+		};
+
+		serial0: serial@100e0000 {
+			compatible = "ns16550a";
+			reg = <0x00 0x100e0000 0x00 0x1000>;
+			interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-parent = <&plic>;
+			clocks = <&pclk_div4>;
+			current-speed = <115200>;
+			reg-shift = <0x02>;
+			reg-offset = <0x20>;
+			reg-io-width = <0x04>;
+		};
+
+		qspi: spi@10120000 {
+			compatible = "metanoia,mt2824-spi";
+			reg = <0x00 0x10120000 0x00 0x10000>;
+			interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-parent = <&plic>;
+			#address-cells = <0x01>;
+			#size-cells = <0x00>;
+			num-cs = <0x01>;
+			clocks = <&pll_soc_out3>;
+			clock-names = "spi_clk_src";
+		};
+
+		mmcsd: mmc@4100000 {
+			compatible = "metanoia,mt2824-dwcmshc";
+			max-frequency = <200000000>;
+			clocks = <&pll_soc_out3>;
+			clock-names = "core";
+			reg = <0x0 0x04100000 0x0 0x00100000>;
+			interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-parent = <&plic>;
+			sdhci-caps-mask = <0x80000006 0x00000000>;
+			bus-width = <4>;
+			disable-wp;
+		};
+	};
+};
diff --git a/arch/riscv/dts/mt5824-evb.dts b/arch/riscv/dts/mt5824-evb.dts
new file mode 100644
index 00000000000..207051e8a14
--- /dev/null
+++ b/arch/riscv/dts/mt5824-evb.dts
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2026 Metanoia Communications Inc. All rights reserved.
+ */
+/dts-v1/;
+
+#include "mt2824.dtsi"
+
+/ {
+	#address-cells = <0x02>;
+	#size-cells = <0x02>;
+	model = "Metanoia MT5824 EVB";
+	compatible = "metanoia,mt5824-evb", "metanoia,mt2824";
+
+	chosen {
+		stdout-path = "uart0:115200n8";
+	};
+
+	ddr: memory@0 {
+		device_type = "memory";
+		reg = <0x0 0x0000000080000000 0x00000000 0x80000000>,
+		      <0x1 0x0000000000000000 0x00000000 0x80000000>;
+	};
+};
+
+&qspi {
+	flash@0 {
+		compatible = "jedec,spi-nor";
+		spi-rx-bus-width = <0x4>;
+		spi-tx-bus-width = <0x4>;
+		spi-max-frequency = <100000000>;
+		reg = <0x0>;
+	};
+};

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 10/10] riscv: metanoia: Add support for the MT5824 EVB platform
  2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
                   ` (8 preceding siblings ...)
  2026-10-02 13:02 ` [PATCH 09/10] riscv: dts: add MT2824 SoC and MT5824 EVB device trees Benoît Monin
@ 2026-10-02 13:02 ` Benoît Monin
  9 siblings, 0 replies; 19+ messages in thread
From: Benoît Monin @ 2026-10-02 13:02 UTC (permalink / raw)
  To: u-boot, Yao Zi
  Cc: Thomas Petazzoni, Tom Rini, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱),
	Benoît Monin

Add board, configuration and SPL support for the Metanoia MT5824
Evaluation Board, built around the MT2824 SoC.

Boot flow: the ROM loads the SPL from the SD card or the QSPI NOR. The
SPL initializes the SoC PLL, loads and validates the DDR firmware from
the boot device and initializes the DDR controller through the MT2824
DDR driver, then sets up the SDHCI controller and starts U-Boot proper
via OpenSBI.

On both boot media, the SPL locates its payloads by name in the GPT
partition table ("uboot", "ddrfw"). The A/B slot is selected by the
ROM one-shot boot: the boot source and the slot selection are left in
scratch registers by the ROM, and the SPL reads them to pick the
matching partition (the FIP flag partition attribute selects the slot
within the "uboot" partitions).

The environment backend (MMC or SPI flash) is selected at runtime
following the boot source. On MMC devices, the environment is stored
in a "u-boot-env" type-GPT partition, with a fixed offset fallback; on
the SPI-NOR, it is stored at a fixed offset.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 arch/riscv/Kconfig                       |   4 +
 board/metanoia/mt5824-evb/Kconfig        |  45 ++++
 board/metanoia/mt5824-evb/MAINTAINERS    |  11 +
 board/metanoia/mt5824-evb/Makefile       |   7 +
 board/metanoia/mt5824-evb/board.c        |  61 +++++
 board/metanoia/mt5824-evb/board.h        |  35 +++
 board/metanoia/mt5824-evb/mt5824-evb.env |   6 +
 board/metanoia/mt5824-evb/spl.c          | 388 +++++++++++++++++++++++++++++++
 configs/metanoia-mt5824-evb_defconfig    |  73 ++++++
 doc/board/index.rst                      |   1 +
 doc/board/metanoia/index.rst             |   9 +
 doc/board/metanoia/mt5824-evb.rst        | 232 ++++++++++++++++++
 include/configs/metanoia-mt5824-evb.h    |  28 +++
 13 files changed, 900 insertions(+)

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index c1b2890f1f3..f1054fc9bd6 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -23,6 +23,9 @@ config TARGET_K230_CANMV
 config TARGET_LICHEERV_NANO
 	bool "Support LicheeRV Nano Board"
 
+config TARGET_METANOIA_MT5824_EVB
+	bool "Support Metanoia MT5824 Evaluation Board"
+
 config TARGET_MICROCHIP_GENERIC
 	bool "Support Microchip PolarFire-SoC Boards"
 
@@ -115,6 +118,7 @@ source "board/aspeed/ibex_ast2700/Kconfig"
 source "board/beagle/beaglev_fire/Kconfig"
 source "board/canaan/k230_canmv/Kconfig"
 source "board/emulation/qemu-riscv/Kconfig"
+source "board/metanoia/mt5824-evb/Kconfig"
 source "board/microchip/mpfs_generic/Kconfig"
 source "board/openpiton/riscv64/Kconfig"
 source "board/sifive/unleashed/Kconfig"
diff --git a/board/metanoia/mt5824-evb/Kconfig b/board/metanoia/mt5824-evb/Kconfig
new file mode 100644
index 00000000000..1829664df59
--- /dev/null
+++ b/board/metanoia/mt5824-evb/Kconfig
@@ -0,0 +1,45 @@
+if TARGET_METANOIA_MT5824_EVB
+
+config SYS_CPU
+	default "andes"
+
+config SYS_BOARD
+	default "mt5824-evb"
+
+config SYS_VENDOR
+	default "metanoia"
+
+config SYS_SOC
+	default "mt2824"
+
+config SYS_CONFIG_NAME
+	default "metanoia-mt5824-evb"
+
+config ENV_SIZE
+	default 0x2000
+
+config ENV_OFFSET
+	default 0x400000
+
+config SPL_TEXT_BASE
+	default 0x68000000
+
+config SPL_OPENSBI_LOAD_ADDR
+	default 0x80000000
+
+config NR_CPUS
+	default 2
+
+config BOARD_SPECIFIC_OPTIONS
+	def_bool y
+	select RISCV_ANDES
+	select ANDES_ERRATA_35102
+	select SUPPORT_SPL
+	select BINMAN if SPL
+	imply EFI_PARTITION
+	imply PARTITION_TYPE_GUID
+	imply SMP
+	imply SPL_RAM_SUPPORT
+	imply SPL_RAM_DEVICE
+	imply SPL_BOARD_INIT
+endif
diff --git a/board/metanoia/mt5824-evb/MAINTAINERS b/board/metanoia/mt5824-evb/MAINTAINERS
new file mode 100644
index 00000000000..f61cc3191ab
--- /dev/null
+++ b/board/metanoia/mt5824-evb/MAINTAINERS
@@ -0,0 +1,11 @@
+Metanoia MT5824 EVB
+M:	Jun Chang <jun.chang@metanoia-comm.com>
+S:	Maintained
+F:	arch/riscv/dts/mt2824*
+F:	arch/riscv/dts/mt5824-evb*
+F:	board/metanoia/mt5824-evb/
+F:	configs/metanoia-mt5824-evb_defconfig
+F:	doc/board/metanoia/
+F:	drivers/mmc/metanoia_sdhci.c
+F:	drivers/ram/mt2824-ddr.c
+F:	include/configs/metanoia-mt5824-evb.h
diff --git a/board/metanoia/mt5824-evb/Makefile b/board/metanoia/mt5824-evb/Makefile
new file mode 100644
index 00000000000..b41735e3e5c
--- /dev/null
+++ b/board/metanoia/mt5824-evb/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2025 Metanoia Communications Inc.,
+# Jun Chang, Metanoia Communications Inc. <jun.chang@metanoia-comm.com>
+
+obj-y	:= board.o
+obj-$(CONFIG_SPL_BUILD)	+= spl.o
diff --git a/board/metanoia/mt5824-evb/board.c b/board/metanoia/mt5824-evb/board.c
new file mode 100644
index 00000000000..d354601290b
--- /dev/null
+++ b/board/metanoia/mt5824-evb/board.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang@metanoia-comm.com>
+ */
+
+#include <env.h>
+#include <env_internal.h>
+#include <fdtdec.h>
+
+#include "board.h"
+
+int dram_init(void)
+{
+	return fdtdec_setup_mem_size_base();
+}
+
+int dram_init_banksize(void)
+{
+	return fdtdec_setup_memory_banksize();
+}
+
+enum boot_source mt2824_get_boot_source(void)
+{
+	u32 val;
+
+	/* read the scratch register used for one shot boot */
+	val = readl(CFG_SCRATCH_POR1);
+
+	if (FIELD_GET(CFG_SCRATCH_POR1_OSB, val) == OSB_ACK)
+		/* boot source from one shot boot */
+		return FIELD_GET(CFG_SCRATCH_POR1_BOOTSRC, val);
+
+	/* read the pinstrap register */
+	val = readl(RST_PINSTRAP);
+
+	/* boot source from the pin strap */
+	return FIELD_GET(RST_PINSTRAP_BOOTSRC, val);
+}
+
+enum env_location env_get_location(enum env_operation op, int prio)
+{
+	if (prio > 0)
+		return ENVL_UNKNOWN;
+
+	switch (mt2824_get_boot_source()) {
+	case BOOT_SOURCE_SD_MMC:
+	case BOOT_SOURCE_SD_MMC2:
+		if (CONFIG_IS_ENABLED(ENV_IS_IN_MMC))
+			return ENVL_MMC;
+		break;
+	case BOOT_SOURCE_QSPI_NOR:
+		if (CONFIG_IS_ENABLED(ENV_IS_IN_SPI_FLASH))
+			return ENVL_SPI_FLASH;
+		break;
+	default:
+		break;
+	}
+
+	return ENVL_NOWHERE;
+}
diff --git a/board/metanoia/mt5824-evb/board.h b/board/metanoia/mt5824-evb/board.h
new file mode 100644
index 00000000000..347156f06ab
--- /dev/null
+++ b/board/metanoia/mt5824-evb/board.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang@metanoia-comm.com>
+ */
+
+#ifndef _METANOIA_MT5824_BOARD_H
+#define _METANOIA_MT5824_BOARD_H
+
+#include <asm/io.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+
+#define RST_PINSTRAP				phys_to_virt(0x10000018)
+
+#define CFG_SCRATCH_POR1			phys_to_virt(0x10010504)
+#define CFG_SCRATCH_POR1_OSB			GENMASK(23, 0)
+#define CFG_SCRATCH_POR1_PARTSEL		BIT(24)
+#define CFG_SCRATCH_POR1_BOOTSRC		GENMASK(27, 25)
+
+#define RST_PINSTRAP_BOOTSRC			GENMASK(7, 6)
+
+#define OSB_ROM_SIG				0x0062734F
+#define OSB_ACK					0x0042534F
+
+enum boot_source {
+	BOOT_SOURCE_RESERVED = 0,
+	BOOT_SOURCE_QSPI_NOR,
+	BOOT_SOURCE_SD_MMC, /* SD */
+	BOOT_SOURCE_SD_MMC2, /* eMMC */
+};
+
+enum boot_source mt2824_get_boot_source(void);
+
+#endif
diff --git a/board/metanoia/mt5824-evb/mt5824-evb.env b/board/metanoia/mt5824-evb/mt5824-evb.env
new file mode 100644
index 00000000000..7c036e118f9
--- /dev/null
+++ b/board/metanoia/mt5824-evb/mt5824-evb.env
@@ -0,0 +1,6 @@
+kernel_addr_r=0x82000000
+fdt_addr_r=0x87000000
+fdtoverlay_addr_r=0x87100000
+pxefile_addr_r=0x87200000
+scriptaddr=0x87300000
+ramdisk_addr_r=0x88000000
diff --git a/board/metanoia/mt5824-evb/spl.c b/board/metanoia/mt5824-evb/spl.c
new file mode 100644
index 00000000000..50c064c52ed
--- /dev/null
+++ b/board/metanoia/mt5824-evb/spl.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Metanoia Communications Inc.,
+ * Jun Chang, Metanoia Communications Inc. <jun.chang@metanoia-comm.com>
+ */
+
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <config.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <mmc.h>
+#include <nvmxip.h>
+#include <part_efi.h>
+#include <spl.h>
+
+#include "board.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define RST_DEVICEID				phys_to_virt(0x10010008)
+
+#define PLIC_RESERVED				phys_to_virt(0x0c000000)
+
+#define CFG_TZSECURECFG0			phys_to_virt(0x10010280)
+
+#define PLL_SOC_ST1				phys_to_virt(0x10040008)
+#define PLL_SOC_CR0				phys_to_virt(0x1004000c)
+
+#define PLIC_RESERVED_NONVEC			BIT(1)
+
+#define TZSECURECFG0_LPDDR_PHY_PPROT_MASK	GENMASK(31, 29)
+#define TZSECURECFG0_LPDDR_PHY_PPROT_BYPASS	BIT(29)
+
+#define PART_BOOT				"uboot"
+#define PART_DDRFW				"ddrfw"
+
+/* partition attribute used by the ROM code to find the SPL */
+#define PART_FIP_FLAG				BIT(0)
+
+/* The ROM code uses this partition GUID to find the SPL */
+static const efi_guid_t fip_type_guid = LEGACY_MBR_PARTITION_GUID;
+
+u32 spl_boot_device(void)
+{
+	switch (mt2824_get_boot_source()) {
+	case BOOT_SOURCE_SD_MMC:
+	case BOOT_SOURCE_SD_MMC2:
+		if (IS_ENABLED(CONFIG_SPL_MMC))
+			return BOOT_DEVICE_MMC1;
+		break;
+	case BOOT_SOURCE_QSPI_NOR:
+		if (IS_ENABLED(CONFIG_SPL_NOR_SUPPORT))
+			return BOOT_DEVICE_NOR;
+		break;
+	case BOOT_SOURCE_RESERVED:
+		if (IS_ENABLED(CONFIG_SPL_RAM_SUPPORT))
+			return BOOT_DEVICE_RAM;
+		break;
+	}
+
+	return BOOT_DEVICE_NONE;
+}
+
+static struct udevice *get_boot_dev(void)
+{
+	struct udevice *dev;
+	struct mmc *mmc;
+	int ret;
+
+	switch (spl_boot_device()) {
+	case BOOT_DEVICE_MMC1:
+		ret = uclass_get_device_by_seq(UCLASS_MMC, 0, &dev);
+		if (ret)
+			return NULL;
+
+		mmc = mmc_get_mmc_dev(dev);
+		if (!mmc)
+			return NULL;
+
+		ret = mmc_init(mmc);
+		if (ret)
+			return NULL;
+
+		return dev;
+
+	case BOOT_DEVICE_NOR:
+		ret = uclass_first_device_err(UCLASS_NVMXIP, &dev);
+		if (ret)
+			return NULL;
+
+		return dev;
+
+	default:
+		return NULL;
+	}
+}
+
+static int get_fip_part_num(void)
+{
+	struct disk_partition info;
+	struct blk_desc *desc;
+	efi_guid_t type_guid;
+	struct udevice *dev;
+	u16 part_flag;
+	u32 val;
+
+	dev = get_boot_dev();
+	if (!dev)
+		return -ENOENT;
+
+	desc = blk_get_by_device(dev);
+	if (!desc)
+		return -ENOENT;
+
+	val = readl(CFG_SCRATCH_POR1);
+
+	if (FIELD_GET(CFG_SCRATCH_POR1_OSB, val) == OSB_ACK &&
+	    !FIELD_GET(CFG_SCRATCH_POR1_PARTSEL, val))
+		part_flag = 0;
+	else
+		part_flag = PART_FIP_FLAG;
+
+	for (int p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
+		int ret = part_get_info(desc, p, &info);
+
+		if (ret)
+			break;
+
+		ret = uuid_str_to_bin(disk_partition_type_guid(&info),
+				      type_guid.b, UUID_STR_FORMAT_GUID);
+		if (ret)
+			continue;
+
+		if (guidcmp(&type_guid, &fip_type_guid))
+			continue;
+
+		if ((info.type_flags & PART_FIP_FLAG) == part_flag)
+			return trailing_strtol(info.name);
+	}
+
+	return -ENOENT;
+}
+
+static void fill_part_name(const char *prefix, char *part_name, size_t part_name_len)
+{
+	int part_num = get_fip_part_num();
+
+	if (part_num >= 0)
+		snprintf(part_name, part_name_len, "%s%d", prefix, part_num);
+	else
+		strlcpy(part_name, prefix, part_name_len);
+}
+
+int spl_mmc_boot_partition(const u32 boot_device)
+{
+	char part_name[PART_NAME_LEN];
+	struct disk_partition info;
+	struct blk_desc *desc;
+	struct udevice *dev;
+	int ret;
+
+	fill_part_name(PART_BOOT, part_name, sizeof(part_name));
+
+	dev = get_boot_dev();
+	if (!dev)
+		goto fallback;
+
+	desc = blk_get_by_device(dev);
+	if (!desc)
+		goto fallback;
+
+	ret = part_get_info_by_name(desc, part_name, &info);
+	if (ret > 0)
+		return ret;
+
+fallback:
+	log_debug("Partition %s not found, using default\n", part_name);
+	return CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION;
+}
+
+unsigned long spl_nor_get_uboot_base(void)
+{
+	char part_name[PART_NAME_LEN];
+	struct disk_partition info;
+	struct nvmxip_plat *plat;
+	struct blk_desc *desc;
+	struct udevice *dev;
+	int ret;
+
+	fill_part_name(PART_BOOT, part_name, sizeof(part_name));
+
+	dev = get_boot_dev();
+	if (!dev)
+		goto fallback;
+
+	desc = blk_get_by_device(dev);
+	if (!desc)
+		goto fallback;
+
+	plat = dev_get_plat(dev);
+
+	ret = part_get_info_by_name(desc, part_name, &info);
+	if (ret > 0)
+		return plat->phys_base + info.start * desc->blksz;
+
+fallback:
+	log_debug("Partition %s not found, using default\n", part_name);
+	return CFG_SYS_UBOOT_BASE;
+}
+
+#ifdef CONFIG_SPL_LOAD_FIT
+int board_fit_config_name_match(const char *name)
+{
+	/* boot using first FIT config */
+	return 0;
+}
+#endif
+
+void spl_board_init(void)
+{
+	if (!IS_ENABLED(CONFIG_SYS_DCACHE_OFF))
+		enable_caches();
+
+	/* set PLIC to non-vector mode */
+	clrbits_le32(PLIC_RESERVED, PLIC_RESERVED_NONVEC);
+}
+
+static int get_ddrfw_addr_size(void **fw_addr, size_t *fw_size)
+{
+	const void *blob = gd->fdt_blob;
+	fdt_size_t size;
+	fdt_addr_t addr;
+	int soc_node;
+	int index;
+	int node;
+
+	soc_node = fdt_path_offset(blob, "/soc");
+	node = fdt_node_offset_by_compatible(blob, soc_node, "metanoia,mt2824-ddrc");
+	if (node < 0)
+		return -ENODEV;
+
+	index = fdt_stringlist_search(blob, node, "reg-names", "fw");
+	if (index < 0)
+		return -ENOENT;
+
+	addr = fdtdec_get_addr_size_auto_parent(blob, soc_node, node, "reg",
+						index, &size, false);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	*fw_addr = (void *)addr;
+	*fw_size = (size_t)size;
+
+	return 0;
+}
+
+static int load_ddrfw(void)
+{
+	char part_name[PART_NAME_LEN];
+	struct disk_partition info;
+	struct blk_desc *desc;
+	struct udevice *dev;
+	lbaint_t blkcnt;
+	lbaint_t start;
+	size_t fw_size;
+	void *fw_addr;
+	int ret;
+
+	ret = get_ddrfw_addr_size(&fw_addr, &fw_size);
+	if (ret < 0)
+		return ret;
+
+	fill_part_name(PART_DDRFW, part_name, sizeof(part_name));
+
+	for (int i = 0; i < 3; i++) {
+		dev = get_boot_dev();
+		if (!dev)
+			continue;
+
+		desc = blk_get_by_device(dev);
+		if (!desc)
+			continue;
+
+		blkcnt = DIV_ROUND_UP(fw_size, desc->blksz);
+
+		ret = part_get_info_by_name(desc, part_name, &info);
+		if (ret > 0 && info.size >= blkcnt) {
+			start = info.start;
+		} else {
+			log_debug("Partition %s not found, using raw offset\n", part_name);
+			start = DIV_ROUND_UP(CFG_DDRFW_OFFSET, desc->blksz);
+		}
+
+		ret = blk_dread(desc, start, blkcnt, fw_addr);
+		if (ret == blkcnt)
+			return 0;
+
+		mdelay(5);
+	}
+
+	return -EIO;
+}
+
+int spl_board_init_f(void)
+{
+	struct udevice *dev;
+	int ret = 0;
+
+	/* Nothing to do if already in RAM */
+	if (spl_boot_device() == BOOT_DEVICE_RAM)
+		return 0;
+
+	/* enable access to the DDR controller */
+	clrsetbits_le32(CFG_TZSECURECFG0, TZSECURECFG0_LPDDR_PHY_PPROT_MASK,
+			TZSECURECFG0_LPDDR_PHY_PPROT_BYPASS);
+
+	/* load ddr firmware from boot device */
+	ret = load_ddrfw();
+	if (ret)
+		return ret;
+
+	return uclass_get_device(UCLASS_RAM, 0, &dev);
+}
+
+static void shuttle_pll_fix(void)
+{
+	u8 step;
+
+	readl(PLL_SOC_ST1);
+	udelay(10);
+	writel(0x040000C8, PLL_SOC_CR0);
+	udelay(10);
+	writel(0x440000C8, PLL_SOC_CR0);
+	udelay(10);
+	writel(0x040000C8, PLL_SOC_CR0);
+	udelay(10);
+	writel(0x840000C8, PLL_SOC_CR0);
+	udelay(10);
+	writel(0x840000C8, PLL_SOC_CR0);
+	udelay(10);
+
+	for (step = 0xc8; step >= 0xb4; step--) {
+		writel(0x84000000 | step, PLL_SOC_CR0);
+		udelay(10);
+		writel(0xc4000000 | step, PLL_SOC_CR0);
+		udelay(10);
+		writel(0x84000000 | step, PLL_SOC_CR0);
+		udelay(10);
+	}
+
+	readl(PLL_SOC_ST1);
+	debug("[PLL/PCLK] change to 900MHz\n");
+}
+
+void board_init_f(ulong dummy)
+{
+	u32 val;
+	int ret;
+
+	/* acknowledge one shot boot from the ROMcode */
+	val = readl(CFG_SCRATCH_POR1);
+	if (FIELD_GET(CFG_SCRATCH_POR1_OSB, val) == OSB_ROM_SIG) {
+		val &= ~CFG_SCRATCH_POR1_OSB;
+		val |= FIELD_PREP(CFG_SCRATCH_POR1_OSB, OSB_ACK);
+	} else {
+		val = 0;
+	}
+	writel(val, CFG_SCRATCH_POR1);
+
+	ret = spl_early_init();
+	if (ret)
+		panic("spl_early_init() failed: %d\n", ret);
+
+	/* check the device ID */
+	if (readl(RST_DEVICEID) == 0x28240000)
+		shuttle_pll_fix();
+
+	riscv_cpu_setup();
+
+	preloader_console_init();
+
+	ret = spl_board_init_f();
+	if (ret)
+		panic("spl_board_init_f() failed: %d\n", ret);
+}
diff --git a/configs/metanoia-mt5824-evb_defconfig b/configs/metanoia-mt5824-evb_defconfig
new file mode 100644
index 00000000000..f6fb141d799
--- /dev/null
+++ b/configs/metanoia-mt5824-evb_defconfig
@@ -0,0 +1,73 @@
+CONFIG_RISCV=y
+CONFIG_TEXT_BASE=0x84000000
+CONFIG_SYS_MALLOC_LEN=0x80000
+CONFIG_SYS_MALLOC_F_LEN=0x40000
+CONFIG_NR_DRAM_BANKS=2
+CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
+CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x68140000
+CONFIG_DEFAULT_DEVICE_TREE="mt5824-evb"
+CONFIG_DM_RESET=y
+CONFIG_SPL_MMC=y
+CONFIG_SPL_STACK_R_ADDR=0x68100000
+CONFIG_SPL_BSS_START_ADDR=0x68020000
+CONFIG_SPL_BSS_MAX_SIZE=0x01000
+CONFIG_SPL_STACK_R=y
+CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0xC0000
+CONFIG_SYS_BOOTM_LEN=0x4000000
+CONFIG_SYS_LOAD_ADDR=0xd0000000
+CONFIG_SPL_SIZE_LIMIT=0x20000
+CONFIG_SPL=y
+CONFIG_TARGET_METANOIA_MT5824_EVB=y
+CONFIG_ARCH_RV64I=y
+CONFIG_RISCV_SMODE=y
+# CONFIG_AVAILABLE_HARTS is not set
+CONFIG_FIT=y
+CONFIG_SPL_FIT_SIGNATURE=y
+CONFIG_SPL_LOAD_FIT_ADDRESS=0x90000000
+CONFIG_BOOTSTD_FULL=y
+CONFIG_BOOTSTD_BOOTCOMMAND=y
+CONFIG_DISTRO_DEFAULTS=y
+CONFIG_BOOTDELAY=3
+CONFIG_SYS_PBSIZE=1050
+# CONFIG_BOARD_INIT is not set
+CONFIG_SPL_MAX_SIZE=0x18000
+CONFIG_SPL_PAD_TO=0x20000
+CONFIG_SPL_SYS_MALLOC_SIMPLE=y
+CONFIG_SPL_SYS_MMCSD_RAW_MODE=y
+CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION=y
+CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION=0x4
+CONFIG_SPL_CACHE=y
+CONFIG_SPL_NOR_SUPPORT=y
+CONFIG_SPL_DM_RESET=y
+CONFIG_SPL_NVMXIP=y
+CONFIG_SPL_OPENSBI_SCRATCH_OPTIONS=0x0
+CONFIG_SYS_PROMPT="RISC-V # "
+# CONFIG_CMD_VBE is not set
+CONFIG_CMD_MMC=y
+CONFIG_MMC_SPEED_MODE_SET=y
+CONFIG_ENV_IS_IN_MMC=y
+CONFIG_ENV_IS_IN_SPI_FLASH=y
+CONFIG_ENV_SECT_SIZE_AUTO=y
+CONFIG_MMC=y
+CONFIG_MMC_IO_VOLTAGE=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_METANOIA=y
+CONFIG_NVMXIP_QSPI=y
+CONFIG_RAM=y
+CONFIG_SPL_RAM=y
+CONFIG_SPL_METANOIA_MT2824_DDR=y
+# CONFIG_RAM_SIFIVE is not set
+CONFIG_RESET_SIMPLE=y
+CONFIG_SYS_NS16550=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_ATCSPI200_SPI=y
+CONFIG_SYSRESET=y
+# CONFIG_BINMAN_FDT is not set
+# CONFIG_USE_PRIVATE_LIBGCC is not set
+CONFIG_MBEDTLS_LIB=y
+CONFIG_SPL_MBEDTLS_LIB=y
+# CONFIG_TOOLS_MKEFICAPSULE is not set
diff --git a/doc/board/index.rst b/doc/board/index.rst
index 4103fef8d8f..66d1d93ca2e 100644
--- a/doc/board/index.rst
+++ b/doc/board/index.rst
@@ -41,6 +41,7 @@ Board-specific doc
    lg/index
    liebherr/index
    mediatek/index
+   metanoia/index
    microchip/index
    microsoft/index
    motorola/index
diff --git a/doc/board/metanoia/index.rst b/doc/board/metanoia/index.rst
new file mode 100644
index 00000000000..5870e12b08c
--- /dev/null
+++ b/doc/board/metanoia/index.rst
@@ -0,0 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Metanoia
+========
+
+.. toctree::
+   :maxdepth: 2
+
+   mt5824-evb
diff --git a/doc/board/metanoia/mt5824-evb.rst b/doc/board/metanoia/mt5824-evb.rst
new file mode 100644
index 00000000000..f921dc50986
--- /dev/null
+++ b/doc/board/metanoia/mt5824-evb.rst
@@ -0,0 +1,232 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+MT5824 EVB
+==========
+
+The MT5824 EVB is the evaluation board for the Metanoia MT2824 SoC.
+
+The MT2824 integrates a dual-core Andes AX45MP RISC-V CPU with 64 KB
+L1 I-cache, 32 KB L1 D-cache and 256 KB unified L2 cache, together
+with the Andes PLIC, PLIC SW and PLMT interrupt and timer
+controllers.
+
+The boot media is either an SD card or a 32 MiB Macronix SPI-NOR flash
+connected to the ATCSPI200 QSPI controller and exposed as a
+memory-mapped device. The boot source and the A/B slot are selected by
+the ROM one-shot boot, through the pinstrap and scratch registers.
+
+Boot flow
+---------
+
+1. The ROM code loads the SPL from the boot device.
+2. The SPL initializes the SoC PLL, loads and validates the DDR
+   firmware from the boot device, then initializes the DDR
+   controller.
+3. The SPL loads U-Boot and OpenSBI from the "uboot" GPT partition of
+   the boot device and starts U-Boot through OpenSBI.
+4. U-Boot selects the environment backend (MMC or SPI flash)
+   following the boot source, then boots with the standard boot flow.
+
+The boot source and the A/B slot selected by the ROM code are passed
+through the one-shot boot scratch register. When the ROM code did not
+perform a one-shot boot, the boot source is read from the pin strap
+register instead.
+
+The SPL and U-Boot locate their payloads ("ddrfw", "uboot") by name in
+the GPT partition table of the boot device, so the same layout works
+on both media. The environment is stored in a partition of type
+"u-boot-env" on MMC devices and at a fixed offset in the SPI-NOR
+flash.
+
+Building
+--------
+
+Add the RISC-V toolchain to ``PATH`` and set the cross compilation
+environment:
+
+.. code-block:: console
+
+    export CROSS_COMPILE=riscv64-linux-gnu-
+    export ARCH=riscv
+
+Build OpenSBI to generate ``fw_dynamic.bin``:
+
+.. code-block:: console
+
+    cd /path/to/opensbi
+    make PLATFORM=generic
+
+Build U-Boot, pointing ``OPENSBI`` at the OpenSBI firmware:
+
+.. code-block:: console
+
+    cd /path/to/u-boot
+    make metanoia-mt5824-evb_defconfig
+    make OPENSBI=/path/to/opensbi/build/platform/generic/firmware/fw_dynamic.bin
+
+The build produces the SPL at ``spl/u-boot-spl.bin`` (with a 128 KiB
+size limit) and the U-Boot FIT image at ``u-boot.itb``, which contains
+OpenSBI, U-Boot proper and the device tree.
+
+Boot partitions
+---------------
+
+The ROM code expects a GPT partition table on the boot device,
+whatever the media is (SD card, eMMC or SPI-NOR). Sectors are 512
+bytes in the examples below.
+
+By default the ROM code loads the SPL from the first partition whose
+type GUID is ``024DEE41-33E7-11D3-9D69-0008C781F39F`` (the MBR
+partition scheme type) and whose attribute bit 48 is set. With the
+one-shot boot configured, it loads the first partition of the same
+type whose attribute bit 48 is clear.
+
+The partition layout without A/B boot is:
+
+.. code-block:: none
+
+    Start    End  Sectors Type-UUID                            Name    Attributes
+     2048   3071     1024 024DEE41-33E7-11D3-9D69-0008C781F39F fip     GUID:48
+     6144   6399      256 8DA63339-0007-60C0-C436-083AC8230908 ddrfw
+     8192   8447      256 3DE21764-95BD-54BD-A5C3-4ABE786F38A8 uENV
+    10240  14335     4096 8DA63339-0007-60C0-C436-083AC8230908 uboot
+
+The partition layout with A/B boot is:
+
+.. code-block:: none
+
+    Start    End  Sectors Type-UUID                            Name    Attributes
+     2048   3071     1024 024DEE41-33E7-11D3-9D69-0008C781F39F fip0    GUID:48
+     4096   5119     1024 024DEE41-33E7-11D3-9D69-0008C781F39F fip1
+     6144   6399      256 8DA63339-0007-60C0-C436-083AC8230908 ddrfw0
+     6400   6655      256 8DA63339-0007-60C0-C436-083AC8230908 ddrfw1
+     8192   8447      256 3DE21764-95BD-54BD-A5C3-4ABE786F38A8 uENV
+    10240  14335     4096 8DA63339-0007-60C0-C436-083AC8230908 uboot0
+    14336  18431     4096 8DA63339-0007-60C0-C436-083AC8230908 uboot1
+
+The partitions are:
+
+- ``fip``, ``fip0``, ``fip1``: SPL firmware image, loaded by the ROM
+  code. Without A/B boot there is a single ``fip`` partition; with A/B
+  boot, ``fip0`` is the default slot (attribute bit 48 set) and
+  ``fip1`` is the one-shot boot slot.
+- ``ddrfw``, ``ddrfw0``, ``ddrfw1``: DDR firmware image, loaded by the
+  SPL.
+- ``uboot``, ``uboot0``, ``uboot1``: U-Boot FIT image (OpenSBI,
+  U-Boot proper and device tree), loaded by the SPL.
+- ``uENV``: U-Boot environment.
+
+In A/B boot, the ROM code stores the selected slot in the one-shot
+boot scratch register, and the SPL adds the matching ``0`` or ``1``
+suffix to the ``ddrfw`` and ``uboot`` partition names.
+
+All the partitions described above are unformatted: they hold raw images
+and are addressed by their GPT partition name, not through a filesystem.
+
+SPL file format
+---------------
+
+The ROM code expects the SPL to be packaged in a TF-A firmware image
+package (FIP). The U-Boot SPL is passed as the trusted boot firmware
+(BL2) component. Only the ``tb-fw`` image and its certificate are
+required by the ROM code to boot the SPL.
+
+Copy the U-Boot SPL and generate the FIP with the TF-A ``cert_create``
+and `fiptool`_ tools:
+
+.. code-block:: console
+
+    export FIPDIR=/path/to/fip
+    cp spl/u-boot-spl.bin $FIPDIR/mtf-bl2.img
+
+    cert_create -n --tfw-nvctr 0 --ntfw-nvctr 0 \
+        --key-alg rsa --key-size 2048 --hash-alg sha256 \
+        --tb-fw-cert $FIPDIR/tb_fw.crt \
+        --tb-fw $FIPDIR/mtf-bl2.img
+
+    fiptool create \
+        --tb-fw-cert $FIPDIR/tb_fw.crt \
+        --tb-fw $FIPDIR/mtf-bl2.img \
+        $FIPDIR/mtf-fip.bin
+
+As long as no root of trust SHA-256 hash has been programmed into the
+SoC, ``cert_create`` generates the certificate with a random key. This
+is fine for development: the ROM code only checks that the FIP is
+present and carries a signature.
+
+.. _`fiptool`: https://github.com/TrustedFirmware-A/trusted-firmware-a/tree/master/tools/fiptool
+
+DDR firmware
+------------
+
+The DDR firmware is a FIT image, which can be signed so that the SPL can
+verify it when built with ``CONFIG_SPL_FIT_SIGNATURE``.
+
+The SPL looks up the ``ddrfw`` partition (or ``ddrfw0``/``ddrfw1`` in
+A/B boot) on the boot device and copies the firmware to the address
+described by the ``fw`` region of the DDR controller node.
+
+The DDR firmware image is generated from an ITS file. For example:
+
+.. code-block:: dts
+
+    /dts-v1/;
+
+    / {
+        description = "Metanoia MT2824 DDR firmware";
+        #address-cells = <1>;
+
+        images {
+            ddrfirmware {
+                description = "DDR firmware";
+                type = "firmware";
+                arch = "riscv";
+                compression = "none";
+                data = /incbin/("./mt2824_evb_lpddr4_3200.bin");
+                hash {
+                    algo = "sha256";
+                };
+            };
+        };
+
+        configurations {
+            default = "conf";
+            conf {
+                description = "mt2824-ddrfw";
+                firmware = "ddrfirmware";
+                signature {
+                    algo = "sha256,rsa2048";
+                    key-name-hint = "dev";
+                    sign-images = "firmware";
+                };
+            };
+        };
+    };
+
+The firmware binary is available from the Metanoia MOSART repository
+available on `GitHub`_ and `GitLab`_.
+
+.. _`GitHub`: https://github.com/metanoia-comm-mosart/ddrfw_2.0
+.. _`GitLab`: https://gitlab.com/metanoia-comm/mosart/public/ddrfw_2.0
+
+U-Boot proper
+-------------
+
+U-Boot proper is a FIT image. It contains OpenSBI, U-Boot proper and
+the device tree.
+
+The SPL looks up the ``uboot`` partition (or ``uboot0``/``uboot1`` in
+A/B boot) on the boot device and starts U-Boot through OpenSBI.
+
+Environment
+-----------
+
+The environment backend is selected at runtime from the boot source.
+
+On SD card and eMMC devices, the environment is stored in
+a partition of type ``3DE21764-95BD-54BD-A5C3-4ABE786F38A8``
+(``u-boot-env``), with a fallback to the numbered partition set in
+``CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION``.
+
+On the SPI-NOR flash, the environment is stored at offset
+``CONFIG_ENV_OFFSET`` (default 0x400000).
diff --git a/include/configs/metanoia-mt5824-evb.h b/include/configs/metanoia-mt5824-evb.h
new file mode 100644
index 00000000000..822483603a3
--- /dev/null
+++ b/include/configs/metanoia-mt5824-evb.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2026 Metanoia Communication Inc.,
+ * Jun Chang, Metanoia Communication Inc. <jun.chang@metanoia-comm.com>
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#define RISCV_MMODE_TIMERBASE	0x0c400000
+
+#define RISCV_MMODE_TIMER_FREQ	20000000
+#define RISCV_SMODE_TIMER_FREQ	20000000
+
+/* Init Heap Pointer */
+#define CFG_MALLOC_F_ADDR	0x68180000
+
+/*
+ * Serial console configuration
+ */
+#define CFG_SYS_NS16550_CLK	225000000
+
+#define MT2824_NOR_FLASH_BASE	0x20000000
+#define CFG_SYS_UBOOT_BASE	(MT2824_NOR_FLASH_BASE + 0x2800 * 512)
+
+#define CFG_DDRFW_OFFSET	0x300000
+
+#endif /* __CONFIG_H */

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 01/10] iopoll: Add readw_poll_sleep_timeout
  2026-10-02 13:02 ` [PATCH 01/10] iopoll: Add readw_poll_sleep_timeout Benoît Monin
@ 2026-10-02 15:25   ` Tom Rini
  2026-10-13  8:44     ` Benoît Monin
  0 siblings, 1 reply; 19+ messages in thread
From: Tom Rini @ 2026-10-02 15:25 UTC (permalink / raw)
  To: Benoît Monin
  Cc: u-boot, Yao Zi, Thomas Petazzoni, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱)

[-- Attachment #1: Type: text/plain, Size: 327 bytes --]

On Fri, Oct 02, 2026 at 03:02:19PM +0200, Benoît Monin wrote:

> The readl poll API with sleep and timeout support was introduced by
> commit ce786ae3913f ("iopoll: Add readl_poll_sleep_timeout"), add the
> 16-bit variant.

Please be clearer that you're referring to the linux kernel commit this
came from.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 03/10] disk: efi: Reuse the GPT entries buffer with simple malloc
  2026-10-02 13:02 ` [PATCH 03/10] disk: efi: Reuse the GPT entries buffer with simple malloc Benoît Monin
@ 2026-10-02 15:26   ` Tom Rini
  2026-10-13  8:45     ` Benoît Monin
  0 siblings, 1 reply; 19+ messages in thread
From: Tom Rini @ 2026-10-02 15:26 UTC (permalink / raw)
  To: Benoît Monin
  Cc: u-boot, Yao Zi, Thomas Petazzoni, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱)

[-- Attachment #1: Type: text/plain, Size: 2610 bytes --]

On Fri, Oct 02, 2026 at 03:02:21PM +0200, Benoît Monin wrote:
> The simple malloc used in SPL has a no-op free(), so allocating the
> GPT partition entries buffer on every partition table read leaks
> memory. With repeated lookups, SPL can exhaust its small heap.
> 
> Keep a single cached buffer and reuse it while the allocation size
> does not change. The entries are re-read on every call, so the buffer
> is device- and LBA-agnostic.
> 
> It is safe to reuse the same buffer in the single-threaded xPL context
> as the returned pointer is only ever handed out within a single
> partition-table lookup and every caller consumes or copies the entries
> before the next lookup.
> 
> The change is gated on CONFIG_IS_ENABLED(SYS_MALLOC_SIMPLE), so builds
> with a real free() (U-Boot proper) keep the original malloc/free behavior.
> 
> Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
> ---
>  disk/part_efi.c | 25 +++++++++++++++++++++----
>  1 file changed, 21 insertions(+), 4 deletions(-)
> 
> diff --git a/disk/part_efi.c b/disk/part_efi.c
> index d8b17ec2e91..8a57fe911c4 100644
> --- a/disk/part_efi.c
> +++ b/disk/part_efi.c
> @@ -1141,7 +1141,7 @@ static int find_valid_gpt(struct blk_desc *desc, gpt_header *gpt_head,
>  static gpt_entry *alloc_read_gpt_entries(struct blk_desc *desc,
>  					 gpt_header *pgpt_head)
>  {
> -	size_t count = 0, blk_cnt;
> +	size_t count = 0, blk_cnt, size;
>  	lbaint_t blk;
>  	gpt_entry *pte = NULL;
>  
> @@ -1158,10 +1158,27 @@ static gpt_entry *alloc_read_gpt_entries(struct blk_desc *desc,
>  		  (u32)le32_to_cpu(pgpt_head->sizeof_partition_entry),
>  		  (ulong)count);
>  
> +	size = PAD_TO_BLOCKSIZE(count, desc);
> +
>  	/* Allocate memory for PTE, remember to FREE */
> -	if (count != 0) {
> -		pte = memalign(ARCH_DMA_MINALIGN,
> -			       PAD_TO_BLOCKSIZE(count, desc));
> +	if (CONFIG_IS_ENABLED(SYS_MALLOC_SIMPLE)) {
> +		/*
> +		 * The simple malloc has a no-op free(): keep one
> +		 * buffer and reuse it. The entries are re-read on
> +		 * every call, so the buffer is device- and LBA-agnostic.
> +		 */
> +		static gpt_entry *cache;
> +		static size_t cache_size;
> +
> +		if (cache && cache_size == size) {
> +			pte = cache;
> +		} else {
> +			pte = memalign(ARCH_DMA_MINALIGN, size);
> +			cache = pte;
> +			cache_size = size;
> +		}
> +	} else {
> +		pte = memalign(ARCH_DMA_MINALIGN, size);
>  	}
>  
>  	if (count == 0 || pte == NULL) {

This doesn't seem identical in the non-SPL case, since now pte is always
assigned. Or am I missing something?

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 04/10] nvmxip: Make NVM XIP devices available in SPL
  2026-10-02 13:02 ` [PATCH 04/10] nvmxip: Make NVM XIP devices available in SPL Benoît Monin
@ 2026-10-02 15:28   ` Tom Rini
  0 siblings, 0 replies; 19+ messages in thread
From: Tom Rini @ 2026-10-02 15:28 UTC (permalink / raw)
  To: Benoît Monin
  Cc: u-boot, Yao Zi, Thomas Petazzoni, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱)

[-- Attachment #1: Type: text/plain, Size: 653 bytes --]

On Fri, Oct 02, 2026 at 03:02:22PM +0200, Benoît Monin wrote:

> Add the SPL variant of the NVMXIP option and build the driver when it
> is enabled for the current phase. This allows the SPL to use the block
> devices emulated on top of a directly mapped non-volatile memory (XIP)
> to read data, such as the next stage image or a firmware, from the
> memory-mapped flash.
> 
> Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>

Ugh, I wish drivers/mtd/Makefile was more like the rest of the
Makefiles, but it's not, and so adding the line there as this patch does
is correct.

Reviewed-by: Tom Rini <trini@konsulko.com>

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 06/10] mmc: metanoia_sdhci: Add SDHCI driver for Metanoia MT2824 SoC
  2026-10-02 13:02 ` [PATCH 06/10] mmc: metanoia_sdhci: Add SDHCI driver for Metanoia MT2824 SoC Benoît Monin
@ 2026-10-02 15:29   ` Tom Rini
  2026-10-13  8:45     ` Benoît Monin
  0 siblings, 1 reply; 19+ messages in thread
From: Tom Rini @ 2026-10-02 15:29 UTC (permalink / raw)
  To: Benoît Monin
  Cc: u-boot, Yao Zi, Thomas Petazzoni, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱)

[-- Attachment #1: Type: text/plain, Size: 1500 bytes --]

On Fri, Oct 02, 2026 at 03:02:24PM +0200, Benoît Monin wrote:

> Add an SDHCI driver for the DesignWare Mobile Storage Host Controller
> (DWC MSHC) integrated in the Metanoia MT2824 SoC. It supports eMMC up to
> HS400/HS400ES and SD cards, and implements the MMC driver model.
> 
> The controller is a DWC MSHC instance, but its PHY programming,
> voltage/speed-mode handling and tuning model are silicon-specific
> and differ from the existing DWC MSHC drivers.
> 
> The PHY pad drive strength, receiver select and weak-pull settings
> depend on the card type (eMMC vs SD) and are re-applied on every ios,
> whereas snps_sdhci.c uses fixed pad values.
> 
> High-speed modes force 1.8V signalling and HS400 uses an explicit PHY
> DLL lock sequence (set_control_reg()/set_ios_post()), which snps_sdhci.c
> does not implement.
> 
> Tuning relies on the generic SDHCI core tuner, whereas snps_sdhci.c uses
> the hardware auto-tuning block.
> 
> Keeping a dedicated driver avoids per-variant branches in the shared
> DWC MSHC drivers, matching how snps_sdhci.c, adi_sdhci.c and
> cv1800b_sdhci.c are already split in U-Boot.
> 
> Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
[snip]
> +static const struct udevice_id metanoia_sdhci_ids[] = {
> +	{ .compatible = "metanoia,mt2824-dwcmshc" },
> +	{ /* sentinel */ }
> +};

I don't see this compatible in Linus' tree currently, so what's the
status on upstreaming all of the device tree parts? Thanks.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 01/10] iopoll: Add readw_poll_sleep_timeout
  2026-10-02 15:25   ` Tom Rini
@ 2026-10-13  8:44     ` Benoît Monin
  0 siblings, 0 replies; 19+ messages in thread
From: Benoît Monin @ 2026-10-13  8:44 UTC (permalink / raw)
  To: Tom Rini
  Cc: u-boot, Yao Zi, Thomas Petazzoni, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱)

On Friday, 2 October 2026 at 17:25:57 CEST, Tom Rini wrote:
> On Fri, Oct 02, 2026 at 03:02:19PM +0200, Benoît Monin wrote:
> 
> > The readl poll API with sleep and timeout support was introduced by
> > commit ce786ae3913f ("iopoll: Add readl_poll_sleep_timeout"), add the
> > 16-bit variant.
> 
> Please be clearer that you're referring to the linux kernel commit this
> came from.
> 
Yes I will clarify the commit message, as ce786ae3913f is a u-boot commit
and it refers to kernel commit 5f5323a14cad.

-- 
Benoît




^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 03/10] disk: efi: Reuse the GPT entries buffer with simple malloc
  2026-10-02 15:26   ` Tom Rini
@ 2026-10-13  8:45     ` Benoît Monin
  0 siblings, 0 replies; 19+ messages in thread
From: Benoît Monin @ 2026-10-13  8:45 UTC (permalink / raw)
  To: Tom Rini
  Cc: u-boot, Yao Zi, Thomas Petazzoni, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱)

On Friday, 2 October 2026 at 17:26:05 CEST, Tom Rini wrote:
[...]
> > +      size = PAD_TO_BLOCKSIZE(count, desc);
> > +
> >        /* Allocate memory for PTE, remember to FREE */
> > -      if (count != 0) {
> > -              pte = memalign(ARCH_DMA_MINALIGN,
> > -                             PAD_TO_BLOCKSIZE(count, desc));
> > +      if (CONFIG_IS_ENABLED(SYS_MALLOC_SIMPLE)) {
> > +              /*
> > +               * The simple malloc has a no-op free(): keep one
> > +               * buffer and reuse it. The entries are re-read on
> > +               * every call, so the buffer is device- and LBA-agnostic.
> > +               */
> > +              static gpt_entry *cache;
> > +              static size_t cache_size;
> > +
> > +              if (cache && cache_size == size) {
> > +                      pte = cache;
> > +              } else {
> > +                      pte = memalign(ARCH_DMA_MINALIGN, size);
> > +                      cache = pte;
> > +                      cache_size = size;
> > +              }
> > +      } else {
> > +              pte = memalign(ARCH_DMA_MINALIGN, size);
> >        }
> >  
> >        if (count == 0 || pte == NULL) {
> 
> This doesn't seem identical in the non-SPL case, since now pte is always
> assigned. Or am I missing something?
> 
I did overlook the count==0 case, will fix that.

-- 
Benoît



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 06/10] mmc: metanoia_sdhci: Add SDHCI driver for Metanoia MT2824 SoC
  2026-10-02 15:29   ` Tom Rini
@ 2026-10-13  8:45     ` Benoît Monin
  2026-10-13 15:05       ` Tom Rini
  0 siblings, 1 reply; 19+ messages in thread
From: Benoît Monin @ 2026-10-13  8:45 UTC (permalink / raw)
  To: Tom Rini
  Cc: u-boot, Yao Zi, Thomas Petazzoni, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱)

On Friday, 2 October 2026 at 17:29:35 CEST, Tom Rini wrote:
> On Fri, Oct 02, 2026 at 03:02:24PM +0200, Benoît Monin wrote:
> 
> > Add an SDHCI driver for the DesignWare Mobile Storage Host Controller
> > (DWC MSHC) integrated in the Metanoia MT2824 SoC. It supports eMMC up to
> > HS400/HS400ES and SD cards, and implements the MMC driver model.
> > 
> > The controller is a DWC MSHC instance, but its PHY programming,
> > voltage/speed-mode handling and tuning model are silicon-specific
> > and differ from the existing DWC MSHC drivers.
> > 
> > The PHY pad drive strength, receiver select and weak-pull settings
> > depend on the card type (eMMC vs SD) and are re-applied on every ios,
> > whereas snps_sdhci.c uses fixed pad values.
> > 
> > High-speed modes force 1.8V signalling and HS400 uses an explicit PHY
> > DLL lock sequence (set_control_reg()/set_ios_post()), which snps_sdhci.c
> > does not implement.
> > 
> > Tuning relies on the generic SDHCI core tuner, whereas snps_sdhci.c uses
> > the hardware auto-tuning block.
> > 
> > Keeping a dedicated driver avoids per-variant branches in the shared
> > DWC MSHC drivers, matching how snps_sdhci.c, adi_sdhci.c and
> > cv1800b_sdhci.c are already split in U-Boot.
> > 
> > Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
> [snip]
> > +static const struct udevice_id metanoia_sdhci_ids[] = {
> > +	{ .compatible = "metanoia,mt2824-dwcmshc" },
> > +	{ /* sentinel */ }
> > +};
> 
> I don't see this compatible in Linus' tree currently, so what's the
> status on upstreaming all of the device tree parts? Thanks.
> 
We are currently preparing a first patch series for Linux, but it has not
been posted yet.

-- 
Benoît




^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 06/10] mmc: metanoia_sdhci: Add SDHCI driver for Metanoia MT2824 SoC
  2026-10-13  8:45     ` Benoît Monin
@ 2026-10-13 15:05       ` Tom Rini
  0 siblings, 0 replies; 19+ messages in thread
From: Tom Rini @ 2026-10-13 15:05 UTC (permalink / raw)
  To: Benoît Monin
  Cc: u-boot, Yao Zi, Thomas Petazzoni, Quentin Schulz, Heiko Stuebner,
	Marek Vasut, Torsten Duwe, Ryan Chen, Guodong Xu, Alexey Charkov,
	Bastien Curutchet, Johan Jonker, Tim Ouyang, Leo Liang, Jun Chang,
	Peng Fan, Jaehoon Chung, Bin Liu, Hiago De Franco, Michal Simek,
	Raymond Mao, Alexander Feilke, Chris Packham,
	Randolph Sheng-Kai Lin(林聖凱)

[-- Attachment #1: Type: text/plain, Size: 1998 bytes --]

On Tue, Oct 13, 2026 at 10:45:09AM +0200, Benoît Monin wrote:
> On Friday, 2 October 2026 at 17:29:35 CEST, Tom Rini wrote:
> > On Fri, Oct 02, 2026 at 03:02:24PM +0200, Benoît Monin wrote:
> > 
> > > Add an SDHCI driver for the DesignWare Mobile Storage Host Controller
> > > (DWC MSHC) integrated in the Metanoia MT2824 SoC. It supports eMMC up to
> > > HS400/HS400ES and SD cards, and implements the MMC driver model.
> > > 
> > > The controller is a DWC MSHC instance, but its PHY programming,
> > > voltage/speed-mode handling and tuning model are silicon-specific
> > > and differ from the existing DWC MSHC drivers.
> > > 
> > > The PHY pad drive strength, receiver select and weak-pull settings
> > > depend on the card type (eMMC vs SD) and are re-applied on every ios,
> > > whereas snps_sdhci.c uses fixed pad values.
> > > 
> > > High-speed modes force 1.8V signalling and HS400 uses an explicit PHY
> > > DLL lock sequence (set_control_reg()/set_ios_post()), which snps_sdhci.c
> > > does not implement.
> > > 
> > > Tuning relies on the generic SDHCI core tuner, whereas snps_sdhci.c uses
> > > the hardware auto-tuning block.
> > > 
> > > Keeping a dedicated driver avoids per-variant branches in the shared
> > > DWC MSHC drivers, matching how snps_sdhci.c, adi_sdhci.c and
> > > cv1800b_sdhci.c are already split in U-Boot.
> > > 
> > > Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
> > [snip]
> > > +static const struct udevice_id metanoia_sdhci_ids[] = {
> > > +	{ .compatible = "metanoia,mt2824-dwcmshc" },
> > > +	{ /* sentinel */ }
> > > +};
> > 
> > I don't see this compatible in Linus' tree currently, so what's the
> > status on upstreaming all of the device tree parts? Thanks.
> > 
> We are currently preparing a first patch series for Linux, but it has not
> been posted yet.

I'd really like to see that get posted and commented on, so we don't
have to have churn here later if there's changes requested.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-10-13 15:05 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-02 13:02 [PATCH 00/10] riscv: Add support for Metanoia MT2824 SoC and MT5824 EVB Benoît Monin
2026-10-02 13:02 ` [PATCH 01/10] iopoll: Add readw_poll_sleep_timeout Benoît Monin
2026-10-02 15:25   ` Tom Rini
2026-10-13  8:44     ` Benoît Monin
2026-10-02 13:02 ` [PATCH 02/10] reset: Add a driver for simple controller Benoît Monin
2026-10-02 13:02 ` [PATCH 03/10] disk: efi: Reuse the GPT entries buffer with simple malloc Benoît Monin
2026-10-02 15:26   ` Tom Rini
2026-10-13  8:45     ` Benoît Monin
2026-10-02 13:02 ` [PATCH 04/10] nvmxip: Make NVM XIP devices available in SPL Benoît Monin
2026-10-02 15:28   ` Tom Rini
2026-10-02 13:02 ` [PATCH 05/10] riscv: andes: Add Kconfig option for Andes erratum #35102 Benoît Monin
2026-10-02 13:02 ` [PATCH 06/10] mmc: metanoia_sdhci: Add SDHCI driver for Metanoia MT2824 SoC Benoît Monin
2026-10-02 15:29   ` Tom Rini
2026-10-13  8:45     ` Benoît Monin
2026-10-13 15:05       ` Tom Rini
2026-10-02 13:02 ` [PATCH 07/10] ram: metanoia: Add driver for MT2824 DDR controller Benoît Monin
2026-10-02 13:02 ` [PATCH 08/10] spi: atcspi200_spi: Add spi-mem support for the Metanoia ATCSPI200 variant Benoît Monin
2026-10-02 13:02 ` [PATCH 09/10] riscv: dts: add MT2824 SoC and MT5824 EVB device trees Benoît Monin
2026-10-02 13:02 ` [PATCH 10/10] riscv: metanoia: Add support for the MT5824 EVB platform Benoît Monin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.