[coreboot-gerrit] Patch set updated for coreboot: 16bec7e armv7/exynos5420: Add output ability and half-duplex mode in SPI driver.

Gabe Black (gabeblack@chromium.org) gerrit at coreboot.org
Wed Jul 10 05:07:38 CEST 2013


Gabe Black (gabeblack at chromium.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3713

-gerrit

commit 16bec7ee85a3aaec67b54d57a6cbb46a135cec73
Author: Hung-Te Lin <hungte at chromium.org>
Date:   Wed Jun 26 20:29:06 2013 +0800

    armv7/exynos5420: Add output ability and half-duplex mode in SPI driver.
    
    The SPI driver (exynos_spi_rx_tx) was implemented with only "read" ability and
    only full-duplex mode. To communicate with devices like ChromeOS EC, we need
    both output (tx) and half-duplex (searching frame header) features.
    
    This commit adds a spi_rx_tx that can handle all cases we need.
    
    Change-Id: I6aba3839eb0711d49c143dc0620245c0dfe782d8
    Signed-off-by: Hung-Te Lin <hungte at chromium.org>
    Signed-off-by: Gabe Black <gabeblack at chromium.org>
---
 src/cpu/samsung/exynos5420/spi.c | 115 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/src/cpu/samsung/exynos5420/spi.c b/src/cpu/samsung/exynos5420/spi.c
index 6637aad..dddbec5 100644
--- a/src/cpu/samsung/exynos5420/spi.c
+++ b/src/cpu/samsung/exynos5420/spi.c
@@ -27,6 +27,8 @@
 #include "cpu.h"
 #include "spi.h"
 
+#define EXYNOS_SPI_MAX_TRANSFER_BYTES (65535)
+
 #if defined(CONFIG_DEBUG_SPI) && CONFIG_DEBUG_SPI
 # define DEBUG_SPI(x,...)	printk(BIOS_DEBUG, "EXYNOS_SPI: " x)
 #else
@@ -122,6 +124,119 @@ static inline void exynos_spi_flush_fifo(struct exynos_spi *regs)
 	setbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
 }
 
+static void exynos_spi_request_bytes(struct exynos_spi *regs, int count,
+				     int width)
+{
+	uint32_t mode_word = SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD,
+		 swap_word = (SPI_TX_SWAP_EN | SPI_RX_SWAP_EN |
+			      SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP |
+			      SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP);
+
+	/* For word address we need to swap bytes */
+	if (width == sizeof(uint32_t)) {
+		setbits_le32(&regs->mode_cfg, mode_word);
+		setbits_le32(&regs->swap_cfg, swap_word);
+		count /= width;
+	} else {
+		/* Select byte access and clear the swap configuration */
+		clrbits_le32(&regs->mode_cfg, mode_word);
+		writel(0, &regs->swap_cfg);
+	}
+
+	exynos_spi_soft_reset(regs);
+
+	if (count) {
+		ASSERT(count < (1 << 16));
+		writel(count | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
+	} else {
+		writel(0, &regs->pkt_cnt);
+	}
+}
+
+int spi_rx_tx(struct spi_slave *slave, uint8_t *rxp, int rx_bytes,
+	      const uint8_t *txp, int tx_bytes);
+int spi_rx_tx(struct spi_slave *slave, uint8_t *rxp, int rx_bytes,
+	      const uint8_t *txp, int tx_bytes)
+{
+	struct exynos_spi_slave *espi = to_exynos_spi(slave);
+	struct exynos_spi *regs = espi->regs;
+
+	int step;
+	int todo = MAX(rx_bytes, tx_bytes);
+	int wait_for_frame_header = espi->half_duplex;
+
+	ASSERT(todo < EXYNOS_SPI_MAX_TRANSFER_BYTES);
+
+	/* Select transfer mode. */
+	if (espi->half_duplex) {
+		step = 1;
+	} else if ((rx_bytes | tx_bytes | (uintptr_t)rxp |(uintptr_t)txp) & 3) {
+		printk(BIOS_CRIT, "%s: WARNING: tranfer mode decreased to 1B\n",
+		       __func__);
+		step = 1;
+	} else {
+		step = sizeof(uint32_t);
+	}
+
+	exynos_spi_request_bytes(regs, espi->half_duplex ? 0 : todo, step);
+
+	/* Note: Some device, like ChromeOS EC, tries to work in half-duplex
+	 * mode and sends a large amount of data (larger than FIFO size).
+	 * Printing lots of debug messages or doing extra delay in the loop
+	 * below may cause rx buffer to overflow and getting unexpected data
+	 * error.
+	 */
+	while (rx_bytes || tx_bytes) {
+		int temp;
+		uint32_t spi_sts = readl(&regs->spi_sts);
+		int rx_lvl = (spi_sts >> SPI_RX_LVL_OFFSET) & SPI_FIFO_LVL_MASK,
+		    tx_lvl = (spi_sts >> SPI_TX_LVL_OFFSET) & SPI_FIFO_LVL_MASK;
+		int min_tx = ((tx_bytes || !espi->half_duplex) ?
+			      (espi->fifo_size / 2) : 1);
+
+		// TODO(hungte) Abort if timeout happens in half-duplex mode.
+
+		/*
+		 * Don't completely fill the txfifo, since we don't want our
+		 * rxfifo to overflow, and it may already contain data.
+		 */
+		while (tx_lvl < min_tx) {
+			if (tx_bytes) {
+				if (step == sizeof(uint32_t)) {
+					temp = *((uint32_t *)txp);
+					txp += sizeof(uint32_t);
+				} else {
+					temp = *txp++;
+				}
+				tx_bytes -= step;
+			} else {
+				temp = -1;
+			}
+			writel(temp, &regs->tx_data);
+			tx_lvl += step;
+		}
+
+		while ((rx_lvl >= step) && rx_bytes) {
+			temp = readl(&regs->rx_data);
+			rx_lvl -= step;
+			if (wait_for_frame_header) {
+				if ((temp & 0xff) == espi->frame_header) {
+					wait_for_frame_header = 0;
+				}
+				break;  /* Restart the outer loop. */
+			}
+			if (step == sizeof(uint32_t)) {
+				*((uint32_t *)rxp) = temp;
+				rxp += sizeof(uint32_t);
+			} else {
+				*rxp++ = temp;
+			}
+			rx_bytes -= step;
+		}
+	}
+	return 0;
+}
+
 static void exynos_spi_rx_tx(struct exynos_spi *regs, int todo,
 			     void *dinp, void const *doutp, int i)
 {



More information about the coreboot-gerrit mailing list