[coreboot-gerrit] Patch set updated for coreboot: c50dfcd cygnus: add QSPI driver

Patrick Georgi (pgeorgi@google.com) gerrit at coreboot.org
Mon Apr 20 15:19:16 CEST 2015


Patrick Georgi (pgeorgi at google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9849

-gerrit

commit c50dfcde8633030050daaaa2f910a90c4f166de9
Author: Corneliu Doban <cdoban at broadcom.com>
Date:   Wed Feb 18 17:25:20 2015 -0800

    cygnus: add QSPI driver
    
    The driver uses the MSPI controller to read/write to/from SPI flash
    
    BUG=chrome-os-partner:35811
    BRANCH=boradcom-firmware
    TEST=bootblock loads and executes verstage
    
    Change-Id: I34c7882170e4f89bee1b6001563c09b16dfea8ca
    Signed-off-by: Patrick Georgi <pgeorgi at chromium.org>
    Original-Commit-Id: 8c3b156019df429e9d12728224ed4eec8436f415
    Original-Signed-off-by: Corneliu Doban <cdoban at broadcom.com>
    Original-Reviewed-on: https://chrome-internal-review.googlesource.com/199776
    Original-Reviewed-by: Scott Branden <sbranden at broadcom.com>
    Original-Tested-by: Corneliu Doban <cdoban at broadcom.com>
    Original-Commit-Queue: Corneliu Doban <cdoban at broadcom.com>
    Original-Change-Id: Ice798ec76011ee47e13174b4c5534b0d0bc8b4ad
    Original-Reviewed-on: https://chromium-review.googlesource.com/256414
    Original-Reviewed-by: Aaron Durbin <adurbin at chromium.org>
    Original-Tested-by: Daisuke Nojiri <dnojiri at chromium.org>
    Original-Commit-Queue: Daisuke Nojiri <dnojiri at chromium.org>
---
 src/mainboard/google/purin/Kconfig               |   4 +-
 src/soc/broadcom/cygnus/include/soc/addressmap.h |   2 +
 src/soc/broadcom/cygnus/spi.c                    | 289 ++++++++++++++++++++++-
 3 files changed, 287 insertions(+), 8 deletions(-)

diff --git a/src/mainboard/google/purin/Kconfig b/src/mainboard/google/purin/Kconfig
index 5d72513..b9d73f0 100644
--- a/src/mainboard/google/purin/Kconfig
+++ b/src/mainboard/google/purin/Kconfig
@@ -30,6 +30,8 @@ config BOARD_SPECIFIC_OPTIONS # dummy
 	select SOC_BROADCOM_CYGNUS
 	select SPI_FLASH
 	select SPI_FLASH_SPANSION
+	select SPI_FLASH_STMICRO # required for the reference board BCM958305K
+	select SPI_ATOMIC_SEQUENCING
 
 config MAINBOARD_DIR
 	string
@@ -49,7 +51,7 @@ config VBOOT_RAMSTAGE_INDEX
 
 config BOOT_MEDIA_SPI_BUS
 	int
-	default 2
+	default 0
 
 config DRAM_SIZE_MB
 	int
diff --git a/src/soc/broadcom/cygnus/include/soc/addressmap.h b/src/soc/broadcom/cygnus/include/soc/addressmap.h
index ef6a709..a9af3d8 100644
--- a/src/soc/broadcom/cygnus/include/soc/addressmap.h
+++ b/src/soc/broadcom/cygnus/include/soc/addressmap.h
@@ -23,4 +23,6 @@
 #define IPROC_PERIPH_BASE		0x19020000
 #define IPROC_PERIPH_GLB_TIM_REG_BASE	(IPROC_PERIPH_BASE + 0x200)
 
+#define IPROC_QSPI_BASE			0x18047000
+
 #endif	/* __SOC_BROADCOM_CYGNUS_ADDRESSMAP_H__ */
diff --git a/src/soc/broadcom/cygnus/spi.c b/src/soc/broadcom/cygnus/spi.c
index 16e09ae..be3e8d0 100644
--- a/src/soc/broadcom/cygnus/spi.c
+++ b/src/soc/broadcom/cygnus/spi.c
@@ -1,7 +1,5 @@
 /*
- * This file is part of the coreboot project.
- *
- * Copyright 2015 Google Inc.
+ * Copyright (C) 2015 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,25 +15,302 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <stddef.h>
+#include <arch/io.h>
+#include <timer.h>
+#include <delay.h>
+#include <stdlib.h>
 #include <spi-generic.h>
+#include <spi_flash.h>
+#include <soc/addressmap.h>
+
+#define IPROC_QSPI_CLK	100000000
+
+/* SPI mode flags */
+#define	SPI_CPHA	0x01		/* clock phase */
+#define	SPI_CPOL	0x02		/* clock polarity */
+#define	SPI_MODE_0	(0|0)		/* original MicroWire */
+#define	SPI_MODE_1	(0|SPI_CPHA)
+#define	SPI_MODE_2	(SPI_CPOL|0)
+#define	SPI_MODE_3	(SPI_CPOL|SPI_CPHA)
+
+#define QSPI_MAX_HZ			50000000
+#define QSPI_MODE			SPI_MODE_3
+
+#define QSPI_WAIT_TIMEOUT		200U  /* msec */
+
+/* Controller attributes */
+#define SPBR_MIN			8U
+#define SPBR_MAX			255U
+#define NUM_TXRAM			32
+#define NUM_RXRAM			32
+#define NUM_CDRAM			16
+
+/*
+ * Register fields
+ */
+#define MSPI_SPCR0_MSB_BITS_8		0x00000020
+
+/* BSPI registers */
+#define BSPI_MAST_N_BOOT_CTRL_REG	0x008
+#define BSPI_BUSY_STATUS_REG		0x00c
+
+/* MSPI registers */
+#define MSPI_SPCR0_LSB_REG		0x200
+#define MSPI_SPCR0_MSB_REG		0x204
+#define MSPI_SPCR1_LSB_REG		0x208
+#define MSPI_SPCR1_MSB_REG		0x20c
+#define MSPI_NEWQP_REG			0x210
+#define MSPI_ENDQP_REG			0x214
+#define MSPI_SPCR2_REG			0x218
+#define MSPI_STATUS_REG			0x220
+#define MSPI_CPTQP_REG			0x224
+#define MSPI_TXRAM_REG			0x240
+#define MSPI_RXRAM_REG			0x2c0
+#define MSPI_CDRAM_REG			0x340
+#define MSPI_WRITE_LOCK_REG		0x380
+#define MSPI_DISABLE_FLUSH_GEN_REG	0x384
+
+/*
+ * Register access macros
+ */
+#define REG_RD(x)	readl(x)
+#define REG_WR(x, y)	writel((y), (x))
+#define REG_CLR(x, y)	REG_WR((x), REG_RD(x) & ~(y))
+#define REG_SET(x, y)	REG_WR((x), REG_RD(x) | (y))
+
+/* QSPI private data */
+struct qspi_priv {
+	/* Slave entry */
+	struct spi_slave slave;
+
+	/* Specified SPI parameters */
+	unsigned int max_hz;
+	unsigned int spi_mode;
+
+	int mspi_enabled;
+	int mspi_16bit;
+
+	int bus_claimed;
+
+	/* Registers */
+	void *reg;
+};
+
+static struct qspi_priv qspi_slave;
+
+/* Macro to get the private data */
+#define to_qspi_slave(s) container_of(s, struct qspi_priv, slave)
 
 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
 {
-	return NULL;
+	struct qspi_priv *priv = &qspi_slave;
+	unsigned int spbr;
+
+	priv->slave.bus = bus;
+	priv->slave.cs = cs;
+	priv->max_hz = QSPI_MAX_HZ;
+	priv->spi_mode = QSPI_MODE;
+	priv->reg = (void *)(IPROC_QSPI_BASE);
+	priv->mspi_enabled = 0;
+	priv->bus_claimed = 0;
+
+	/* MSPI: Basic hardware initialization */
+	REG_WR(priv->reg + MSPI_SPCR1_LSB_REG, 0);
+	REG_WR(priv->reg + MSPI_SPCR1_MSB_REG, 0);
+	REG_WR(priv->reg + MSPI_NEWQP_REG, 0);
+	REG_WR(priv->reg + MSPI_ENDQP_REG, 0);
+	REG_WR(priv->reg + MSPI_SPCR2_REG, 0);
+
+	/* MSPI: SCK configuration */
+	spbr = (IPROC_QSPI_CLK - 1) / (2 * priv->max_hz) + 1;
+	REG_WR(priv->reg + MSPI_SPCR0_LSB_REG,
+	       MAX(MIN(spbr, SPBR_MAX), SPBR_MIN));
+
+	/* MSPI: Mode configuration (8 bits by default) */
+	priv->mspi_16bit = 0;
+	REG_WR(priv->reg + MSPI_SPCR0_MSB_REG,
+	       0x80 |			/* Master */
+	       (8 << 2) |		/* 8 bits per word */
+	       (priv->spi_mode & 3));	/* mode: CPOL / CPHA */
+
+	return &priv->slave;
+}
+
+static int mspi_enable(struct qspi_priv *priv)
+{
+	struct stopwatch sw;
+
+	/* Switch to MSPI if not yet */
+	if ((REG_RD(priv->reg + BSPI_MAST_N_BOOT_CTRL_REG) & 1) == 0) {
+		stopwatch_init_msecs_expire(&sw, QSPI_WAIT_TIMEOUT);
+		while (!stopwatch_expired(&sw)) {
+			if ((REG_RD(priv->reg + BSPI_BUSY_STATUS_REG) & 1)
+			    == 0) {
+				REG_WR(priv->reg + BSPI_MAST_N_BOOT_CTRL_REG,
+				       1);
+				udelay(1);
+				break;
+			}
+			udelay(1);
+		}
+		if (REG_RD(priv->reg + BSPI_MAST_N_BOOT_CTRL_REG) != 1)
+			return -1;
+	}
+	priv->mspi_enabled = 1;
+	return 0;
 }
 
 int spi_claim_bus(struct spi_slave *slave)
 {
+	struct qspi_priv *priv = to_qspi_slave(slave);
+
+	if (priv->bus_claimed)
+		return -1;
+
+	if (!priv->mspi_enabled)
+		if (mspi_enable(priv))
+			return -1;
+
+	/* MSPI: Enable write lock */
+	REG_WR(priv->reg + MSPI_WRITE_LOCK_REG, 1);
+
+	priv->bus_claimed = 1;
+
 	return 0;
 }
 
 void spi_release_bus(struct spi_slave *slave)
 {
+	struct qspi_priv *priv = to_qspi_slave(slave);
+
+	/* MSPI: Disable write lock */
+	REG_WR(priv->reg + MSPI_WRITE_LOCK_REG, 0);
+
+	priv->bus_claimed = 0;
 }
 
-int spi_xfer(struct spi_slave *slave, const void *dout,
-	     unsigned out_bytes, void *din, unsigned in_bytes)
+#define RXRAM_16B(p, i)	(REG_RD((p)->reg + MSPI_RXRAM_REG + ((i) << 2)) & 0xff)
+#define RXRAM_8B(p, i)	(REG_RD((p)->reg + MSPI_RXRAM_REG + \
+				((((i) << 1) + 1) << 2)) & 0xff)
+
+int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bytesout,
+	     void *din, unsigned int bytesin)
 {
+	struct qspi_priv *priv = to_qspi_slave(slave);
+	const u8 *tx = (const u8 *)dout;
+	u8 *rx = (u8 *)din;
+	unsigned int bytes = bytesout + bytesin;
+	unsigned int rx_idx = 0;
+	unsigned int tx_idx = 0;
+	unsigned int in = 0;
+	unsigned int chunk;
+	unsigned int queues;
+	unsigned int i;
+	struct stopwatch sw;
+
+	if (!priv->bus_claimed)
+		return -1;
+
+	if (bytes & 1) {
+		/* Use 8-bit queue for odd-bytes transfer */
+		if (priv->mspi_16bit) {
+			REG_SET(priv->reg + MSPI_SPCR0_MSB_REG,
+				MSPI_SPCR0_MSB_BITS_8);
+			priv->mspi_16bit = 0;
+		}
+	} else {
+		/* Use 16-bit queue for even-bytes transfer */
+		if (!priv->mspi_16bit) {
+			REG_CLR(priv->reg + MSPI_SPCR0_MSB_REG,
+				MSPI_SPCR0_MSB_BITS_8);
+			priv->mspi_16bit = 1;
+		}
+	}
+
+	while (bytes) {
+		/* Separate code for 16bit and 8bit transfers for performance */
+		if (priv->mspi_16bit) {
+			/* Determine how many bytes to process this time */
+			chunk = min(bytes, NUM_CDRAM * 2);
+			queues = (chunk - 1) / 2 + 1;
+			bytes -= chunk;
+
+			/* Fill CDRAMs */
+			for (i = 0; i < queues; i++)
+				REG_WR(priv->reg + MSPI_CDRAM_REG + (i << 2),
+				       0xc2);
+
+			/* Fill TXRAMs */
+			for (i = 0; i < chunk; i++) {
+				REG_WR(priv->reg + MSPI_TXRAM_REG + (i << 2),
+				       (tx && (tx_idx < bytesout)) ?
+						tx[tx_idx] : 0xff);
+				tx_idx++;
+			}
+		} else {
+			/* Determine how many bytes to process this time */
+			chunk = min(bytes, NUM_CDRAM);
+			queues = chunk;
+			bytes -= chunk;
+
+			/* Fill CDRAMs and TXRAMS */
+			for (i = 0; i < chunk; i++) {
+				REG_WR(priv->reg + MSPI_CDRAM_REG + (i << 2),
+				       0x82);
+				REG_WR(priv->reg + MSPI_TXRAM_REG + (i << 3),
+				       (tx && (tx_idx < bytesout)) ?
+						tx[tx_idx] : 0xff);
+				tx_idx++;
+			}
+		}
+
+		/* Setup queue pointers */
+		REG_WR(priv->reg + MSPI_NEWQP_REG, 0);
+		REG_WR(priv->reg + MSPI_ENDQP_REG, queues - 1);
+
+		/* Deassert CS */
+		if (bytes == 0)
+			REG_CLR(priv->reg + MSPI_CDRAM_REG +
+				((queues - 1) << 2), 0x0);
+
+		/* Kick off */
+		REG_WR(priv->reg + MSPI_STATUS_REG, 0);
+		REG_WR(priv->reg + MSPI_SPCR2_REG, 0xc0);	/* cont | spe */
+
+		/* Wait for completion */
+		stopwatch_init_msecs_expire(&sw, QSPI_WAIT_TIMEOUT);
+		while (!stopwatch_expired(&sw)) {
+			if (REG_RD(priv->reg + MSPI_STATUS_REG) & 1)
+				break;
+		}
+		if ((REG_RD(priv->reg + MSPI_STATUS_REG) & 1) == 0) {
+			/* Make sure no operation is in progress */
+			REG_WR(priv->reg + MSPI_SPCR2_REG, 0);
+			udelay(1);
+			return -1;
+		}
+
+		/* Read data */
+		if (rx) {
+			if (priv->mspi_16bit) {
+				for (i = 0; i < chunk; i++) {
+					if (rx_idx >= bytesout) {
+						rx[in] = RXRAM_16B(priv, i);
+						in++;
+					}
+					rx_idx++;
+				}
+			} else {
+				for (i = 0; i < chunk; i++) {
+					if (rx_idx >= bytesout) {
+						rx[in] = RXRAM_8B(priv, i);
+						in++;
+					}
+					rx_idx++;
+				}
+			}
+		}
+	}
+
 	return 0;
 }



More information about the coreboot-gerrit mailing list