summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/drivers/spi/Kconfig9
-rw-r--r--src/mainboard/google/nyan/Kconfig2
-rw-r--r--src/soc/nvidia/tegra124/spi.c53
-rw-r--r--src/soc/nvidia/tegra124/spi.h1
4 files changed, 53 insertions, 12 deletions
diff --git a/src/drivers/spi/Kconfig b/src/drivers/spi/Kconfig
index 6b31053497..d832e0c233 100644
--- a/src/drivers/spi/Kconfig
+++ b/src/drivers/spi/Kconfig
@@ -103,4 +103,13 @@ config SPI_FLASH_WINBOND
Select this option if your chipset driver needs to store certain
data in the SPI flash and your SPI flash is made by Winbond.
+config SPI_FLASH_FAST_READ_DUAL_OUTPUT_3B
+ bool
+ default n
+ depends on SPI_FLASH
+ help
+ Select this option if your SPI flash supports the fast read dual-
+ output command (opcode 0x3b) where the opcode and address are sent
+ to the chip on MOSI and data is received on both MOSI and MISO.
+
endif # SPI_FLASH
diff --git a/src/mainboard/google/nyan/Kconfig b/src/mainboard/google/nyan/Kconfig
index 1a7b2604b0..320039f401 100644
--- a/src/mainboard/google/nyan/Kconfig
+++ b/src/mainboard/google/nyan/Kconfig
@@ -28,6 +28,8 @@ config BOARD_SPECIFIC_OPTIONS # dummy
select MAINBOARD_HAS_BOOTBLOCK_INIT
select MAINBOARD_DO_NATIVE_VGA_INIT
select BOARD_ROMSIZE_KB_1024
+ select SPI_FLASH
+ select SPI_FLASH_FAST_READ_DUAL_OUTPUT_3B
config MAINBOARD_DIR
string
diff --git a/src/soc/nvidia/tegra124/spi.c b/src/soc/nvidia/tegra124/spi.c
index c13f2c355e..ae8a9a7626 100644
--- a/src/soc/nvidia/tegra124/spi.c
+++ b/src/soc/nvidia/tegra124/spi.c
@@ -710,18 +710,20 @@ static int xfer_finish(struct tegra_spi_channel *spi)
return ret;
}
+unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len)
+{
+ return buf_len;
+}
+
int spi_xfer(struct spi_slave *slave, const void *dout,
- unsigned int bitsout, void *din, unsigned int bitsin)
+ unsigned int out_bytes, void *din, unsigned int in_bytes)
{
- unsigned int out_bytes = bitsout / 8, in_bytes = bitsin / 8;
struct tegra_spi_channel *spi = to_tegra_spi(slave->bus);
u8 *out_buf = (u8 *)dout;
u8 *in_buf = (u8 *)din;
unsigned int todo;
int ret = 0, frame_started = 1;
- ASSERT(bitsout % 8 == 0 && bitsin % 8 == 0);
-
/* tegra bus numbers start at 1 */
ASSERT(slave->bus >= 1 && slave->bus <= ARRAY_SIZE(tegra_spi_channels));
@@ -842,19 +844,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;
+
+ channel = to_tegra_spi(spi->slave->bus);
- /* TODO: Dual mode (BOTH_EN_BIT) and packed mode */
- spi_read_cmd[0] = JEDEC_READ;
+ 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 +883,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, 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 (spi_xfer(spi->slave, NULL, 0, dest, count * 8)) {
+ if (channel->dual_mode) {
+ setbits_le32(&channel->regs->command1, SPI_CMD1_BOTH_EN_BIT);
+ }
+ if (spi_xfer(spi->slave, NULL, 0, dest, count)) {
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 +949,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 b722240dcc..fa238f2d03 100644
--- a/src/soc/nvidia/tegra124/spi.h
+++ b/src/soc/nvidia/tegra124/spi.h
@@ -56,6 +56,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;