root/drivers/spi/spi-nxp-xspi.c
// SPDX-License-Identifier: GPL-2.0+

/*
 * NXP xSPI controller driver.
 *
 * Copyright 2025 NXP
 *
 * xSPI is a flexible SPI host controller which supports single
 * external devices. This device can have up to eight bidirectional
 * data lines, this means xSPI support Single/Dual/Quad/Octal mode
 * data transfer (1/2/4/8 bidirectional data lines).
 *
 * xSPI controller is driven by the LUT(Look-up Table) registers
 * LUT registers are a look-up-table for sequences of instructions.
 * A valid sequence consists of five LUT registers.
 * Maximum 16 LUT sequences can be programmed simultaneously.
 *
 * LUTs are being created at run-time based on the commands passed
 * from the spi-mem framework, thus using single LUT index.
 *
 * Software triggered Flash read/write access by IP Bus.
 *
 * Memory mapped read access by AHB Bus.
 *
 * Based on SPI MEM interface and spi-nxp-fspi.c driver.
 *
 * Author:
 *     Haibo Chen <haibo.chen@nxp.com>
 * Co-author:
 *     Han Xu <han.xu@nxp.com>
 */

#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>

/* Runtime pm timeout */
#define XSPI_RPM_TIMEOUT_MS 50  /* 50ms */
/*
 * The driver only uses one single LUT entry, that is updated on
 * each call of exec_op(). Index 0 is preset at boot with a basic
 * read operation, so let's use the last entry (15).
 */
#define XSPI_SEQID_LUT                  15

#define XSPI_MCR                        0x0
#define XSPI_MCR_CKN_FA_EN              BIT(26)
#define XSPI_MCR_DQS_FA_SEL_MASK        GENMASK(25, 24)
#define XSPI_MCR_ISD3FA                 BIT(17)
#define XSPI_MCR_ISD2FA                 BIT(16)
#define XSPI_MCR_DOZE                   BIT(15)
#define XSPI_MCR_MDIS                   BIT(14)
#define XSPI_MCR_DLPEN                  BIT(12)
#define XSPI_MCR_CLR_TXF                BIT(11)
#define XSPI_MCR_CLR_RXF                BIT(10)
#define XSPI_MCR_IPS_TG_RST             BIT(9)
#define XSPI_MCR_VAR_LAT_EN             BIT(8)
#define XSPI_MCR_DDR_EN                 BIT(7)
#define XSPI_MCR_DQS_EN                 BIT(6)
#define XSPI_MCR_DQS_LAT_EN             BIT(5)
#define XSPI_MCR_DQS_OUT_EN             BIT(4)
#define XSPI_MCR_SWRSTHD                BIT(1)
#define XSPI_MCR_SWRSTSD                BIT(0)

#define XSPI_IPCR                       0x8

#define XSPI_FLSHCR                     0xC
#define XSPI_FLSHCR_TDH_MASK            GENMASK(17, 16)
#define XSPI_FLSHCR_TCSH_MASK           GENMASK(11, 8)
#define XSPI_FLSHCR_TCSS_MASK           GENMASK(3, 0)

#define XSPI_BUF0CR                     0x10
#define XSPI_BUF1CR                     0x14
#define XSPI_BUF2CR                     0x18
#define XSPI_BUF3CR                     0x1C
#define XSPI_BUF3CR_ALLMST              BIT(31)
#define XSPI_BUF3CR_ADATSZ_MASK         GENMASK(17, 8)
#define XSPI_BUF3CR_MSTRID_MASK         GENMASK(3, 0)

#define XSPI_BFGENCR                    0x20
#define XSPI_BFGENCR_SEQID_WR_MASK      GENMASK(31, 28)
#define XSPI_BFGENCR_ALIGN_MASK         GENMASK(24, 22)
#define XSPI_BFGENCR_PPWF_CLR           BIT(20)
#define XSPI_BFGENCR_WR_FLUSH_EN        BIT(21)
#define XSPI_BFGENCR_SEQID_WR_EN        BIT(17)
#define XSPI_BFGENCR_SEQID_MASK         GENMASK(15, 12)

#define XSPI_BUF0IND                    0x30
#define XSPI_BUF1IND                    0x34
#define XSPI_BUF2IND                    0x38

#define XSPI_DLLCRA                     0x60
#define XSPI_DLLCRA_DLLEN               BIT(31)
#define XSPI_DLLCRA_FREQEN              BIT(30)
#define XSPI_DLLCRA_DLL_REFCNTR_MASK    GENMASK(27, 24)
#define XSPI_DLLCRA_DLLRES_MASK         GENMASK(23, 20)
#define XSPI_DLLCRA_SLV_FINE_MASK       GENMASK(19, 16)
#define XSPI_DLLCRA_SLV_DLY_MASK        GENMASK(14, 12)
#define XSPI_DLLCRA_SLV_DLY_COARSE_MASK GENMASK(11,  8)
#define XSPI_DLLCRA_SLV_DLY_FINE_MASK   GENMASK(7, 5)
#define XSPI_DLLCRA_DLL_CDL8            BIT(4)
#define XSPI_DLLCRA_SLAVE_AUTO_UPDT     BIT(3)
#define XSPI_DLLCRA_SLV_EN              BIT(2)
#define XSPI_DLLCRA_SLV_DLL_BYPASS      BIT(1)
#define XSPI_DLLCRA_SLV_UPD             BIT(0)

#define XSPI_SFAR                       0x100

#define XSPI_SFACR                      0x104
#define XSPI_SFACR_FORCE_A10            BIT(22)
#define XSPI_SFACR_WA_4B_EN             BIT(21)
#define XSPI_SFACR_CAS_INTRLVD          BIT(20)
#define XSPI_SFACR_RX_BP_EN             BIT(18)
#define XSPI_SFACR_BYTE_SWAP            BIT(17)
#define XSPI_SFACR_WA                   BIT(16)
#define XSPI_SFACR_CAS_MASK             GENMASK(3, 0)

#define XSPI_SMPR                       0x108
#define XSPI_SMPR_DLLFSMPFA_MASK        GENMASK(26, 24)
#define XSPI_SMPR_FSDLY                 BIT(6)
#define XSPI_SMPR_FSPHS                 BIT(5)

#define XSPI_RBSR                       0x10C

#define XSPI_RBCT                       0x110
#define XSPI_RBCT_WMRK_MASK             GENMASK(6, 0)

#define XSPI_DLLSR                      0x12C
#define XSPI_DLLSR_DLLA_LOCK            BIT(15)
#define XSPI_DLLSR_SLVA_LOCK            BIT(14)
#define XSPI_DLLSR_DLLA_RANGE_ERR       BIT(13)
#define XSPI_DLLSR_DLLA_FINE_UNDERFLOW  BIT(12)

#define XSPI_TBSR                       0x150

#define XSPI_TBDR                       0x154

#define XSPI_TBCT                       0x158
#define XSPI_TBCT_WMRK_MASK             GENMASK(7, 0)

#define XSPI_SR                         0x15C
#define XSPI_SR_TXFULL                  BIT(27)
#define XSPI_SR_TXDMA                   BIT(26)
#define XSPI_SR_TXWA                    BIT(25)
#define XSPI_SR_TXNE                    BIT(24)
#define XSPI_SR_RXDMA                   BIT(23)
#define XSPI_SR_ARB_STATE_MASK          GENMASK(23, 20)
#define XSPI_SR_RXFULL                  BIT(19)
#define XSPI_SR_RXWE                    BIT(16)
#define XSPI_SR_ARB_LCK                 BIT(15)
#define XSPI_SR_AHBnFUL                 BIT(11)
#define XSPI_SR_AHBnNE                  BIT(7)
#define XSPI_SR_AHBTRN                  BIT(6)
#define XSPI_SR_AWRACC                  BIT(4)
#define XSPI_SR_AHB_ACC                 BIT(2)
#define XSPI_SR_IP_ACC                  BIT(1)
#define XSPI_SR_BUSY                    BIT(0)

#define XSPI_FR                         0x160
#define XSPI_FR_DLPFF                   BIT(31)
#define XSPI_FR_DLLABRT                 BIT(28)
#define XSPI_FR_TBFF                    BIT(27)
#define XSPI_FR_TBUF                    BIT(26)
#define XSPI_FR_DLLUNLCK                BIT(24)
#define XSPI_FR_ILLINE                  BIT(23)
#define XSPI_FR_RBOF                    BIT(17)
#define XSPI_FR_RBDF                    BIT(16)
#define XSPI_FR_AAEF                    BIT(15)
#define XSPI_FR_AITEF                   BIT(14)
#define XSPI_FR_AIBSEF                  BIT(13)
#define XSPI_FR_ABOF                    BIT(12)
#define XSPI_FR_CRCAEF                  BIT(10)
#define XSPI_FR_PPWF                    BIT(8)
#define XSPI_FR_IPIEF                   BIT(6)
#define XSPI_FR_IPEDERR                 BIT(5)
#define XSPI_FR_PERFOVF                 BIT(2)
#define XSPI_FR_RDADDR                  BIT(1)
#define XSPI_FR_TFF                     BIT(0)

#define XSPI_RSER                       0x164
#define XSPI_RSER_TFIE                  BIT(0)

#define XSPI_SFA1AD                     0x180

#define XSPI_SFA2AD                     0x184

#define XSPI_RBDR0                      0x200

#define XSPI_LUTKEY                     0x300
#define XSPI_LUT_KEY_VAL                (0x5AF05AF0UL)

#define XSPI_LCKCR                      0x304
#define XSPI_LOKCR_LOCK                 BIT(0)
#define XSPI_LOKCR_UNLOCK               BIT(1)

#define XSPI_LUT                        0x310
#define XSPI_LUT_OFFSET                 (XSPI_SEQID_LUT * 5 * 4)
#define XSPI_LUT_REG(idx) \
        (XSPI_LUT + XSPI_LUT_OFFSET + (idx) * 4)

#define XSPI_MCREXT                     0x4FC
#define XSPI_MCREXT_RST_MASK            GENMASK(3, 0)


#define XSPI_FRAD0_WORD2                0x808
#define XSPI_FRAD0_WORD2_MD0ACP_MASK    GENMASK(2, 0)

#define XSPI_FRAD0_WORD3                0x80C
#define XSPI_FRAD0_WORD3_VLD            BIT(31)

#define XSPI_TG0MDAD                    0x900
#define XSPI_TG0MDAD_VLD                BIT(31)

#define XSPI_TG1MDAD                    0x910

#define XSPI_MGC                        0x920
#define XSPI_MGC_GVLD                   BIT(31)
#define XSPI_MGC_GVLDMDAD               BIT(29)
#define XSPI_MGC_GVLDFRAD               BIT(27)

#define XSPI_MTO                        0x928

#define XSPI_ERRSTAT                    0x938
#define XSPI_INT_EN                     0x93C

#define XSPI_SFP_TG_IPCR                0x958
#define XSPI_SFP_TG_IPCR_SEQID_MASK     GENMASK(27, 24)
#define XSPI_SFP_TG_IPCR_ARB_UNLOCK     BIT(23)
#define XSPI_SFP_TG_IPCR_ARB_LOCK       BIT(22)
#define XSPI_SFP_TG_IPCR_IDATSZ_MASK    GENMASK(15, 0)

#define XSPI_SFP_TG_SFAR 0x95C

/* Register map end */

/********* XSPI CMD definitions ***************************/
#define LUT_STOP        0x00
#define LUT_CMD_SDR     0x01
#define LUT_ADDR_SDR    0x02
#define LUT_DUMMY       0x03
#define LUT_MODE8_SDR   0x04
#define LUT_MODE2_SDR   0x05
#define LUT_MODE4_SDR   0x06
#define LUT_READ_SDR    0x07
#define LUT_WRITE_SDR   0x08
#define LUT_JMP_ON_CS   0x09
#define LUT_ADDR_DDR    0x0A
#define LUT_MODE8_DDR   0x0B
#define LUT_MODE2_DDR   0x0C
#define LUT_MODE4_DDR   0x0D
#define LUT_READ_DDR    0x0E
#define LUT_WRITE_DDR   0x0F
#define LUT_DATA_LEARN  0x10
#define LUT_CMD_DDR     0x11
#define LUT_CADDR_SDR   0x12
#define LUT_CADDR_DDR   0x13
#define JMP_TO_SEQ      0x14

#define XSPI_64BIT_LE   0x3
/*
 * Calculate number of required PAD bits for LUT register.
 *
 * The pad stands for the number of IO lines [0:7].
 * For example, the octal read needs eight IO lines,
 * so you should use LUT_PAD(8). This macro
 * returns 3 i.e. use eight (2^3) IP lines for read.
 */
#define LUT_PAD(x) (fls(x) - 1)

/*
 * Macro for constructing the LUT entries with the following
 * register layout:
 *
 *  ---------------------------------------------------
 *  | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
 *  ---------------------------------------------------
 */
#define PAD_SHIFT               8
#define INSTR_SHIFT             10
#define OPRND_SHIFT             16

/* Macros for constructing the LUT register. */
#define LUT_DEF(idx, ins, pad, opr)                       \
        ((((ins) << INSTR_SHIFT) | ((pad) << PAD_SHIFT) | \
        (opr)) << (((idx) % 2) * OPRND_SHIFT))

#define NXP_XSPI_MIN_IOMAP      SZ_4M
#define NXP_XSPI_MAX_CHIPSELECT         2
#define POLL_TOUT_US            5000

/* Access flash memory using IP bus only */
#define XSPI_QUIRK_USE_IP_ONLY  BIT(0)

struct nxp_xspi_devtype_data {
        unsigned int rxfifo;
        unsigned int txfifo;
        unsigned int ahb_buf_size;
        unsigned int quirks;
};

static struct nxp_xspi_devtype_data imx94_data = {
        .rxfifo = SZ_512,       /* (128 * 4 bytes)  */
        .txfifo = SZ_1K,        /* (256 * 4 bytes)  */
        .ahb_buf_size = SZ_4K,  /* (1024 * 4 bytes)  */
};

struct nxp_xspi {
        void __iomem *iobase;
        void __iomem *ahb_addr;
        u32 memmap_phy;
        u32 memmap_phy_size;
        u32 memmap_start;
        u32 memmap_len;
        struct clk *clk;
        struct device *dev;
        struct completion c;
        const struct nxp_xspi_devtype_data *devtype_data;
        /* mutex lock for each operation */
        struct mutex lock;
        int selected;
#define XSPI_DTR_PROTO          BIT(0)
        int flags;
        /* Save the previous operation clock rate */
        unsigned long pre_op_rate;
        /* The max clock rate xspi supported output to device */
        unsigned long support_max_rate;
};

static inline int needs_ip_only(struct nxp_xspi *xspi)
{
        return xspi->devtype_data->quirks & XSPI_QUIRK_USE_IP_ONLY;
}

static irqreturn_t nxp_xspi_irq_handler(int irq, void *dev_id)
{
        struct nxp_xspi *xspi = dev_id;
        u32 reg;

        reg = readl(xspi->iobase + XSPI_FR);
        if (reg & XSPI_FR_TFF) {
                /* Clear interrupt */
                writel(XSPI_FR_TFF, xspi->iobase + XSPI_FR);
                complete(&xspi->c);
                return IRQ_HANDLED;
        }

        return IRQ_NONE;
}

static int nxp_xspi_check_buswidth(struct nxp_xspi *xspi, u8 width)
{
        return (is_power_of_2(width) && width <= 8) ? 0 : -EOPNOTSUPP;
}

static bool nxp_xspi_supports_op(struct spi_mem *mem,
                                 const struct spi_mem_op *op)
{
        struct nxp_xspi *xspi = spi_controller_get_devdata(mem->spi->controller);
        int ret;

        ret = nxp_xspi_check_buswidth(xspi, op->cmd.buswidth);

        if (op->addr.nbytes)
                ret |= nxp_xspi_check_buswidth(xspi, op->addr.buswidth);

        if (op->dummy.nbytes)
                ret |= nxp_xspi_check_buswidth(xspi, op->dummy.buswidth);

        if (op->data.nbytes)
                ret |= nxp_xspi_check_buswidth(xspi, op->data.buswidth);

        if (ret)
                return false;

        /*
         * The number of address bytes should be equal to or less than 4 bytes.
         */
        if (op->addr.nbytes > 4)
                return false;

        /* Max 32 dummy clock cycles supported */
        if (op->dummy.buswidth &&
            (op->dummy.nbytes * 8 / op->dummy.buswidth > 64))
                return false;

        if (needs_ip_only(xspi) && op->data.dir == SPI_MEM_DATA_IN &&
            op->data.nbytes > xspi->devtype_data->rxfifo)
                return false;

        if (op->data.dir == SPI_MEM_DATA_OUT &&
                        op->data.nbytes > xspi->devtype_data->txfifo)
                return false;

        return spi_mem_default_supports_op(mem, op);
}

static void nxp_xspi_prepare_lut(struct nxp_xspi *xspi,
                                 const struct spi_mem_op *op)
{
        void __iomem *base = xspi->iobase;
        u32 lutval[5] = {};
        int lutidx = 1, i;

        /* cmd */
        if (op->cmd.dtr) {
                lutval[0] |= LUT_DEF(0, LUT_CMD_DDR, LUT_PAD(op->cmd.buswidth),
                                     op->cmd.opcode >> 8);
                lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_CMD_DDR,
                                              LUT_PAD(op->cmd.buswidth),
                                              op->cmd.opcode & 0x00ff);
                lutidx++;
        } else {
                lutval[0] |= LUT_DEF(0, LUT_CMD_SDR, LUT_PAD(op->cmd.buswidth),
                                     op->cmd.opcode);
        }

        /* Addr bytes */
        if (op->addr.nbytes) {
                lutval[lutidx / 2] |= LUT_DEF(lutidx, op->addr.dtr ?
                                              LUT_ADDR_DDR : LUT_ADDR_SDR,
                                              LUT_PAD(op->addr.buswidth),
                                              op->addr.nbytes * 8);
                lutidx++;
        }

        /* Dummy bytes, if needed */
        if (op->dummy.nbytes) {
                lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_DUMMY,
                                              LUT_PAD(op->data.buswidth),
                                              op->dummy.nbytes * 8 /
                                                /* need distinguish ddr mode */
                                              op->dummy.buswidth / (op->dummy.dtr ? 2 : 1));
                lutidx++;
        }

        /* Read/Write data bytes */
        if (op->data.nbytes) {
                lutval[lutidx / 2] |= LUT_DEF(lutidx,
                                              op->data.dir == SPI_MEM_DATA_IN ?
                                              (op->data.dtr ? LUT_READ_DDR : LUT_READ_SDR) :
                                              (op->data.dtr ? LUT_WRITE_DDR : LUT_WRITE_SDR),
                                              LUT_PAD(op->data.buswidth),
                                              0);
                lutidx++;
        }

        /* Stop condition. */
        lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_STOP, 0, 0);

        /* Unlock LUT */
        writel(XSPI_LUT_KEY_VAL, xspi->iobase + XSPI_LUTKEY);
        writel(XSPI_LOKCR_UNLOCK, xspi->iobase + XSPI_LCKCR);

        /* Fill LUT */
        for (i = 0; i < ARRAY_SIZE(lutval); i++)
                writel(lutval[i], base + XSPI_LUT_REG(i));

        dev_dbg(xspi->dev, "CMD[%02x] lutval[0:%08x 1:%08x 2:%08x 3:%08x 4:%08x], size: 0x%08x\n",
                op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], lutval[4],
                op->data.nbytes);

        /* Lock LUT */
        writel(XSPI_LUT_KEY_VAL, xspi->iobase + XSPI_LUTKEY);
        writel(XSPI_LOKCR_LOCK, xspi->iobase + XSPI_LCKCR);
}

static void nxp_xspi_disable_ddr(struct nxp_xspi *xspi)
{
        void __iomem *base = xspi->iobase;
        u32 reg;

        /* Disable module */
        reg = readl(base + XSPI_MCR);
        reg |= XSPI_MCR_MDIS;
        writel(reg, base + XSPI_MCR);

        reg &= ~XSPI_MCR_DDR_EN;
        reg &= ~XSPI_MCR_DQS_FA_SEL_MASK;
        /* Use dummy pad loopback mode to sample data */
        reg |= FIELD_PREP(XSPI_MCR_DQS_FA_SEL_MASK, 0x01);
        writel(reg, base + XSPI_MCR);
        xspi->support_max_rate = 133000000;

        reg = readl(base + XSPI_FLSHCR);
        reg &= ~XSPI_FLSHCR_TDH_MASK;
        writel(reg, base + XSPI_FLSHCR);

        /* Select sampling at inverted clock */
        reg = FIELD_PREP(XSPI_SMPR_DLLFSMPFA_MASK, 0) | XSPI_SMPR_FSPHS;
        writel(reg, base + XSPI_SMPR);

        /* Enable module */
        reg = readl(base + XSPI_MCR);
        reg &= ~XSPI_MCR_MDIS;
        writel(reg, base + XSPI_MCR);
}

static void nxp_xspi_enable_ddr(struct nxp_xspi *xspi)
{
        void __iomem *base = xspi->iobase;
        u32 reg;

        /* Disable module */
        reg = readl(base + XSPI_MCR);
        reg |= XSPI_MCR_MDIS;
        writel(reg, base + XSPI_MCR);

        reg |= XSPI_MCR_DDR_EN;
        reg &= ~XSPI_MCR_DQS_FA_SEL_MASK;
        /* Use external dqs to sample data */
        reg |= FIELD_PREP(XSPI_MCR_DQS_FA_SEL_MASK, 0x03);
        writel(reg, base + XSPI_MCR);
        xspi->support_max_rate = 200000000;

        reg = readl(base + XSPI_FLSHCR);
        reg &= ~XSPI_FLSHCR_TDH_MASK;
        reg |= FIELD_PREP(XSPI_FLSHCR_TDH_MASK, 0x01);
        writel(reg, base + XSPI_FLSHCR);

        reg = FIELD_PREP(XSPI_SMPR_DLLFSMPFA_MASK, 0x04);
        writel(reg, base + XSPI_SMPR);

        /* Enable module */
        reg = readl(base + XSPI_MCR);
        reg &= ~XSPI_MCR_MDIS;
        writel(reg, base + XSPI_MCR);
}

static void nxp_xspi_sw_reset(struct nxp_xspi *xspi)
{
        void __iomem *base = xspi->iobase;
        bool mdis_flag = false;
        u32 reg;
        int ret;

        reg = readl(base + XSPI_MCR);

        /*
         * Per RM, when reset SWRSTSD and SWRSTHD, XSPI must be
         * enabled (MDIS = 0).
         * So if MDIS is 1, should clear it before assert SWRSTSD
         * and SWRSTHD.
         */
        if (reg & XSPI_MCR_MDIS) {
                reg &= ~XSPI_MCR_MDIS;
                writel(reg, base + XSPI_MCR);
                mdis_flag = true;
        }

        /* Software reset for AHB domain and Serial flash memory domain */
        reg |= XSPI_MCR_SWRSTHD | XSPI_MCR_SWRSTSD;
        /* Software Reset for IPS Target Group Queue 0 */
        reg |= XSPI_MCR_IPS_TG_RST;
        writel(reg, base + XSPI_MCR);

        /* IPS_TG_RST will self-clear to 0 once IPS_TG_RST complete */
        ret = readl_poll_timeout(base + XSPI_MCR, reg, !(reg & XSPI_MCR_IPS_TG_RST),
                              100, 5000);
        if (ret == -ETIMEDOUT)
                dev_warn(xspi->dev, "XSPI_MCR_IPS_TG_RST do not self-clear in 5ms!");

        /*
         * Per RM, must wait for at least three system cycles and
         * three flash cycles after changing the value of reset field.
         * delay 5us for safe.
         */
        fsleep(5);

        /*
         * Per RM, before dessert SWRSTSD and SWRSTHD, XSPI must be
         * disabled (MIDS = 1).
         */
        reg = readl(base + XSPI_MCR);
        reg |= XSPI_MCR_MDIS;
        writel(reg, base + XSPI_MCR);

        /* deassert software reset */
        reg &= ~(XSPI_MCR_SWRSTHD | XSPI_MCR_SWRSTSD);
        writel(reg, base + XSPI_MCR);

        /*
         * Per RM, must wait for at least three system cycles and
         * three flash cycles after changing the value of reset field.
         * delay 5us for safe.
         */
        fsleep(5);

        /* Re-enable XSPI if it is enabled at beginning */
        if (!mdis_flag) {
                reg &= ~XSPI_MCR_MDIS;
                writel(reg, base + XSPI_MCR);
        }
}

static void nxp_xspi_dll_bypass(struct nxp_xspi *xspi)
{
        void __iomem *base = xspi->iobase;
        int ret;
        u32 reg;

        nxp_xspi_sw_reset(xspi);

        writel(0, base + XSPI_DLLCRA);

        /* Set SLV EN first */
        reg = XSPI_DLLCRA_SLV_EN;
        writel(reg, base + XSPI_DLLCRA);

        reg = XSPI_DLLCRA_FREQEN |
              FIELD_PREP(XSPI_DLLCRA_SLV_DLY_COARSE_MASK, 0x0) |
              XSPI_DLLCRA_SLV_EN | XSPI_DLLCRA_SLV_DLL_BYPASS;
        writel(reg, base + XSPI_DLLCRA);

        reg |= XSPI_DLLCRA_SLV_UPD;
        writel(reg, base + XSPI_DLLCRA);

        ret = readl_poll_timeout(base + XSPI_DLLSR, reg,
                              reg & XSPI_DLLSR_SLVA_LOCK, 0, POLL_TOUT_US);
        if (ret)
                dev_err(xspi->dev,
                        "DLL SLVA unlock, the DLL status is %x, need to check!\n",
                        readl(base + XSPI_DLLSR));
}

static void nxp_xspi_dll_auto(struct nxp_xspi *xspi, unsigned long rate)
{
        void __iomem *base = xspi->iobase;
        int ret;
        u32 reg;

        nxp_xspi_sw_reset(xspi);

        writel(0, base + XSPI_DLLCRA);

        /* Set SLV EN first */
        reg = XSPI_DLLCRA_SLV_EN;
        writel(reg, base + XSPI_DLLCRA);

        reg = FIELD_PREP(XSPI_DLLCRA_DLL_REFCNTR_MASK, 0x02) |
              FIELD_PREP(XSPI_DLLCRA_DLLRES_MASK, 0x08) |
              XSPI_DLLCRA_SLAVE_AUTO_UPDT | XSPI_DLLCRA_SLV_EN;
        if (rate > 133000000)
                reg |= XSPI_DLLCRA_FREQEN;

        writel(reg, base + XSPI_DLLCRA);

        reg |= XSPI_DLLCRA_SLV_UPD;
        writel(reg, base + XSPI_DLLCRA);

        reg |= XSPI_DLLCRA_DLLEN;
        writel(reg, base + XSPI_DLLCRA);

        ret = readl_poll_timeout(base + XSPI_DLLSR, reg,
                              reg & XSPI_DLLSR_DLLA_LOCK, 0, POLL_TOUT_US);
        if (ret)
                dev_err(xspi->dev,
                        "DLL unlock, the DLL status is %x, need to check!\n",
                        readl(base + XSPI_DLLSR));

        ret = readl_poll_timeout(base + XSPI_DLLSR, reg,
                              reg & XSPI_DLLSR_SLVA_LOCK, 0, POLL_TOUT_US);
        if (ret)
                dev_err(xspi->dev,
                        "DLL SLVA unlock, the DLL status is %x, need to check!\n",
                        readl(base + XSPI_DLLSR));
}

static void nxp_xspi_select_mem(struct nxp_xspi *xspi, struct spi_device *spi,
                                const struct spi_mem_op *op)
{
        /* xspi only support one DTR mode: 8D-8D-8D */
        bool op_is_dtr = op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr;
        unsigned long root_clk_rate, rate;
        uint64_t cs0_top_address;
        uint64_t cs1_top_address;
        u32 reg;
        int ret;

        /*
         * Return when following condition all meet,
         * 1, if previously selected target device is same as current
         *    requested target device.
         * 2, the DTR or STR mode do not change.
         * 3, previous operation max rate equals current one.
         *
         * For other case, need to re-config.
         */
        if (xspi->selected == spi_get_chipselect(spi, 0) &&
            (!!(xspi->flags & XSPI_DTR_PROTO) == op_is_dtr) &&
            (xspi->pre_op_rate == op->max_freq))
                return;

        if (op_is_dtr) {
                nxp_xspi_enable_ddr(xspi);
                xspi->flags |= XSPI_DTR_PROTO;
        } else {
                nxp_xspi_disable_ddr(xspi);
                xspi->flags &= ~XSPI_DTR_PROTO;
        }
        rate = min_t(unsigned long, xspi->support_max_rate, op->max_freq);
        /*
         * There is two dividers between xspi_clk_root(from SoC CCM) and xspi_sfif.
         * xspi_clk_root ---->divider1 ----> ipg_clk_2xsfif
         *                              |
         *                              |
         *                              |---> divider2 ---> ipg_clk_sfif
         * divider1 is controlled by SOCCR, SOCCR default value is 0.
         * divider2 fix to divide 2.
         * when SOCCR = 0:
         *        ipg_clk_2xsfif = xspi_clk_root
         *        ipg_clk_sfif = ipg_clk_2xsfif / 2 = xspi_clk_root / 2
         * ipg_clk_2xsfif is used for DTR mode.
         * xspi_sck(output to device) is defined based on xspi_sfif clock.
         */
        root_clk_rate = rate * 2;

        clk_disable_unprepare(xspi->clk);

        ret = clk_set_rate(xspi->clk, root_clk_rate);
        if (ret)
                return;

        ret = clk_prepare_enable(xspi->clk);
        if (ret)
                return;

        xspi->pre_op_rate = op->max_freq;
        xspi->selected = spi_get_chipselect(spi, 0);

        if (xspi->selected) {           /* CS1 select */
                cs0_top_address = xspi->memmap_phy;
                cs1_top_address = SZ_4G - 1;
        } else {                        /* CS0 select */
                cs0_top_address = SZ_4G - 1;
                cs1_top_address = SZ_4G - 1;
        }
        writel(cs0_top_address, xspi->iobase + XSPI_SFA1AD);
        writel(cs1_top_address, xspi->iobase + XSPI_SFA2AD);

        reg = readl(xspi->iobase + XSPI_SFACR);
        if (op->data.swap16)
                reg |= XSPI_SFACR_BYTE_SWAP;
        else
                reg &= ~XSPI_SFACR_BYTE_SWAP;
        writel(reg, xspi->iobase + XSPI_SFACR);

        if (!op_is_dtr || rate < 60000000)
                nxp_xspi_dll_bypass(xspi);
        else
                nxp_xspi_dll_auto(xspi, rate);
}

static int nxp_xspi_ahb_read(struct nxp_xspi *xspi, const struct spi_mem_op *op)
{
        u32 start = op->addr.val;
        u32 len = op->data.nbytes;

        /* If necessary, ioremap before AHB read */
        if ((!xspi->ahb_addr) || start < xspi->memmap_start ||
             start + len > xspi->memmap_start + xspi->memmap_len) {
                if (xspi->ahb_addr)
                        iounmap(xspi->ahb_addr);

                xspi->memmap_start = start;
                xspi->memmap_len = len > NXP_XSPI_MIN_IOMAP ?
                                len : NXP_XSPI_MIN_IOMAP;

                xspi->ahb_addr = ioremap(xspi->memmap_phy + xspi->memmap_start,
                                         xspi->memmap_len);

                if (!xspi->ahb_addr) {
                        dev_err(xspi->dev, "failed to alloc memory\n");
                        return -ENOMEM;
                }
        }

        /* Read out the data directly from the AHB buffer. */
        memcpy_fromio(op->data.buf.in,
                        xspi->ahb_addr + start - xspi->memmap_start, len);

        return 0;
}

static int nxp_xspi_fill_txfifo(struct nxp_xspi *xspi,
                                 const struct spi_mem_op *op)
{
        void __iomem *base = xspi->iobase;
        u8 *buf = (u8 *)op->data.buf.out;
        u32 reg, left;
        int i;

        for (i = 0; i < ALIGN(op->data.nbytes, 4); i += 4) {
                reg = readl(base + XSPI_FR);
                reg |= XSPI_FR_TBFF;
                writel(reg, base + XSPI_FR);
                /* Read again to check whether the tx fifo has rom */
                reg = readl(base + XSPI_FR);
                if (!(reg & XSPI_FR_TBFF)) {
                        WARN_ON(1);
                        return -EIO;
                }

                if (i == ALIGN_DOWN(op->data.nbytes, 4)) {
                        /* Use 0xFF for extra bytes */
                        left = 0xFFFFFFFF;
                        /* The last 1 to 3 bytes */
                        memcpy((u8 *)&left, buf + i, op->data.nbytes - i);
                        writel(left, base + XSPI_TBDR);
                } else {
                        writel(*(u32 *)(buf + i), base + XSPI_TBDR);
                }
        }

        return 0;
}

static int nxp_xspi_read_rxfifo(struct nxp_xspi *xspi,
                                const struct spi_mem_op *op)
{
        u32 watermark, watermark_bytes, reg;
        void __iomem *base = xspi->iobase;
        u8 *buf = (u8 *) op->data.buf.in;
        int i, ret, len;

        /*
         * Config the rx watermark half of the 64 memory-mapped RX data buffer RBDRn
         * refer to the RBCT config in nxp_xspi_do_op()
         */
        watermark = 32;
        watermark_bytes = watermark * 4;

        len = op->data.nbytes;

        while (len >= watermark_bytes) {
                /* Make sure the RX FIFO contains valid data before read */
                ret = readl_poll_timeout(base + XSPI_FR, reg,
                                      reg & XSPI_FR_RBDF, 0, POLL_TOUT_US);
                if (ret) {
                        WARN_ON(1);
                        return ret;
                }

                for (i = 0; i < watermark; i++)
                        *(u32 *)(buf + i * 4) = readl(base + XSPI_RBDR0 + i * 4);

                len = len - watermark_bytes;
                buf = buf + watermark_bytes;
                /* Pop up data to RXFIFO for next read. */
                reg = readl(base + XSPI_FR);
                reg |= XSPI_FR_RBDF;
                writel(reg, base + XSPI_FR);
        }

        /* Wait for the total data transfer finished */
        ret = readl_poll_timeout(base + XSPI_SR, reg, !(reg & XSPI_SR_BUSY), 0, POLL_TOUT_US);
        if (ret) {
                WARN_ON(1);
                return ret;
        }

        i = 0;
        while (len >= 4) {
                *(u32 *)(buf) = readl(base + XSPI_RBDR0 + i);
                i += 4;
                len -= 4;
                buf += 4;
        }

        if (len > 0) {
                reg = readl(base + XSPI_RBDR0 + i);
                memcpy(buf, (u8 *)&reg, len);
        }

        /* Invalid RXFIFO first */
        reg = readl(base + XSPI_MCR);
        reg |= XSPI_MCR_CLR_RXF;
        writel(reg, base + XSPI_MCR);
        /* Wait for the CLR_RXF clear */
        ret = readl_poll_timeout(base + XSPI_MCR, reg,
                              !(reg & XSPI_MCR_CLR_RXF), 1, POLL_TOUT_US);
        WARN_ON(ret);

        return ret;
}

static int nxp_xspi_do_op(struct nxp_xspi *xspi, const struct spi_mem_op *op)
{
        void __iomem *base = xspi->iobase;
        int watermark, err = 0;
        u32 reg, len;

        len = op->data.nbytes;
        if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) {
                /* Clear the TX FIFO. */
                reg = readl(base + XSPI_MCR);
                reg |= XSPI_MCR_CLR_TXF;
                writel(reg, base + XSPI_MCR);
                /* Wait for the CLR_TXF clear */
                err = readl_poll_timeout(base + XSPI_MCR, reg,
                                      !(reg & XSPI_MCR_CLR_TXF), 1, POLL_TOUT_US);
                if (err) {
                        WARN_ON(1);
                        return err;
                }

                /* Cover the no 4bytes alignment data length */
                watermark = (xspi->devtype_data->txfifo - ALIGN(op->data.nbytes, 4)) / 4 + 1;
                reg = FIELD_PREP(XSPI_TBCT_WMRK_MASK, watermark);
                writel(reg, base + XSPI_TBCT);
                /*
                 * According to the RM, for TBDR register, a write transaction on the
                 * flash memory with data size of less than 32 bits leads to the removal
                 * of one data entry from the TX buffer. The valid bits are used and the
                 * rest of the bits are discarded.
                 * But for data size large than 32 bits, according to test, for no 4bytes
                 * alignment data, the last 1~3 bytes will lost, because TX buffer use
                 * 4 bytes entries.
                 * So here adjust the transfer data length to make it 4bytes alignment.
                 * then will meet the upper watermark setting, trigger the 4bytes entries
                 * pop out.
                 * Will use extra 0xff to append, refer to nxp_xspi_fill_txfifo().
                 */
                if (len > 4)
                        len = ALIGN(op->data.nbytes, 4);

        } else if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN) {
                /* Invalid RXFIFO first */
                reg = readl(base + XSPI_MCR);
                reg |= XSPI_MCR_CLR_RXF;
                writel(reg, base + XSPI_MCR);
                /* Wait for the CLR_RXF clear */
                err = readl_poll_timeout(base + XSPI_MCR, reg,
                                      !(reg & XSPI_MCR_CLR_RXF), 1, POLL_TOUT_US);
                if (err) {
                        WARN_ON(1);
                        return err;
                }

                reg = FIELD_PREP(XSPI_RBCT_WMRK_MASK, 31);
                writel(reg, base + XSPI_RBCT);
        }

        init_completion(&xspi->c);

        /* Config the data address */
        writel(op->addr.val + xspi->memmap_phy, base + XSPI_SFP_TG_SFAR);

        /* Config the data size and lut id, trigger the transfer */
        reg = FIELD_PREP(XSPI_SFP_TG_IPCR_SEQID_MASK, XSPI_SEQID_LUT) |
              FIELD_PREP(XSPI_SFP_TG_IPCR_IDATSZ_MASK, len);
        writel(reg, base + XSPI_SFP_TG_IPCR);

        if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) {
                err = nxp_xspi_fill_txfifo(xspi, op);
                if (err)
                        return err;
        }

        /* Wait for the interrupt. */
        if (!wait_for_completion_timeout(&xspi->c, msecs_to_jiffies(1000)))
                err = -ETIMEDOUT;

        /* Invoke IP data read. */
        if (!err && op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN)
                err = nxp_xspi_read_rxfifo(xspi, op);

        return err;
}

static int nxp_xspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
        struct nxp_xspi *xspi = spi_controller_get_devdata(mem->spi->controller);
        void __iomem *base = xspi->iobase;
        u32 reg;
        int err;

        guard(mutex)(&xspi->lock);

        PM_RUNTIME_ACQUIRE_AUTOSUSPEND(xspi->dev, pm);
        err = PM_RUNTIME_ACQUIRE_ERR(&pm);
        if (err)
                return err;

        /* Wait for controller being ready. */
        err = readl_poll_timeout(base + XSPI_SR, reg,
                              !(reg & XSPI_SR_BUSY), 1, POLL_TOUT_US);
        if (err) {
                dev_err(xspi->dev, "SR keeps in BUSY!");
                return err;
        }

        nxp_xspi_select_mem(xspi, mem->spi, op);

        nxp_xspi_prepare_lut(xspi, op);

        /*
         * For read:
         *     the address in AHB mapped range will use AHB read.
         *     the address out of AHB mapped range will use IP read.
         * For write:
         *     all use IP write.
         */
        if ((op->data.dir == SPI_MEM_DATA_IN) && !needs_ip_only(xspi)
                && ((op->addr.val + op->data.nbytes) <= xspi->memmap_phy_size))
                err = nxp_xspi_ahb_read(xspi, op);
        else
                err = nxp_xspi_do_op(xspi, op);

        nxp_xspi_sw_reset(xspi);

        return err;
}

static int nxp_xspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
        struct nxp_xspi *xspi = spi_controller_get_devdata(mem->spi->controller);

        if (op->data.dir == SPI_MEM_DATA_OUT) {
                if (op->data.nbytes > xspi->devtype_data->txfifo)
                        op->data.nbytes = xspi->devtype_data->txfifo;
        } else {
                /* Limit data bytes to RX FIFO in case of IP read only */
                if (needs_ip_only(xspi) && (op->data.nbytes > xspi->devtype_data->rxfifo))
                        op->data.nbytes = xspi->devtype_data->rxfifo;

                /* Address in AHB mapped range prefer to use AHB read. */
                if (!needs_ip_only(xspi) && (op->addr.val < xspi->memmap_phy_size)
                        && ((op->addr.val + op->data.nbytes) > xspi->memmap_phy_size))
                        op->data.nbytes = xspi->memmap_phy_size - op->addr.val;
        }

        return 0;
}

static void nxp_xspi_config_ahb_buffer(struct nxp_xspi *xspi)
{
        void __iomem *base = xspi->iobase;
        u32 ahb_data_trans_size;
        u32 reg;

        writel(0xA, base + XSPI_BUF0CR);
        writel(0x2, base + XSPI_BUF1CR);
        writel(0xD, base + XSPI_BUF2CR);

        /* Configure buffer3 for All Master Access */
        reg = FIELD_PREP(XSPI_BUF3CR_MSTRID_MASK, 0x06) |
              XSPI_BUF3CR_ALLMST;

        ahb_data_trans_size = xspi->devtype_data->ahb_buf_size / 8;
        reg |= FIELD_PREP(XSPI_BUF3CR_ADATSZ_MASK, ahb_data_trans_size);
        writel(reg, base + XSPI_BUF3CR);

        /* Only the buffer3 is used */
        writel(0, base + XSPI_BUF0IND);
        writel(0, base + XSPI_BUF1IND);
        writel(0, base + XSPI_BUF2IND);

        /* AHB only use ID=15 for read */
        reg = FIELD_PREP(XSPI_BFGENCR_SEQID_MASK, XSPI_SEQID_LUT);
        reg |= XSPI_BFGENCR_WR_FLUSH_EN;
        /* No limit for align */
        reg |= FIELD_PREP(XSPI_BFGENCR_ALIGN_MASK, 0);
        writel(reg, base + XSPI_BFGENCR);
}

static int nxp_xspi_default_setup(struct nxp_xspi *xspi)
{
        void __iomem *base = xspi->iobase;
        u32 reg;

        /* Bypass SFP check, clear MGC_GVLD, MGC_GVLDMDAD, MGC_GVLDFRAD */
        writel(0, base + XSPI_MGC);

        /* Enable the EENV0 SFP check */
        reg = readl(base + XSPI_TG0MDAD);
        reg |= XSPI_TG0MDAD_VLD;
        writel(reg, base + XSPI_TG0MDAD);

        /* Give read/write access right to EENV0 */
        reg = readl(base + XSPI_FRAD0_WORD2);
        reg &= ~XSPI_FRAD0_WORD2_MD0ACP_MASK;
        reg |= FIELD_PREP(XSPI_FRAD0_WORD2_MD0ACP_MASK, 0x03);
        writel(reg, base + XSPI_FRAD0_WORD2);

        /* Enable the FRAD check for EENV0 */
        reg = readl(base + XSPI_FRAD0_WORD3);
        reg |= XSPI_FRAD0_WORD3_VLD;
        writel(reg, base + XSPI_FRAD0_WORD3);

        /*
         * Config the timeout to max value, this timeout will affect the
         * TBDR and RBDRn access right after IP cmd triggered.
         */
        writel(0xFFFFFFFF, base + XSPI_MTO);

        /* Disable module */
        reg = readl(base + XSPI_MCR);
        reg |= XSPI_MCR_MDIS;
        writel(reg, base + XSPI_MCR);

        nxp_xspi_sw_reset(xspi);

        reg = readl(base + XSPI_MCR);
        reg &= ~(XSPI_MCR_CKN_FA_EN | XSPI_MCR_DQS_FA_SEL_MASK |
                 XSPI_MCR_DOZE | XSPI_MCR_VAR_LAT_EN |
                 XSPI_MCR_DDR_EN | XSPI_MCR_DQS_OUT_EN);
        reg |= XSPI_MCR_DQS_EN;
        reg |= XSPI_MCR_ISD3FA | XSPI_MCR_ISD2FA;
        writel(reg, base + XSPI_MCR);

        reg = readl(base + XSPI_SFACR);
        reg &= ~(XSPI_SFACR_FORCE_A10 | XSPI_SFACR_WA_4B_EN |
                 XSPI_SFACR_BYTE_SWAP | XSPI_SFACR_WA |
                 XSPI_SFACR_CAS_MASK);
        reg |= XSPI_SFACR_FORCE_A10;
        writel(reg, base + XSPI_SFACR);

        nxp_xspi_config_ahb_buffer(xspi);

        reg = FIELD_PREP(XSPI_FLSHCR_TCSH_MASK, 0x03) |
              FIELD_PREP(XSPI_FLSHCR_TCSS_MASK, 0x03);
        writel(reg, base + XSPI_FLSHCR);

        /* Enable module */
        reg = readl(base + XSPI_MCR);
        reg &= ~XSPI_MCR_MDIS;
        writel(reg, base + XSPI_MCR);

        xspi->selected = -1;

        /* Enable the interrupt */
        writel(XSPI_RSER_TFIE, base + XSPI_RSER);

        return 0;
}

static const char *nxp_xspi_get_name(struct spi_mem *mem)
{
        struct nxp_xspi *xspi = spi_controller_get_devdata(mem->spi->controller);
        struct device *dev = &mem->spi->dev;
        const char *name;

        /* Set custom name derived from the platform_device of the controller. */
        if (of_get_available_child_count(xspi->dev->of_node) == 1)
                return dev_name(xspi->dev);

        name = devm_kasprintf(dev, GFP_KERNEL,
                              "%s-%d", dev_name(xspi->dev),
                              spi_get_chipselect(mem->spi, 0));

        if (!name) {
                dev_err(dev, "failed to get memory for custom flash name\n");
                return ERR_PTR(-ENOMEM);
        }

        return name;
}

static const struct spi_controller_mem_ops nxp_xspi_mem_ops = {
        .adjust_op_size = nxp_xspi_adjust_op_size,
        .supports_op = nxp_xspi_supports_op,
        .exec_op = nxp_xspi_exec_op,
        .get_name = nxp_xspi_get_name,
};

static const struct spi_controller_mem_caps nxp_xspi_mem_caps = {
        .dtr = true,
        .per_op_freq = true,
        .swap16 = true,
};

static void nxp_xspi_cleanup(void *data)
{
        struct nxp_xspi *xspi = data;
        u32 reg;

        pm_runtime_get_sync(xspi->dev);

        /* Disable interrupt */
        writel(0, xspi->iobase + XSPI_RSER);
        /* Clear all the internal logic flags */
        writel(0xFFFFFFFF, xspi->iobase + XSPI_FR);
        /* Disable the hardware */
        reg = readl(xspi->iobase + XSPI_MCR);
        reg |= XSPI_MCR_MDIS;
        writel(reg, xspi->iobase + XSPI_MCR);

        pm_runtime_put_sync(xspi->dev);

        if (xspi->ahb_addr)
                iounmap(xspi->ahb_addr);
}

static int nxp_xspi_probe(struct platform_device *pdev)
{
        struct device *dev = &pdev->dev;
        struct spi_controller *ctlr;
        struct nxp_xspi *xspi;
        struct resource *res;
        int ret, irq;

        ctlr = devm_spi_alloc_host(dev, sizeof(*xspi));
        if (!ctlr)
                return -ENOMEM;

        ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL |
                          SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL;

        xspi = spi_controller_get_devdata(ctlr);
        xspi->dev = dev;
        xspi->devtype_data = device_get_match_data(dev);
        if (!xspi->devtype_data)
                return -ENODEV;

        platform_set_drvdata(pdev, xspi);

        /* Find the resources - configuration register address space */
        xspi->iobase = devm_platform_ioremap_resource_byname(pdev, "base");
        if (IS_ERR(xspi->iobase))
                return PTR_ERR(xspi->iobase);

        /* Find the resources - controller memory mapped space */
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mmap");
        if (!res)
                return -ENODEV;

        /* Assign memory mapped starting address and mapped size. */
        xspi->memmap_phy = res->start;
        xspi->memmap_phy_size = resource_size(res);

        /* Find the clocks */
        xspi->clk = devm_clk_get(dev, "per");
        if (IS_ERR(xspi->clk))
                return PTR_ERR(xspi->clk);

        /* Find the irq */
        irq = platform_get_irq(pdev, 0);
        if (irq < 0)
                return dev_err_probe(dev, irq,  "Failed to get irq source");

        pm_runtime_set_autosuspend_delay(dev, XSPI_RPM_TIMEOUT_MS);
        pm_runtime_use_autosuspend(dev);
        ret = devm_pm_runtime_enable(dev);
        if (ret)
                return ret;

        PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
        ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
        if (ret)
                return dev_err_probe(dev, ret, "Failed to enable clock");

        /* Clear potential interrupt by write xspi errstat */
        writel(0xFFFFFFFF, xspi->iobase + XSPI_ERRSTAT);
        writel(0xFFFFFFFF, xspi->iobase + XSPI_FR);

        nxp_xspi_default_setup(xspi);

        ret = devm_request_irq(dev, irq,
                        nxp_xspi_irq_handler, 0, pdev->name, xspi);
        if (ret)
                return dev_err_probe(dev, ret, "failed to request irq");

        ret = devm_mutex_init(dev, &xspi->lock);
        if (ret)
                return ret;

        ret = devm_add_action_or_reset(dev, nxp_xspi_cleanup, xspi);
        if (ret)
                return ret;

        ctlr->bus_num = -1;
        ctlr->num_chipselect = NXP_XSPI_MAX_CHIPSELECT;
        ctlr->mem_ops = &nxp_xspi_mem_ops;
        ctlr->mem_caps = &nxp_xspi_mem_caps;

        return devm_spi_register_controller(dev, ctlr);
}

static int nxp_xspi_runtime_suspend(struct device *dev)
{
        struct nxp_xspi *xspi = dev_get_drvdata(dev);
        u32 reg;

        reg = readl(xspi->iobase + XSPI_MCR);
        reg |= XSPI_MCR_MDIS;
        writel(reg, xspi->iobase + XSPI_MCR);

        clk_disable_unprepare(xspi->clk);

        return 0;
}

static int nxp_xspi_runtime_resume(struct device *dev)
{
        struct nxp_xspi *xspi = dev_get_drvdata(dev);
        u32 reg;
        int ret;

        ret = clk_prepare_enable(xspi->clk);
        if (ret)
                return ret;

        reg = readl(xspi->iobase + XSPI_MCR);
        reg &= ~XSPI_MCR_MDIS;
        writel(reg, xspi->iobase + XSPI_MCR);

        return 0;
}

static int nxp_xspi_suspend(struct device *dev)
{
        int ret;

        ret = pinctrl_pm_select_sleep_state(dev);
        if (ret) {
                dev_err(dev, "select flexspi sleep pinctrl failed!\n");
                return ret;
        }

        return pm_runtime_force_suspend(dev);
}

static int nxp_xspi_resume(struct device *dev)
{
        struct nxp_xspi *xspi = dev_get_drvdata(dev);
        int ret;

        ret = pm_runtime_force_resume(dev);
        if (ret)
                return ret;

        nxp_xspi_default_setup(xspi);

        ret = pinctrl_pm_select_default_state(dev);
        if (ret)
                dev_err(dev, "select flexspi default pinctrl failed!\n");

        return ret;
}


static const struct dev_pm_ops nxp_xspi_pm_ops = {
        RUNTIME_PM_OPS(nxp_xspi_runtime_suspend, nxp_xspi_runtime_resume, NULL)
        SYSTEM_SLEEP_PM_OPS(nxp_xspi_suspend, nxp_xspi_resume)
};

static const struct of_device_id nxp_xspi_dt_ids[] = {
        { .compatible = "nxp,imx94-xspi", .data = (void *)&imx94_data, },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, nxp_xspi_dt_ids);

static struct platform_driver nxp_xspi_driver = {
        .driver = {
                .name   = "nxp-xspi",
                .of_match_table = nxp_xspi_dt_ids,
                .pm =   pm_ptr(&nxp_xspi_pm_ops),
        },
        .probe          = nxp_xspi_probe,
};
module_platform_driver(nxp_xspi_driver);

MODULE_DESCRIPTION("NXP xSPI Controller Driver");
MODULE_AUTHOR("NXP Semiconductor");
MODULE_AUTHOR("Haibo Chen <haibo.chen@nxp.com>");
MODULE_LICENSE("GPL");