// SPDX-License-Identifier: GPL-2.0 // // Realtek SPI Nor Flash Controller Driver (SFC) // // Copyright (c) 2024-2026 Realtek Semiconductor Corp. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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_END BIT(2) #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 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; bool is_4byte; dma_addr_t dma_buffer; resource_size_t flash_phys_base; size_t dirmap_size; }; 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 int rtk_spi_byte_transfer(struct rtk_spi_host *host, loff_t offset, size_t len, unsigned char *buf, bool is_read) { if (offset + len > host->dirmap_size) { dev_err(host->dev, "PIO transfer out of bounds\n"); return -EINVAL; } if (is_read) memcpy_fromio(buf, host->iobase + offset, len); else memcpy_toio(host->iobase + offset, buf, len); return 0; } 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); writel(host->is_4byte ? 0x1 : 0x0, host->regbase + SFC_ADR_FOUR_BYTE_EN); } 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); return rtk_spi_byte_transfer(host, offset, len, op->data.buf.in, true); } 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; 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; if (op->addr.val + op->data.nbytes > host->dirmap_size) return -EINVAL; writeb(*buf, host->iobase + op->addr.val); } else { if (op->addr.val >= host->dirmap_size) return -EINVAL; readb(host->iobase + op->addr.val); } return 0; } 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; if (offset + len > host->dirmap_size) { dev_err(host->dev, "DMA transfer out of bounds\n"); return -EINVAL; } 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"); val = readl(host->mdbase + MD_FDMA_CTRL1); val |= DMA_END | DMA_W_EN_START; writel(val, host->mdbase + MD_FDMA_CTRL1); if (readl_poll_timeout(host->mdbase + MD_FDMA_CTRL1, val, !(val & DMA_END), 10, 1000)) { dev_err(host->dev, "Failed to stop DMA gracefully\n"); } return ret; } return 0; } 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); u32 opcode = op->cmd.opcode; /* * 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 ((op->addr.nbytes == 4 || opcode == RTK_SPI_OP_EN4B) && !host->is_4byte) { host->is_4byte = true; writel(0x1, host->regbase + SFC_ADR_FOUR_BYTE_EN); } else if ((op->addr.nbytes == 3 || opcode == RTK_SPI_OP_EX4B) && host->is_4byte) { host->is_4byte = false; writel(0x0, host->regbase + SFC_ADR_FOUR_BYTE_EN); } 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; } /* * The data window is access-triggered: each CPU access to host->iobase * generates one complete SPI transaction. Consecutive byte accesses * therefore cannot be merged into a single transaction, so * multi-byte stateless commands are not supported by this controller. */ if (!op->addr.nbytes && op->data.nbytes > 1) 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); ret = rtk_spi_byte_transfer(host, addr, chunk_len, buf, true); if (ret) return ret; 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); ret = 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); host->rstc = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(host->rstc)) return PTR_ERR(host->rstc); ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(35)); if (ret) { dev_err(dev, "Failed to set dma mask\n"); return ret; } host->buffer = dmam_alloc_coherent(dev, SFC_DMA_MAX_LEN, &host->dma_buffer, GFP_KERNEL); if (!host->buffer) return -ENOMEM; host->regbase = devm_platform_ioremap_resource_byname(pdev, "ctrl"); if (IS_ERR(host->regbase)) return PTR_ERR(host->regbase); host->mdbase = devm_platform_ioremap_resource_byname(pdev, "dma"); if (IS_ERR(host->mdbase)) return PTR_ERR(host->mdbase); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap"); host->iobase = devm_ioremap_resource(dev, res); if (IS_ERR(host->iobase)) return PTR_ERR(host->iobase); host->dirmap_size = resource_size(res); host->flash_phys_base = res->start; host->is_4byte = false; ret = clk_prepare_enable(host->clk); if (ret) return ret; ret = reset_control_assert(host->rstc); usleep_range(10, 20); ret = reset_control_deassert(host->rstc); 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); 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; 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); reset_control_assert(host->rstc); 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); if (pm_runtime_resume_and_get(dev) < 0) dev_warn(dev, "Failed to resume device during remove\n"); pm_runtime_put_noidle(dev); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_dont_use_autosuspend(dev); reset_control_assert(host->rstc); clk_disable_unprepare(host->clk); } 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 DEFINE_RUNTIME_DEV_PM_OPS(rtk_spi_pm_ops, rtk_spi_runtime_suspend, rtk_spi_runtime_resume, NULL); 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 "); MODULE_LICENSE("GPL");