summaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/spi/Kconfig10
-rw-r--r--src/drivers/spi/spi_flash.c47
2 files changed, 54 insertions, 3 deletions
diff --git a/src/drivers/spi/Kconfig b/src/drivers/spi/Kconfig
index f96bf9f88b..cc8703f3fb 100644
--- a/src/drivers/spi/Kconfig
+++ b/src/drivers/spi/Kconfig
@@ -26,6 +26,16 @@ config SPI_FLASH
if SPI_FLASH
+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_MEMORY_MAPPED
bool
default y if ARCH_X86
diff --git a/src/drivers/spi/spi_flash.c b/src/drivers/spi/spi_flash.c
index de19b2ba06..0d67c43385 100644
--- a/src/drivers/spi/spi_flash.c
+++ b/src/drivers/spi/spi_flash.c
@@ -25,9 +25,50 @@ 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(struct spi_slave *spi, const void *dout,
+ unsigned int bytes_out, void *din, unsigned int bytes_in)
+{
+ int ret = 1;
+
+#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
+
+ ret = 0;
+done:
+ return ret;
+}
+
int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len)
{
- int ret = spi_xfer(spi, &cmd, sizeof(cmd), response, len);
+ int ret = do_spi_flash_cmd(spi, &cmd, sizeof(cmd), response, len);
if (ret)
printk(BIOS_WARNING, "SF: Failed to send command %02x: %d\n", cmd, ret);
@@ -37,7 +78,7 @@ int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len)
int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
size_t cmd_len, void *data, size_t data_len)
{
- int ret = spi_xfer(spi, cmd, cmd_len, data, data_len);
+ int ret = do_spi_flash_cmd(spi, cmd, cmd_len, data, data_len);
if (ret) {
printk(BIOS_WARNING, "SF: Failed to send read command (%zu bytes): %d\n",
data_len, ret);
@@ -54,7 +95,7 @@ int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
memcpy(buff, cmd, cmd_len);
memcpy(buff + cmd_len, data, data_len);
- ret = spi_xfer(spi, buff, cmd_len + data_len, NULL, 0);
+ ret = do_spi_flash_cmd(spi, buff, cmd_len + data_len, NULL, 0);
if (ret) {
printk(BIOS_WARNING, "SF: Failed to send write command (%zu bytes): %d\n",
data_len, ret);