summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/drivers/spi/Kconfig10
-rw-r--r--src/drivers/spi/spi-generic.c99
-rw-r--r--src/drivers/spi/spi_flash.c47
-rw-r--r--src/include/spi-generic.h62
-rw-r--r--src/mainboard/google/purin/Kconfig1
-rw-r--r--src/soc/broadcom/cygnus/spi.c1
-rw-r--r--src/soc/imgtec/pistachio/Kconfig1
-rw-r--r--src/soc/imgtec/pistachio/spi.c7
-rw-r--r--src/soc/intel/baytrail/spi.c1
-rw-r--r--src/soc/intel/braswell/spi.c1
-rw-r--r--src/soc/intel/broadwell/spi.c1
-rw-r--r--src/soc/intel/fsp_baytrail/spi.c1
-rw-r--r--src/soc/intel/fsp_broadwell_de/spi.c1
-rw-r--r--src/soc/mediatek/mt8173/Kconfig1
-rw-r--r--src/soc/mediatek/mt8173/spi.c1
-rw-r--r--src/soc/qualcomm/ipq40xx/Kconfig1
-rw-r--r--src/soc/qualcomm/ipq806x/Kconfig1
-rw-r--r--src/southbridge/amd/agesa/hudson/spi.c1
-rw-r--r--src/southbridge/amd/cimx/sb800/spi.c1
-rw-r--r--src/southbridge/amd/sb700/spi.c1
-rw-r--r--src/southbridge/intel/common/spi.c1
-rw-r--r--src/southbridge/intel/fsp_rangeley/spi.c1
22 files changed, 188 insertions, 54 deletions
diff --git a/src/drivers/spi/Kconfig b/src/drivers/spi/Kconfig
index b55de58962..c8d86ff104 100644
--- a/src/drivers/spi/Kconfig
+++ b/src/drivers/spi/Kconfig
@@ -61,16 +61,6 @@ config SPI_FLASH_INCLUDE_ALL_DRIVERS
default n if COMMON_CBFS_SPI_WRAPPER
default y
-config SPI_ATOMIC_SEQUENCING
- bool
- default y if ARCH_X86
- default n if !ARCH_X86
- help
- Select this option if the SPI controller uses "atomic sequencing."
- Atomic sequencing is when the sequence of commands is pre-programmed
- in the SPI controller. Hardware manages the transaction instead of
- software. This is common on x86 platforms.
-
config SPI_FLASH_SMM
bool "SPI flash driver support in SMM"
default n
diff --git a/src/drivers/spi/spi-generic.c b/src/drivers/spi/spi-generic.c
index 4fcd04c13a..805e17af5a 100644
--- a/src/drivers/spi/spi-generic.c
+++ b/src/drivers/spi/spi-generic.c
@@ -14,6 +14,7 @@
* GNU General Public License for more details.
*/
+#include <assert.h>
#include <spi-generic.h>
#include <string.h>
@@ -32,10 +33,55 @@ void spi_release_bus(const struct spi_slave *slave)
ctrlr->release_bus(slave);
}
+static int spi_xfer_single_op(const struct spi_slave *slave,
+ struct spi_op *op)
+{
+ const struct spi_ctrlr *ctrlr = slave->ctrlr;
+ int ret;
+
+ if (!ctrlr || !ctrlr->xfer)
+ return -1;
+
+ ret = ctrlr->xfer(slave, op->dout, op->bytesout, op->din, op->bytesin);
+ if (ret)
+ op->status = SPI_OP_FAILURE;
+ else
+ op->status = SPI_OP_SUCCESS;
+
+ return ret;
+}
+
+static int spi_xfer_vector_default(const struct spi_slave *slave,
+ struct spi_op vectors[], size_t count)
+{
+ size_t i;
+ int ret;
+
+ for (i = 0; i < count; i++) {
+ ret = spi_xfer_single_op(slave, &vectors[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int spi_xfer_vector(const struct spi_slave *slave,
+ struct spi_op vectors[], size_t count)
+{
+ const struct spi_ctrlr *ctrlr = slave->ctrlr;
+
+ if (ctrlr && ctrlr->xfer_vector)
+ return ctrlr->xfer_vector(slave, vectors, count);
+
+ return spi_xfer_vector_default(slave, vectors, count);
+}
+
int spi_xfer(const struct spi_slave *slave, const void *dout, size_t bytesout,
void *din, size_t bytesin)
{
const struct spi_ctrlr *ctrlr = slave->ctrlr;
+
if (ctrlr && ctrlr->xfer)
return ctrlr->xfer(slave, dout, bytesout, din, bytesin);
@@ -76,3 +122,56 @@ int __attribute__((weak)) spi_setup_slave(unsigned int bus, unsigned int cs,
return 0;
}
+
+static int spi_xfer_combine_two_vectors(const struct spi_slave *slave,
+ struct spi_op *v1, struct spi_op *v2)
+{
+ struct spi_op op = {
+ .dout = v1->dout, .bytesout = v1->bytesout,
+ .din = v2->din, .bytesin = v2->bytesin,
+ };
+ int ret;
+
+ /*
+ * Combine two vectors only if:
+ * v1 has non-NULL dout and NULL din and
+ * v2 has non-NULL din and NULL dout and
+ *
+ * In all other cases, do not combine the two vectors.
+ */
+ if ((!v1->dout || v1->din) || (v2->dout || !v2->din))
+ return -1;
+
+ ret = spi_xfer_single_op(slave, &op);
+ v1->status = v2->status = op.status;
+
+ return ret;
+}
+
+/*
+ * Helper function to allow chipsets to combine two vectors if possible. This
+ * function can only handle upto 2 vectors.
+ *
+ * Two vectors are combined if first vector has a non-NULL dout and NULL din and
+ * second vector has a non-NULL din and NULL dout. Otherwise, each vector is
+ * operated upon one at a time.
+ *
+ * Returns 0 on success and non-zero on failure.
+ */
+int spi_xfer_two_vectors(const struct spi_slave *slave,
+ struct spi_op vectors[], size_t count)
+{
+ int ret;
+
+ assert (count <= 2);
+
+ if (count == 2) {
+ ret = spi_xfer_combine_two_vectors(slave, &vectors[0],
+ &vectors[1]);
+
+ if (!ret || (vectors[0].status != SPI_OP_NOT_EXECUTED))
+ return ret;
+ }
+
+ return spi_xfer_vector_default(slave, vectors, count);
+}
diff --git a/src/drivers/spi/spi_flash.c b/src/drivers/spi/spi_flash.c
index 3b4272b709..a0e310554a 100644
--- a/src/drivers/spi/spi_flash.c
+++ b/src/drivers/spi/spi_flash.c
@@ -32,47 +32,30 @@ static void spi_flash_addr(u32 addr, u8 *cmd)
cmd[3] = addr >> 0;
}
-/*
- * If atomic sequencing is used, the cycle type is known to the SPI
- * controller so that it can perform consecutive transfers and arbitrate
- * automatically. Otherwise the SPI controller transfers whatever the
- * user requests immediately, without regard to sequence. Atomic
- * sequencing is commonly used on x86 platforms.
- *
- * SPI flash commands are simple two-step sequences. The command byte is
- * always written first and may be followed by an address. Then data is
- * either read or written. For atomic sequencing we'll pass everything into
- * spi_xfer() at once and let the controller handle the details. Otherwise
- * we will write all output bytes first and then read if necessary.
- *
- * FIXME: This really should be abstracted better, but that will
- * require overhauling the entire SPI infrastructure.
- */
static int do_spi_flash_cmd(const struct spi_slave *spi, const void *dout,
size_t bytes_out, void *din, size_t bytes_in)
{
int ret = 1;
+ /*
+ * SPI flash requires command-response kind of behavior. Thus, two
+ * separate SPI vectors are required -- first to transmit dout and other
+ * to receive in din. If some specialized SPI flash controllers
+ * (e.g. x86) can perform both command and response together, it should
+ * be handled at SPI flash controller driver level.
+ */
+ struct spi_op vectors[] = {
+ [0] = { .dout = dout, .bytesout = bytes_out,
+ .din = NULL, .bytesin = 0, },
+ [1] = { .dout = NULL, .bytesout = 0,
+ .din = din, .bytesin = bytes_in },
+ };
if (spi_claim_bus(spi))
return ret;
-#if CONFIG_SPI_ATOMIC_SEQUENCING == 1
- if (spi_xfer(spi, dout, bytes_out, din, bytes_in) < 0)
- goto done;
-#else
- if (dout && bytes_out) {
- if (spi_xfer(spi, dout, bytes_out, NULL, 0) < 0)
- goto done;
- }
-
- if (din && bytes_in) {
- if (spi_xfer(spi, NULL, 0, din, bytes_in) < 0)
- goto done;
- }
-#endif
+ if (spi_xfer_vector(spi, vectors, ARRAY_SIZE(vectors)) == 0)
+ ret = 0;
- ret = 0;
-done:
spi_release_bus(spi);
return ret;
}
diff --git a/src/include/spi-generic.h b/src/include/spi-generic.h
index d28fefd026..7eb18a6ac8 100644
--- a/src/include/spi-generic.h
+++ b/src/include/spi-generic.h
@@ -36,20 +36,46 @@ struct spi_slave {
const struct spi_ctrlr *ctrlr;
};
+/* Representation of SPI operation status. */
+enum spi_op_status {
+ SPI_OP_NOT_EXECUTED = 0,
+ SPI_OP_SUCCESS = 1,
+ SPI_OP_FAILURE = 2,
+};
+
+/*
+ * Representation of a SPI operation.
+ *
+ * dout: Pointer to data to send.
+ * bytesout: Count of data in bytes to send.
+ * din: Pointer to store received data.
+ * bytesin: Count of data in bytes to receive.
+ */
+struct spi_op {
+ const void *dout;
+ size_t bytesout;
+ void *din;
+ size_t bytesin;
+ enum spi_op_status status;
+};
+
/*-----------------------------------------------------------------------
* Representation of a SPI contoller.
*
* claim_bus: Claim SPI bus and prepare for communication.
* release_bus: Release SPI bus.
- * xfer: SPI transfer
* setup: Setup given SPI device bus.
+ * xfer: Perform one SPI transfer operation.
+ * xfer_vector: Vector of SPI transfer operations.
*/
struct spi_ctrlr {
int (*claim_bus)(const struct spi_slave *slave);
void (*release_bus)(const struct spi_slave *slave);
+ int (*setup)(const struct spi_slave *slave);
int (*xfer)(const struct spi_slave *slave, const void *dout,
size_t bytesout, void *din, size_t bytesin);
- int (*setup)(const struct spi_slave *slave);
+ int (*xfer_vector)(const struct spi_slave *slave,
+ struct spi_op vectors[], size_t count);
};
/*-----------------------------------------------------------------------
@@ -134,6 +160,19 @@ void spi_release_bus(const struct spi_slave *slave);
int spi_xfer(const struct spi_slave *slave, const void *dout, size_t bytesout,
void *din, size_t bytesin);
+/*-----------------------------------------------------------------------
+ * Vector of SPI transfer operations
+ *
+ * spi_xfer_vector() interface:
+ * slave: The SPI slave which will be sending/receiving the data.
+ * vectors: Array of SPI op structures.
+ * count: Number of SPI op vectors.
+ *
+ * Returns: 0 on success, not 0 on failure
+ */
+int spi_xfer_vector(const struct spi_slave *slave,
+ struct spi_op vectors[], size_t count);
+
unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len);
/*-----------------------------------------------------------------------
@@ -158,4 +197,23 @@ static inline int spi_w8r8(const struct spi_slave *slave, unsigned char byte)
return ret < 0 ? ret : din[1];
}
+/*
+ * Helper function to allow chipsets to combine two vectors if possible. It can
+ * only handle upto 2 vectors.
+ *
+ * This function is provided to support command-response kind of transactions
+ * expected by users like flash. Some special SPI flash controllers can handle
+ * such command-response operations in a single transaction. For these special
+ * controllers, separate command and response vectors can be combined into a
+ * single operation.
+ *
+ * Two vectors are combined if first vector has a non-NULL dout and NULL din and
+ * second vector has a non-NULL din and NULL dout. Otherwise, each vector is
+ * operated upon one at a time.
+ *
+ * Returns 0 on success and non-zero on failure.
+ */
+int spi_xfer_two_vectors(const struct spi_slave *slave,
+ struct spi_op vectors[], size_t count);
+
#endif /* _SPI_GENERIC_H_ */
diff --git a/src/mainboard/google/purin/Kconfig b/src/mainboard/google/purin/Kconfig
index eabab2ba60..ca0909b25e 100644
--- a/src/mainboard/google/purin/Kconfig
+++ b/src/mainboard/google/purin/Kconfig
@@ -26,7 +26,6 @@ config BOARD_SPECIFIC_OPTIONS # dummy
select SPI_FLASH
select SPI_FLASH_SPANSION
select SPI_FLASH_STMICRO # required for the reference board BCM958305K
- select SPI_ATOMIC_SEQUENCING
config CHROMEOS
select VBOOT_VBNV_FLASH
diff --git a/src/soc/broadcom/cygnus/spi.c b/src/soc/broadcom/cygnus/spi.c
index e597efced9..f03d453307 100644
--- a/src/soc/broadcom/cygnus/spi.c
+++ b/src/soc/broadcom/cygnus/spi.c
@@ -279,6 +279,7 @@ static const struct spi_ctrlr spi_ctrlr = {
.claim_bus = spi_ctrlr_claim_bus,
.release_bus = spi_ctrlr_release_bus,
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/soc/imgtec/pistachio/Kconfig b/src/soc/imgtec/pistachio/Kconfig
index da33cc5c96..1ce488c35b 100644
--- a/src/soc/imgtec/pistachio/Kconfig
+++ b/src/soc/imgtec/pistachio/Kconfig
@@ -22,7 +22,6 @@ config CPU_IMGTEC_PISTACHIO
select GENERIC_UDELAY
select HAVE_MONOTONIC_TIMER
select HAVE_UART_SPECIAL
- select SPI_ATOMIC_SEQUENCING
select GENERIC_GPIO_LIB
select HAVE_HARD_RESET
select UART_OVERRIDE_REFCLK
diff --git a/src/soc/imgtec/pistachio/spi.c b/src/soc/imgtec/pistachio/spi.c
index e956e46f7a..2b706f0980 100644
--- a/src/soc/imgtec/pistachio/spi.c
+++ b/src/soc/imgtec/pistachio/spi.c
@@ -22,10 +22,6 @@
#include <string.h>
#include <timer.h>
-#if !CONFIG_SPI_ATOMIC_SEQUENCING
-#error "Unsupported SPI driver API"
-#endif
-
/* Imgtec controller uses 16 bit packet length. */
#define IMGTEC_SPI_MAX_TRANSFER_SIZE ((1 << 16) - 1)
@@ -496,7 +492,7 @@ static int do_spi_xfer(const struct spi_slave *slave, const void *dout,
}
static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
- size_t bytesout, void *din, size_t bytesin)
+ size_t bytesout, void *din, size_t bytesin)
{
unsigned int in_sz, out_sz;
int ret;
@@ -541,6 +537,7 @@ static const struct spi_ctrlr spi_ctrlr = {
.claim_bus = spi_ctrlr_claim_bus,
.release_bus = spi_ctrlr_release_bus,
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
/* Set up communications parameters for a SPI slave. */
diff --git a/src/soc/intel/baytrail/spi.c b/src/soc/intel/baytrail/spi.c
index 0f7b0c6ced..639954bcfe 100644
--- a/src/soc/intel/baytrail/spi.c
+++ b/src/soc/intel/baytrail/spi.c
@@ -612,6 +612,7 @@ static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/soc/intel/braswell/spi.c b/src/soc/intel/braswell/spi.c
index 2a0ddf8179..86b335107b 100644
--- a/src/soc/intel/braswell/spi.c
+++ b/src/soc/intel/braswell/spi.c
@@ -596,6 +596,7 @@ static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/soc/intel/broadwell/spi.c b/src/soc/intel/broadwell/spi.c
index d2ae94314d..7a7eaf7178 100644
--- a/src/soc/intel/broadwell/spi.c
+++ b/src/soc/intel/broadwell/spi.c
@@ -646,6 +646,7 @@ int spi_flash_protect(u32 start, u32 size)
static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/soc/intel/fsp_baytrail/spi.c b/src/soc/intel/fsp_baytrail/spi.c
index 997bd138ae..dee7440ef6 100644
--- a/src/soc/intel/fsp_baytrail/spi.c
+++ b/src/soc/intel/fsp_baytrail/spi.c
@@ -592,6 +592,7 @@ spi_xfer_exit:
static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/soc/intel/fsp_broadwell_de/spi.c b/src/soc/intel/fsp_broadwell_de/spi.c
index 5848966f38..a6b6f3541a 100644
--- a/src/soc/intel/fsp_broadwell_de/spi.c
+++ b/src/soc/intel/fsp_broadwell_de/spi.c
@@ -609,6 +609,7 @@ spi_xfer_exit:
static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/soc/mediatek/mt8173/Kconfig b/src/soc/mediatek/mt8173/Kconfig
index ec3481e366..7a6ad871cb 100644
--- a/src/soc/mediatek/mt8173/Kconfig
+++ b/src/soc/mediatek/mt8173/Kconfig
@@ -9,7 +9,6 @@ config SOC_MEDIATEK_MT8173
select ARM64_USE_ARM_TRUSTED_FIRMWARE
select BOOTBLOCK_CONSOLE
select HAVE_UART_SPECIAL
- select SPI_ATOMIC_SEQUENCING if SPI_FLASH
select HAVE_MONOTONIC_TIMER
select GENERIC_UDELAY
select GENERIC_GPIO_LIB
diff --git a/src/soc/mediatek/mt8173/spi.c b/src/soc/mediatek/mt8173/spi.c
index 53d5b8ce15..415764a1b6 100644
--- a/src/soc/mediatek/mt8173/spi.c
+++ b/src/soc/mediatek/mt8173/spi.c
@@ -293,6 +293,7 @@ static const struct spi_ctrlr spi_ctrlr = {
.claim_bus = spi_ctrlr_claim_bus,
.release_bus = spi_ctrlr_release_bus,
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/soc/qualcomm/ipq40xx/Kconfig b/src/soc/qualcomm/ipq40xx/Kconfig
index f7386227d0..05f29e4be9 100644
--- a/src/soc/qualcomm/ipq40xx/Kconfig
+++ b/src/soc/qualcomm/ipq40xx/Kconfig
@@ -7,7 +7,6 @@ config SOC_QC_IPQ40XX
select ARCH_RAMSTAGE_ARMV7
select BOOTBLOCK_CONSOLE
select HAVE_UART_SPECIAL
- select SPI_ATOMIC_SEQUENCING
select GENERIC_GPIO_LIB
select HAVE_MONOTONIC_TIMER
diff --git a/src/soc/qualcomm/ipq806x/Kconfig b/src/soc/qualcomm/ipq806x/Kconfig
index 7ba5df5794..32b61bc53a 100644
--- a/src/soc/qualcomm/ipq806x/Kconfig
+++ b/src/soc/qualcomm/ipq806x/Kconfig
@@ -7,7 +7,6 @@ config SOC_QC_IPQ806X
select ARCH_RAMSTAGE_ARMV7
select BOOTBLOCK_CONSOLE
select HAVE_UART_SPECIAL
- select SPI_ATOMIC_SEQUENCING
select GENERIC_GPIO_LIB
if SOC_QC_IPQ806X
diff --git a/src/southbridge/amd/agesa/hudson/spi.c b/src/southbridge/amd/agesa/hudson/spi.c
index 8a4adfb14b..00f6b29303 100644
--- a/src/southbridge/amd/agesa/hudson/spi.c
+++ b/src/southbridge/amd/agesa/hudson/spi.c
@@ -167,6 +167,7 @@ int chipset_volatile_group_end(const struct spi_flash *flash)
static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/southbridge/amd/cimx/sb800/spi.c b/src/southbridge/amd/cimx/sb800/spi.c
index edf192ae0b..1e847439f9 100644
--- a/src/southbridge/amd/cimx/sb800/spi.c
+++ b/src/southbridge/amd/cimx/sb800/spi.c
@@ -158,6 +158,7 @@ int chipset_volatile_group_end(const struct spi_flash *flash)
static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/southbridge/amd/sb700/spi.c b/src/southbridge/amd/sb700/spi.c
index 2e16ca8d5c..5d56415b44 100644
--- a/src/southbridge/amd/sb700/spi.c
+++ b/src/southbridge/amd/sb700/spi.c
@@ -120,6 +120,7 @@ static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/southbridge/intel/common/spi.c b/src/southbridge/intel/common/spi.c
index 093017eda2..ee94937291 100644
--- a/src/southbridge/intel/common/spi.c
+++ b/src/southbridge/intel/common/spi.c
@@ -659,6 +659,7 @@ static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)
diff --git a/src/southbridge/intel/fsp_rangeley/spi.c b/src/southbridge/intel/fsp_rangeley/spi.c
index 8026698721..0bffeb5293 100644
--- a/src/southbridge/intel/fsp_rangeley/spi.c
+++ b/src/southbridge/intel/fsp_rangeley/spi.c
@@ -724,6 +724,7 @@ spi_xfer_exit:
static const struct spi_ctrlr spi_ctrlr = {
.xfer = spi_ctrlr_xfer,
+ .xfer_vector = spi_xfer_two_vectors,
};
int spi_setup_slave(unsigned int bus, unsigned int cs, struct spi_slave *slave)