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 SOC_BROADCOM_CYGNUS
|
||||||
select SPI_FLASH
|
select SPI_FLASH
|
||||||
select SPI_FLASH_SPANSION
|
select SPI_FLASH_SPANSION
|
||||||
|
select SPI_FLASH_STMICRO # required for the reference board BCM958305K
|
||||||
|
select SPI_ATOMIC_SEQUENCING
|
||||||
|
|
||||||
config MAINBOARD_DIR
|
config MAINBOARD_DIR
|
||||||
string
|
string
|
||||||
|
@ -49,7 +51,7 @@ config VBOOT_RAMSTAGE_INDEX
|
||||||
|
|
||||||
config BOOT_MEDIA_SPI_BUS
|
config BOOT_MEDIA_SPI_BUS
|
||||||
int
|
int
|
||||||
default 2
|
default 0
|
||||||
|
|
||||||
config DRAM_SIZE_MB
|
config DRAM_SIZE_MB
|
||||||
int
|
int
|
||||||
|
|
|
@ -23,4 +23,6 @@
|
||||||
#define IPROC_PERIPH_BASE 0x19020000
|
#define IPROC_PERIPH_BASE 0x19020000
|
||||||
#define IPROC_PERIPH_GLB_TIM_REG_BASE (IPROC_PERIPH_BASE + 0x200)
|
#define IPROC_PERIPH_GLB_TIM_REG_BASE (IPROC_PERIPH_BASE + 0x200)
|
||||||
|
|
||||||
|
#define IPROC_QSPI_BASE 0x18047000
|
||||||
|
|
||||||
#endif /* __SOC_BROADCOM_CYGNUS_ADDRESSMAP_H__ */
|
#endif /* __SOC_BROADCOM_CYGNUS_ADDRESSMAP_H__ */
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the coreboot project.
|
* Copyright (C) 2015 Broadcom Corporation
|
||||||
*
|
|
||||||
* Copyright 2015 Google Inc.
|
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* 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
|
* 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-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)
|
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)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void spi_release_bus(struct spi_slave *slave)
|
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,
|
#define RXRAM_16B(p, i) (REG_RD((p)->reg + MSPI_RXRAM_REG + ((i) << 2)) & 0xff)
|
||||||
unsigned out_bytes, void *din, unsigned in_bytes)
|
#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;
|
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