diff options
Diffstat (limited to 'src/drivers/spi')
-rw-r--r-- | src/drivers/spi/Kconfig | 10 | ||||
-rw-r--r-- | src/drivers/spi/spi-generic.c | 99 | ||||
-rw-r--r-- | src/drivers/spi/spi_flash.c | 47 |
3 files changed, 114 insertions, 42 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; } |