summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHung-Te Lin <hungte@chromium.org>2013-06-26 20:22:50 +0800
committerStefan Reinauer <stefan.reinauer@coreboot.org>2013-07-10 23:17:16 +0200
commitcab3621446542fadf67e9406c4ae39fb63a0536f (patch)
tree49e7d7453f4dea3791e1280f672de9a73b0a3929
parented1742cafec5627023e12f8bde52282247d17ddd (diff)
armv7/exynos5420: Revise SPI open/close/reset procedure.
The original Exynos SPI open/close procedure was copied from U-Boot SPL with some assumptions that only works in SPL stage. For example, it tries to always work in 4-byte transmission mode with only RX data is swapped, and claims a packet for initial address command (and with incorrect size). This commit revises open/close and reset so only the required SPI registers are configured. Change-Id: Ieba1f03d80a8949c39a6658218831ded39853744 Signed-off-by: Hung-Te Lin <hungte@chromium.org> Signed-off-by: Gabe Black <gabeblack@chromium.org> Reviewed-on: http://review.coreboot.org/3712 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
-rw-r--r--src/cpu/samsung/exynos5420/spi.c76
1 files changed, 39 insertions, 37 deletions
diff --git a/src/cpu/samsung/exynos5420/spi.c b/src/cpu/samsung/exynos5420/spi.c
index 82c37650da..6637aad9e8 100644
--- a/src/cpu/samsung/exynos5420/spi.c
+++ b/src/cpu/samsung/exynos5420/spi.c
@@ -102,6 +102,26 @@ void spi_cs_deactivate(struct spi_slave *slave)
setbits_le32(&regs->cs_reg, SPI_SLAVE_SIG_INACT);
}
+static inline void exynos_spi_soft_reset(struct exynos_spi *regs)
+{
+ /* The soft reset clears only FIFO and status register.
+ * All special function registers are not changed. */
+ setbits_le32(&regs->ch_cfg, SPI_CH_RST);
+ clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
+}
+
+static inline void exynos_spi_flush_fifo(struct exynos_spi *regs)
+{
+ /*
+ * Flush spi tx, rx fifos and reset the SPI controller
+ * and clear rx/tx channel
+ */
+ clrbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
+ clrbits_le32(&regs->ch_cfg, SPI_CH_HS_EN);
+ exynos_spi_soft_reset(regs);
+ setbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
+}
+
static void exynos_spi_rx_tx(struct exynos_spi *regs, int todo,
void *dinp, void const *doutp, int i)
{
@@ -114,8 +134,7 @@ static void exynos_spi_rx_tx(struct exynos_spi *regs, int todo,
ASSERT(todo % 4 == 0);
out_bytes = in_bytes = todo;
- setbits_le32(&regs->ch_cfg, SPI_CH_RST);
- clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
+ exynos_spi_soft_reset(regs);
writel(((todo * 8) / 32) | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
while (in_bytes) {
@@ -143,35 +162,27 @@ static void exynos_spi_rx_tx(struct exynos_spi *regs, int todo,
}
}
-/* set up SPI channel */
int spi_claim_bus(struct spi_slave *slave)
{
struct exynos_spi_slave *espi = to_exynos_spi(slave);
struct exynos_spi *regs = espi->regs;
- /* set the spi1 GPIO */
+ exynos_spi_flush_fifo(regs);
- /* set pktcnt and enable it */
- writel(4 | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
- /* set FB_CLK_SEL */
- writel(SPI_FB_DELAY_180, &regs->fb_clk);
- /* set CH_WIDTH and BUS_WIDTH as word */
- setbits_le32(&regs->mode_cfg,
- SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
- clrbits_le32(&regs->ch_cfg, SPI_CH_CPOL_L); /* CPOL: active high */
+ // Select Active High Clock, Format A (SCP 30.2.1.8).
+ clrbits_le32(&regs->ch_cfg, SPI_CH_CPOL_L | SPI_CH_CPHA_B);
- /* clear rx and tx channel if set priveously */
- clrbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
-
- setbits_le32(&regs->swap_cfg,
- SPI_RX_SWAP_EN | SPI_RX_BYTE_SWAP | SPI_RX_HWORD_SWAP);
-
- /* do a soft reset */
- setbits_le32(&regs->ch_cfg, SPI_CH_RST);
- clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
+ // Set FeedBack Clock Selection.
+ writel(SPI_FB_DELAY_180, &regs->fb_clk);
- /* now set rx and tx channel ON */
- setbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON | SPI_CH_HS_EN);
+ // HIGH speed is required for Tx/Rx to work in 50MHz (SCP 30.2.1.6).
+ if (espi->half_duplex) {
+ clrbits_le32(&regs->ch_cfg, SPI_CH_HS_EN);
+ printk(BIOS_DEBUG, "%s: LOW speed.\n", __func__);
+ } else {
+ setbits_le32(&regs->ch_cfg, SPI_CH_HS_EN);
+ printk(BIOS_DEBUG, "%s: HIGH speed.\n", __func__);
+ }
return 0;
}
@@ -209,21 +220,12 @@ static int exynos_spi_read(struct spi_slave *slave, void *dest, uint32_t len,
void spi_release_bus(struct spi_slave *slave)
{
struct exynos_spi *regs = to_exynos_spi(slave)->regs;
- /*
- * Let put controller mode to BYTE as
- * SPI driver does not support WORD mode yet
- */
- clrbits_le32(&regs->mode_cfg,
- SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
+ /* Reset swap mode to make sure no one relying on default values (Ex,
+ * payload or kernel) will go wrong. */
+ clrbits_le32(&regs->mode_cfg, (SPI_MODE_CH_WIDTH_WORD |
+ SPI_MODE_BUS_WIDTH_WORD));
writel(0, &regs->swap_cfg);
-
- /*
- * Flush spi tx, rx fifos and reset the SPI controller
- * and clear rx/tx channel
- */
- clrsetbits_le32(&regs->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
- clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
- clrbits_le32(&regs->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
+ exynos_spi_flush_fifo(regs);
}
// SPI as CBFS media.