Merge branch 'master' of https://gitlab.denx.de/u-boot/custodians/u-boot-spi
authorTom Rini <trini@konsulko.com>
Mon, 16 Sep 2019 17:13:12 +0000 (13:13 -0400)
committerTom Rini <trini@konsulko.com>
Mon, 16 Sep 2019 17:13:12 +0000 (13:13 -0400)
- fix mvebu_a3700_spi clock prescale (Marek BehĂșn)
- unmark MXS_SPI, DEPRECATED (Lukasz)
- add spi_write_then_read (Jagan)
- fix SST26* flash ICs (Eugeniy)
- fix soft_spi data abort (Christophe)

13 files changed:
doc/driver-model/migration.rst
drivers/mtd/spi/Makefile
drivers/mtd/spi/sf.c [deleted file]
drivers/mtd/spi/sf_dataflash.c
drivers/mtd/spi/sf_internal.h
drivers/mtd/spi/spi-nor-core.c
drivers/mtd/spi/spi-nor-ids.c
drivers/spi/Kconfig
drivers/spi/mvebu_a3700_spi.c
drivers/spi/soft_spi.c
drivers/spi/spi-uclass.c
include/linux/mtd/spi-nor.h
include/spi.h

index 75b85235ef38a6250ea395fa78c7b04c5ada510e..fd499fb74a84b9546138c84f5ad641c30367d256 100644 (file)
@@ -58,10 +58,8 @@ to move the migration with in the deadline.
 
 No dm conversion yet::
 
-       drivers/spi/cf_spi.c
        drivers/spi/fsl_espi.c
        drivers/spi/lpc32xx_ssp.c
-       drivers/spi/mxs_spi.c
        drivers/spi/sh_spi.c
        drivers/spi/soft_spi_legacy.c
 
@@ -74,6 +72,7 @@ Partially converted::
        drivers/spi/fsl_dspi.c
        drivers/spi/kirkwood_spi.c
        drivers/spi/mxc_spi.c
+       drivers/spi/mxs_spi.c
        drivers/spi/omap3_spi.c
        drivers/spi/sh_qspi.c
 
index f99f6cb16e2969f91c693b7eef92382f87d71596..20db1015d9ed6323af60bf818051f1296c1a7775 100644 (file)
@@ -18,6 +18,6 @@ spi-nor-y += spi-nor-core.o
 endif
 
 obj-$(CONFIG_SPI_FLASH) += spi-nor.o
-obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o sf.o
+obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o
 obj-$(CONFIG_SPI_FLASH_MTD) += sf_mtd.o
 obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o
diff --git a/drivers/mtd/spi/sf.c b/drivers/mtd/spi/sf.c
deleted file mode 100644 (file)
index ee3cf8b..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * SPI flash interface
- *
- * Copyright (C) 2008 Atmel Corporation
- * Copyright (C) 2010 Reinhard Meyer, EMK Elektronik
- */
-
-#include <common.h>
-#include <spi.h>
-
-static int spi_flash_read_write(struct spi_slave *spi,
-                               const u8 *cmd, size_t cmd_len,
-                               const u8 *data_out, u8 *data_in,
-                               size_t data_len)
-{
-       unsigned long flags = SPI_XFER_BEGIN;
-       int ret;
-
-       if (data_len == 0)
-               flags |= SPI_XFER_END;
-
-       ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags);
-       if (ret) {
-               debug("SF: Failed to send command (%zu bytes): %d\n",
-                     cmd_len, ret);
-       } else if (data_len != 0) {
-               ret = spi_xfer(spi, data_len * 8, data_out, data_in,
-                                       SPI_XFER_END);
-               if (ret)
-                       debug("SF: Failed to transfer %zu bytes of data: %d\n",
-                             data_len, ret);
-       }
-
-       return ret;
-}
-
-int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
-               size_t cmd_len, void *data, size_t data_len)
-{
-       return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len);
-}
-
-int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len)
-{
-       return spi_flash_cmd_read(spi, &cmd, 1, response, len);
-}
-
-int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
-               const void *data, size_t data_len)
-{
-       return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len);
-}
index b6a2631747a9d89aea718b5f20eb2b62ec1e4e2c..55fb4bd31ac1df1db0d162b3c18174968abc3d20 100644 (file)
@@ -76,12 +76,14 @@ struct dataflash {
 static inline int dataflash_status(struct spi_slave *spi)
 {
        int ret;
+       u8 opcode = OP_READ_STATUS;
        u8 status;
+
        /*
         * NOTE:  at45db321c over 25 MHz wants to write
         * a dummy byte after the opcode...
         */
-       ret = spi_flash_cmd(spi, OP_READ_STATUS, &status, 1);
+       ret =  spi_write_then_read(spi, &opcode, 1, NULL, &status, 1);
        return ret ? -EIO : status;
 }
 
@@ -173,7 +175,7 @@ static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
                      command[0], command[1], command[2], command[3],
                      pageaddr);
 
-               status = spi_flash_cmd_write(spi, command, 4, NULL, 0);
+               status = spi_write_then_read(spi, command, 4, NULL, NULL, 0);
                if (status < 0) {
                        debug("%s: erase send command error!\n", dev->name);
                        return -EIO;
@@ -248,7 +250,7 @@ static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len,
        command[3] = (uint8_t)(addr >> 0);
 
        /* plus 4 "don't care" bytes, command len: 4 + 4 "don't care" bytes */
-       status = spi_flash_cmd_read(spi, command, 8, buf, len);
+       status = spi_write_then_read(spi, command, 8, NULL, buf, len);
 
        spi_release_bus(spi);
 
@@ -327,7 +329,8 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
                        debug("TRANSFER: (%x) %x %x %x\n",
                              command[0], command[1], command[2], command[3]);
 
-                       status = spi_flash_cmd_write(spi, command, 4, NULL, 0);
+                       status = spi_write_then_read(spi, command, 4,
+                                                    NULL, NULL, 0);
                        if (status < 0) {
                                debug("%s: write(<pagesize) command error!\n",
                                      dev->name);
@@ -352,8 +355,8 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
                debug("PROGRAM: (%x) %x %x %x\n",
                      command[0], command[1], command[2], command[3]);
 
-               status = spi_flash_cmd_write(spi, command,
-                                            4, writebuf, writelen);
+               status = spi_write_then_read(spi, command, 4,
+                                            writebuf, NULL, writelen);
                if (status < 0) {
                        debug("%s: write send command error!\n", dev->name);
                        return -EIO;
@@ -376,8 +379,8 @@ int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
                debug("COMPARE: (%x) %x %x %x\n",
                      command[0], command[1], command[2], command[3]);
 
-               status = spi_flash_cmd_write(spi, command,
-                                            4, writebuf, writelen);
+               status = spi_write_then_read(spi, command, 4,
+                                            writebuf, NULL, writelen);
                if (status < 0) {
                        debug("%s: write(compare) send command error!\n",
                              dev->name);
@@ -508,6 +511,7 @@ static struct data_flash_info *jedec_probe(struct spi_slave *spi)
        uint8_t                 id[5];
        uint32_t                jedec;
        struct data_flash_info  *info;
+       u8 opcode               = CMD_READ_ID;
        int status;
 
        /*
@@ -519,7 +523,7 @@ static struct data_flash_info *jedec_probe(struct spi_slave *spi)
         * That's not an error; only rev C and newer chips handle it, and
         * only Atmel sells these chips.
         */
-       tmp = spi_flash_cmd(spi, CMD_READ_ID, id, sizeof(id));
+       tmp = spi_write_then_read(spi, &opcode, 1, NULL, id, sizeof(id));
        if (tmp < 0) {
                printf("dataflash: error %d reading JEDEC ID\n", tmp);
                return ERR_PTR(tmp);
index a6bf734830a71956b8fdb292774cf6567a326672..bb8c19a31cf70df90de1d89e2a3fdad236354a56 100644 (file)
@@ -65,6 +65,7 @@ struct flash_info {
 #define NO_CHIP_ERASE          BIT(12) /* Chip does not support chip erase */
 #define SPI_NOR_SKIP_SFDP      BIT(13) /* Skip parsing of SFDP tables */
 #define USE_CLSR               BIT(14) /* use CLSR command */
+#define SPI_NOR_HAS_SST26LOCK  BIT(15) /* Flash supports lock/unlock via BPR */
 };
 
 extern const struct flash_info spi_nor_ids[];
@@ -72,24 +73,6 @@ extern const struct flash_info spi_nor_ids[];
 #define JEDEC_MFR(info)        ((info)->id[0])
 #define JEDEC_ID(info)         (((info)->id[1]) << 8 | ((info)->id[2]))
 
-/* Send a single-byte command to the device and read the response */
-int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len);
-
-/*
- * Send a multi-byte command to the device and read the response. Used
- * for flash array reads, etc.
- */
-int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
-               size_t cmd_len, void *data, size_t data_len);
-
-/*
- * Send a multi-byte command to the device followed by (optional)
- * data. Used for programming the flash array, etc.
- */
-int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
-               const void *data, size_t data_len);
-
-
 /* Get software write-protect value (BP bits) */
 int spi_flash_cmd_get_sw_write_prot(struct spi_flash *flash);
 
index 1acff745d1a225e124ee6ec79bffeb3a86a21d16..990e39d7c2f6f50c72cfb99619ef83c3160cc391 100644 (file)
@@ -945,6 +945,177 @@ read_err:
 }
 
 #ifdef CONFIG_SPI_FLASH_SST
+/*
+ * sst26 flash series has its own block protection implementation:
+ * 4x   - 8  KByte blocks - read & write protection bits - upper addresses
+ * 1x   - 32 KByte blocks - write protection bits
+ * rest - 64 KByte blocks - write protection bits
+ * 1x   - 32 KByte blocks - write protection bits
+ * 4x   - 8  KByte blocks - read & write protection bits - lower addresses
+ *
+ * We'll support only per 64k lock/unlock so lower and upper 64 KByte region
+ * will be treated as single block.
+ */
+#define SST26_BPR_8K_NUM               4
+#define SST26_MAX_BPR_REG_LEN          (18 + 1)
+#define SST26_BOUND_REG_SIZE           ((32 + SST26_BPR_8K_NUM * 8) * SZ_1K)
+
+enum lock_ctl {
+       SST26_CTL_LOCK,
+       SST26_CTL_UNLOCK,
+       SST26_CTL_CHECK
+};
+
+static bool sst26_process_bpr(u32 bpr_size, u8 *cmd, u32 bit, enum lock_ctl ctl)
+{
+       switch (ctl) {
+       case SST26_CTL_LOCK:
+               cmd[bpr_size - (bit / 8) - 1] |= BIT(bit % 8);
+               break;
+       case SST26_CTL_UNLOCK:
+               cmd[bpr_size - (bit / 8) - 1] &= ~BIT(bit % 8);
+               break;
+       case SST26_CTL_CHECK:
+               return !!(cmd[bpr_size - (bit / 8) - 1] & BIT(bit % 8));
+       }
+
+       return false;
+}
+
+/*
+ * Lock, unlock or check lock status of the flash region of the flash (depending
+ * on the lock_ctl value)
+ */
+static int sst26_lock_ctl(struct spi_nor *nor, loff_t ofs, uint64_t len, enum lock_ctl ctl)
+{
+       struct mtd_info *mtd = &nor->mtd;
+       u32 i, bpr_ptr, rptr_64k, lptr_64k, bpr_size;
+       bool lower_64k = false, upper_64k = false;
+       u8 bpr_buff[SST26_MAX_BPR_REG_LEN] = {};
+       int ret;
+
+       /* Check length and offset for 64k alignment */
+       if ((ofs & (SZ_64K - 1)) || (len & (SZ_64K - 1))) {
+               dev_err(nor->dev, "length or offset is not 64KiB allighned\n");
+               return -EINVAL;
+       }
+
+       if (ofs + len > mtd->size) {
+               dev_err(nor->dev, "range is more than device size: %#llx + %#llx > %#llx\n",
+                       ofs, len, mtd->size);
+               return -EINVAL;
+       }
+
+       /* SST26 family has only 16 Mbit, 32 Mbit and 64 Mbit IC */
+       if (mtd->size != SZ_2M &&
+           mtd->size != SZ_4M &&
+           mtd->size != SZ_8M)
+               return -EINVAL;
+
+       bpr_size = 2 + (mtd->size / SZ_64K / 8);
+
+       ret = nor->read_reg(nor, SPINOR_OP_READ_BPR, bpr_buff, bpr_size);
+       if (ret < 0) {
+               dev_err(nor->dev, "fail to read block-protection register\n");
+               return ret;
+       }
+
+       rptr_64k = min_t(u32, ofs + len, mtd->size - SST26_BOUND_REG_SIZE);
+       lptr_64k = max_t(u32, ofs, SST26_BOUND_REG_SIZE);
+
+       upper_64k = ((ofs + len) > (mtd->size - SST26_BOUND_REG_SIZE));
+       lower_64k = (ofs < SST26_BOUND_REG_SIZE);
+
+       /* Lower bits in block-protection register are about 64k region */
+       bpr_ptr = lptr_64k / SZ_64K - 1;
+
+       /* Process 64K blocks region */
+       while (lptr_64k < rptr_64k) {
+               if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
+                       return EACCES;
+
+               bpr_ptr++;
+               lptr_64k += SZ_64K;
+       }
+
+       /* 32K and 8K region bits in BPR are after 64k region bits */
+       bpr_ptr = (mtd->size - 2 * SST26_BOUND_REG_SIZE) / SZ_64K;
+
+       /* Process lower 32K block region */
+       if (lower_64k)
+               if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
+                       return EACCES;
+
+       bpr_ptr++;
+
+       /* Process upper 32K block region */
+       if (upper_64k)
+               if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
+                       return EACCES;
+
+       bpr_ptr++;
+
+       /* Process lower 8K block regions */
+       for (i = 0; i < SST26_BPR_8K_NUM; i++) {
+               if (lower_64k)
+                       if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
+                               return EACCES;
+
+               /* In 8K area BPR has both read and write protection bits */
+               bpr_ptr += 2;
+       }
+
+       /* Process upper 8K block regions */
+       for (i = 0; i < SST26_BPR_8K_NUM; i++) {
+               if (upper_64k)
+                       if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
+                               return EACCES;
+
+               /* In 8K area BPR has both read and write protection bits */
+               bpr_ptr += 2;
+       }
+
+       /* If we check region status we don't need to write BPR back */
+       if (ctl == SST26_CTL_CHECK)
+               return 0;
+
+       ret = nor->write_reg(nor, SPINOR_OP_WRITE_BPR, bpr_buff, bpr_size);
+       if (ret < 0) {
+               dev_err(nor->dev, "fail to write block-protection register\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int sst26_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+       return sst26_lock_ctl(nor, ofs, len, SST26_CTL_UNLOCK);
+}
+
+static int sst26_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+       return sst26_lock_ctl(nor, ofs, len, SST26_CTL_LOCK);
+}
+
+/*
+ * Returns EACCES (positive value) if region is locked, 0 if region is unlocked,
+ * and negative on errors.
+ */
+static int sst26_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+       /*
+        * is_locked function is used for check before reading or erasing flash
+        * region, so offset and length might be not 64k allighned, so adjust
+        * them to be 64k allighned as sst26_lock_ctl works only with 64k
+        * allighned regions.
+        */
+       ofs -= ofs & (SZ_64K - 1);
+       len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len;
+
+       return sst26_lock_ctl(nor, ofs, len, SST26_CTL_CHECK);
+}
+
 static int sst_write_byteprogram(struct spi_nor *nor, loff_t to, size_t len,
                                 size_t *retlen, const u_char *buf)
 {
@@ -2302,6 +2473,16 @@ int spi_nor_scan(struct spi_nor *nor)
 #endif
 
 #ifdef CONFIG_SPI_FLASH_SST
+       /*
+        * sst26 series block protection implementation differs from other
+        * series.
+        */
+       if (info->flags & SPI_NOR_HAS_SST26LOCK) {
+               nor->flash_lock = sst26_lock;
+               nor->flash_unlock = sst26_unlock;
+               nor->flash_is_locked = sst26_is_locked;
+       }
+
        /* sst nor chips use AAI word program */
        if (info->flags & SST_WRITE)
                mtd->_write = sst_write;
index a3920ba520e0eb9d414a0e2f5f913e0b52ab5b1e..6996c0a2864f9ef549035e02aa3dd73ee4fa2a41 100644 (file)
@@ -214,10 +214,10 @@ const struct flash_info spi_nor_ids[] = {
        { INFO("sst25wf040b", 0x621613, 0, 64 * 1024,  8, SECT_4K) },
        { INFO("sst25wf040",  0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
        { INFO("sst25wf080",  0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
-       { INFO("sst26vf064b", 0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
-       { INFO("sst26wf016",  0xbf2651, 0, 64 * 1024,  32, SECT_4K) },
-       { INFO("sst26wf032",  0xbf2622, 0, 64 * 1024,  64, SECT_4K) },
-       { INFO("sst26wf064",  0xbf2643, 0, 64 * 1024, 128, SECT_4K) },
+       { INFO("sst26vf064b", 0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_SST26LOCK | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("sst26wf016",  0xbf2651, 0, 64 * 1024,  32, SECT_4K | SPI_NOR_HAS_SST26LOCK) },
+       { INFO("sst26wf032",  0xbf2622, 0, 64 * 1024,  64, SECT_4K | SPI_NOR_HAS_SST26LOCK) },
+       { INFO("sst26wf064",  0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_SST26LOCK) },
 #endif
 #ifdef CONFIG_SPI_FLASH_STMICRO                /* STMICRO */
        /* ST Microelectronics -- newer production may have feature updates */
index f459c0a41138fe56fe4092637ad7fd86d898c6a8..8dd3213d485b492139fa8cc310255e7b0ac057a6 100644 (file)
@@ -419,7 +419,6 @@ config MXC_SPI
 
 config MXS_SPI
        bool "MXS SPI Driver"
-       depends on DEPRECATED
        help
          Enable the MXS SPI controller driver. This driver can be used
          on the i.MX23 and i.MX28 SoCs.
index feeafdceaa618680de3f41a5c8945b4e200a124a..99ad505f24e4daf3c964596a5fdb3a8667b1cc6c 100644 (file)
@@ -181,10 +181,9 @@ static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
        data = readl(&reg->cfg);
 
        prescale = DIV_ROUND_UP(clk_get_rate(&plat->clk), hz);
-       if (prescale > 0x1f)
-               prescale = 0x1f;
-       else if (prescale > 0xf)
+       if (prescale > 0xf)
                prescale = 0x10 + (prescale + 1) / 2;
+       prescale = min(prescale, 0x1fu);
 
        data &= ~MVEBU_SPI_A3700_CLK_PRESCALE_MASK;
        data |= prescale & MVEBU_SPI_A3700_CLK_PRESCALE_MASK;
index b06883f9d0448bd026bee20eadcb4e54a10f5a3f..b80f810bd157ae464bd39a94f0d9bdab6f66b86b 100644 (file)
@@ -215,8 +215,8 @@ static int soft_spi_probe(struct udevice *dev)
        int cs_flags, clk_flags;
        int ret;
 
-       cs_flags = (slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW;
-       clk_flags = (slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0;
+       cs_flags = (slave && slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW;
+       clk_flags = (slave && slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0;
 
        if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
                                 GPIOD_IS_OUT | cs_flags) ||
index 88cb2a126227156d18342796d4022f25c3aefbba..76c4b53c165c764e496a40c101e6e34c65b8a887 100644 (file)
@@ -108,6 +108,30 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
        return dm_spi_xfer(slave->dev, bitlen, dout, din, flags);
 }
 
+int spi_write_then_read(struct spi_slave *slave, const u8 *opcode,
+                       size_t n_opcode, const u8 *txbuf, u8 *rxbuf,
+                       size_t n_buf)
+{
+       unsigned long flags = SPI_XFER_BEGIN;
+       int ret;
+
+       if (n_buf == 0)
+               flags |= SPI_XFER_END;
+
+       ret = spi_xfer(slave, n_opcode * 8, opcode, NULL, flags);
+       if (ret) {
+               debug("spi: failed to send command (%zu bytes): %d\n",
+                     n_opcode, ret);
+       } else if (n_buf != 0) {
+               ret = spi_xfer(slave, n_buf * 8, txbuf, rxbuf, SPI_XFER_END);
+               if (ret)
+                       debug("spi: failed to transfer %zu bytes of data: %d\n",
+                             n_buf, ret);
+       }
+
+       return ret;
+}
+
 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
 static int spi_child_post_bind(struct udevice *dev)
 {
index 88e80af57941556474c87276bbaa11e5364caf82..709b49d3936da1cc0858c1d42afd6736cf01e2f0 100644 (file)
 #define SPINOR_OP_WRDI         0x04    /* Write disable */
 #define SPINOR_OP_AAI_WP       0xad    /* Auto address increment word program */
 
+/* Used for SST26* flashes only. */
+#define SPINOR_OP_READ_BPR     0x72    /* Read block protection register */
+#define SPINOR_OP_WRITE_BPR    0x42    /* Write block protection register */
+
 /* Used for S3AN flashes only */
 #define SPINOR_OP_XSE          0x50    /* Sector erase */
 #define SPINOR_OP_XPP          0x82    /* Page program */
index 378594163b874246e606a41edf6e404e81412f81..5eec0c4775e5c9a3122f772841e1952fb803b6ac 100644 (file)
@@ -248,6 +248,26 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen);
 int  spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
                void *din, unsigned long flags);
 
+/**
+ * spi_write_then_read - SPI synchronous write followed by read
+ *
+ * This performs a half duplex transaction in which the first transaction
+ * is to send the opcode and if the length of buf is non-zero then it start
+ * the second transaction as tx or rx based on the need from respective slave.
+ *
+ * @slave:     The SPI slave device with which opcode/data will be exchanged
+ * @opcode:    opcode used for specific transfer
+ * @n_opcode:  size of opcode, in bytes
+ * @txbuf:     buffer into which data to be written
+ * @rxbuf:     buffer into which data will be read
+ * @n_buf:     size of buf (whether it's [tx|rx]buf), in bytes
+ *
+ * Returns: 0 on success, not 0 on failure
+ */
+int spi_write_then_read(struct spi_slave *slave, const u8 *opcode,
+                       size_t n_opcode, const u8 *txbuf, u8 *rxbuf,
+                       size_t n_buf);
+
 /* Copy memory mapped data */
 void spi_flash_copy_mmap(void *data, void *offset, size_t len);