spi: ca_sflash: Add CAxxxx SPI Flash Controller
authorPengpeng Chen <pengpeng.chen@cortina-access.com>
Thu, 30 Jul 2020 19:52:45 +0000 (12:52 -0700)
committerJagan Teki <jagan@amarulasolutions.com>
Fri, 18 Dec 2020 10:46:37 +0000 (16:16 +0530)
Add SPI Flash controller driver for Cortina Access
CAxxxx SoCs

Signed-off-by: Pengpeng Chen <pengpeng.chen@cortina-access.com>
Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>
CC: Vignesh R <vigneshr@ti.com>
CC: Tom Rini <trini@konsulko.com>
[jagan: rebase on master]
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
Reviewed-by: Jagan Teki <jagan@amarulasolutions.com>
MAINTAINERS
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/ca_sflash.c [new file with mode: 0644]

index 127e30c..da2a05b 100644 (file)
@@ -195,6 +195,7 @@ F:  drivers/watchdog/cortina_wdt.c
 F:     drivers/serial/serial_cortina.c
 F:     drivers/led/led_cortina.c
 F:     drivers/mmc/ca_dw_mmc.c
+F:     drivers/spi/ca_sflash.c
 F:     drivers/i2c/i2c-cortina.c
 F:     drivers/i2c/i2c-cortina.h
 
@@ -800,6 +801,7 @@ F:  drivers/watchdog/cortina_wdt.c
 F:     drivers/serial/serial_cortina.c
 F:     drivers/led/led_cortina.c
 F:     drivers/mmc/ca_dw_mmc.c
+F:     drivers/spi/ca_sflash.c
 F:     drivers/i2c/i2c-cortina.c
 F:     drivers/i2c/i2c-cortina.h
 
index f7a9852..cd19b2d 100644 (file)
@@ -106,6 +106,14 @@ config BCMSTB_SPI
          be used to access the SPI flash on platforms embedding this
          Broadcom SPI core.
 
+config CORTINA_SFLASH
+       bool "Cortina-Access Serial Flash controller driver"
+       depends on DM_SPI && SPI_MEM
+       help
+         Enable the Cortina-Access Serial Flash controller driver. This driver
+         can be used to access the SPI NOR/NAND flash on platforms embedding this
+         Cortina-Access IP core.
+
 config CADENCE_QSPI
        bool "Cadence QSPI driver"
        help
index d9b5bd9..dc9ea34 100644 (file)
@@ -24,6 +24,7 @@ obj-$(CONFIG_BCM63XX_HSSPI) += bcm63xx_hsspi.o
 obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o
 obj-$(CONFIG_BCMSTB_SPI) += bcmstb_spi.o
 obj-$(CONFIG_CF_SPI) += cf_spi.o
+obj-$(CONFIG_CORTINA_SFLASH) += ca_sflash.o
 obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
 obj-$(CONFIG_DESIGNWARE_SPI) += designware_spi.o
 obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
diff --git a/drivers/spi/ca_sflash.c b/drivers/spi/ca_sflash.c
new file mode 100644 (file)
index 0000000..00af6bf
--- /dev/null
@@ -0,0 +1,576 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Cortina SPI-FLASH Controller
+ *
+ * Copyright (C) 2020 Cortina Access Inc. All Rights Reserved.
+ *
+ * Author: PengPeng Chen <pengpeng.chen@cortina-access.com>
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <linux/compat.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <linux/sizes.h>
+#include <spi.h>
+#include <spi-mem.h>
+#include <reset.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct ca_sflash_regs {
+       u32 idr;                /* 0x00:Flash word ID Register */
+       u32 tc;                 /* 0x04:Flash Timeout Counter Register */
+       u32 sr;                 /* 0x08:Flash Status Register */
+       u32 tr;                 /* 0x0C:Flash Type Register */
+       u32 asr;                /* 0x10:Flash ACCESS START/BUSY Register */
+       u32 isr;                /* 0x14:Flash Interrupt Status Register */
+       u32 imr;                /* 0x18:Flash Interrupt Mask Register */
+       u32 fcr;                /* 0x1C:NAND Flash FIFO Control Register */
+       u32 ffsr;               /* 0x20:Flash FIFO Status Register */
+       u32 ffar;               /* 0x24:Flash FIFO ADDRESS Register */
+       u32 ffmar;              /* 0x28:Flash FIFO MATCHING ADDRESS Register */
+       u32 ffdr;               /* 0x2C:Flash FIFO Data Register */
+       u32 ar;                 /* 0x30:Serial Flash Access Register */
+       u32 ear;                /* 0x34:Serial Flash Extend Access Register */
+       u32 adr;                /* 0x38:Serial Flash ADdress Register */
+       u32 dr;                 /* 0x3C:Serial Flash Data Register */
+       u32 tmr;                /* 0x40:Serial Flash Timing Register */
+};
+
+/*
+ * FLASH_TYPE
+ */
+#define CA_FLASH_TR_PIN                        BIT(15)
+#define CA_FLASH_TR_TYPE_MSK           GENMASK(14, 12)
+#define CA_FLASH_TR_TYPE(tp)           (((tp) << 12) & CA_FLASH_TR_TYPE_MSK)
+#define CA_FLASH_TR_WIDTH                      BIT(11)
+#define CA_FLASH_TR_SIZE_MSK           GENMASK(10, 9)
+#define CA_FLASH_TR_SIZE(sz)           (((sz) << 9) & CA_FLASH_TR_SIZE_MSK)
+
+/*
+ * FLASH_FLASH_ACCESS_START
+ */
+#define CA_FLASH_ASR_IND_START_EN      BIT(1)
+#define CA_FLASH_ASR_DMA_START_EN      BIT(3)
+#define CA_FLASH_ASR_WR_ACCESS_EN      BIT(9)
+
+/*
+ * FLASH_FLASH_INTERRUPT
+ */
+#define CA_FLASH_ISR_REG_IRQ           BIT(1)
+#define CA_FLASH_ISR_FIFO_IRQ          BIT(2)
+
+/*
+ * FLASH_SF_ACCESS
+ */
+#define CA_SF_AR_OP_MSK                GENMASK(7, 0)
+#define CA_SF_AR_OP(op)                ((op) << 0 & CA_SF_AR_OP_MSK)
+#define CA_SF_AR_ACCODE_MSK            GENMASK(11, 8)
+#define CA_SF_AR_ACCODE(ac)            (((ac) << 8) & CA_SF_AR_ACCODE_MSK)
+#define CA_SF_AR_FORCE_TERM            BIT(12)
+#define CA_SF_AR_FORCE_BURST           BIT(13)
+#define CA_SF_AR_AUTO_MODE_EN          BIT(15)
+#define CA_SF_AR_CHIP_EN_ALT           BIT(16)
+#define CA_SF_AR_HI_SPEED_RD           BIT(17)
+#define CA_SF_AR_MIO_INF_DC            BIT(24)
+#define CA_SF_AR_MIO_INF_AC            BIT(25)
+#define CA_SF_AR_MIO_INF_CC            BIT(26)
+#define CA_SF_AR_DDR_MSK               GENMASK(29, 28)
+#define CA_SF_AR_DDR(ddr)              (((ddr) << 28) & CA_SF_AR_DDR_MSK)
+#define CA_SF_AR_MIO_INF_MSK           GENMASK(31, 30)
+#define CA_SF_AR_MIO_INF(io)           (((io) << 30) & CA_SF_AR_MIO_INF_MSK)
+
+/*
+ * FLASH_SF_EXT_ACCESS
+ */
+#define CA_SF_EAR_OP_MSK               GENMASK(7, 0)
+#define CA_SF_EAR_OP(op)               (((op) << 0) & CA_SF_EAR_OP_MSK)
+#define CA_SF_EAR_DATA_CNT_MSK         GENMASK(20, 8)
+#define CA_SF_EAR_DATA_CNT(cnt)                (((cnt) << 8) & CA_SF_EAR_DATA_CNT_MSK)
+#define CA_SF_EAR_DATA_CNT_MAX         (4096)
+#define CA_SF_EAR_ADDR_CNT_MSK         GENMASK(23, 21)
+#define CA_SF_EAR_ADDR_CNT(cnt)                (((cnt) << 21) & CA_SF_EAR_ADDR_CNT_MSK)
+#define CA_SF_EAR_ADDR_CNT_MAX         (5)
+#define CA_SF_EAR_DUMY_CNT_MSK         GENMASK(29, 24)
+#define CA_SF_EAR_DUMY_CNT(cnt)                (((cnt) << 24) & CA_SF_EAR_DUMY_CNT_MSK)
+#define CA_SF_EAR_DUMY_CNT_MAX         (32)
+#define CA_SF_EAR_DRD_CMD_EN           BIT(31)
+
+/*
+ * FLASH_SF_ADDRESS
+ */
+#define CA_SF_ADR_REG_MSK              GENMASK(31, 0)
+#define CA_SF_ADR_REG(addr)            (((addr) << 0) & CA_SF_ADR_REG_MSK)
+
+/*
+ * FLASH_SF_DATA
+ */
+#define CA_SF_DR_REG_MSK               GENMASK(31, 0)
+#define CA_SF_DR_REG(addr)             (((addr) << 0) & CA_SF_DR_REG_MSK)
+
+/*
+ * FLASH_SF_TIMING
+ */
+#define CA_SF_TMR_IDLE_MSK             GENMASK(7, 0)
+#define CA_SF_TMR_IDLE(idle)           (((idle) << 0) & CA_SF_TMR_IDLE_MSK)
+#define CA_SF_TMR_HOLD_MSK             GENMASK(15, 8)
+#define CA_SF_TMR_HOLD(hold)           (((hold) << 8) & CA_SF_TMR_HOLD_MSK)
+#define CA_SF_TMR_SETUP_MSK            GENMASK(23, 16)
+#define CA_SF_TMR_SETUP(setup)         (((setup) << 16) & CA_SF_TMR_SETUP_MSK)
+#define CA_SF_TMR_CLK_MSK              GENMASK(26, 24)
+#define CA_SF_TMR_CLK(clk)             (((clk) << 24) & CA_SF_TMR_CLK_MSK)
+
+#define CA_SFLASH_IND_WRITE            0
+#define CA_SFLASH_IND_READ             1
+#define CA_SFLASH_MEM_MAP              3
+#define CA_SFLASH_FIFO_TIMEOUT_US      30000
+#define CA_SFLASH_BUSY_TIMEOUT_US      40000
+
+#define CA_SF_AC_OP                    0x00
+#define CA_SF_AC_OP_1_DATA             0x01
+#define CA_SF_AC_OP_2_DATA             0x02
+#define CA_SF_AC_OP_3_DATA             0x03
+#define CA_SF_AC_OP_4_DATA             0x04
+#define CA_SF_AC_OP_3_ADDR             0x05
+#define CA_SF_AC_OP_4_ADDR             (CA_SF_AC_OP_3_ADDR)
+#define CA_SF_AC_OP_3_ADDR_1_DATA      0x06
+#define CA_SF_AC_OP_4_ADDR_1_DATA      (CA_SF_AC_OP_3_ADDR_1_DATA << 2)
+#define CA_SF_AC_OP_3_ADDR_2_DATA      0x07
+#define CA_SF_AC_OP_4_ADDR_2_DATA      (CA_SF_AC_OP_3_ADDR_2_DATA << 2)
+#define CA_SF_AC_OP_3_ADDR_3_DATA      0x08
+#define CA_SF_AC_OP_4_ADDR_3_DATA      (CA_SF_AC_OP_3_ADDR_3_DATA << 2)
+#define CA_SF_AC_OP_3_ADDR_4_DATA      0x09
+#define CA_SF_AC_OP_4_ADDR_4_DATA      (CA_SF_AC_OP_3_ADDR_4_DATA << 2)
+#define CA_SF_AC_OP_3_ADDR_X_1_DATA    0x0A
+#define CA_SF_AC_OP_4_ADDR_X_1_DATA    (CA_SF_AC_OP_3_ADDR_X_1_DATA << 2)
+#define CA_SF_AC_OP_3_ADDR_X_2_DATA    0x0B
+#define CA_SF_AC_OP_4_ADDR_X_2_DATA    (CA_SF_AC_OP_3_ADDR_X_2_DATA << 2)
+#define CA_SF_AC_OP_3_ADDR_X_3_DATA    0x0C
+#define CA_SF_AC_OP_4_ADDR_X_3_DATA    (CA_SF_AC_OP_3_ADDR_X_3_DATA << 2)
+#define CA_SF_AC_OP_3_ADDR_X_4_DATA    0x0D
+#define CA_SF_AC_OP_4_ADDR_X_4_DATA    (CA_SF_AC_OP_3_ADDR_X_4_DATA << 2)
+#define CA_SF_AC_OP_3_ADDR_4X_1_DATA   0x0E
+#define CA_SF_AC_OP_4_ADDR_4X_1_DATA   (CA_SF_AC_OP_3_ADDR_4X_1_DATA << 2)
+#define CA_SF_AC_OP_EXTEND             0x0F
+
+#define CA_SF_ACCESS_MIO_SINGLE                0
+#define CA_SF_ACCESS_MIO_DUAL          1
+#define CA_SF_ACCESS_MIO_QUARD         2
+
+enum access_type {
+       RD_ACCESS,
+       WR_ACCESS,
+};
+
+struct ca_sflash_priv {
+       struct ca_sflash_regs *regs;
+       u8 rx_width;
+       u8 tx_width;
+};
+
+/*
+ * This function doesn't do anything except help with debugging
+ */
+static int ca_sflash_claim_bus(struct udevice *dev)
+{
+       debug("%s:\n", __func__);
+       return 0;
+}
+
+static int ca_sflash_release_bus(struct udevice *dev)
+{
+       debug("%s:\n", __func__);
+       return 0;
+}
+
+static int ca_sflash_set_speed(struct udevice *dev, uint speed)
+{
+       debug("%s:\n", __func__);
+       return 0;
+}
+
+static int ca_sflash_set_mode(struct udevice *dev, uint mode)
+{
+       struct ca_sflash_priv *priv = dev_get_priv(dev);
+
+       if (mode & SPI_RX_QUAD)
+               priv->rx_width = 4;
+       else if (mode & SPI_RX_DUAL)
+               priv->rx_width = 2;
+       else
+               priv->rx_width = 1;
+
+       if (mode & SPI_TX_QUAD)
+               priv->tx_width = 4;
+       else if (mode & SPI_TX_DUAL)
+               priv->tx_width = 2;
+       else
+               priv->tx_width = 1;
+
+       debug("%s: mode=%d, rx_width=%d, tx_width=%d\n",
+             __func__, mode, priv->rx_width, priv->tx_width);
+
+       return 0;
+}
+
+static int _ca_sflash_wait_for_not_busy(struct ca_sflash_priv *priv)
+{
+       u32 asr;
+
+       if (readl_poll_timeout(&priv->regs->asr, asr,
+                              !(asr & CA_FLASH_ASR_IND_START_EN),
+                              CA_SFLASH_BUSY_TIMEOUT_US)) {
+               pr_err("busy timeout (stat:%#x)\n", asr);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int _ca_sflash_wait_cmd(struct ca_sflash_priv *priv,
+                              enum access_type type)
+{
+       if (type == WR_ACCESS) {
+               /* Enable write access and start the sflash indirect access */
+               clrsetbits_le32(&priv->regs->asr, GENMASK(31, 0),
+                               CA_FLASH_ASR_WR_ACCESS_EN
+                               | CA_FLASH_ASR_IND_START_EN);
+       } else if (type == RD_ACCESS) {
+               /* Start the sflash indirect access */
+               clrsetbits_le32(&priv->regs->asr, GENMASK(31, 0),
+                               CA_FLASH_ASR_IND_START_EN);
+       } else {
+               printf("%s: !error access type.\n", __func__);
+               return -1;
+       }
+
+       /* Wait til the action(rd/wr) completed */
+       return _ca_sflash_wait_for_not_busy(priv);
+}
+
+static int _ca_sflash_read(struct ca_sflash_priv *priv,
+                          u8 *buf, unsigned int data_len)
+{
+       u32 reg_data;
+       int len;
+
+       len = data_len;
+       while (len >= 4) {
+               if (_ca_sflash_wait_cmd(priv, RD_ACCESS))
+                       return -1;
+               reg_data = readl(&priv->regs->dr);
+               *buf++ = reg_data & 0xFF;
+               *buf++ = (reg_data >> 8) & 0xFF;
+               *buf++ = (reg_data >> 16) & 0xFF;
+               *buf++ = (reg_data >> 24) & 0xFF;
+               len -= 4;
+               debug("%s: reg_data=%#08x\n",
+                     __func__, reg_data);
+       }
+
+       if (len > 0) {
+               if (_ca_sflash_wait_cmd(priv, RD_ACCESS))
+                       return -1;
+               reg_data = readl(&priv->regs->dr);
+               debug("%s: reg_data=%#08x\n",
+                     __func__, reg_data);
+       }
+
+       switch (len) {
+       case 3:
+               *buf++ = reg_data & 0xFF;
+               *buf++ = (reg_data >> 8) & 0xFF;
+               *buf++ = (reg_data >> 16) & 0xFF;
+               break;
+       case 2:
+               *buf++ = reg_data & 0xFF;
+               *buf++ = (reg_data >> 8) & 0xFF;
+               break;
+       case 1:
+               *buf++ = reg_data & 0xFF;
+               break;
+       case 0:
+               break;
+       default:
+               printf("%s: error data_length %d!\n", __func__, len);
+       }
+
+       return 0;
+}
+
+static int _ca_sflash_mio_set(struct ca_sflash_priv *priv,
+                             u8 width)
+{
+       if (width == 4) {
+               setbits_le32(&priv->regs->ar,
+                            CA_SF_AR_MIO_INF_DC
+                            | CA_SF_AR_MIO_INF(CA_SF_ACCESS_MIO_QUARD)
+                            | CA_SF_AR_FORCE_BURST);
+       } else if (width == 2) {
+               setbits_le32(&priv->regs->ar,
+                            CA_SF_AR_MIO_INF_DC
+                            | CA_SF_AR_MIO_INF(CA_SF_ACCESS_MIO_DUAL)
+                            | CA_SF_AR_FORCE_BURST);
+       } else if (width == 1) {
+               setbits_le32(&priv->regs->ar,
+                            CA_SF_AR_MIO_INF(CA_SF_ACCESS_MIO_SINGLE)
+                            | CA_SF_AR_FORCE_BURST);
+       } else {
+               printf("%s: error rx/tx width  %d!\n", __func__, width);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int _ca_sflash_write(struct ca_sflash_priv *priv,
+                           u8 *buf, unsigned int data_len)
+{
+       u32 reg_data;
+       int len;
+
+       len = data_len;
+       while (len > 0) {
+               reg_data = buf[0]
+                       | (buf[1] << 8)
+                       | (buf[2] << 16)
+                       | (buf[3] << 24);
+
+               debug("%s: reg_data=%#08x\n",
+                     __func__, reg_data);
+               /* Fill data */
+               clrsetbits_le32(&priv->regs->dr, GENMASK(31, 0), reg_data);
+
+               if (_ca_sflash_wait_cmd(priv, WR_ACCESS))
+                       return -1;
+
+               len -= 4;
+               buf += 4;
+       }
+
+       return 0;
+}
+
+static int _ca_sflash_access_data(struct ca_sflash_priv *priv,
+                                 struct spi_mem_op *op)
+{
+       int total_cnt;
+       unsigned int len;
+       unsigned int data_cnt = op->data.nbytes;
+       u64 addr_offset = op->addr.val;
+       u8 addr_cnt = op->addr.nbytes;
+       u8 *data_buf = NULL;
+       u8 *buf = NULL;
+
+       if (op->data.dir == SPI_MEM_DATA_IN)
+               data_buf = (u8 *)op->data.buf.in;
+       else
+               data_buf = (u8 *)op->data.buf.out;
+
+       if (data_cnt > CA_SF_EAR_DATA_CNT_MAX)
+               buf = malloc(CA_SF_EAR_DATA_CNT_MAX);
+       else
+               buf = malloc(data_cnt);
+
+       total_cnt = data_cnt;
+       while (total_cnt > 0) {
+               /* Fill address */
+               if (addr_cnt > 0)
+                       clrsetbits_le32(&priv->regs->adr,
+                                       GENMASK(31, 0), (u32)addr_offset);
+
+               if (total_cnt > CA_SF_EAR_DATA_CNT_MAX) {
+                       len = CA_SF_EAR_DATA_CNT_MAX;
+                       addr_offset += CA_SF_EAR_DATA_CNT_MAX;
+                       /* Clear start bit before next bulk read */
+                       clrbits_le32(&priv->regs->asr, GENMASK(31, 0));
+               } else {
+                       len = total_cnt;
+               }
+
+               memset(buf, 0, len);
+               if (op->data.dir == SPI_MEM_DATA_IN) {
+                       if (_ca_sflash_read(priv, buf, len))
+                               break;
+                       memcpy(data_buf, buf, len);
+               } else {
+                       memcpy(buf, data_buf, len);
+                       if (_ca_sflash_write(priv, buf, len))
+                               break;
+               }
+
+               total_cnt -= len;
+               data_buf += len;
+       }
+       if (buf)
+               free(buf);
+
+       return total_cnt > 0 ? -1 : 0;
+}
+
+static int _ca_sflash_issue_cmd(struct ca_sflash_priv *priv,
+                               struct spi_mem_op *op, u8 opcode)
+{
+       u8 dummy_cnt = op->dummy.nbytes;
+       u8 addr_cnt = op->addr.nbytes;
+       u8 mio_width;
+       unsigned int data_cnt = op->data.nbytes;
+       u64 addr_offset = op->addr.val;
+
+       /* Set the access register */
+       clrsetbits_le32(&priv->regs->ar,
+                       GENMASK(31, 0), CA_SF_AR_ACCODE(opcode));
+
+       if (opcode == CA_SF_AC_OP_EXTEND) { /* read_data, write_data */
+               if (data_cnt > 6) {
+                       if (op->data.dir == SPI_MEM_DATA_IN)
+                               mio_width = priv->rx_width;
+                       else
+                               mio_width = priv->tx_width;
+                       if (_ca_sflash_mio_set(priv, mio_width))
+                               return -1;
+               }
+               debug("%s: FLASH ACCESS reg=%#08x\n",
+                     __func__, readl(&priv->regs->ar));
+
+               /* Use command in extend_access register */
+               clrsetbits_le32(&priv->regs->ear,
+                               GENMASK(31, 0), CA_SF_EAR_OP(op->cmd.opcode)
+                               | CA_SF_EAR_DUMY_CNT(dummy_cnt * 8 - 1)
+                               | CA_SF_EAR_ADDR_CNT(addr_cnt - 1)
+                               | CA_SF_EAR_DATA_CNT(4 - 1)
+                               | CA_SF_EAR_DRD_CMD_EN);
+               debug("%s: FLASH EXT ACCESS reg=%#08x\n",
+                     __func__, readl(&priv->regs->ear));
+
+               if (_ca_sflash_access_data(priv, op))
+                       return -1;
+       } else { /* reset_op, wr_enable, wr_disable */
+               setbits_le32(&priv->regs->ar,
+                            CA_SF_AR_OP(op->cmd.opcode));
+               debug("%s: FLASH ACCESS reg=%#08x\n",
+                     __func__, readl(&priv->regs->ar));
+
+               if (opcode == CA_SF_AC_OP_4_ADDR) { /* erase_op */
+                       /* Configure address length */
+                       if (addr_cnt > 3)       /* 4 Bytes address */
+                               setbits_le32(&priv->regs->tr,
+                                            CA_FLASH_TR_SIZE(2));
+                       else                            /* 3 Bytes address */
+                               clrbits_le32(&priv->regs->tr,
+                                            CA_FLASH_TR_SIZE_MSK);
+
+                       /* Fill address */
+                       if (addr_cnt > 0)
+                               clrsetbits_le32(&priv->regs->adr,
+                                               GENMASK(31, 0),
+                                               (u32)addr_offset);
+               }
+
+               if (_ca_sflash_wait_cmd(priv, RD_ACCESS))
+                       return -1;
+       }
+       /* elapse 10us before issuing any other command */
+       udelay(10);
+
+       return 0;
+}
+
+static int ca_sflash_exec_op(struct spi_slave *slave,
+                            const struct spi_mem_op *op)
+{
+       struct ca_sflash_priv *priv = dev_get_priv(slave->dev->parent);
+       u8 opcode;
+
+       debug("%s: cmd:%#02x addr.val:%#llx addr.len:%#x data.len:%#x data.dir:%#x\n",
+             __func__, op->cmd.opcode, op->addr.val,
+             op->addr.nbytes, op->data.nbytes, op->data.dir);
+
+       if (op->data.nbytes == 0 && op->addr.nbytes == 0) {
+               opcode = CA_SF_AC_OP;
+       } else if (op->data.nbytes == 0 && op->addr.nbytes > 0) {
+               opcode = CA_SF_AC_OP_4_ADDR;
+       } else if (op->data.nbytes > 0) {
+               opcode = CA_SF_AC_OP_EXTEND;
+       } else {
+               printf("%s: can't support cmd.opcode:(%#02x) type currently!\n",
+                      __func__, op->cmd.opcode);
+               return -1;
+       }
+
+       return _ca_sflash_issue_cmd(priv, (struct spi_mem_op *)op, opcode);
+}
+
+static void ca_sflash_init(struct ca_sflash_priv *priv)
+{
+       /* Set FLASH_TYPE as serial flash, value: 0x0400*/
+       clrsetbits_le32(&priv->regs->tr,
+                       GENMASK(31, 0), CA_FLASH_TR_SIZE(2));
+       debug("%s: FLASH_TYPE reg=%#x\n",
+             __func__, readl(&priv->regs->tr));
+
+       /* Minimize flash timing, value: 0x07010101 */
+       clrsetbits_le32(&priv->regs->tmr,
+                       GENMASK(31, 0),
+                       CA_SF_TMR_CLK(0x07)
+                       | CA_SF_TMR_SETUP(0x01)
+                       | CA_SF_TMR_HOLD(0x01)
+                       | CA_SF_TMR_IDLE(0x01));
+       debug("%s: FLASH_TIMING reg=%#x\n",
+             __func__, readl(&priv->regs->tmr));
+}
+
+static int ca_sflash_probe(struct udevice *dev)
+{
+       struct ca_sflash_priv *priv = dev_get_priv(dev);
+       struct resource res;
+       int ret;
+
+       /* Map the registers */
+       ret = dev_read_resource_byname(dev, "sflash-regs", &res);
+       if (ret) {
+               dev_err(dev, "can't get regs base addresses(ret = %d)!\n", ret);
+               return ret;
+       }
+       priv->regs = devm_ioremap(dev, res.start, resource_size(&res));
+       if (IS_ERR(priv->regs))
+               return PTR_ERR(priv->regs);
+
+       ca_sflash_init(priv);
+
+       printf("SFLASH: Controller probed ready\n");
+       return 0;
+}
+
+static const struct spi_controller_mem_ops ca_sflash_mem_ops = {
+       .exec_op = ca_sflash_exec_op,
+};
+
+static const struct dm_spi_ops ca_sflash_ops = {
+       .claim_bus = ca_sflash_claim_bus,
+       .release_bus = ca_sflash_release_bus,
+       .set_speed = ca_sflash_set_speed,
+       .set_mode = ca_sflash_set_mode,
+       .mem_ops = &ca_sflash_mem_ops,
+};
+
+static const struct udevice_id ca_sflash_ids[] = {
+       {.compatible = "cortina,ca-sflash"},
+       {}
+};
+
+U_BOOT_DRIVER(ca_sflash) = {
+       .name = "ca_sflash",
+       .id = UCLASS_SPI,
+       .of_match = ca_sflash_ids,
+       .ops = &ca_sflash_ops,
+       .priv_auto_alloc_size = sizeof(struct ca_sflash_priv),
+       .probe = ca_sflash_probe,
+};