diff --git a/src/soc/nvidia/tegra124/spi.c b/src/soc/nvidia/tegra124/spi.c index aefac1d044..767bd97ab3 100644 --- a/src/soc/nvidia/tegra124/spi.c +++ b/src/soc/nvidia/tegra124/spi.c @@ -842,19 +842,37 @@ static int tegra_spi_cbfs_close(struct cbfs_media *media) return 0; } -#define JEDEC_READ 0x03 -#define JEDEC_READ_OUTSIZE 0x04 -/* JEDEC_READ_INSIZE : any length */ +#define JEDEC_READ 0x03 +#define JEDEC_READ_OUTSIZE 0x04 +#define JEDEC_FAST_READ_DUAL 0x3b +#define JEDEC_FAST_READ_DUAL_OUTSIZE 0x05 static size_t tegra_spi_cbfs_read(struct cbfs_media *media, void *dest, size_t offset, size_t count) { struct tegra_spi_media *spi = (struct tegra_spi_media *)media->context; - u8 spi_read_cmd[JEDEC_READ_OUTSIZE]; + u8 spi_read_cmd[JEDEC_FAST_READ_DUAL_OUTSIZE]; + unsigned int read_cmd_bytes; int ret = count; + struct tegra_spi_channel *channel; - /* TODO: Dual mode (BOTH_EN_BIT) and packed mode */ - spi_read_cmd[0] = JEDEC_READ; + channel = to_tegra_spi(spi->slave->bus); + + if (channel->dual_mode) { + /* + * Command 0x3b will interleave data only, command 0xbb will + * interleave the address as well. It's nice to see the address + * plainly when debugging, and we're mostly concerned with + * large transfers so the optimization of using 0xbb isn't + * really worthwhile. + */ + spi_read_cmd[0] = JEDEC_FAST_READ_DUAL; + spi_read_cmd[4] = 0x00; /* dummy byte */ + read_cmd_bytes = JEDEC_FAST_READ_DUAL_OUTSIZE; + } else { + spi_read_cmd[0] = JEDEC_READ; + read_cmd_bytes = JEDEC_READ_OUTSIZE; + } spi_read_cmd[1] = (offset >> 16) & 0xff; spi_read_cmd[2] = (offset >> 8) & 0xff; spi_read_cmd[3] = offset & 0xff; @@ -863,18 +881,23 @@ static size_t tegra_spi_cbfs_read(struct cbfs_media *media, void *dest, spi_cs_activate(spi->slave); if (spi_xfer(spi->slave, spi_read_cmd, - sizeof(spi_read_cmd) * 8, NULL, 0) < 0) { + read_cmd_bytes * 8, NULL, 0) < 0) { ret = -1; printk(BIOS_ERR, "%s: Failed to transfer %u bytes\n", __func__, sizeof(spi_read_cmd)); goto tegra_spi_cbfs_read_exit; } + if (channel->dual_mode) { + setbits_le32(&channel->regs->command1, SPI_CMD1_BOTH_EN_BIT); + } if (spi_xfer(spi->slave, NULL, 0, dest, count * 8)) { ret = -1; printk(BIOS_ERR, "%s: Failed to transfer %u bytes\n", __func__, count); } + if (channel->dual_mode) + clrbits_le32(&channel->regs->command1, SPI_CMD1_BOTH_EN_BIT); tegra_spi_cbfs_read_exit: /* de-assert /CS */ @@ -924,6 +947,10 @@ int initialize_tegra_spi_cbfs_media(struct cbfs_media *media, media->map = tegra_spi_cbfs_map; media->unmap = tegra_spi_cbfs_unmap; +#if CONFIG_SPI_FLASH_FAST_READ_DUAL_OUTPUT_3B == 1 + channel->dual_mode = 1; +#endif + return 0; } diff --git a/src/soc/nvidia/tegra124/spi.h b/src/soc/nvidia/tegra124/spi.h index 157fa131e2..857c35f1b9 100644 --- a/src/soc/nvidia/tegra124/spi.h +++ b/src/soc/nvidia/tegra124/spi.h @@ -57,6 +57,7 @@ struct tegra_spi_channel { /* stuff that is specific to the attached device */ int rx_frame_header_enable; u8 frame_header; + int dual_mode; /* for x2 transfers with bit interleaving */ /* context (used internally) */ u8 *in_buf, *out_buf;