* [PATCH v2 0/2] spi: realtek: Add support for RTD1625 SPI Flash Controller
@ 2026-09-06 14:57 Yu-Chun Lin
2026-09-06 14:57 ` [PATCH v2 1/2] dt-bindings: spi: Add Realtek RTD1625 SPI support Yu-Chun Lin
2026-09-06 14:57 ` [PATCH v2 2/2] spi: spi-mem: Add Realtek SPI flash controller driver Yu-Chun Lin
0 siblings, 2 replies; 7+ messages in thread
From: Yu-Chun Lin @ 2026-09-06 14:57 UTC (permalink / raw)
To: broonie, robh, krzk+dt, conor+dt, jyanchou, p.zabel
Cc: afaerber, devicetree, linux-spi, linux-kernel, james.tai,
cy.huang, stanley_chang, eleanor.lin
Hi all,
This patch series introduces support for the Realtek RTD1625 SPI Flash
controller.
Thansk,
Yu-Chun
---
Changes in v2:
- Dropped the DTS patch to avoid apply conflicts.
- Removed clock and reset header inclusions, and replaced macros with raw
numbers in the dt-binding example to resolve dependencies.
v1: https://lore.kernel.org/lkml/20260904031252.244280-1-eleanor.lin@realtek.com/
---
Jyan Chou (2):
dt-bindings: spi: Add Realtek RTD1625 SPI support
spi: spi-mem: Add Realtek SPI flash controller driver
.../bindings/spi/realtek,rtd1625-nor.yaml | 65 ++
MAINTAINERS | 6 +
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-rtk-nor.c | 637 ++++++++++++++++++
5 files changed, 719 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/realtek,rtd1625-nor.yaml
create mode 100644 drivers/spi/spi-rtk-nor.c
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/2] dt-bindings: spi: Add Realtek RTD1625 SPI support
2026-09-06 14:57 [PATCH v2 0/2] spi: realtek: Add support for RTD1625 SPI Flash Controller Yu-Chun Lin
@ 2026-09-06 14:57 ` Yu-Chun Lin
2026-09-07 17:29 ` Conor Dooley
2026-09-06 14:57 ` [PATCH v2 2/2] spi: spi-mem: Add Realtek SPI flash controller driver Yu-Chun Lin
1 sibling, 1 reply; 7+ messages in thread
From: Yu-Chun Lin @ 2026-09-06 14:57 UTC (permalink / raw)
To: broonie, robh, krzk+dt, conor+dt, jyanchou, p.zabel
Cc: afaerber, devicetree, linux-spi, linux-kernel, james.tai,
cy.huang, stanley_chang, eleanor.lin
From: Jyan Chou <jyanchou@realtek.com>
Add DT binding schema for Realtek RTD1625 SPI Flash Controller
Signed-off-by: Jyan Chou <jyanchou@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v2:
- Removed clock and reset header inclusions, and replaced macros with raw
numbers in the dt-binding example to resolve dependencies.
---
.../bindings/spi/realtek,rtd1625-nor.yaml | 65 +++++++++++++++++++
1 file changed, 65 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/realtek,rtd1625-nor.yaml
diff --git a/Documentation/devicetree/bindings/spi/realtek,rtd1625-nor.yaml b/Documentation/devicetree/bindings/spi/realtek,rtd1625-nor.yaml
new file mode 100644
index 000000000000..b967fdac333f
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/realtek,rtd1625-nor.yaml
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/realtek,rtd1625-nor.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek RTD1625 SPI Flash Controller
+
+maintainers:
+ - Jyan Chou <jyanchou@realtek.com>
+ - Yu-Chun Lin <eleanor.lin@realtek.com>
+
+properties:
+ compatible:
+ enum:
+ - realtek,rtd1625-nor
+
+ reg:
+ maxItems: 3
+
+ reg-names:
+ items:
+ - const: ctrl
+ - const: dma
+ - const: dirmap
+
+ clocks:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - resets
+ - "#address-cells"
+ - "#size-cells"
+
+allOf:
+ - $ref: /schemas/spi/spi-controller.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ spi@14b000 {
+ compatible = "realtek,rtd1625-nor";
+ reg = <0x14b000 0x50>, <0x14bf00 0x30>, <0x88100000 0x2000000>;
+ reg-names = "ctrl", "dma", "dirmap";
+ clocks = <&cc 18>;
+ resets = <&cc 26>;
+ pinctrl-0 = <&spi_pins>;
+ pinctrl-names = "default";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 2/2] spi: spi-mem: Add Realtek SPI flash controller driver
2026-09-06 14:57 [PATCH v2 0/2] spi: realtek: Add support for RTD1625 SPI Flash Controller Yu-Chun Lin
2026-09-06 14:57 ` [PATCH v2 1/2] dt-bindings: spi: Add Realtek RTD1625 SPI support Yu-Chun Lin
@ 2026-09-06 14:57 ` Yu-Chun Lin
2026-09-06 15:09 ` sashiko-bot
` (2 more replies)
1 sibling, 3 replies; 7+ messages in thread
From: Yu-Chun Lin @ 2026-09-06 14:57 UTC (permalink / raw)
To: broonie, robh, krzk+dt, conor+dt, jyanchou, p.zabel
Cc: afaerber, devicetree, linux-spi, linux-kernel, james.tai,
cy.huang, stanley_chang, eleanor.lin
From: Jyan Chou <jyanchou@realtek.com>
Add a spi-mem driver for the SPI Flash Controller (SFC) found on Realtek
DHC SoC. SFC supports Dual I/O.
Implement the exec_op API for regular control commands and the dirmap API
for hardware-accelerated read/write operations.
The controller is described by three register ranges. The first one (ctrl)
holds the control registers. The second one (dma) holds the DMA engine
registers, used to transfer the payload between the flash and main memory.
The third range is the memory aperture (dirmap) that maps the flash
contents.
Signed-off-by: Jyan Chou <jyanchou@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v2:
- None.
---
MAINTAINERS | 6 +
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-rtk-nor.c | 637 ++++++++++++++++++++++++++++++++++++++
4 files changed, 654 insertions(+)
create mode 100644 drivers/spi/spi-rtk-nor.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c..705f7ddf792f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23011,6 +23011,12 @@ S: Maintained
F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml
F: drivers/spi/spi-realtek-rtl-snand.c
+REALTEK SPI-NOR
+M: Jyan Chou <jyanchou@realtek.com>
+M: Yu-Chun <eleanor.lin@realtek.com>
+S: Supported
+F: drivers/spi/spi-rtk-nor.c
+
REALTEK SYSTIMER DRIVER
M: Hao-Wen Ting <haowen.ting@realtek.com>
S: Maintained
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 357cebef9917..4b758ca31490 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -976,6 +976,16 @@ config SPI_RSPI
help
SPI driver for Renesas RSPI and QSPI blocks.
+config SPI_RTK_NOR
+ tristate "Realtek SPI-NOR flash controller"
+ depends on ARCH_REALTEK || COMPILE_TEST
+ depends on SPI_MEM
+ help
+ This enables support for the SPI Flash Controller found on Realtek
+ DHC SoCs. The controller supports single and dual I/O transfers and
+ offloads the payload to an integrated DMA engine.
+ Say Y or M here if you are building a kernel for a Realtek DHC SoC
+
config SPI_RZV2H_RSPI
tristate "Renesas RZ/V2H RSPI controller"
depends on ARCH_RENESAS || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index a18814fbde93..37c6eec9b541 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -133,6 +133,7 @@ obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
obj-$(CONFIG_SPI_REALTEK_SNAND) += spi-realtek-rtl-snand.o
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
+obj-$(CONFIG_SPI_RTK_NOR) += spi-rtk-nor.o
obj-$(CONFIG_SPI_RZV2H_RSPI) += spi-rzv2h-rspi.o
obj-$(CONFIG_SPI_RZV2M_CSI) += spi-rzv2m-csi.o
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
diff --git a/drivers/spi/spi-rtk-nor.c b/drivers/spi/spi-rtk-nor.c
new file mode 100644
index 000000000000..0206ea99f3c9
--- /dev/null
+++ b/drivers/spi/spi-rtk-nor.c
@@ -0,0 +1,637 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Realtek SPI Nor Flash Controller Driver (SFC)
+ *
+ * Copyright (c) 2024-2026 Realtek Technologies Co., Ltd.
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/iopoll.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mem.h>
+#include <linux/types.h>
+
+#define SFC_OPCODE 0x00
+#define DUAL_MODE_EN BIT(9)
+
+#define SFC_CTL 0x04
+#define RW_DATAEN BIT(4)
+#define ADDR_EN BIT(3)
+
+#define SFC_SCK 0x08
+#define FDIV_MASK GENMASK(7, 0)
+
+#define SFC_CE 0x0c
+#define DESLT_TIME 0x1a
+#define DESLT_TIME_SHIFT 16
+#define PH_CNT 0x13
+#define PH_CNT_SHIFT 8
+#define PL_CNT 0x7
+
+#define WT_PROM_DONE BIT(8)
+
+#define SFC_POS_LATCH 0x14
+#define FALLING_EDGE_EN 0x0
+
+#define SFC_WAIT_WR 0x18
+#define SFC_EN_WR 0x1c
+#define WT_PROM_EN BIT(8)
+
+#define SFC_ADR_FOUR_BYTE_EN 0x28
+
+#define MD_FDMA_DDR_SADDR 0x0c
+#define MD_FDMA_FL_SADDR 0x10
+
+#define MD_FDMA_CTRL2 0x14
+#define MAX_XFER_DMA_LEN (BIT(26) | BIT(27))
+#define MAX_XFER_256 BIT(26)
+#define DMA_TO_FLASH BIT(25)
+
+#define MD_FDMA_CTRL1 0x18
+#define DMA_W_EN_START BIT(3)
+#define DMA_W_EN BIT(1)
+#define DMA_START BIT(0)
+
+#define MD_FDMA_DDR_SADDR1 0x20
+
+#define SFC_DMA_TIMEOUT 20000
+#define SFC_DMA_MAX_LEN 0x100
+
+#define SFC_CTL_DMYCNT_MASK GENMASK(31, 24)
+#define SFC_CTL_DMYCNT_SHIFT 24
+#define SFC_SR_WIP BIT(0)
+
+#define DMA_HIGH_BITS_MASK 0x7
+
+#define RTK_SPI_OP_RDSR 0x05
+#define RTK_SPI_OP_WREN 0x06
+#define RTK_SPI_OP_EN4B 0xb7
+#define RTK_SPI_OP_EX4B 0xe9
+
+#define SFC_AUTOSUSPEND_TIMEOUT 2000
+
+struct rtk_spi_host {
+ struct device *dev;
+ struct clk *clk;
+ struct reset_control *rstc;
+ void __iomem *regbase;
+ void __iomem *iobase;
+ void __iomem *mdbase;
+ void *buffer;
+ dma_addr_t dma_buffer;
+ resource_size_t flash_phys_base;
+};
+
+static int rtk_spi_read_status(struct rtk_spi_host *host)
+{
+ int timeout = SFC_DMA_TIMEOUT;
+ u8 status;
+
+ while (timeout--) {
+ writel(RTK_SPI_OP_RDSR, host->regbase + SFC_OPCODE);
+ writel(RW_DATAEN, host->regbase + SFC_CTL);
+
+ status = readb(host->iobase);
+ if (!(status & SFC_SR_WIP))
+ return 0;
+
+ usleep_range(100, 200);
+ }
+
+ dev_err(host->dev, "Timeout waiting for Flash ready\n");
+
+ return -ETIMEDOUT;
+}
+
+static u32 rtk_spi_calc_dummy_cycles(const struct spi_mem_op *op)
+{
+ if (!op->dummy.nbytes)
+ return 0;
+
+ return (op->dummy.nbytes * 8) / op->dummy.buswidth;
+}
+
+static void rtk_spi_read_mode(struct rtk_spi_host *host, const struct spi_mem_op *op)
+{
+ u32 opcode = op->cmd.opcode;
+ u32 dummy_cycles, val;
+
+ if (op->data.buswidth == 2)
+ opcode |= DUAL_MODE_EN;
+
+ writel(opcode, host->regbase + SFC_OPCODE);
+
+ val = readl(host->regbase + SFC_CTL);
+ val |= RW_DATAEN | ADDR_EN;
+
+ dummy_cycles = rtk_spi_calc_dummy_cycles(op);
+
+ val &= ~SFC_CTL_DMYCNT_MASK;
+ val |= (dummy_cycles << SFC_CTL_DMYCNT_SHIFT) & SFC_CTL_DMYCNT_MASK;
+ writel(val, host->regbase + SFC_CTL);
+
+ readl(host->iobase);
+}
+
+static void rtk_spi_write_mode(struct rtk_spi_host *host,
+ const struct spi_mem_op *op)
+{
+ u32 opcode = op->cmd.opcode;
+ u32 val;
+
+ writel(opcode, host->regbase + SFC_OPCODE);
+
+ val = readl(host->regbase + SFC_CTL);
+ val |= RW_DATAEN | ADDR_EN;
+ writel(val, host->regbase + SFC_CTL);
+}
+
+static void rtk_spi_enable_auto_write(struct rtk_spi_host *host)
+{
+ u32 val;
+
+ val = WT_PROM_DONE | RTK_SPI_OP_RDSR;
+ writel(val, host->regbase + SFC_WAIT_WR);
+
+ val = WT_PROM_EN | RTK_SPI_OP_WREN;
+ writel(val, host->regbase + SFC_EN_WR);
+}
+
+static void rtk_spi_disable_auto_write(struct rtk_spi_host *host)
+{
+ writel(RTK_SPI_OP_RDSR, host->regbase + SFC_WAIT_WR);
+ writel(RTK_SPI_OP_WREN, host->regbase + SFC_EN_WR);
+}
+
+static void rtk_spi_init(struct rtk_spi_host *host)
+{
+ u32 val;
+
+ val = readl(host->regbase + SFC_SCK);
+ val &= ~FDIV_MASK;
+ val |= (3 << 0) & FDIV_MASK;
+ writel(val, host->regbase + SFC_SCK);
+
+ val = (DESLT_TIME << DESLT_TIME_SHIFT) | (PH_CNT << PH_CNT_SHIFT) | PL_CNT;
+ writel(val, host->regbase + SFC_CE);
+
+ writel(FALLING_EDGE_EN, host->regbase + SFC_POS_LATCH);
+ writel(RTK_SPI_OP_RDSR, host->regbase + SFC_WAIT_WR);
+ writel(RTK_SPI_OP_WREN, host->regbase + SFC_EN_WR);
+}
+
+static int rtk_spi_command_read(struct rtk_spi_host *host, const struct spi_mem_op *op)
+{
+ size_t len = op->data.nbytes;
+ loff_t offset = op->addr.val;
+ u8 opcode = op->cmd.opcode;
+ u32 dummy_cycles, val;
+
+ writel(opcode, host->regbase + SFC_OPCODE);
+
+ val = readl(host->regbase + SFC_CTL);
+ val &= ~(SFC_CTL_DMYCNT_MASK | ADDR_EN);
+ val |= RW_DATAEN;
+
+ if (op->addr.nbytes > 0)
+ val |= ADDR_EN;
+
+ dummy_cycles = rtk_spi_calc_dummy_cycles(op);
+ val |= (dummy_cycles << SFC_CTL_DMYCNT_SHIFT) & SFC_CTL_DMYCNT_MASK;
+ writel(val, host->regbase + SFC_CTL);
+
+ memcpy_fromio(op->data.buf.in, host->iobase + offset, len);
+
+ return 0;
+}
+
+static int rtk_spi_do_write_and_cmds(struct rtk_spi_host *host, const struct spi_mem_op *op)
+{
+ u8 opcode = op->cmd.opcode;
+ u32 ctl_val = 0;
+ int ret = 0;
+
+ writel(opcode, host->regbase + SFC_OPCODE);
+
+ if (op->data.nbytes > 0)
+ ctl_val |= RW_DATAEN;
+ if (op->addr.nbytes > 0)
+ ctl_val |= ADDR_EN;
+
+ writel(ctl_val, host->regbase + SFC_CTL);
+
+ if (op->data.nbytes > 0) {
+ const u8 *buf = op->data.buf.out;
+ size_t i;
+
+ for (i = 0; i < op->data.nbytes; i++)
+ writeb(buf[i], host->iobase + op->addr.val + i);
+ } else if (op->addr.nbytes > 0) {
+ readb(host->iobase + op->addr.val);
+
+ /* Wait for internal flash erase/programming to complete */
+ ret = rtk_spi_read_status(host);
+ } else {
+ readb(host->iobase);
+
+ /*
+ * For pure commands that require internal state synchronization
+ * (such as Chip Erase), poll the flash status.
+ */
+ ret = rtk_spi_read_status(host);
+ }
+
+ if (ret) {
+ dev_err(host->dev, "opcode 0x%02x failed: %d\n", opcode, ret);
+ return ret;
+ }
+
+ /*
+ * Hardware Workaround:
+ * The controller's auto-mode engine requires SFC_ADR_FOUR_BYTE_EN to be
+ * explicitly updated when the flash enters or exits 4-byte mode via control
+ * commands.
+ */
+ if (opcode == RTK_SPI_OP_EN4B)
+ writel(0x1, host->regbase + SFC_ADR_FOUR_BYTE_EN);
+ else if (opcode == RTK_SPI_OP_EX4B)
+ writel(0x0, host->regbase + SFC_ADR_FOUR_BYTE_EN);
+
+ return ret;
+}
+
+static void rtk_spi_byte_transfer(struct rtk_spi_host *host, loff_t offset,
+ size_t len, unsigned char *buf, bool is_read)
+{
+ if (is_read)
+ memcpy_fromio(buf, host->iobase + offset, len);
+ else
+ memcpy_toio(host->iobase + offset, buf, len);
+}
+
+static int rtk_spi_dma_transfer(struct rtk_spi_host *host, loff_t offset,
+ size_t len, bool is_read)
+{
+ u64 dma_buffer, timeout_us = SFC_DMA_TIMEOUT * 100;
+ u32 flash_phys_addr, val;
+ int ret;
+
+ writel(DMA_W_EN_START | DMA_W_EN, host->mdbase + MD_FDMA_CTRL1);
+
+ dma_buffer = host->dma_buffer;
+
+ /* Setup MD DDR address and flash address */
+ writel(lower_32_bits(dma_buffer), host->mdbase + MD_FDMA_DDR_SADDR);
+ writel(upper_32_bits(dma_buffer) & DMA_HIGH_BITS_MASK,
+ host->mdbase + MD_FDMA_DDR_SADDR1);
+
+ /* MD_FDMA_FL_SADDR is a 32-bit hardware register */
+ flash_phys_addr = lower_32_bits(host->flash_phys_base + offset);
+ writel(flash_phys_addr, host->mdbase + MD_FDMA_FL_SADDR);
+
+ if (is_read)
+ val = MAX_XFER_DMA_LEN | len;
+ else
+ val = DMA_TO_FLASH | MAX_XFER_256 | len;
+
+ writel(val, host->mdbase + MD_FDMA_CTRL2);
+
+ writel(DMA_W_EN | DMA_START, host->mdbase + MD_FDMA_CTRL1);
+ udelay(1);
+
+ ret = readl_poll_timeout(host->mdbase + MD_FDMA_CTRL1, val,
+ !(val & DMA_START), 100, timeout_us);
+ if (ret) {
+ dev_err(host->dev, "DMA transfer timed out\n");
+ return ret;
+ }
+
+ return rtk_spi_read_status(host);
+}
+
+static int rtk_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ struct rtk_spi_host *host = spi_controller_get_devdata(mem->spi->controller);
+
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ return rtk_spi_command_read(host, op);
+
+ return rtk_spi_do_write_and_cmds(host, op);
+}
+
+static bool rtk_spi_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ if (op->cmd.buswidth != 1)
+ return false;
+
+ if (op->cmd.dtr || op->addr.dtr || op->data.dtr)
+ return false;
+
+ if (op->addr.nbytes != 0) {
+ if (op->addr.buswidth > 1)
+ return false;
+ if (op->addr.nbytes < 3 || op->addr.nbytes > 4)
+ return false;
+ }
+
+ if (op->dummy.nbytes != 0) {
+ if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
+ return false;
+ }
+
+ if (op->data.nbytes != 0 && op->data.buswidth > 2)
+ return false;
+
+ return spi_mem_default_supports_op(mem, op);
+}
+
+static int rtk_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
+{
+ const struct spi_mem_op *op = desc->info.op_tmpl;
+
+ if (op->data.dir == SPI_MEM_DATA_IN && op->addr.nbytes != 3 && op->addr.nbytes != 4)
+ return -EOPNOTSUPP;
+
+ if (op->data.dir != SPI_MEM_DATA_IN && op->data.dir != SPI_MEM_DATA_OUT)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static ssize_t rtk_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf)
+{
+ struct rtk_spi_host *host = spi_controller_get_devdata(desc->mem->spi->controller);
+ const struct spi_mem_op *op = desc->info.op_tmpl;
+ loff_t addr = desc->info.offset + offs;
+ size_t chunk_len;
+ int ret;
+
+ /*
+ * Handle unaligned address bytes at the beginning of the read operation.
+ * The hardware requires 4-byte alignment for DMA transfers.
+ */
+ if (addr & 0x3) {
+ rtk_spi_read_mode(host, op);
+ chunk_len = min_t(size_t, 4 - (addr & 0x3), len);
+ rtk_spi_byte_transfer(host, addr, chunk_len, buf, true);
+
+ return chunk_len;
+ }
+
+ rtk_spi_read_mode(host, op);
+ chunk_len = min_t(size_t, len, SFC_DMA_MAX_LEN);
+ ret = rtk_spi_dma_transfer(host, addr, chunk_len, true);
+ if (ret) {
+ dev_err(host->dev, "DMA read transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ memcpy(buf, host->buffer, chunk_len);
+
+ return chunk_len;
+}
+
+static ssize_t rtk_spi_dirmap_write(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, const void *buf)
+{
+ struct rtk_spi_host *host = spi_controller_get_devdata(desc->mem->spi->controller);
+ const struct spi_mem_op *op = desc->info.op_tmpl;
+ loff_t addr = desc->info.offset + offs;
+ size_t chunk_len;
+ int ret = 0;
+
+ rtk_spi_enable_auto_write(host);
+ rtk_spi_write_mode(host, op);
+
+ /*
+ * Handle unaligned address bytes at the beginning of the write operation.
+ * The hardware requires 4-byte alignment for DMA transfers.
+ */
+ if (addr & 0x3) {
+ chunk_len = min_t(size_t, 4 - (addr & 0x3), len);
+ rtk_spi_byte_transfer(host, addr, chunk_len, (u8 *)buf, false);
+
+ goto out;
+ }
+
+ chunk_len = min_t(size_t, len, SFC_DMA_MAX_LEN);
+
+ memcpy(host->buffer, buf, chunk_len);
+
+ ret = rtk_spi_dma_transfer(host, addr, chunk_len, false);
+ if (ret)
+ dev_err(host->dev, "DMA write transfer failed: %d\n", ret);
+
+out:
+ rtk_spi_disable_auto_write(host);
+
+ return ret < 0 ? ret : chunk_len;
+}
+
+static const struct spi_controller_mem_ops rtk_spi_mem_ops = {
+ .supports_op = rtk_spi_supports_op,
+ .exec_op = rtk_spi_exec_op,
+ .dirmap_create = rtk_spi_dirmap_create,
+ .dirmap_read = rtk_spi_dirmap_read,
+ .dirmap_write = rtk_spi_dirmap_write,
+};
+
+static int rtk_spi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct spi_controller *ctrl;
+ struct rtk_spi_host *host;
+ struct resource *res;
+ int ret;
+
+ ctrl = devm_spi_alloc_host(dev, sizeof(*host));
+ if (!ctrl)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ctrl);
+ host = spi_controller_get_devdata(ctrl);
+ host->dev = dev;
+
+ host->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(host->clk))
+ return PTR_ERR(host->clk);
+
+ ret = clk_prepare_enable(host->clk);
+ if (ret)
+ return ret;
+
+ host->rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(host->rstc)) {
+ ret = PTR_ERR(host->rstc);
+ goto err_disable_clk;
+ }
+
+ reset_control_assert(host->rstc);
+ usleep_range(10, 20);
+ reset_control_deassert(host->rstc);
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(35));
+ if (ret) {
+ dev_err(dev, "Failed to set dma mask\n");
+ goto err_disable_clk;
+ }
+
+ host->buffer = dmam_alloc_coherent(dev, SFC_DMA_MAX_LEN,
+ &host->dma_buffer, GFP_KERNEL);
+ if (!host->buffer) {
+ ret = -ENOMEM;
+ goto err_disable_clk;
+ }
+
+ host->regbase = devm_platform_ioremap_resource_byname(pdev, "ctrl");
+ if (IS_ERR(host->regbase)) {
+ ret = PTR_ERR(host->regbase);
+ goto err_disable_clk;
+ }
+
+ host->mdbase = devm_platform_ioremap_resource_byname(pdev, "dma");
+ if (IS_ERR(host->mdbase)) {
+ ret = PTR_ERR(host->mdbase);
+ goto err_disable_clk;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap");
+ host->iobase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(host->iobase)) {
+ ret = PTR_ERR(host->iobase);
+ goto err_disable_clk;
+ }
+
+ host->flash_phys_base = res->start;
+
+ ctrl->mode_bits = SPI_RX_DUAL | SPI_TX_DUAL;
+ ctrl->bus_num = -1;
+ ctrl->mem_ops = &rtk_spi_mem_ops;
+ ctrl->num_chipselect = 1;
+ ctrl->auto_runtime_pm = true;
+
+ rtk_spi_init(host);
+
+ pm_runtime_set_autosuspend_delay(dev, SFC_AUTOSUSPEND_TIMEOUT);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_get_noresume(dev);
+
+ ret = spi_register_controller(ctrl);
+ if (ret < 0) {
+ dev_err(dev, "failed to register controller\n");
+ goto err_pm_disable;
+ }
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+
+err_disable_clk:
+ clk_disable_unprepare(host->clk);
+
+ return ret;
+}
+
+static void rtk_spi_remove(struct platform_device *pdev)
+{
+ struct spi_controller *ctrl = platform_get_drvdata(pdev);
+ struct rtk_spi_host *host = spi_controller_get_devdata(ctrl);
+ struct device *dev = &pdev->dev;
+
+ spi_unregister_controller(ctrl);
+
+ pm_runtime_get_sync(dev);
+ pm_runtime_disable(dev);
+ clk_disable_unprepare(host->clk);
+ pm_runtime_put_noidle(dev);
+ pm_runtime_set_suspended(dev);
+}
+
+static const struct of_device_id rtk_spi_dt_ids[] = {
+ { .compatible = "realtek,rtd1625-nor" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, rtk_spi_dt_ids);
+
+static int rtk_spi_runtime_suspend(struct device *dev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct rtk_spi_host *host = spi_controller_get_devdata(ctlr);
+
+ reset_control_assert(host->rstc);
+
+ clk_disable_unprepare(host->clk);
+
+ return 0;
+}
+
+static int rtk_spi_runtime_resume(struct device *dev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct rtk_spi_host *host = spi_controller_get_devdata(ctlr);
+ int ret;
+
+ ret = clk_prepare_enable(host->clk);
+ if (ret < 0) {
+ dev_err(dev, "clk_prepare_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ reset_control_assert(host->rstc);
+ usleep_range(10, 20);
+ reset_control_deassert(host->rstc);
+
+ rtk_spi_init(host);
+
+ return 0;
+}
+
+static int rtk_spi_suspend(struct device *dev)
+{
+ return pm_runtime_force_suspend(dev);
+}
+
+static int rtk_spi_resume(struct device *dev)
+{
+ return pm_runtime_force_resume(dev);
+}
+
+static const struct dev_pm_ops rtk_spi_pm_ops = {
+ RUNTIME_PM_OPS(rtk_spi_runtime_suspend, rtk_spi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(rtk_spi_suspend, rtk_spi_resume)
+};
+
+static struct platform_driver rtk_spi_driver = {
+ .driver = {
+ .name = "rtk-spi-nor",
+ .of_match_table = rtk_spi_dt_ids,
+ .pm = pm_ptr(&rtk_spi_pm_ops),
+ },
+ .probe = rtk_spi_probe,
+ .remove = rtk_spi_remove,
+};
+module_platform_driver(rtk_spi_driver);
+
+MODULE_DESCRIPTION("Realtek SPI Nor Controller Driver");
+MODULE_AUTHOR("Jyan Chou <jyanchou@realtek.com>");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] spi: spi-mem: Add Realtek SPI flash controller driver
2026-09-06 14:57 ` [PATCH v2 2/2] spi: spi-mem: Add Realtek SPI flash controller driver Yu-Chun Lin
@ 2026-09-06 15:09 ` sashiko-bot
2026-09-06 15:18 ` Kuan-Wei Chiu
2026-09-07 13:47 ` Mark Brown
2 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-09-06 15:09 UTC (permalink / raw)
To: Yu-Chun Lin; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [Critical] The driver fails to configure the hardware's address length dynamically based on the requested `op->addr.nbytes`, relying instead on snooping `EN4B` opcodes, which breaks stateless 4-byte addressing.
- [Critical] The controller's 4-byte addressing mode configuration (SFC_ADR_FOUR_BYTE_EN) is lost across runtime suspends, leading to address truncation and data corruption.
- [High] Multi-byte writes and reads via `exec_op` are fragmented into independent 1-byte SPI transactions due to a byte-wise I/O loop, breaking register accesses and fallback operations.
- [High] The driver intercepts SPI commands and enforces a fixed ~4-second timeout by internally polling the flash WIP bit using a hardcoded RDSR command, breaking long-running operations.
- [High] The driver advertises support for Dual I/O operations for all commands but fails to configure the Dual mode hardware bit for writes and `exec_op` reads.
- [High] The driver lacks bounds checking against the `dirmap` memory resource size, allowing out-of-bounds physical memory accesses.
- [Medium] Unbalanced clk_disable_unprepare() if pm_runtime_get_sync() fails during device removal.
--
commit 76380ef4b88916eb9469e84777c4aa38898c403e
Author: Jyan Chou <jyanchou@realtek.com>
spi: spi-mem: Add Realtek SPI flash controller driver
This commit adds a spi-mem driver for the Realtek SPI Flash Controller
found on DHC SoCs. It implements single and dual I/O transfers with support
for hardware-accelerated dirmap reads and writes.
> diff --git a/drivers/spi/spi-rtk-nor.c b/drivers/spi/spi-rtk-nor.c
> new file mode 100644
> index 0000000000000..0206ea99f3c95
> --- /dev/null
> +++ b/drivers/spi/spi-rtk-nor.c
[ ... ]
> +static int rtk_spi_read_status(struct rtk_spi_host *host)
> +{
> + int timeout = SFC_DMA_TIMEOUT;
> + u8 status;
> +
> + while (timeout--) {
> + writel(RTK_SPI_OP_RDSR, host->regbase + SFC_OPCODE);
> + writel(RW_DATAEN, host->regbase + SFC_CTL);
> +
> + status = readb(host->iobase);
> + if (!(status & SFC_SR_WIP))
> + return 0;
> +
> + usleep_range(100, 200);
> + }
[Severity: High]
Does this hardcoded loop enforce a fixed timeout of around 4 seconds
(20,000 iterations of ~200us) for all polled operations?
Long-running flash operations like Chip Erase might prematurely abort from
the kernel's perspective even if the hardware continues working. Furthermore,
if a command like Deep Power Down is issued, the flash might stop responding
to RDSR, causing this loop to constantly see 0xFF and eventually time out.
> +
> + dev_err(host->dev, "Timeout waiting for Flash ready\n");
> +
> + return -ETIMEDOUT;
> +}
[ ... ]
> +static void rtk_spi_read_mode(struct rtk_spi_host *host, const struct spi_mem_op *op)
> +{
> + u32 opcode = op->cmd.opcode;
> + u32 dummy_cycles, val;
> +
> + if (op->data.buswidth == 2)
> + opcode |= DUAL_MODE_EN;
> +
> + writel(opcode, host->regbase + SFC_OPCODE);
> +
> + val = readl(host->regbase + SFC_CTL);
> + val |= RW_DATAEN | ADDR_EN;
[Severity: Critical]
Will this instruct the hardware to send an address without ever specifying
its length based on op->addr.nbytes?
If the spi-nor core issues stateless 4-byte opcodes (where
op->addr.nbytes == 4) without a dedicated EN4B command, the hardware might
only send 3 address bytes, causing data corruption for reading and writing
on flashes larger than 16MB.
> +
> + dummy_cycles = rtk_spi_calc_dummy_cycles(op);
[ ... ]
> +static void rtk_spi_write_mode(struct rtk_spi_host *host,
> + const struct spi_mem_op *op)
> +{
> + u32 opcode = op->cmd.opcode;
> + u32 val;
> +
> + writel(opcode, host->regbase + SFC_OPCODE);
> +
> + val = readl(host->regbase + SFC_CTL);
> + val |= RW_DATAEN | ADDR_EN;
> + writel(val, host->regbase + SFC_CTL);
[Severity: High]
Is the DUAL_MODE_EN bit missing for write operations?
If the SPI subsystem determines dual mode is supported and issues a
dual-lane write, the flash will expect two lanes of data but the controller
will operate in single-lane mode, which could scramble the bits.
> +}
[ ... ]
> +static int rtk_spi_command_read(struct rtk_spi_host *host, const struct spi_mem_op *op)
> +{
> + size_t len = op->data.nbytes;
> + loff_t offset = op->addr.val;
> + u8 opcode = op->cmd.opcode;
> + u32 dummy_cycles, val;
> +
> + writel(opcode, host->regbase + SFC_OPCODE);
[Severity: High]
Similarly to the write mode setup, is the DUAL_MODE_EN bit also omitted here
when exec_op is used for fallback dual reads?
> +
> + val = readl(host->regbase + SFC_CTL);
> + val &= ~(SFC_CTL_DMYCNT_MASK | ADDR_EN);
> + val |= RW_DATAEN;
> +
> + if (op->addr.nbytes > 0)
> + val |= ADDR_EN;
> +
> + dummy_cycles = rtk_spi_calc_dummy_cycles(op);
> + val |= (dummy_cycles << SFC_CTL_DMYCNT_SHIFT) & SFC_CTL_DMYCNT_MASK;
> + writel(val, host->regbase + SFC_CTL);
> +
> + memcpy_fromio(op->data.buf.in, host->iobase + offset, len);
[Severity: High]
Could this memory I/O read split restart address-less commands like RDID
multiple times since it triggers discrete read transactions?
Also, is there any bounds checking against the mapped dirmap resource size?
If offset (derived from op->addr.val) or len exceeds the physical memory
window mapped by devm_ioremap_resource(), it could lead to out-of-bounds
physical memory accesses, kernel panics, or Data Aborts.
> +
> + return 0;
> +}
> +
> +static int rtk_spi_do_write_and_cmds(struct rtk_spi_host *host, const struct spi_mem_op *op)
> +{
> + u8 opcode = op->cmd.opcode;
> + u32 ctl_val = 0;
> + int ret = 0;
> +
> + writel(opcode, host->regbase + SFC_OPCODE);
> +
> + if (op->data.nbytes > 0)
> + ctl_val |= RW_DATAEN;
> + if (op->addr.nbytes > 0)
> + ctl_val |= ADDR_EN;
> +
> + writel(ctl_val, host->regbase + SFC_CTL);
> +
> + if (op->data.nbytes > 0) {
> + const u8 *buf = op->data.buf.out;
> + size_t i;
> +
> + for (i = 0; i < op->data.nbytes; i++)
> + writeb(buf[i], host->iobase + op->addr.val + i);
[Severity: High]
Does this byte-wise loop fragment a contiguous write transfer into
independent 1-byte SPI transactions?
Each writeb to the mapped memory generates a discrete bus sequence (CS
assert -> command -> data -> CS deassert). Commands requiring multiple
contiguous data bytes, like WRSR or fallback Page Programs, could fail
or corrupt state.
Also, similar to rtk_spi_command_read(), this seems to write directly to
physical memory using an unbounded op->addr.val, which could exceed the
size of the mapped dirmap resource.
> + } else if (op->addr.nbytes > 0) {
> + readb(host->iobase + op->addr.val);
> +
> + /* Wait for internal flash erase/programming to complete */
> + ret = rtk_spi_read_status(host);
> + } else {
> + readb(host->iobase);
> +
> + /*
> + * For pure commands that require internal state synchronization
> + * (such as Chip Erase), poll the flash status.
> + */
> + ret = rtk_spi_read_status(host);
[Severity: High]
Does this intercept the spi-mem command completion and force the hardware
driver to synchronously poll the flash status internally?
This polling is usually intended for the spi-nor core, and combined with
the ~4-second timeout, could cause longer operations like Chip Erase to
abort prematurely.
> + }
> +
> + if (ret) {
> + dev_err(host->dev, "opcode 0x%02x failed: %d\n", opcode, ret);
> + return ret;
> + }
> +
> + /*
> + * Hardware Workaround:
> + * The controller's auto-mode engine requires SFC_ADR_FOUR_BYTE_EN to be
> + * explicitly updated when the flash enters or exits 4-byte mode via control
> + * commands.
> + */
> + if (opcode == RTK_SPI_OP_EN4B)
> + writel(0x1, host->regbase + SFC_ADR_FOUR_BYTE_EN);
> + else if (opcode == RTK_SPI_OP_EX4B)
> + writel(0x0, host->regbase + SFC_ADR_FOUR_BYTE_EN);
[Severity: Critical]
Is it possible for the hardware to get permanently stuck in 3-byte address
mode if the spi-nor core uses stateless 4-byte opcodes instead of explicitly
issuing EN4B?
Additionally, this dynamic SFC_ADR_FOUR_BYTE_EN state appears to be lost
across runtime suspends because rtk_spi_runtime_resume() resets the hardware
and does not restore this setting. If the controller resumes and defaults
back to 3-byte mode while the flash chip is in 4-byte mode, subsequent
reads and writes could be globally corrupted.
> +
> + return ret;
> +}
[ ... ]
> +static bool rtk_spi_supports_op(struct spi_mem *mem,
> + const struct spi_mem_op *op)
> +{
> + if (op->cmd.buswidth != 1)
> + return false;
> +
> + if (op->cmd.dtr || op->addr.dtr || op->data.dtr)
> + return false;
> +
> + if (op->addr.nbytes != 0) {
> + if (op->addr.buswidth > 1)
> + return false;
> + if (op->addr.nbytes < 3 || op->addr.nbytes > 4)
> + return false;
> + }
> +
> + if (op->dummy.nbytes != 0) {
> + if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
> + return false;
> + }
> +
> + if (op->data.nbytes != 0 && op->data.buswidth > 2)
> + return false;
[Severity: High]
Does this check allow 2-lane wide writes to proceed?
As noted in rtk_spi_write_mode(), the hardware is not configured with
DUAL_MODE_EN for writes, leading to a mismatch between what is advertised
here and what the controller actually does.
> +
> + return spi_mem_default_supports_op(mem, op);
> +}
> +
> +static int rtk_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
> +{
[Severity: High]
Does this function fail to check if desc->info.length exceeds the hardware's
mapped memory window size?
Failing to reject oversized dirmap descriptions could result in out-of-bounds
physical memory accesses later during transfers.
> + const struct spi_mem_op *op = desc->info.op_tmpl;
> +
> + if (op->data.dir == SPI_MEM_DATA_IN && op->addr.nbytes != 3 && op->addr.nbytes != 4)
> + return -EOPNOTSUPP;
[ ... ]
> +static void rtk_spi_remove(struct platform_device *pdev)
> +{
> + struct spi_controller *ctrl = platform_get_drvdata(pdev);
> + struct rtk_spi_host *host = spi_controller_get_devdata(ctrl);
> + struct device *dev = &pdev->dev;
> +
> + spi_unregister_controller(ctrl);
> +
> + pm_runtime_get_sync(dev);
> + pm_runtime_disable(dev);
> + clk_disable_unprepare(host->clk);
[Severity: Medium]
If pm_runtime_get_sync(dev) fails during module removal (for instance, when
the device is suspended and runtime resume encounters an error), the device
clock is not enabled.
Will this lead to an unbalanced clk_disable_unprepare() call and a clock
enable count underflow?
> + pm_runtime_put_noidle(dev);
> + pm_runtime_set_suspended(dev);
> +}
[ ... ]
> +static int rtk_spi_runtime_resume(struct device *dev)
> +{
> + struct spi_controller *ctlr = dev_get_drvdata(dev);
> + struct rtk_spi_host *host = spi_controller_get_devdata(ctlr);
> + int ret;
> +
> + ret = clk_prepare_enable(host->clk);
> + if (ret < 0) {
> + dev_err(dev, "clk_prepare_enable failed: %d\n", ret);
> + return ret;
> + }
> +
> + reset_control_assert(host->rstc);
> + usleep_range(10, 20);
> + reset_control_deassert(host->rstc);
> +
> + rtk_spi_init(host);
[Severity: Critical]
When the hardware is reset and initialized upon resume, the dynamic
SFC_ADR_FOUR_BYTE_EN state is wiped.
Since rtk_spi_init() doesn't restore this state, could the controller revert
to 3-byte address mode while the flash chip remains in 4-byte mode, leading
to address truncation and data corruption?
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260906145755.1230079-1-eleanor.lin@realtek.com?part=2
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] spi: spi-mem: Add Realtek SPI flash controller driver
2026-09-06 14:57 ` [PATCH v2 2/2] spi: spi-mem: Add Realtek SPI flash controller driver Yu-Chun Lin
2026-09-06 15:09 ` sashiko-bot
@ 2026-09-06 15:18 ` Kuan-Wei Chiu
2026-09-07 13:47 ` Mark Brown
2 siblings, 0 replies; 7+ messages in thread
From: Kuan-Wei Chiu @ 2026-09-06 15:18 UTC (permalink / raw)
To: Yu-Chun Lin
Cc: broonie, robh, krzk+dt, conor+dt, jyanchou, p.zabel, afaerber,
devicetree, linux-spi, linux-kernel, james.tai, cy.huang,
stanley_chang
Hi Yu-Chun,
On Sun, Sep 06, 2026 at 10:57:55PM +0800, Yu-Chun Lin wrote:
> From: Jyan Chou <jyanchou@realtek.com>
>
> Add a spi-mem driver for the SPI Flash Controller (SFC) found on Realtek
> DHC SoC. SFC supports Dual I/O.
>
> Implement the exec_op API for regular control commands and the dirmap API
> for hardware-accelerated read/write operations.
>
> The controller is described by three register ranges. The first one (ctrl)
> holds the control registers. The second one (dma) holds the DMA engine
> registers, used to transfer the payload between the flash and main memory.
> The third range is the memory aperture (dirmap) that maps the flash
> contents.
>
> Signed-off-by: Jyan Chou <jyanchou@realtek.com>
> Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> ---
> Changes in v2:
> - None.
> ---
> MAINTAINERS | 6 +
> drivers/spi/Kconfig | 10 +
> drivers/spi/Makefile | 1 +
> drivers/spi/spi-rtk-nor.c | 637 ++++++++++++++++++++++++++++++++++++++
> 4 files changed, 654 insertions(+)
> create mode 100644 drivers/spi/spi-rtk-nor.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3a19da74d00c..705f7ddf792f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -23011,6 +23011,12 @@ S: Maintained
> F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml
> F: drivers/spi/spi-realtek-rtl-snand.c
>
> +REALTEK SPI-NOR
> +M: Jyan Chou <jyanchou@realtek.com>
> +M: Yu-Chun <eleanor.lin@realtek.com>
nit: Yu-Chun -> Yu-Chun Lin ?
Regards,
Kuan-Wei
> +S: Supported
> +F: drivers/spi/spi-rtk-nor.c
> +
> REALTEK SYSTIMER DRIVER
> M: Hao-Wen Ting <haowen.ting@realtek.com>
> S: Maintained
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index 357cebef9917..4b758ca31490 100644
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] spi: spi-mem: Add Realtek SPI flash controller driver
2026-09-06 14:57 ` [PATCH v2 2/2] spi: spi-mem: Add Realtek SPI flash controller driver Yu-Chun Lin
2026-09-06 15:09 ` sashiko-bot
2026-09-06 15:18 ` Kuan-Wei Chiu
@ 2026-09-07 13:47 ` Mark Brown
2 siblings, 0 replies; 7+ messages in thread
From: Mark Brown @ 2026-09-07 13:47 UTC (permalink / raw)
To: Yu-Chun Lin
Cc: robh, krzk+dt, conor+dt, jyanchou, p.zabel, afaerber, devicetree,
linux-spi, linux-kernel, james.tai, cy.huang, stanley_chang
[-- Attachment #1: Type: text/plain, Size: 2066 bytes --]
On Sun, Sep 06, 2026 at 10:57:55PM +0800, Yu-Chun Lin wrote:
> Add a spi-mem driver for the SPI Flash Controller (SFC) found on Realtek
> DHC SoC. SFC supports Dual I/O.
> drivers/spi/Makefile | 1 +
> drivers/spi/spi-rtk-nor.c | 637 ++++++++++++++++++++++++++++++++++++++
Are we sule Realtek won't have more flash controllers?
> @@ -0,0 +1,637 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Realtek SPI Nor Flash Controller Driver (SFC)
> + *
> + * Copyright (c) 2024-2026 Realtek Technologies Co., Ltd.
> + */
Please make the entire comment block a C++ one so things look more
intentional.
> +static int rtk_spi_do_write_and_cmds(struct rtk_spi_host *host, const struct spi_mem_op *op)
> +{
> + } else {
> + readb(host->iobase);
> +
> + /*
> + * For pure commands that require internal state synchronization
> + * (such as Chip Erase), poll the flash status.
> + */
> + ret = rtk_spi_read_status(host);
> + }
Shouldn't this be done by the spi-nor core if it's needed? There's also
some timeout logic there which this will override.
> +static int rtk_spi_dma_transfer(struct rtk_spi_host *host, loff_t offset,
> + size_t len, bool is_read)
> +{
> + writel(DMA_W_EN | DMA_START, host->mdbase + MD_FDMA_CTRL1);
> + udelay(1);
> + ret = readl_poll_timeout(host->mdbase + MD_FDMA_CTRL1, val,
> + !(val & DMA_START), 100, timeout_us);
> + if (ret) {
> + dev_err(host->dev, "DMA transfer timed out\n");
> + return ret;
> + }
Don't we need to turn off the hardware here?
> +static int rtk_spi_runtime_resume(struct device *dev)
> +{
> + struct spi_controller *ctlr = dev_get_drvdata(dev);
> + struct rtk_spi_host *host = spi_controller_get_devdata(ctlr);
> + int ret;
> +
> + ret = clk_prepare_enable(host->clk);
> + if (ret < 0) {
> + dev_err(dev, "clk_prepare_enable failed: %d\n", ret);
> + return ret;
> + }
> +
> + reset_control_assert(host->rstc);
> + usleep_range(10, 20);
> + reset_control_deassert(host->rstc);
> +
> + rtk_spi_init(host);
> +
> + return 0;
> +}
rtk_spi_init() doesn't handle 4 byte mode does it?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: spi: Add Realtek RTD1625 SPI support
2026-09-06 14:57 ` [PATCH v2 1/2] dt-bindings: spi: Add Realtek RTD1625 SPI support Yu-Chun Lin
@ 2026-09-07 17:29 ` Conor Dooley
0 siblings, 0 replies; 7+ messages in thread
From: Conor Dooley @ 2026-09-07 17:29 UTC (permalink / raw)
To: Yu-Chun Lin
Cc: broonie, robh, krzk+dt, conor+dt, jyanchou, p.zabel, afaerber,
devicetree, linux-spi, linux-kernel, james.tai, cy.huang,
stanley_chang
[-- Attachment #1: Type: text/plain, Size: 75 bytes --]
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-07 17:29 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-06 14:57 [PATCH v2 0/2] spi: realtek: Add support for RTD1625 SPI Flash Controller Yu-Chun Lin
2026-09-06 14:57 ` [PATCH v2 1/2] dt-bindings: spi: Add Realtek RTD1625 SPI support Yu-Chun Lin
2026-09-07 17:29 ` Conor Dooley
2026-09-06 14:57 ` [PATCH v2 2/2] spi: spi-mem: Add Realtek SPI flash controller driver Yu-Chun Lin
2026-09-06 15:09 ` sashiko-bot
2026-09-06 15:18 ` Kuan-Wei Chiu
2026-09-07 13:47 ` Mark Brown
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.