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@chromium.org> Original-Commit-Id: 8c3b156019df429e9d12728224ed4eec8436f415 Original-Signed-off-by: Corneliu Doban <cdoban@broadcom.com> Original-Reviewed-on: https://chrome-internal-review.googlesource.com/199776 Original-Reviewed-by: Scott Branden <sbranden@broadcom.com> Original-Tested-by: Corneliu Doban <cdoban@broadcom.com> Original-Commit-Queue: Corneliu Doban <cdoban@broadcom.com> Original-Change-Id: Ice798ec76011ee47e13174b4c5534b0d0bc8b4ad Original-Reviewed-on: https://chromium-review.googlesource.com/256414 Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org> Original-Tested-by: Daisuke Nojiri <dnojiri@chromium.org> Original-Commit-Queue: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-on: http://review.coreboot.org/9849 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
parent
82a7bc45f7
commit
b048432578
|
@ -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
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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,307 @@
|
|||
* 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) read32(x)
|
||||
#define REG_WR(x, y) write32((x), (y))
|
||||
#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;
|
||||
}
|
||||
|
||||
unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len)
|
||||
{
|
||||
return min(65535, buf_len);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue