summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/drivers/spi/gigadevice.c16
-rw-r--r--src/drivers/spi/spi_flash.c41
-rw-r--r--src/drivers/spi/spi_flash_internal.h2
-rw-r--r--src/drivers/spi/winbond.c22
-rw-r--r--src/include/spi-generic.h3
-rw-r--r--src/include/spi_flash.h7
6 files changed, 87 insertions, 4 deletions
diff --git a/src/drivers/spi/gigadevice.c b/src/drivers/spi/gigadevice.c
index cc4cf1dddb..1ff594a24a 100644
--- a/src/drivers/spi/gigadevice.c
+++ b/src/drivers/spi/gigadevice.c
@@ -41,7 +41,9 @@
struct gigadevice_spi_flash_params {
uint16_t id;
- uint8_t l2_page_size_shift;
+ uint8_t dual_spi : 1;
+ uint8_t _reserved_for_flags : 3;
+ uint8_t l2_page_size_shift : 4;
uint8_t pages_per_sector_shift : 4;
uint8_t sectors_per_block_shift : 4;
uint8_t nr_blocks_shift;
@@ -63,6 +65,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 4,
+ .dual_spi = 1,
.name = "GD25Q80",
}, /* also GD25Q80B */
{
@@ -71,6 +74,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 5,
+ .dual_spi = 1,
.name = "GD25Q16",
}, /* also GD25Q16B */
{
@@ -79,6 +83,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 6,
+ .dual_spi = 1,
.name = "GD25Q32B",
}, /* also GD25Q32B */
{
@@ -87,6 +92,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 7,
+ .dual_spi = 1,
.name = "GD25Q64",
}, /* also GD25Q64B, GD25B64C */
{
@@ -95,6 +101,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 8,
+ .dual_spi = 1,
.name = "GD25Q128",
}, /* also GD25Q128B */
{
@@ -103,6 +110,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 4,
+ .dual_spi = 1,
.name = "GD25VQ80C",
},
{
@@ -111,6 +119,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 5,
+ .dual_spi = 1,
.name = "GD25VQ16C",
},
{
@@ -119,6 +128,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 4,
+ .dual_spi = 1,
.name = "GD25LQ80",
},
{
@@ -127,6 +137,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 5,
+ .dual_spi = 1,
.name = "GD25LQ16",
},
{
@@ -135,6 +146,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 6,
+ .dual_spi = 1,
.name = "GD25LQ32",
},
{
@@ -143,6 +155,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 7,
+ .dual_spi = 1,
.name = "GD25LQ64C",
}, /* also GD25LB64C */
{
@@ -151,6 +164,7 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 8,
+ .dual_spi = 1,
.name = "GD25LQ128",
},
};
diff --git a/src/drivers/spi/spi_flash.c b/src/drivers/spi/spi_flash.c
index 5e42a375f6..a81306e386 100644
--- a/src/drivers/spi/spi_flash.c
+++ b/src/drivers/spi/spi_flash.c
@@ -59,6 +59,34 @@ static int do_spi_flash_cmd(const struct spi_slave *spi, const void *dout,
return ret;
}
+static int do_dual_read_cmd(const struct spi_slave *spi, const void *dout,
+ size_t bytes_out, void *din, size_t bytes_in)
+{
+ int ret;
+
+ /*
+ * spi_xfer_vector() will automatically fall back to .xfer() if
+ * .xfer_vector() is unimplemented. So using vector API here is more
+ * flexible, even though a controller that implements .xfer_vector()
+ * and (the non-vector based) .xfer_dual() but not .xfer() would be
+ * pretty odd.
+ */
+ struct spi_op vector = { .dout = dout, .bytesout = bytes_out,
+ .din = NULL, .bytesin = 0 };
+
+ ret = spi_claim_bus(spi);
+ if (ret)
+ return ret;
+
+ ret = spi_xfer_vector(spi, &vector, 1);
+
+ if (!ret)
+ ret = spi->ctrlr->xfer_dual(spi, NULL, 0, din, bytes_in);
+
+ spi_release_bus(spi);
+ return ret;
+}
+
int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response, size_t len)
{
int ret = do_spi_flash_cmd(spi, &cmd, sizeof(cmd), response, len);
@@ -105,6 +133,11 @@ static int spi_flash_read_chunked(const struct spi_flash *flash, u32 offset,
cmd_len = 4;
cmd[0] = CMD_READ_ARRAY_SLOW;
do_cmd = do_spi_flash_cmd;
+ } else if (flash->flags.dual_spi && flash->spi.ctrlr->xfer_dual) {
+ cmd_len = 5;
+ cmd[0] = CMD_READ_FAST_DUAL_OUTPUT;
+ cmd[4] = 0;
+ do_cmd = do_dual_read_cmd;
} else {
cmd_len = 5;
cmd[0] = CMD_READ_ARRAY_FAST;
@@ -347,8 +380,12 @@ int spi_flash_probe(unsigned int bus, unsigned int cs, struct spi_flash *flash)
return -1;
}
- printk(BIOS_INFO, "SF: Detected %s with sector size 0x%x, total 0x%x\n",
- flash->name, flash->sector_size, flash->size);
+ const char *mode_string = "";
+ if (flash->flags.dual_spi && spi.ctrlr->xfer_dual)
+ mode_string = " (Dual SPI mode)";
+ printk(BIOS_INFO,
+ "SF: Detected %s with sector size 0x%x, total 0x%x%s\n",
+ flash->name, flash->sector_size, flash->size, mode_string);
if (bus == CONFIG_BOOT_DEVICE_SPI_FLASH_BUS
&& flash->size != CONFIG_ROM_SIZE) {
printk(BIOS_ERR, "SF size 0x%x does not correspond to"
diff --git a/src/drivers/spi/spi_flash_internal.h b/src/drivers/spi/spi_flash_internal.h
index f15c737461..4a9e289029 100644
--- a/src/drivers/spi/spi_flash_internal.h
+++ b/src/drivers/spi/spi_flash_internal.h
@@ -23,6 +23,8 @@
#define CMD_READ_ARRAY_FAST 0x0b
#define CMD_READ_ARRAY_LEGACY 0xe8
+#define CMD_READ_FAST_DUAL_OUTPUT 0x3b
+
#define CMD_READ_STATUS 0x05
#define CMD_WRITE_ENABLE 0x06
diff --git a/src/drivers/spi/winbond.c b/src/drivers/spi/winbond.c
index d0ef3cd6e0..9e9bb00464 100644
--- a/src/drivers/spi/winbond.c
+++ b/src/drivers/spi/winbond.c
@@ -26,7 +26,9 @@
struct winbond_spi_flash_params {
uint16_t id;
- uint8_t l2_page_size_shift;
+ uint8_t dual_spi : 1;
+ uint8_t _reserved_for_flags : 3;
+ uint8_t l2_page_size_shift : 4;
uint8_t pages_per_sector_shift : 4;
uint8_t sectors_per_block_shift : 4;
uint8_t nr_blocks_shift;
@@ -123,6 +125,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 4,
.name = "W25X80",
+ .dual_spi = 1,
},
{
.id = 0x3015,
@@ -131,6 +134,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 5,
.name = "W25X16",
+ .dual_spi = 1,
},
{
.id = 0x3016,
@@ -139,6 +143,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 6,
.name = "W25X32",
+ .dual_spi = 1,
},
{
.id = 0x3017,
@@ -147,6 +152,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 7,
.name = "W25X64",
+ .dual_spi = 1,
},
{
.id = 0x4014,
@@ -155,6 +161,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 4,
.name = "W25Q80_V",
+ .dual_spi = 1,
},
{
.id = 0x4015,
@@ -163,6 +170,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 5,
.name = "W25Q16_V",
+ .dual_spi = 1,
.protection_granularity_shift = 16,
.bp_bits = 3,
},
@@ -173,6 +181,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 5,
.name = "W25Q16DW",
+ .dual_spi = 1,
.protection_granularity_shift = 16,
.bp_bits = 3,
},
@@ -183,6 +192,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 6,
.name = "W25Q32_V",
+ .dual_spi = 1,
.protection_granularity_shift = 16,
.bp_bits = 3,
},
@@ -193,6 +203,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 6,
.name = "W25Q32DW",
+ .dual_spi = 1,
.protection_granularity_shift = 16,
.bp_bits = 3,
},
@@ -203,6 +214,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 7,
.name = "W25Q64_V",
+ .dual_spi = 1,
.protection_granularity_shift = 17,
.bp_bits = 3,
},
@@ -213,6 +225,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 7,
.name = "W25Q64DW",
+ .dual_spi = 1,
.protection_granularity_shift = 17,
.bp_bits = 3,
},
@@ -223,6 +236,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 8,
.name = "W25Q128_V",
+ .dual_spi = 1,
.protection_granularity_shift = 18,
.bp_bits = 3,
},
@@ -233,6 +247,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 8,
.name = "W25Q128FW",
+ .dual_spi = 1,
.protection_granularity_shift = 18,
.bp_bits = 3,
},
@@ -243,6 +258,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 8,
.name = "W25Q128J",
+ .dual_spi = 1,
.protection_granularity_shift = 18,
.bp_bits = 3,
},
@@ -253,6 +269,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 9,
.name = "W25Q256_V",
+ .dual_spi = 1,
.protection_granularity_shift = 16,
.bp_bits = 4,
},
@@ -263,6 +280,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.sectors_per_block_shift = 4,
.nr_blocks_shift = 9,
.name = "W25Q256J",
+ .dual_spi = 1,
.protection_granularity_shift = 16,
.bp_bits = 4,
},
@@ -681,6 +699,8 @@ int spi_flash_probe_winbond(const struct spi_slave *spi, u8 *idcode,
flash->erase_cmd = CMD_W25_SE;
flash->status_cmd = CMD_W25_RDSR;
+ flash->flags.dual_spi = params->dual_spi;
+
flash->ops = &spi_flash_ops;
flash->driver_private = params;
diff --git a/src/include/spi-generic.h b/src/include/spi-generic.h
index c24aadd926..d0f957f1f9 100644
--- a/src/include/spi-generic.h
+++ b/src/include/spi-generic.h
@@ -125,6 +125,7 @@ enum {
* setup: Setup given SPI device bus.
* xfer: Perform one SPI transfer operation.
* xfer_vector: Vector of SPI transfer operations.
+ * xfer_dual: (optional) Perform one SPI transfer in Dual SPI mode.
* max_xfer_size: Maximum transfer size supported by the controller
* (0 = invalid,
* SPI_CTRLR_DEFAULT_MAX_XFER_SIZE = unlimited)
@@ -145,6 +146,8 @@ struct spi_ctrlr {
size_t bytesout, void *din, size_t bytesin);
int (*xfer_vector)(const struct spi_slave *slave,
struct spi_op vectors[], size_t count);
+ int (*xfer_dual)(const struct spi_slave *slave, const void *dout,
+ size_t bytesout, void *din, size_t bytesin);
uint32_t max_xfer_size;
uint32_t flags;
int (*flash_probe)(const struct spi_slave *slave,
diff --git a/src/include/spi_flash.h b/src/include/spi_flash.h
index 936b0abe85..3a0c383676 100644
--- a/src/include/spi_flash.h
+++ b/src/include/spi_flash.h
@@ -90,6 +90,13 @@ struct spi_flash_ops {
struct spi_flash {
struct spi_slave spi;
u8 vendor;
+ union {
+ u8 raw;
+ struct {
+ u8 dual_spi : 1;
+ u8 _reserved : 7;
+ };
+ } flags;
u16 model;
const char *name;
u32 size;