- 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)
This commit is contained in:
Tom Rini 2019-09-16 13:13:12 -04:00
commit 0d6160a340
13 changed files with 253 additions and 93 deletions

View file

@ -58,10 +58,8 @@ to move the migration with in the deadline.
No dm conversion yet:: No dm conversion yet::
drivers/spi/cf_spi.c
drivers/spi/fsl_espi.c drivers/spi/fsl_espi.c
drivers/spi/lpc32xx_ssp.c drivers/spi/lpc32xx_ssp.c
drivers/spi/mxs_spi.c
drivers/spi/sh_spi.c drivers/spi/sh_spi.c
drivers/spi/soft_spi_legacy.c drivers/spi/soft_spi_legacy.c
@ -74,6 +72,7 @@ Partially converted::
drivers/spi/fsl_dspi.c drivers/spi/fsl_dspi.c
drivers/spi/kirkwood_spi.c drivers/spi/kirkwood_spi.c
drivers/spi/mxc_spi.c drivers/spi/mxc_spi.c
drivers/spi/mxs_spi.c
drivers/spi/omap3_spi.c drivers/spi/omap3_spi.c
drivers/spi/sh_qspi.c drivers/spi/sh_qspi.c

View file

@ -18,6 +18,6 @@ spi-nor-y += spi-nor-core.o
endif endif
obj-$(CONFIG_SPI_FLASH) += spi-nor.o 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_MTD) += sf_mtd.o
obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o

View file

@ -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);
}

View file

@ -76,12 +76,14 @@ struct dataflash {
static inline int dataflash_status(struct spi_slave *spi) static inline int dataflash_status(struct spi_slave *spi)
{ {
int ret; int ret;
u8 opcode = OP_READ_STATUS;
u8 status; u8 status;
/* /*
* NOTE: at45db321c over 25 MHz wants to write * NOTE: at45db321c over 25 MHz wants to write
* a dummy byte after the opcode... * 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; 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], command[0], command[1], command[2], command[3],
pageaddr); 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) { if (status < 0) {
debug("%s: erase send command error!\n", dev->name); debug("%s: erase send command error!\n", dev->name);
return -EIO; 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); command[3] = (uint8_t)(addr >> 0);
/* plus 4 "don't care" bytes, command len: 4 + 4 "don't care" bytes */ /* 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); 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", debug("TRANSFER: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]); 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) { if (status < 0) {
debug("%s: write(<pagesize) command error!\n", debug("%s: write(<pagesize) command error!\n",
dev->name); 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", debug("PROGRAM: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]); command[0], command[1], command[2], command[3]);
status = spi_flash_cmd_write(spi, command, status = spi_write_then_read(spi, command, 4,
4, writebuf, writelen); writebuf, NULL, writelen);
if (status < 0) { if (status < 0) {
debug("%s: write send command error!\n", dev->name); debug("%s: write send command error!\n", dev->name);
return -EIO; 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", debug("COMPARE: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]); command[0], command[1], command[2], command[3]);
status = spi_flash_cmd_write(spi, command, status = spi_write_then_read(spi, command, 4,
4, writebuf, writelen); writebuf, NULL, writelen);
if (status < 0) { if (status < 0) {
debug("%s: write(compare) send command error!\n", debug("%s: write(compare) send command error!\n",
dev->name); dev->name);
@ -508,6 +511,7 @@ static struct data_flash_info *jedec_probe(struct spi_slave *spi)
uint8_t id[5]; uint8_t id[5];
uint32_t jedec; uint32_t jedec;
struct data_flash_info *info; struct data_flash_info *info;
u8 opcode = CMD_READ_ID;
int status; 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 * That's not an error; only rev C and newer chips handle it, and
* only Atmel sells these chips. * 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) { if (tmp < 0) {
printf("dataflash: error %d reading JEDEC ID\n", tmp); printf("dataflash: error %d reading JEDEC ID\n", tmp);
return ERR_PTR(tmp); return ERR_PTR(tmp);

View file

@ -65,6 +65,7 @@ struct flash_info {
#define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */ #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 SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */
#define USE_CLSR BIT(14) /* use CLSR command */ #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[]; 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_MFR(info) ((info)->id[0])
#define JEDEC_ID(info) (((info)->id[1]) << 8 | ((info)->id[2])) #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) */ /* Get software write-protect value (BP bits) */
int spi_flash_cmd_get_sw_write_prot(struct spi_flash *flash); int spi_flash_cmd_get_sw_write_prot(struct spi_flash *flash);

View file

@ -945,6 +945,177 @@ read_err:
} }
#ifdef CONFIG_SPI_FLASH_SST #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, static int sst_write_byteprogram(struct spi_nor *nor, loff_t to, size_t len,
size_t *retlen, const u_char *buf) size_t *retlen, const u_char *buf)
{ {
@ -2302,6 +2473,16 @@ int spi_nor_scan(struct spi_nor *nor)
#endif #endif
#ifdef CONFIG_SPI_FLASH_SST #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 */ /* sst nor chips use AAI word program */
if (info->flags & SST_WRITE) if (info->flags & SST_WRITE)
mtd->_write = sst_write; mtd->_write = sst_write;

View file

@ -214,10 +214,10 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("sst25wf040b", 0x621613, 0, 64 * 1024, 8, SECT_4K) }, { INFO("sst25wf040b", 0x621613, 0, 64 * 1024, 8, SECT_4K) },
{ INFO("sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, { INFO("sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
{ INFO("sst25wf080", 0xbf2505, 0, 64 * 1024, 16, 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("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) }, { INFO("sst26wf016", 0xbf2651, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_HAS_SST26LOCK) },
{ INFO("sst26wf032", 0xbf2622, 0, 64 * 1024, 64, SECT_4K) }, { INFO("sst26wf032", 0xbf2622, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_SST26LOCK) },
{ INFO("sst26wf064", 0xbf2643, 0, 64 * 1024, 128, SECT_4K) }, { INFO("sst26wf064", 0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_SST26LOCK) },
#endif #endif
#ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ #ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */
/* ST Microelectronics -- newer production may have feature updates */ /* ST Microelectronics -- newer production may have feature updates */

View file

@ -419,7 +419,6 @@ config MXC_SPI
config MXS_SPI config MXS_SPI
bool "MXS SPI Driver" bool "MXS SPI Driver"
depends on DEPRECATED
help help
Enable the MXS SPI controller driver. This driver can be used Enable the MXS SPI controller driver. This driver can be used
on the i.MX23 and i.MX28 SoCs. on the i.MX23 and i.MX28 SoCs.

View file

@ -181,10 +181,9 @@ static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
data = readl(&reg->cfg); data = readl(&reg->cfg);
prescale = DIV_ROUND_UP(clk_get_rate(&plat->clk), hz); prescale = DIV_ROUND_UP(clk_get_rate(&plat->clk), hz);
if (prescale > 0x1f) if (prescale > 0xf)
prescale = 0x1f;
else if (prescale > 0xf)
prescale = 0x10 + (prescale + 1) / 2; prescale = 0x10 + (prescale + 1) / 2;
prescale = min(prescale, 0x1fu);
data &= ~MVEBU_SPI_A3700_CLK_PRESCALE_MASK; data &= ~MVEBU_SPI_A3700_CLK_PRESCALE_MASK;
data |= prescale & MVEBU_SPI_A3700_CLK_PRESCALE_MASK; data |= prescale & MVEBU_SPI_A3700_CLK_PRESCALE_MASK;

View file

@ -215,8 +215,8 @@ static int soft_spi_probe(struct udevice *dev)
int cs_flags, clk_flags; int cs_flags, clk_flags;
int ret; int ret;
cs_flags = (slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW; cs_flags = (slave && slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW;
clk_flags = (slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0; clk_flags = (slave && slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0;
if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs, if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
GPIOD_IS_OUT | cs_flags) || GPIOD_IS_OUT | cs_flags) ||

View 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); 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) #if !CONFIG_IS_ENABLED(OF_PLATDATA)
static int spi_child_post_bind(struct udevice *dev) static int spi_child_post_bind(struct udevice *dev)
{ {

View file

@ -91,6 +91,10 @@
#define SPINOR_OP_WRDI 0x04 /* Write disable */ #define SPINOR_OP_WRDI 0x04 /* Write disable */
#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ #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 */ /* Used for S3AN flashes only */
#define SPINOR_OP_XSE 0x50 /* Sector erase */ #define SPINOR_OP_XSE 0x50 /* Sector erase */
#define SPINOR_OP_XPP 0x82 /* Page program */ #define SPINOR_OP_XPP 0x82 /* Page program */

View 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, int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags); 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 */ /* Copy memory mapped data */
void spi_flash_copy_mmap(void *data, void *offset, size_t len); void spi_flash_copy_mmap(void *data, void *offset, size_t len);