// SPDX-License-Identifier: GPL-2.0-or-later
//
// Nuvoton MA35D1 QSPI controller driver
//
// Copyright (c) 2026 Nuvoton Technology Corp.
// Author: Chi-Wen Weng <cwweng@nuvoton.com>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/spinlock.h>
/* Register offset definitions */
#define NUVOTON_QSPI_CTL_OFFSET 0x00 /* Control Register, RW */
#define NUVOTON_QSPI_CLKDIV_OFFSET 0x04 /* Clock Divider Register, RW */
#define NUVOTON_QSPI_SSCTL_OFFSET 0x08 /* Slave Select Register, RW */
#define NUVOTON_QSPI_FIFOCTL_OFFSET 0x10 /* FIFO Control Register, RW */
#define NUVOTON_QSPI_STATUS_OFFSET 0x14 /* Status Register, RW */
#define NUVOTON_QSPI_TX_OFFSET 0x20 /* Data Transmit Register, WO */
#define NUVOTON_QSPI_RX_OFFSET 0x30 /* Data Receive Register, RO */
/* QSPI Control Register bit masks */
#define NUVOTON_QSPI_CTL_QUADIOEN_MASK BIT(22) /* Quad I/O Mode Enable */
#define NUVOTON_QSPI_CTL_DUALIOEN_MASK BIT(21) /* Dual I/O Mode Enable */
#define NUVOTON_QSPI_CTL_DATDIR_MASK BIT(20) /* Data Port Direction Control */
#define NUVOTON_QSPI_CTL_REORDER_MASK BIT(19) /* Byte Reorder Function Enable */
#define NUVOTON_QSPI_CTL_LSB_MASK BIT(13) /* Send LSB First */
#define NUVOTON_QSPI_CTL_DWIDTH_MASK GENMASK(12, 8) /* Data Width */
#define NUVOTON_QSPI_CTL_SUSPITV_MASK GENMASK(7, 4) /* Suspend Interval */
#define NUVOTON_QSPI_CTL_CLKPOL_MASK BIT(3) /* Clock Polarity */
#define NUVOTON_QSPI_CTL_TXNEG_MASK BIT(2) /* Transmit on Negative Edge */
#define NUVOTON_QSPI_CTL_RXNEG_MASK BIT(1) /* Receive on Negative Edge */
#define NUVOTON_QSPI_CTL_SPIEN_MASK BIT(0) /* QSPI Transfer Control Enable */
/* QSPI Clock Divider Register bit masks */
#define NUVOTON_QSPI_CLKDIV_MASK GENMASK(8, 0) /* Clock Divider */
/* QSPI Slave Select Control Register bit masks */
#define NUVOTON_QSPI_SSCTL_SS1_MASK BIT(1) /* Slave Selection 1 Control */
#define NUVOTON_QSPI_SSCTL_SS0_MASK BIT(0) /* Slave Selection 0 Control */
/* QSPI FIFO Control Register bit masks */
#define NUVOTON_QSPI_FIFOCTL_TXRST_MASK BIT(1) /* Transmit Reset */
#define NUVOTON_QSPI_FIFOCTL_RXRST_MASK BIT(0) /* Receive Reset */
/* QSPI Status Register bit masks */
#define NUVOTON_QSPI_STATUS_TXRXRST_MASK BIT(23) /* TX or RX Reset Status */
#define NUVOTON_QSPI_STATUS_TXFULL_MASK BIT(17) /* Transmit FIFO Full */
#define NUVOTON_QSPI_STATUS_SPIENSTS_MASK BIT(15) /* QSPI Enable Status */
#define NUVOTON_QSPI_STATUS_RXEMPTY_MASK BIT(8) /* Receive FIFO Empty */
#define NUVOTON_QSPI_STATUS_BUSY_MASK BIT(0) /* Busy Status */
#define NUVOTON_QSPI_MAX_NUM_CS 2
#define NUVOTON_QSPI_DEFAULT_NUM_CS 2
#define NUVOTON_QSPI_DEFAULT_BPW 8
/* Bound PIO operations to avoid long atomic polling loops. */
#define NUVOTON_QSPI_MAX_TRANSFER_SIZE SZ_4K
#define NUVOTON_QSPI_MAX_MESSAGE_SIZE SZ_8K
#define NUVOTON_QSPI_TIMEOUT_US 10000
struct nuvoton_qspi {
void __iomem *regs;
struct clk *clk;
struct device *dev;
/* Protects read-modify-write accesses to the SSCTL register. */
spinlock_t ssctl_lock;
u32 speed_hz;
};
static u32 nuvoton_qspi_read(struct nuvoton_qspi *qspi, u32 reg)
{
return readl(qspi->regs + reg);
}
static void nuvoton_qspi_write(struct nuvoton_qspi *qspi, u32 val, u32 reg)
{
writel(val, qspi->regs + reg);
}
static void nuvoton_qspi_update_bits(struct nuvoton_qspi *qspi, u32 reg,
u32 mask, u32 val)
{
u32 tmp;
tmp = nuvoton_qspi_read(qspi, reg);
tmp &= ~mask;
tmp |= val & mask;
nuvoton_qspi_write(qspi, tmp, reg);
}
static int nuvoton_qspi_wait_ready(struct nuvoton_qspi *qspi)
{
u32 val;
return readl_poll_timeout(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
val,
!(val & NUVOTON_QSPI_STATUS_BUSY_MASK),
0, NUVOTON_QSPI_TIMEOUT_US);
}
static int nuvoton_qspi_reset_fifo(struct nuvoton_qspi *qspi)
{
u32 val;
val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_FIFOCTL_OFFSET);
val |= NUVOTON_QSPI_FIFOCTL_TXRST_MASK |
NUVOTON_QSPI_FIFOCTL_RXRST_MASK;
nuvoton_qspi_write(qspi, val, NUVOTON_QSPI_FIFOCTL_OFFSET);
/*
* Give the controller a short time to latch the FIFO reset request
* before polling the reset status bit.
*/
udelay(1);
return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
val,
!(val & NUVOTON_QSPI_STATUS_TXRXRST_MASK),
1, NUVOTON_QSPI_TIMEOUT_US);
}
static int nuvoton_qspi_set_speed(struct nuvoton_qspi *qspi, u32 speed_hz)
{
unsigned long clk_rate;
u32 div;
if (!speed_hz)
return -EINVAL;
if (qspi->speed_hz == speed_hz)
return 0;
clk_rate = clk_get_rate(qspi->clk);
if (!clk_rate) {
dev_err(qspi->dev, "failed to get clock rate\n");
return -EINVAL;
}
div = DIV_ROUND_UP(clk_rate, speed_hz) - 1;
if (div > FIELD_MAX(NUVOTON_QSPI_CLKDIV_MASK)) {
dev_err(qspi->dev, "unsupported SPI clock %u Hz\n", speed_hz);
return -EINVAL;
}
nuvoton_qspi_write(qspi, FIELD_PREP(NUVOTON_QSPI_CLKDIV_MASK, div),
NUVOTON_QSPI_CLKDIV_OFFSET);
qspi->speed_hz = speed_hz;
return 0;
}
static int nuvoton_qspi_set_bits_per_word(struct nuvoton_qspi *qspi, u8 bpw)
{
if (bpw != NUVOTON_QSPI_DEFAULT_BPW)
return -EINVAL;
nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
NUVOTON_QSPI_CTL_DWIDTH_MASK |
NUVOTON_QSPI_CTL_REORDER_MASK,
FIELD_PREP(NUVOTON_QSPI_CTL_DWIDTH_MASK, bpw));
return 0;
}
static int nuvoton_qspi_setup_transfer(struct spi_device *spi,
u32 speed_hz, u8 bpw)
{
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
u32 mode = spi->mode & SPI_MODE_X_MASK;
u32 ctl = 0;
int ret;
if (!speed_hz)
speed_hz = spi->max_speed_hz;
if (!bpw)
bpw = NUVOTON_QSPI_DEFAULT_BPW;
ret = nuvoton_qspi_set_speed(qspi, speed_hz);
if (ret)
return ret;
ret = nuvoton_qspi_set_bits_per_word(qspi, bpw);
if (ret)
return ret;
if (mode == SPI_MODE_0 || mode == SPI_MODE_3)
ctl |= NUVOTON_QSPI_CTL_TXNEG_MASK;
else
ctl |= NUVOTON_QSPI_CTL_RXNEG_MASK;
if (spi->mode & SPI_CPOL)
ctl |= NUVOTON_QSPI_CTL_CLKPOL_MASK;
if (spi->mode & SPI_LSB_FIRST)
ctl |= NUVOTON_QSPI_CTL_LSB_MASK;
nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
NUVOTON_QSPI_CTL_TXNEG_MASK |
NUVOTON_QSPI_CTL_RXNEG_MASK |
NUVOTON_QSPI_CTL_CLKPOL_MASK |
NUVOTON_QSPI_CTL_LSB_MASK, ctl);
return 0;
}
static void nuvoton_qspi_set_bus_width(struct nuvoton_qspi *qspi,
unsigned int buswidth,
enum spi_mem_data_dir dir)
{
u32 ctl = 0;
if (buswidth == 4)
ctl |= NUVOTON_QSPI_CTL_QUADIOEN_MASK;
else if (buswidth == 2)
ctl |= NUVOTON_QSPI_CTL_DUALIOEN_MASK;
if (buswidth > 1 && dir == SPI_MEM_DATA_OUT)
ctl |= NUVOTON_QSPI_CTL_DATDIR_MASK;
nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
NUVOTON_QSPI_CTL_QUADIOEN_MASK |
NUVOTON_QSPI_CTL_DUALIOEN_MASK |
NUVOTON_QSPI_CTL_DATDIR_MASK, ctl);
}
static u32 nuvoton_qspi_tx_byte(const void *txbuf, unsigned int idx)
{
if (!txbuf)
return 0;
return ((const u8 *)txbuf)[idx];
}
static void nuvoton_qspi_rx_byte(void *rxbuf, unsigned int idx, u32 val)
{
if (rxbuf)
((u8 *)rxbuf)[idx] = val;
}
static int nuvoton_qspi_wait_tx_not_full(struct nuvoton_qspi *qspi)
{
u32 val;
return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
val,
!(val & NUVOTON_QSPI_STATUS_TXFULL_MASK),
0, NUVOTON_QSPI_TIMEOUT_US);
}
static int nuvoton_qspi_wait_rx_not_empty(struct nuvoton_qspi *qspi)
{
u32 val;
return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
val,
!(val & NUVOTON_QSPI_STATUS_RXEMPTY_MASK),
0, NUVOTON_QSPI_TIMEOUT_US);
}
static int nuvoton_qspi_txrx(struct nuvoton_qspi *qspi, const void *txbuf,
void *rxbuf, unsigned int len)
{
unsigned int i;
u32 val;
int ret;
if (!len)
return 0;
if (len > NUVOTON_QSPI_MAX_TRANSFER_SIZE)
return -EMSGSIZE;
ret = nuvoton_qspi_reset_fifo(qspi);
if (ret) {
dev_err(qspi->dev, "FIFO reset timed out\n");
return ret;
}
/*
* Use conservative byte-by-byte PIO access. This keeps the initial driver
* simple and avoids relying on FIFO threshold interrupts or DMA support.
*
* The MA35D1 QSPI controller pushes one RX FIFO entry for each TX byte in
* single, dual-output and quad-output modes. Drain RX after every TX byte
* and discard the value for TX-only transfers to avoid RX FIFO overflow.
*/
for (i = 0; i < len; i++) {
ret = nuvoton_qspi_wait_tx_not_full(qspi);
if (ret) {
dev_err(qspi->dev, "TX FIFO full timeout\n");
return ret;
}
nuvoton_qspi_write(qspi, nuvoton_qspi_tx_byte(txbuf, i),
NUVOTON_QSPI_TX_OFFSET);
ret = nuvoton_qspi_wait_rx_not_empty(qspi);
if (ret) {
dev_err(qspi->dev, "RX FIFO empty timeout\n");
return ret;
}
val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_RX_OFFSET);
if (rxbuf)
nuvoton_qspi_rx_byte(rxbuf, i, val);
}
ret = nuvoton_qspi_wait_ready(qspi);
if (ret)
dev_err(qspi->dev, "controller busy timeout\n");
return ret;
}
static int nuvoton_qspi_hw_init(struct nuvoton_qspi *qspi)
{
u32 val;
int ret;
ret = nuvoton_qspi_set_bits_per_word(qspi, NUVOTON_QSPI_DEFAULT_BPW);
if (ret)
return ret;
nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
NUVOTON_QSPI_CTL_SUSPITV_MASK |
NUVOTON_QSPI_CTL_TXNEG_MASK |
NUVOTON_QSPI_CTL_RXNEG_MASK |
NUVOTON_QSPI_CTL_CLKPOL_MASK |
NUVOTON_QSPI_CTL_LSB_MASK,
NUVOTON_QSPI_CTL_TXNEG_MASK);
val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_CTL_OFFSET);
nuvoton_qspi_write(qspi, val | NUVOTON_QSPI_CTL_SPIEN_MASK,
NUVOTON_QSPI_CTL_OFFSET);
ret = readl_poll_timeout(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET, val,
(val & NUVOTON_QSPI_STATUS_SPIENSTS_MASK),
1, NUVOTON_QSPI_TIMEOUT_US);
if (ret) {
dev_err(qspi->dev, "failed to enable controller\n");
return ret;
}
ret = nuvoton_qspi_reset_fifo(qspi);
if (ret)
dev_err(qspi->dev, "FIFO reset timed out\n");
return ret;
}
static size_t nuvoton_qspi_max_transfer_size(struct spi_device *spi)
{
return NUVOTON_QSPI_MAX_TRANSFER_SIZE;
}
static size_t nuvoton_qspi_max_message_size(struct spi_device *spi)
{
return NUVOTON_QSPI_MAX_MESSAGE_SIZE;
}
static int nuvoton_qspi_mem_adjust_op_size(struct spi_mem *mem,
struct spi_mem_op *op)
{
if (op->data.nbytes > NUVOTON_QSPI_MAX_TRANSFER_SIZE)
op->data.nbytes = NUVOTON_QSPI_MAX_TRANSFER_SIZE;
return 0;
}
static bool nuvoton_qspi_mem_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
if (!spi_mem_default_supports_op(mem, op))
return false;
if (op->cmd.buswidth > 4 || op->addr.buswidth > 4 ||
op->dummy.buswidth > 4 || op->data.buswidth > 4)
return false;
if (op->cmd.nbytes != 1)
return false;
if (op->addr.nbytes > 4)
return false;
return true;
}
static void nuvoton_qspi_set_cs_level(struct nuvoton_qspi *qspi,
unsigned int cs, bool assert)
{
unsigned long flags;
u32 mask;
u32 val;
switch (cs) {
case 0:
mask = NUVOTON_QSPI_SSCTL_SS0_MASK;
break;
case 1:
mask = NUVOTON_QSPI_SSCTL_SS1_MASK;
break;
default:
dev_warn(qspi->dev, "invalid chip select %u\n", cs);
return;
}
spin_lock_irqsave(&qspi->ssctl_lock, flags);
val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_SSCTL_OFFSET);
if (assert)
val |= mask;
else
val &= ~mask;
nuvoton_qspi_write(qspi, val, NUVOTON_QSPI_SSCTL_OFFSET);
spin_unlock_irqrestore(&qspi->ssctl_lock, flags);
}
static void nuvoton_qspi_set_cs(struct spi_device *spi, bool level)
{
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
/*
* The SPI core passes the physical CS level to ->set_cs(). This
* initial driver only supports active-low native chip selects.
*/
nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), !level);
}
static void nuvoton_qspi_mem_set_cs(struct spi_device *spi, bool assert)
{
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
/* The direct spi-mem path passes a logical assertion state. */
nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), assert);
}
static int nuvoton_qspi_mem_exec_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
struct spi_device *spi = mem->spi;
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
u8 opcode = op->cmd.opcode;
u8 addr[4];
int ret;
int i;
ret = nuvoton_qspi_setup_transfer(spi, op->max_freq,
NUVOTON_QSPI_DEFAULT_BPW);
if (ret)
return ret;
nuvoton_qspi_mem_set_cs(spi, true);
nuvoton_qspi_set_bus_width(qspi, op->cmd.buswidth, SPI_MEM_DATA_OUT);
ret = nuvoton_qspi_txrx(qspi, &opcode, NULL, 1);
if (ret)
goto out_deassert_cs;
if (op->addr.nbytes) {
for (i = 0; i < op->addr.nbytes; i++)
addr[i] = op->addr.val >> (8 * (op->addr.nbytes - i - 1));
nuvoton_qspi_set_bus_width(qspi, op->addr.buswidth,
SPI_MEM_DATA_OUT);
ret = nuvoton_qspi_txrx(qspi, addr, NULL, op->addr.nbytes);
if (ret)
goto out_deassert_cs;
}
if (op->dummy.nbytes) {
nuvoton_qspi_set_bus_width(qspi, op->dummy.buswidth,
SPI_MEM_DATA_OUT);
ret = nuvoton_qspi_txrx(qspi, NULL, NULL, op->dummy.nbytes);
if (ret)
goto out_deassert_cs;
}
if (op->data.nbytes) {
nuvoton_qspi_set_bus_width(qspi, op->data.buswidth,
op->data.dir);
ret = nuvoton_qspi_txrx(qspi,
op->data.dir == SPI_MEM_DATA_OUT ?
op->data.buf.out : NULL,
op->data.dir == SPI_MEM_DATA_IN ?
op->data.buf.in : NULL,
op->data.nbytes);
}
out_deassert_cs:
nuvoton_qspi_set_bus_width(qspi, 1, SPI_MEM_DATA_IN);
nuvoton_qspi_mem_set_cs(spi, false);
return ret;
}
static const struct spi_controller_mem_ops nuvoton_qspi_mem_ops = {
.adjust_op_size = nuvoton_qspi_mem_adjust_op_size,
.supports_op = nuvoton_qspi_mem_supports_op,
.exec_op = nuvoton_qspi_mem_exec_op,
};
static const struct spi_controller_mem_caps nuvoton_qspi_mem_caps = {
.per_op_freq = true,
};
static int nuvoton_qspi_transfer_one(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *xfer)
{
struct nuvoton_qspi *qspi = spi_controller_get_devdata(ctlr);
unsigned int tx_nbits = xfer->tx_nbits ?: SPI_NBITS_SINGLE;
unsigned int rx_nbits = xfer->rx_nbits ?: SPI_NBITS_SINGLE;
enum spi_mem_data_dir dir = SPI_MEM_DATA_IN;
unsigned int buswidth = 1;
int ret;
ret = nuvoton_qspi_setup_transfer(spi, xfer->speed_hz,
xfer->bits_per_word);
if (ret)
return ret;
if (xfer->tx_buf && xfer->rx_buf &&
(tx_nbits != SPI_NBITS_SINGLE ||
rx_nbits != SPI_NBITS_SINGLE))
return -EOPNOTSUPP;
if (xfer->tx_buf) {
dir = SPI_MEM_DATA_OUT;
if (tx_nbits == SPI_NBITS_QUAD)
buswidth = 4;
else if (tx_nbits == SPI_NBITS_DUAL)
buswidth = 2;
} else if (xfer->rx_buf) {
if (rx_nbits == SPI_NBITS_QUAD)
buswidth = 4;
else if (rx_nbits == SPI_NBITS_DUAL)
buswidth = 2;
}
nuvoton_qspi_set_bus_width(qspi, buswidth, dir);
ret = nuvoton_qspi_txrx(qspi, xfer->tx_buf, xfer->rx_buf,
xfer->len);
nuvoton_qspi_set_bus_width(qspi, 1, SPI_MEM_DATA_IN);
return ret;
}
static int nuvoton_qspi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct spi_controller *ctlr;
struct nuvoton_qspi *qspi;
struct reset_control *rst;
u32 num_cs = NUVOTON_QSPI_DEFAULT_NUM_CS;
int ret;
ctlr = devm_spi_alloc_host(dev, sizeof(*qspi));
if (!ctlr)
return -ENOMEM;
platform_set_drvdata(pdev, ctlr);
qspi = spi_controller_get_devdata(ctlr);
qspi->dev = dev;
spin_lock_init(&qspi->ssctl_lock);
qspi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(qspi->regs))
return PTR_ERR(qspi->regs);
rst = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(rst))
return dev_err_probe(dev, PTR_ERR(rst),
"failed to get reset\n");
qspi->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(qspi->clk))
return dev_err_probe(dev, PTR_ERR(qspi->clk),
"failed to get and enable clock\n");
ret = reset_control_assert(rst);
if (ret)
return dev_err_probe(dev, ret, "failed to assert reset\n");
udelay(2);
ret = reset_control_deassert(rst);
if (ret)
return dev_err_probe(dev, ret, "failed to deassert reset\n");
ret = device_property_read_u32(dev, "num-cs", &num_cs);
if (ret && ret != -EINVAL)
return dev_err_probe(dev, ret, "failed to read num-cs\n");
if (!num_cs || num_cs > NUVOTON_QSPI_MAX_NUM_CS)
return dev_err_probe(dev, -EINVAL, "invalid num-cs %u\n",
num_cs);
ctlr->num_chipselect = num_cs;
ctlr->max_transfer_size = nuvoton_qspi_max_transfer_size;
ctlr->max_message_size = nuvoton_qspi_max_message_size;
ctlr->mem_ops = &nuvoton_qspi_mem_ops;
ctlr->mem_caps = &nuvoton_qspi_mem_caps;
ctlr->set_cs = nuvoton_qspi_set_cs;
ctlr->transfer_one = nuvoton_qspi_transfer_one;
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST |
SPI_RX_DUAL | SPI_TX_DUAL |
SPI_RX_QUAD | SPI_TX_QUAD;
ctlr->dev.of_node = dev->of_node;
ret = nuvoton_qspi_hw_init(qspi);
if (ret)
return ret;
ret = devm_spi_register_controller(dev, ctlr);
if (ret)
return dev_err_probe(dev, ret,
"failed to register spi controller\n");
return 0;
}
static const struct of_device_id nuvoton_qspi_of_match[] = {
{ .compatible = "nuvoton,ma35d1-qspi" },
{ }
};
MODULE_DEVICE_TABLE(of, nuvoton_qspi_of_match);
static struct platform_driver nuvoton_qspi_driver = {
.driver = {
.name = "ma35d1-qspi",
.of_match_table = nuvoton_qspi_of_match,
},
.probe = nuvoton_qspi_probe,
};
module_platform_driver(nuvoton_qspi_driver);
MODULE_DESCRIPTION("Nuvoton MA35D1 QSPI controller driver");
MODULE_AUTHOR("Chi-Wen Weng <cwweng@nuvoton.com>");
MODULE_LICENSE("GPL");