root/drivers/spi/spi-sn-f-ospi.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Socionext SPI flash controller F_OSPI driver
 * Copyright (C) 2021 Socionext Inc.
 */

#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>

/* Registers */
#define OSPI_PROT_CTL_INDIR                     0x00
#define   OSPI_PROT_MODE_DATA_MASK              GENMASK(31, 30)
#define   OSPI_PROT_MODE_ALT_MASK               GENMASK(29, 28)
#define   OSPI_PROT_MODE_ADDR_MASK              GENMASK(27, 26)
#define   OSPI_PROT_MODE_CODE_MASK              GENMASK(25, 24)
#define     OSPI_PROT_MODE_SINGLE               0
#define     OSPI_PROT_MODE_DUAL                 1
#define     OSPI_PROT_MODE_QUAD                 2
#define     OSPI_PROT_MODE_OCTAL                3
#define   OSPI_PROT_DATA_RATE_DATA              BIT(23)
#define   OSPI_PROT_DATA_RATE_ALT               BIT(22)
#define   OSPI_PROT_DATA_RATE_ADDR              BIT(21)
#define   OSPI_PROT_DATA_RATE_CODE              BIT(20)
#define     OSPI_PROT_SDR                       0
#define     OSPI_PROT_DDR                       1
#define   OSPI_PROT_BIT_POS_DATA                BIT(19)
#define   OSPI_PROT_BIT_POS_ALT                 BIT(18)
#define   OSPI_PROT_BIT_POS_ADDR                BIT(17)
#define   OSPI_PROT_BIT_POS_CODE                BIT(16)
#define   OSPI_PROT_SAMP_EDGE                   BIT(12)
#define   OSPI_PROT_DATA_UNIT_MASK              GENMASK(11, 10)
#define     OSPI_PROT_DATA_UNIT_1B              0
#define     OSPI_PROT_DATA_UNIT_2B              1
#define     OSPI_PROT_DATA_UNIT_4B              3
#define   OSPI_PROT_TRANS_DIR_WRITE             BIT(9)
#define   OSPI_PROT_DATA_EN                     BIT(8)
#define   OSPI_PROT_ALT_SIZE_MASK               GENMASK(7, 5)
#define   OSPI_PROT_ADDR_SIZE_MASK              GENMASK(4, 2)
#define   OSPI_PROT_CODE_SIZE_MASK              GENMASK(1, 0)

#define OSPI_CLK_CTL                            0x10
#define   OSPI_CLK_CTL_BOOT_INT_CLK_EN          BIT(16)
#define   OSPI_CLK_CTL_PHA                      BIT(12)
#define     OSPI_CLK_CTL_PHA_180                0
#define     OSPI_CLK_CTL_PHA_90                 1
#define   OSPI_CLK_CTL_DIV                      GENMASK(9, 8)
#define     OSPI_CLK_CTL_DIV_1                  0
#define     OSPI_CLK_CTL_DIV_2                  1
#define     OSPI_CLK_CTL_DIV_4                  2
#define     OSPI_CLK_CTL_DIV_8                  3
#define   OSPI_CLK_CTL_INT_CLK_EN               BIT(0)

#define OSPI_CS_CTL1                            0x14
#define OSPI_CS_CTL2                            0x18
#define OSPI_SSEL                               0x20
#define OSPI_CMD_IDX_INDIR                      0x40
#define OSPI_ADDR                               0x50
#define OSPI_ALT_INDIR                          0x60
#define OSPI_DMY_INDIR                          0x70
#define OSPI_DAT                                0x80
#define OSPI_DAT_SWP_INDIR                      0x90

#define OSPI_DAT_SIZE_INDIR                     0xA0
#define   OSPI_DAT_SIZE_EN                      BIT(15)
#define   OSPI_DAT_SIZE_MASK                    GENMASK(10, 0)
#define   OSPI_DAT_SIZE_MAX                     (OSPI_DAT_SIZE_MASK + 1)

#define OSPI_TRANS_CTL                          0xC0
#define   OSPI_TRANS_CTL_STOP_REQ               BIT(1)  /* RW1AC */
#define   OSPI_TRANS_CTL_START_REQ              BIT(0)  /* RW1AC */

#define OSPI_ACC_MODE                           0xC4
#define   OSPI_ACC_MODE_BOOT_DISABLE            BIT(0)

#define OSPI_SWRST                              0xD0
#define   OSPI_SWRST_INDIR_WRITE_FIFO           BIT(9)  /* RW1AC */
#define   OSPI_SWRST_INDIR_READ_FIFO            BIT(8)  /* RW1AC */

#define OSPI_STAT                               0xE0
#define   OSPI_STAT_IS_AXI_WRITING              BIT(10)
#define   OSPI_STAT_IS_AXI_READING              BIT(9)
#define   OSPI_STAT_IS_SPI_INT_CLK_STOP         BIT(4)
#define   OSPI_STAT_IS_SPI_IDLE                 BIT(3)

#define OSPI_IRQ                                0xF0
#define   OSPI_IRQ_CS_DEASSERT                  BIT(8)
#define   OSPI_IRQ_WRITE_BUF_READY              BIT(2)
#define   OSPI_IRQ_READ_BUF_READY               BIT(1)
#define   OSPI_IRQ_CS_TRANS_COMP                BIT(0)
#define   OSPI_IRQ_ALL                          \
                (OSPI_IRQ_CS_DEASSERT | OSPI_IRQ_WRITE_BUF_READY \
                 | OSPI_IRQ_READ_BUF_READY | OSPI_IRQ_CS_TRANS_COMP)

#define OSPI_IRQ_STAT_EN                        0xF4
#define OSPI_IRQ_SIG_EN                         0xF8

/* Parameters */
#define OSPI_NUM_CS                             4
#define OSPI_DUMMY_CYCLE_MAX                    255
#define OSPI_WAIT_MAX_MSEC                      100

struct f_ospi {
        void __iomem *base;
        struct device *dev;
        struct clk *clk;
        struct mutex mlock;
};

static u32 f_ospi_get_dummy_cycle(const struct spi_mem_op *op)
{
        if (!op->dummy.nbytes)
                return 0;

        return (op->dummy.nbytes * 8) / op->dummy.buswidth;
}

static void f_ospi_clear_irq(struct f_ospi *ospi)
{
        writel(OSPI_IRQ_CS_DEASSERT | OSPI_IRQ_CS_TRANS_COMP,
               ospi->base + OSPI_IRQ);
}

static void f_ospi_enable_irq_status(struct f_ospi *ospi, u32 irq_bits)
{
        u32 val;

        val = readl(ospi->base + OSPI_IRQ_STAT_EN);
        val |= irq_bits;
        writel(val, ospi->base + OSPI_IRQ_STAT_EN);
}

static void f_ospi_disable_irq_status(struct f_ospi *ospi, u32 irq_bits)
{
        u32 val;

        val = readl(ospi->base + OSPI_IRQ_STAT_EN);
        val &= ~irq_bits;
        writel(val, ospi->base + OSPI_IRQ_STAT_EN);
}

static void f_ospi_disable_irq_output(struct f_ospi *ospi, u32 irq_bits)
{
        u32 val;

        val = readl(ospi->base + OSPI_IRQ_SIG_EN);
        val &= ~irq_bits;
        writel(val, ospi->base + OSPI_IRQ_SIG_EN);
}

static int f_ospi_prepare_config(struct f_ospi *ospi)
{
        u32 val, stat0, stat1;

        /* G4: Disable internal clock */
        val = readl(ospi->base + OSPI_CLK_CTL);
        val &= ~(OSPI_CLK_CTL_BOOT_INT_CLK_EN | OSPI_CLK_CTL_INT_CLK_EN);
        writel(val, ospi->base + OSPI_CLK_CTL);

        /* G5: Wait for stop */
        stat0 = OSPI_STAT_IS_AXI_WRITING | OSPI_STAT_IS_AXI_READING;
        stat1 = OSPI_STAT_IS_SPI_IDLE | OSPI_STAT_IS_SPI_INT_CLK_STOP;

        return readl_poll_timeout(ospi->base + OSPI_STAT,
                                  val, (val & (stat0 | stat1)) == stat1,
                                  0, OSPI_WAIT_MAX_MSEC);
}

static int f_ospi_unprepare_config(struct f_ospi *ospi)
{
        u32 val;

        /* G11: Enable internal clock */
        val = readl(ospi->base + OSPI_CLK_CTL);
        val |= OSPI_CLK_CTL_BOOT_INT_CLK_EN | OSPI_CLK_CTL_INT_CLK_EN;
        writel(val, ospi->base + OSPI_CLK_CTL);

        /* G12: Wait for clock to start */
        return readl_poll_timeout(ospi->base + OSPI_STAT,
                                  val, !(val & OSPI_STAT_IS_SPI_INT_CLK_STOP),
                                  0, OSPI_WAIT_MAX_MSEC);
}

static void f_ospi_config_clk(struct f_ospi *ospi, u32 device_hz)
{
        long rate_hz = clk_get_rate(ospi->clk);
        u32 div = DIV_ROUND_UP(rate_hz, device_hz);
        u32 div_reg;
        u32 val;

        if (rate_hz < device_hz) {
                dev_warn(ospi->dev, "Device frequency too large: %d\n",
                         device_hz);
                div_reg = OSPI_CLK_CTL_DIV_1;
        } else {
                if (div == 1) {
                        div_reg = OSPI_CLK_CTL_DIV_1;
                } else if (div == 2) {
                        div_reg = OSPI_CLK_CTL_DIV_2;
                } else if (div <= 4) {
                        div_reg = OSPI_CLK_CTL_DIV_4;
                } else if (div <= 8) {
                        div_reg = OSPI_CLK_CTL_DIV_8;
                } else {
                        dev_warn(ospi->dev, "Device frequency too small: %d\n",
                                 device_hz);
                        div_reg = OSPI_CLK_CTL_DIV_8;
                }
        }

        /*
         * G7: Set clock mode
         * clock phase is fixed at 180 degrees and configure edge direction
         * instead.
         */
        val = readl(ospi->base + OSPI_CLK_CTL);

        val &= ~(OSPI_CLK_CTL_PHA | OSPI_CLK_CTL_DIV);
        val |= FIELD_PREP(OSPI_CLK_CTL_PHA, OSPI_CLK_CTL_PHA_180)
             | FIELD_PREP(OSPI_CLK_CTL_DIV, div_reg);

        writel(val, ospi->base + OSPI_CLK_CTL);
}

static void f_ospi_config_dll(struct f_ospi *ospi)
{
        /* G8: Configure DLL, nothing */
}

static u8 f_ospi_get_mode(struct f_ospi *ospi, int width, int data_size)
{
        u8 mode = OSPI_PROT_MODE_SINGLE;

        switch (width) {
        case 1:
                mode = OSPI_PROT_MODE_SINGLE;
                break;
        case 2:
                mode = OSPI_PROT_MODE_DUAL;
                break;
        case 4:
                mode = OSPI_PROT_MODE_QUAD;
                break;
        case 8:
                mode = OSPI_PROT_MODE_OCTAL;
                break;
        default:
                if (data_size)
                        dev_err(ospi->dev, "Invalid buswidth: %d\n", width);
                break;
        }

        return mode;
}

static void f_ospi_config_indir_protocol(struct f_ospi *ospi,
                                         struct spi_mem *mem,
                                         const struct spi_mem_op *op)
{
        struct spi_device *spi = mem->spi;
        u8 mode;
        u32 prot = 0, val;
        int unit;

        /* Set one chip select */
        writel(BIT(spi_get_chipselect(spi, 0)), ospi->base + OSPI_SSEL);

        mode = f_ospi_get_mode(ospi, op->cmd.buswidth, 1);
        prot |= FIELD_PREP(OSPI_PROT_MODE_CODE_MASK, mode);

        mode = f_ospi_get_mode(ospi, op->addr.buswidth, op->addr.nbytes);
        prot |= FIELD_PREP(OSPI_PROT_MODE_ADDR_MASK, mode);

        mode = f_ospi_get_mode(ospi, op->data.buswidth, op->data.nbytes);
        prot |= FIELD_PREP(OSPI_PROT_MODE_DATA_MASK, mode);

        prot |= FIELD_PREP(OSPI_PROT_DATA_RATE_DATA, OSPI_PROT_SDR);
        prot |= FIELD_PREP(OSPI_PROT_DATA_RATE_ALT,  OSPI_PROT_SDR);
        prot |= FIELD_PREP(OSPI_PROT_DATA_RATE_ADDR, OSPI_PROT_SDR);
        prot |= FIELD_PREP(OSPI_PROT_DATA_RATE_CODE, OSPI_PROT_SDR);

        if (spi->mode & SPI_LSB_FIRST)
                prot |= OSPI_PROT_BIT_POS_DATA | OSPI_PROT_BIT_POS_ALT
                      | OSPI_PROT_BIT_POS_ADDR | OSPI_PROT_BIT_POS_CODE;

        if (spi->mode & SPI_CPHA)
                prot |= OSPI_PROT_SAMP_EDGE;

        /* Examine nbytes % 4 */
        switch (op->data.nbytes & 0x3) {
        case 0:
                unit = OSPI_PROT_DATA_UNIT_4B;
                val = 0;
                break;
        case 2:
                unit = OSPI_PROT_DATA_UNIT_2B;
                val = OSPI_DAT_SIZE_EN | (op->data.nbytes - 1);
                break;
        default:
                unit = OSPI_PROT_DATA_UNIT_1B;
                val = OSPI_DAT_SIZE_EN | (op->data.nbytes - 1);
                break;
        }
        prot |= FIELD_PREP(OSPI_PROT_DATA_UNIT_MASK, unit);

        switch (op->data.dir) {
        case SPI_MEM_DATA_IN:
                prot |= OSPI_PROT_DATA_EN;
                break;

        case SPI_MEM_DATA_OUT:
                prot |= OSPI_PROT_TRANS_DIR_WRITE | OSPI_PROT_DATA_EN;
                break;

        case SPI_MEM_NO_DATA:
                prot |= OSPI_PROT_TRANS_DIR_WRITE;
                break;

        default:
                dev_warn(ospi->dev, "Unsupported direction");
                break;
        }

        prot |= FIELD_PREP(OSPI_PROT_ADDR_SIZE_MASK, op->addr.nbytes);
        prot |= FIELD_PREP(OSPI_PROT_CODE_SIZE_MASK, 1);        /* 1byte */

        writel(prot, ospi->base + OSPI_PROT_CTL_INDIR);
        writel(val, ospi->base + OSPI_DAT_SIZE_INDIR);
}

static int f_ospi_indir_prepare_op(struct f_ospi *ospi, struct spi_mem *mem,
                                   const struct spi_mem_op *op)
{
        u32 irq_stat_en;
        int ret;

        ret = f_ospi_prepare_config(ospi);
        if (ret)
                return ret;

        f_ospi_config_clk(ospi, op->max_freq);

        f_ospi_config_indir_protocol(ospi, mem, op);

        writel(f_ospi_get_dummy_cycle(op), ospi->base + OSPI_DMY_INDIR);
        writel(op->addr.val, ospi->base + OSPI_ADDR);
        writel(op->cmd.opcode, ospi->base + OSPI_CMD_IDX_INDIR);

        f_ospi_clear_irq(ospi);

        switch (op->data.dir) {
        case SPI_MEM_DATA_IN:
                irq_stat_en = OSPI_IRQ_READ_BUF_READY | OSPI_IRQ_CS_TRANS_COMP;
                break;

        case SPI_MEM_DATA_OUT:
                irq_stat_en = OSPI_IRQ_WRITE_BUF_READY | OSPI_IRQ_CS_TRANS_COMP;
                break;

        case SPI_MEM_NO_DATA:
                irq_stat_en = OSPI_IRQ_CS_TRANS_COMP;
                break;

        default:
                dev_warn(ospi->dev, "Unsupported direction");
                irq_stat_en = 0;
        }

        f_ospi_disable_irq_status(ospi, ~irq_stat_en);
        f_ospi_enable_irq_status(ospi, irq_stat_en);

        return f_ospi_unprepare_config(ospi);
}

static void f_ospi_indir_start_xfer(struct f_ospi *ospi)
{
        /* Write only 1, auto cleared */
        writel(OSPI_TRANS_CTL_START_REQ, ospi->base + OSPI_TRANS_CTL);
}

static void f_ospi_indir_stop_xfer(struct f_ospi *ospi)
{
        /* Write only 1, auto cleared */
        writel(OSPI_TRANS_CTL_STOP_REQ, ospi->base + OSPI_TRANS_CTL);
}

static int f_ospi_indir_wait_xfer_complete(struct f_ospi *ospi)
{
        u32 val;

        return readl_poll_timeout(ospi->base + OSPI_IRQ, val,
                                  val & OSPI_IRQ_CS_TRANS_COMP,
                                  0, OSPI_WAIT_MAX_MSEC);
}

static int f_ospi_indir_read(struct f_ospi *ospi, struct spi_mem *mem,
                             const struct spi_mem_op *op)
{
        u8 *buf = op->data.buf.in;
        u32 val;
        int i, ret;

        mutex_lock(&ospi->mlock);

        /* E1-2: Prepare transfer operation */
        ret = f_ospi_indir_prepare_op(ospi, mem, op);
        if (ret)
                goto out;

        f_ospi_indir_start_xfer(ospi);

        /* E3-4: Wait for ready and read data */
        for (i = 0; i < op->data.nbytes; i++) {
                ret = readl_poll_timeout(ospi->base + OSPI_IRQ, val,
                                         val & OSPI_IRQ_READ_BUF_READY,
                                         0, OSPI_WAIT_MAX_MSEC);
                if (ret)
                        goto out;

                buf[i] = readl(ospi->base + OSPI_DAT) & 0xFF;
        }

        /* E5-6: Stop transfer if data size is nothing */
        if (!(readl(ospi->base + OSPI_DAT_SIZE_INDIR) & OSPI_DAT_SIZE_EN))
                f_ospi_indir_stop_xfer(ospi);

        /* E7-8: Wait for completion and clear */
        ret = f_ospi_indir_wait_xfer_complete(ospi);
        if (ret)
                goto out;

        writel(OSPI_IRQ_CS_TRANS_COMP, ospi->base + OSPI_IRQ);

        /* E9: Do nothing if data size is valid */
        if (readl(ospi->base + OSPI_DAT_SIZE_INDIR) & OSPI_DAT_SIZE_EN)
                goto out;

        /* E10-11: Reset and check read fifo */
        writel(OSPI_SWRST_INDIR_READ_FIFO, ospi->base + OSPI_SWRST);

        ret = readl_poll_timeout(ospi->base + OSPI_SWRST, val,
                                 !(val & OSPI_SWRST_INDIR_READ_FIFO),
                                 0, OSPI_WAIT_MAX_MSEC);
out:
        mutex_unlock(&ospi->mlock);

        return ret;
}

static int f_ospi_indir_write(struct f_ospi *ospi, struct spi_mem *mem,
                              const struct spi_mem_op *op)
{
        u8 *buf = (u8 *)op->data.buf.out;
        u32 val;
        int i, ret;

        mutex_lock(&ospi->mlock);

        /* F1-3: Prepare transfer operation */
        ret = f_ospi_indir_prepare_op(ospi, mem, op);
        if (ret)
                goto out;

        f_ospi_indir_start_xfer(ospi);

        if (!(readl(ospi->base + OSPI_PROT_CTL_INDIR) & OSPI_PROT_DATA_EN))
                goto nodata;

        /* F4-5: Wait for buffer ready and write data */
        for (i = 0; i < op->data.nbytes; i++) {
                ret = readl_poll_timeout(ospi->base + OSPI_IRQ, val,
                                         val & OSPI_IRQ_WRITE_BUF_READY,
                                         0, OSPI_WAIT_MAX_MSEC);
                if (ret)
                        goto out;

                writel(buf[i], ospi->base + OSPI_DAT);
        }

        /* F6-7: Stop transfer if data size is nothing */
        if (!(readl(ospi->base + OSPI_DAT_SIZE_INDIR) & OSPI_DAT_SIZE_EN))
                f_ospi_indir_stop_xfer(ospi);

nodata:
        /* F8-9: Wait for completion and clear */
        ret = f_ospi_indir_wait_xfer_complete(ospi);
        if (ret)
                goto out;

        writel(OSPI_IRQ_CS_TRANS_COMP, ospi->base + OSPI_IRQ);
out:
        mutex_unlock(&ospi->mlock);

        return ret;
}

static int f_ospi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
        struct f_ospi *ospi = spi_controller_get_devdata(mem->spi->controller);
        int err = 0;

        switch (op->data.dir) {
        case SPI_MEM_DATA_IN:
                err = f_ospi_indir_read(ospi, mem, op);
                break;

        case SPI_MEM_DATA_OUT:
                fallthrough;
        case SPI_MEM_NO_DATA:
                err = f_ospi_indir_write(ospi, mem, op);
                break;

        default:
                dev_warn(ospi->dev, "Unsupported direction");
                err = -EOPNOTSUPP;
        }

        return err;
}

static bool f_ospi_supports_op_width(struct spi_mem *mem,
                                     const struct spi_mem_op *op)
{
        static const u8 width_available[] = { 0, 1, 2, 4, 8 };
        u8 width_op[] = { op->cmd.buswidth, op->addr.buswidth,
                          op->dummy.buswidth, op->data.buswidth };
        bool is_match_found;
        int i, j;

        for (i = 0; i < ARRAY_SIZE(width_op); i++) {
                is_match_found = false;

                for (j = 0; j < ARRAY_SIZE(width_available); j++) {
                        if (width_op[i] == width_available[j]) {
                                is_match_found = true;
                                break;
                        }
                }

                if (!is_match_found)
                        return false;
        }

        return true;
}

static bool f_ospi_supports_op(struct spi_mem *mem,
                               const struct spi_mem_op *op)
{
        if (f_ospi_get_dummy_cycle(op) > OSPI_DUMMY_CYCLE_MAX)
                return false;

        if (op->addr.nbytes > 4)
                return false;

        if (!f_ospi_supports_op_width(mem, op))
                return false;

        return spi_mem_default_supports_op(mem, op);
}

static int f_ospi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
        op->data.nbytes = min_t(int, op->data.nbytes, OSPI_DAT_SIZE_MAX);

        return 0;
}

static const struct spi_controller_mem_ops f_ospi_mem_ops = {
        .adjust_op_size = f_ospi_adjust_op_size,
        .supports_op = f_ospi_supports_op,
        .exec_op = f_ospi_exec_op,
};

static const struct spi_controller_mem_caps f_ospi_mem_caps = {
        .per_op_freq = true,
};

static int f_ospi_init(struct f_ospi *ospi)
{
        int ret;

        ret = f_ospi_prepare_config(ospi);
        if (ret)
                return ret;

        /* Disable boot signal */
        writel(OSPI_ACC_MODE_BOOT_DISABLE, ospi->base + OSPI_ACC_MODE);

        f_ospi_config_dll(ospi);

        /* Disable IRQ */
        f_ospi_clear_irq(ospi);
        f_ospi_disable_irq_status(ospi, OSPI_IRQ_ALL);
        f_ospi_disable_irq_output(ospi, OSPI_IRQ_ALL);

        return f_ospi_unprepare_config(ospi);
}

static int f_ospi_probe(struct platform_device *pdev)
{
        struct spi_controller *ctlr;
        struct device *dev = &pdev->dev;
        struct f_ospi *ospi;
        u32 num_cs = OSPI_NUM_CS;
        int ret;

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

        ctlr->mode_bits = SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL
                | SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL
                | SPI_MODE_0 | SPI_MODE_1 | SPI_LSB_FIRST;
        ctlr->mem_ops = &f_ospi_mem_ops;
        ctlr->mem_caps = &f_ospi_mem_caps;
        ctlr->bus_num = -1;
        of_property_read_u32(dev->of_node, "num-cs", &num_cs);
        if (num_cs > OSPI_NUM_CS) {
                dev_err(dev, "num-cs too large: %d\n", num_cs);
                return -ENOMEM;
        }
        ctlr->num_chipselect = num_cs;

        ospi = spi_controller_get_devdata(ctlr);
        ospi->dev = dev;

        platform_set_drvdata(pdev, ospi);

        ospi->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(ospi->base))
                return PTR_ERR(ospi->base);

        ospi->clk = devm_clk_get_enabled(dev, NULL);
        if (IS_ERR(ospi->clk))
                return PTR_ERR(ospi->clk);

        ret = devm_mutex_init(dev, &ospi->mlock);
        if (ret)
                return ret;

        ret = f_ospi_init(ospi);
        if (ret)
                return ret;

        return devm_spi_register_controller(dev, ctlr);
}

static const struct of_device_id f_ospi_dt_ids[] = {
        { .compatible = "socionext,f-ospi" },
        {}
};
MODULE_DEVICE_TABLE(of, f_ospi_dt_ids);

static struct platform_driver f_ospi_driver = {
        .driver = {
                .name = "socionext,f-ospi",
                .of_match_table = f_ospi_dt_ids,
        },
        .probe = f_ospi_probe,
};
module_platform_driver(f_ospi_driver);

MODULE_DESCRIPTION("Socionext F_OSPI controller driver");
MODULE_AUTHOR("Socionext Inc.");
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_LICENSE("GPL");