mirror of https://github.com/wolfSSL/wolfBoot.git
Versal QSPI support
parent
ae32d5b6f3
commit
90a96e411b
|
|
@ -92,3 +92,7 @@ CFLAGS_EXTRA+=-DWOLFBOOT_SHA_BLOCK_SIZE=4096
|
|||
|
||||
# UART Configuration - UART0 for APU console
|
||||
CFLAGS_EXTRA+=-DDEBUG_UART_NUM=0
|
||||
|
||||
# QSPI flash debug and test options (uncomment to enable)
|
||||
#CFLAGS_EXTRA+=-DDEBUG_QSPI
|
||||
#CFLAGS_EXTRA+=-DTEST_EXT_FLASH
|
||||
|
|
|
|||
949
hal/versal.c
949
hal/versal.c
|
|
@ -259,6 +259,709 @@ void hal_delay_us(uint32_t us)
|
|||
}
|
||||
|
||||
|
||||
/* ============================================================================
|
||||
* QSPI Flash Driver
|
||||
* ============================================================================
|
||||
* Bare-metal QSPI driver for Versal VMK180
|
||||
* Dual parallel MT25QU01GBBB (128MB each, 256MB total)
|
||||
*/
|
||||
|
||||
#ifdef EXT_FLASH
|
||||
|
||||
/* Debug logging for QSPI driver */
|
||||
#ifdef DEBUG_QSPI
|
||||
#define QSPI_DEBUG_PRINTF(...) wolfBoot_printf(__VA_ARGS__)
|
||||
#else
|
||||
#define QSPI_DEBUG_PRINTF(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
/* QSPI device structure */
|
||||
typedef struct {
|
||||
uint32_t mode; /* GQSPI_GEN_FIFO_MODE_SPI/DSPI/QSPI */
|
||||
uint32_t bus; /* GQSPI_GEN_FIFO_BUS_LOW/UP/BOTH */
|
||||
uint32_t cs; /* GQSPI_GEN_FIFO_CS_LOWER/UPPER/BOTH */
|
||||
uint32_t stripe; /* 0 or GQSPI_GEN_FIFO_STRIPE for dual parallel */
|
||||
} QspiDev_t;
|
||||
|
||||
static QspiDev_t qspiDev;
|
||||
static int qspi_initialized = 0;
|
||||
|
||||
/* Forward declarations */
|
||||
static int qspi_transfer(QspiDev_t *dev, const uint8_t *txData, uint32_t txLen,
|
||||
uint8_t *rxData, uint32_t rxLen, uint32_t dummyClocks);
|
||||
static int qspi_wait_ready(QspiDev_t *dev);
|
||||
|
||||
/* Wait for GenFIFO empty (all entries processed) with timeout */
|
||||
static int qspi_wait_genfifo_empty(void)
|
||||
{
|
||||
uint32_t timeout = GQSPI_TIMEOUT_TRIES;
|
||||
uint32_t isr;
|
||||
|
||||
isr = GQSPI_ISR;
|
||||
while (!(isr & GQSPI_IXR_GEN_FIFO_EMPTY) && --timeout) {
|
||||
isr = GQSPI_ISR;
|
||||
}
|
||||
if (timeout == 0) {
|
||||
QSPI_DEBUG_PRINTF("QSPI: GenFIFO empty timeout\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait for TX FIFO empty with timeout */
|
||||
static int qspi_wait_tx_empty(void)
|
||||
{
|
||||
uint32_t timeout = GQSPI_TIMEOUT_TRIES;
|
||||
while (!(GQSPI_ISR & GQSPI_IXR_TX_FIFO_EMPTY) && --timeout)
|
||||
;
|
||||
if (timeout == 0) {
|
||||
QSPI_DEBUG_PRINTF("QSPI: TX empty timeout\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write to GenFIFO (without triggering - batch mode) */
|
||||
static int qspi_gen_fifo_push(uint32_t entry)
|
||||
{
|
||||
uint32_t timeout = GQSPI_TIMEOUT_TRIES;
|
||||
uint32_t isr;
|
||||
|
||||
/* Wait for GenFIFO not full */
|
||||
isr = GQSPI_ISR;
|
||||
while (!(isr & GQSPI_IXR_GEN_FIFO_NOT_FULL) && --timeout) {
|
||||
isr = GQSPI_ISR;
|
||||
}
|
||||
if (timeout == 0) {
|
||||
QSPI_DEBUG_PRINTF("QSPI: GenFIFO full timeout\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write the entry to GenFIFO */
|
||||
GQSPI_GEN_FIFO = entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Trigger GenFIFO processing and wait for completion */
|
||||
static int qspi_gen_fifo_start_and_wait(void)
|
||||
{
|
||||
uint32_t cfg;
|
||||
uint32_t timeout = GQSPI_TIMEOUT_TRIES;
|
||||
uint32_t isr;
|
||||
|
||||
dsb(); /* Ensure all writes complete */
|
||||
|
||||
/* Trigger GenFIFO processing by setting START_GEN_FIFO */
|
||||
cfg = GQSPI_CFG;
|
||||
cfg |= GQSPI_CFG_START_GEN_FIFO;
|
||||
GQSPI_CFG = cfg;
|
||||
dsb();
|
||||
|
||||
/* Wait for GenFIFO to empty (all entries processed) */
|
||||
isr = GQSPI_ISR;
|
||||
while (!(isr & GQSPI_IXR_GEN_FIFO_EMPTY) && --timeout) {
|
||||
isr = GQSPI_ISR;
|
||||
}
|
||||
if (timeout == 0) {
|
||||
QSPI_DEBUG_PRINTF("QSPI: GenFIFO start timeout\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Legacy wrapper for compatibility */
|
||||
static int qspi_gen_fifo_write(uint32_t entry)
|
||||
{
|
||||
int ret = qspi_gen_fifo_push(entry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Chip select control */
|
||||
static int qspi_cs(QspiDev_t *dev, int assert)
|
||||
{
|
||||
uint32_t entry;
|
||||
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) | GQSPI_GEN_FIFO_MODE_SPI;
|
||||
if (assert) {
|
||||
entry |= (dev->cs & GQSPI_GEN_FIFO_CS_MASK);
|
||||
}
|
||||
/* Idle clocks for CS setup/hold */
|
||||
entry |= GQSPI_GEN_FIFO_IMM(2);
|
||||
|
||||
return qspi_gen_fifo_write(entry);
|
||||
}
|
||||
|
||||
/* TX via FIFO (polling mode) */
|
||||
static int qspi_fifo_tx(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
uint32_t tmp32;
|
||||
uint32_t timeout;
|
||||
|
||||
while (len > 0) {
|
||||
/* Wait for TX FIFO not full */
|
||||
timeout = GQSPI_TIMEOUT_TRIES;
|
||||
while ((GQSPI_ISR & GQSPI_IXR_TX_FIFO_FULL) && --timeout)
|
||||
;
|
||||
if (timeout == 0) {
|
||||
QSPI_DEBUG_PRINTF("QSPI: TX FIFO full timeout\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len >= 4) {
|
||||
tmp32 = *((uint32_t*)data);
|
||||
GQSPI_TXD = tmp32;
|
||||
data += 4;
|
||||
len -= 4;
|
||||
} else {
|
||||
tmp32 = 0;
|
||||
memcpy(&tmp32, data, len);
|
||||
GQSPI_TXD = tmp32;
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RX via FIFO (polling mode) */
|
||||
static int qspi_fifo_rx(uint8_t *data, uint32_t len)
|
||||
{
|
||||
uint32_t tmp32;
|
||||
uint32_t timeout;
|
||||
|
||||
while (len > 0) {
|
||||
/* Wait for RX FIFO not empty */
|
||||
timeout = GQSPI_TIMEOUT_TRIES;
|
||||
while (!(GQSPI_ISR & GQSPI_IXR_RX_FIFO_NOT_EMPTY) && --timeout)
|
||||
;
|
||||
if (timeout == 0) {
|
||||
QSPI_DEBUG_PRINTF("QSPI: RX FIFO empty timeout\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp32 = GQSPI_RXD;
|
||||
if (len >= 4) {
|
||||
*((uint32_t*)data) = tmp32;
|
||||
data += 4;
|
||||
len -= 4;
|
||||
} else {
|
||||
memcpy(data, &tmp32, len);
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Core QSPI transfer function using GenFIFO */
|
||||
static int qspi_transfer(QspiDev_t *dev, const uint8_t *txData, uint32_t txLen,
|
||||
uint8_t *rxData, uint32_t rxLen, uint32_t dummyClocks)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t entry;
|
||||
uint32_t i;
|
||||
|
||||
/* Enable GQSPI controller */
|
||||
GQSPI_EN = 1;
|
||||
dsb();
|
||||
|
||||
/* Base entry: bus + CS + SPI mode */
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_GEN_FIFO_MODE_SPI;
|
||||
|
||||
/* === CS Assert + TX Phase (batch all entries, then trigger) === */
|
||||
|
||||
/* CS assertion entry - just set CS with some idle clocks */
|
||||
ret = qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
|
||||
/* TX Phase - send command bytes via immediate data */
|
||||
for (i = 0; i < txLen && ret == 0; i++) {
|
||||
uint32_t txEntry = entry | GQSPI_GEN_FIFO_TX |
|
||||
GQSPI_GEN_FIFO_IMM(txData[i]);
|
||||
ret = qspi_gen_fifo_push(txEntry);
|
||||
}
|
||||
|
||||
/* Trigger and wait for TX to complete */
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
|
||||
/* Dummy clocks phase (for fast read commands) */
|
||||
if (ret == 0 && dummyClocks > 0) {
|
||||
ret = qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(dummyClocks));
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
}
|
||||
|
||||
/* === RX Phase === */
|
||||
if (dev->stripe) {
|
||||
/* Striped mode: IMM(1) reads 1 byte from each flash = 2 bytes total */
|
||||
for (i = 0; i < rxLen && ret == 0; i += 2) {
|
||||
uint32_t rxEntry = entry | GQSPI_GEN_FIFO_RX |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
GQSPI_GEN_FIFO_STRIPE |
|
||||
GQSPI_GEN_FIFO_IMM(1);
|
||||
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
if (ret == 0) {
|
||||
/* Read 2 bytes (one from each flash, interleaved) */
|
||||
ret = qspi_fifo_rx(&rxData[i], 2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Single flash: read 1 byte at a time */
|
||||
for (i = 0; i < rxLen && ret == 0; i++) {
|
||||
uint32_t rxEntry = entry | GQSPI_GEN_FIFO_RX |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
GQSPI_GEN_FIFO_IMM(1);
|
||||
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = qspi_fifo_rx(&rxData[i], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* === CS Deassert === */
|
||||
/* Remove CS bits from entry for deassert */
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) | GQSPI_GEN_FIFO_MODE_SPI;
|
||||
qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
qspi_gen_fifo_start_and_wait();
|
||||
|
||||
/* Disable controller */
|
||||
GQSPI_EN = 0;
|
||||
dsb();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write page data to flash (for page programming) */
|
||||
static int qspi_write_page(QspiDev_t *dev, const uint8_t *cmd, uint32_t cmdLen,
|
||||
const uint8_t *data, uint32_t dataLen)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t entry;
|
||||
uint32_t i;
|
||||
|
||||
/* Enable GQSPI controller */
|
||||
GQSPI_EN = 1;
|
||||
dsb();
|
||||
|
||||
/* Base entry: bus + CS + SPI mode (page program uses SPI mode) */
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_GEN_FIFO_MODE_SPI;
|
||||
|
||||
/* CS assertion */
|
||||
ret = qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
|
||||
/* TX Phase - send command bytes (includes address) via immediate mode */
|
||||
for (i = 0; i < cmdLen && ret == 0; i++) {
|
||||
uint32_t txEntry = entry | GQSPI_GEN_FIFO_TX |
|
||||
GQSPI_GEN_FIFO_IMM(cmd[i]);
|
||||
ret = qspi_gen_fifo_push(txEntry);
|
||||
}
|
||||
|
||||
/* Trigger and wait for command to complete */
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
|
||||
/* TX Phase - send data via TX FIFO (not immediate mode) */
|
||||
if (ret == 0 && dataLen > 0) {
|
||||
uint32_t txEntry = entry | GQSPI_GEN_FIFO_TX | GQSPI_GEN_FIFO_DATA_XFER;
|
||||
|
||||
/* Note: stripe mode is handled externally by de-interleaving data
|
||||
* and writing to each flash separately */
|
||||
while (dataLen > 0 && ret == 0) {
|
||||
uint32_t chunkLen = (dataLen > 255) ? 255 : dataLen;
|
||||
uint32_t chunkEntry = txEntry | GQSPI_GEN_FIFO_IMM(chunkLen);
|
||||
|
||||
ret = qspi_gen_fifo_push(chunkEntry);
|
||||
if (ret != 0) break;
|
||||
|
||||
/* Start GenFIFO processing so it drains TX FIFO as we fill it */
|
||||
GQSPI_CFG |= GQSPI_CFG_START_GEN_FIFO;
|
||||
dsb();
|
||||
|
||||
/* Push data to TX FIFO */
|
||||
ret = qspi_fifo_tx(data, chunkLen);
|
||||
if (ret != 0) break;
|
||||
|
||||
/* Wait for GenFIFO to complete */
|
||||
ret = qspi_wait_genfifo_empty();
|
||||
|
||||
data += chunkLen;
|
||||
dataLen -= chunkLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* CS Deassert */
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) | GQSPI_GEN_FIFO_MODE_SPI;
|
||||
qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
qspi_gen_fifo_start_and_wait();
|
||||
|
||||
/* Disable controller */
|
||||
GQSPI_EN = 0;
|
||||
dsb();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read flash ID */
|
||||
static int qspi_read_id(QspiDev_t *dev, uint8_t *id, uint32_t len)
|
||||
{
|
||||
uint8_t cmd[1];
|
||||
int ret;
|
||||
|
||||
cmd[0] = FLASH_CMD_READ_ID;
|
||||
ret = qspi_transfer(dev, cmd, 1, id, len, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read flash status register */
|
||||
static int qspi_read_status(QspiDev_t *dev, uint8_t *status)
|
||||
{
|
||||
uint8_t cmd[1];
|
||||
uint8_t data[4]; /* Space for 2 bytes from each chip */
|
||||
int ret;
|
||||
QspiDev_t tmpDev;
|
||||
|
||||
/* For dual parallel, read status from each chip separately */
|
||||
if (dev->stripe) {
|
||||
/* Read from lower chip */
|
||||
tmpDev = *dev;
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_LOW;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_LOWER;
|
||||
tmpDev.stripe = 0;
|
||||
cmd[0] = FLASH_CMD_READ_STATUS;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[0], 1, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* Read from upper chip */
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_UP;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_UPPER;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[1], 1, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* AND the status from both chips */
|
||||
*status = data[0] & data[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd[0] = FLASH_CMD_READ_STATUS;
|
||||
ret = qspi_transfer(dev, cmd, 1, data, 1, 0);
|
||||
if (ret == 0) {
|
||||
*status = data[0];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read flash flag status register */
|
||||
static int qspi_read_flag_status(QspiDev_t *dev, uint8_t *status)
|
||||
{
|
||||
uint8_t cmd[1];
|
||||
uint8_t data[4];
|
||||
int ret;
|
||||
QspiDev_t tmpDev;
|
||||
|
||||
/* For dual parallel, read status from each chip separately */
|
||||
if (dev->stripe) {
|
||||
/* Read from lower chip */
|
||||
tmpDev = *dev;
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_LOW;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_LOWER;
|
||||
tmpDev.stripe = 0;
|
||||
cmd[0] = FLASH_CMD_READ_FLAG_STATUS;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[0], 1, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* Read from upper chip */
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_UP;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_UPPER;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[1], 1, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* AND the status from both chips */
|
||||
*status = data[0] & data[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd[0] = FLASH_CMD_READ_FLAG_STATUS;
|
||||
ret = qspi_transfer(dev, cmd, 1, data, 1, 0);
|
||||
if (ret == 0) {
|
||||
*status = data[0];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for flash ready (not busy) */
|
||||
static int qspi_wait_ready(QspiDev_t *dev)
|
||||
{
|
||||
uint8_t status = 0;
|
||||
uint32_t timeout = GQSPI_FLASH_READY_TRIES;
|
||||
int ret;
|
||||
|
||||
while (timeout-- > 0) {
|
||||
ret = qspi_read_flag_status(dev, &status);
|
||||
if (ret == 0 && (status & FLASH_FSR_READY)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
QSPI_DEBUG_PRINTF("QSPI: Flash ready timeout\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write Enable */
|
||||
static int qspi_write_enable(QspiDev_t *dev)
|
||||
{
|
||||
uint8_t cmd[1];
|
||||
uint8_t status = 0;
|
||||
int ret;
|
||||
uint32_t timeout = GQSPI_FLASH_READY_TRIES;
|
||||
QspiDev_t tmpDev;
|
||||
|
||||
cmd[0] = FLASH_CMD_WRITE_ENABLE;
|
||||
|
||||
/* For dual parallel, send write enable to both chips separately */
|
||||
if (dev->stripe) {
|
||||
/* Send to lower chip */
|
||||
tmpDev = *dev;
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_LOW;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_LOWER;
|
||||
tmpDev.stripe = 0;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, NULL, 0, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* Send to upper chip */
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_UP;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_UPPER;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, NULL, 0, 0);
|
||||
if (ret != 0) return ret;
|
||||
} else {
|
||||
ret = qspi_transfer(dev, cmd, 1, NULL, 0, 0);
|
||||
if (ret != 0) return ret;
|
||||
}
|
||||
|
||||
/* Wait for WEL bit to be set */
|
||||
while (timeout-- > 0) {
|
||||
ret = qspi_read_status(dev, &status);
|
||||
if (ret == 0 && (status & FLASH_SR_WEL)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
QSPI_DEBUG_PRINTF("QSPI: Write enable timeout\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write Disable */
|
||||
static int qspi_write_disable(QspiDev_t *dev)
|
||||
{
|
||||
uint8_t cmd[1];
|
||||
|
||||
cmd[0] = FLASH_CMD_WRITE_DISABLE;
|
||||
return qspi_transfer(dev, cmd, 1, NULL, 0, 0);
|
||||
}
|
||||
|
||||
/* Enter 4-byte address mode */
|
||||
static int qspi_enter_4byte_addr(QspiDev_t *dev)
|
||||
{
|
||||
uint8_t cmd[1];
|
||||
int ret;
|
||||
|
||||
qspi_wait_ready(dev);
|
||||
ret = qspi_write_enable(dev);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
cmd[0] = FLASH_CMD_ENTER_4B_MODE;
|
||||
ret = qspi_transfer(dev, cmd, 1, NULL, 0, 0);
|
||||
QSPI_DEBUG_PRINTF("QSPI: Enter 4-byte mode: ret=%d\n", ret);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = qspi_wait_ready(dev);
|
||||
}
|
||||
qspi_write_disable(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef TEST_EXT_FLASH
|
||||
#ifndef TEST_EXT_ADDRESS
|
||||
#define TEST_EXT_ADDRESS 0x2800000 /* 40MB */
|
||||
#endif
|
||||
|
||||
static int test_ext_flash(QspiDev_t* dev)
|
||||
{
|
||||
int ret;
|
||||
uint32_t i;
|
||||
uint8_t pageData[FLASH_PAGE_SIZE * 4];
|
||||
|
||||
(void)dev;
|
||||
wolfBoot_printf("Testing ext flash at 0x%x...\n", TEST_EXT_ADDRESS);
|
||||
|
||||
#ifndef TEST_FLASH_READONLY
|
||||
/* Erase sector */
|
||||
ret = ext_flash_erase(TEST_EXT_ADDRESS, FLASH_SECTOR_SIZE);
|
||||
wolfBoot_printf("Erase Sector: Ret %d\n", ret);
|
||||
|
||||
/* Write Pages */
|
||||
for (i = 0; i < sizeof(pageData); i++) {
|
||||
pageData[i] = (i & 0xff);
|
||||
}
|
||||
ret = ext_flash_write(TEST_EXT_ADDRESS, pageData, sizeof(pageData));
|
||||
wolfBoot_printf("Write Page: Ret %d\n", ret);
|
||||
#endif /* !TEST_FLASH_READONLY */
|
||||
|
||||
/* Read page */
|
||||
memset(pageData, 0, sizeof(pageData));
|
||||
ret = ext_flash_read(TEST_EXT_ADDRESS, pageData, sizeof(pageData));
|
||||
wolfBoot_printf("Read Page: Ret %d\n", ret);
|
||||
if (ret != 0) {
|
||||
wolfBoot_printf("Flash read failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Print first 32 bytes of data */
|
||||
wolfBoot_printf("Data: ");
|
||||
for (i = 0; i < 32 && i < sizeof(pageData); i++) {
|
||||
wolfBoot_printf("%02x ", pageData[i]);
|
||||
}
|
||||
wolfBoot_printf("...\n");
|
||||
|
||||
#ifndef TEST_FLASH_READONLY
|
||||
wolfBoot_printf("Checking pattern...\n");
|
||||
/* Check data */
|
||||
for (i = 0; i < sizeof(pageData); i++) {
|
||||
if (pageData[i] != (i & 0xff)) {
|
||||
wolfBoot_printf("Check Data @ %d failed: got 0x%02x, expected 0x%02x\n",
|
||||
i, pageData[i], (i & 0xff));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
wolfBoot_printf("Flash Test Passed!\n");
|
||||
#else
|
||||
wolfBoot_printf("Flash Read Test Complete (readonly mode)\n");
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* TEST_EXT_FLASH */
|
||||
|
||||
/* Initialize QSPI controller */
|
||||
static void qspi_init(void)
|
||||
{
|
||||
uint32_t cfg;
|
||||
uint8_t id[4];
|
||||
int ret;
|
||||
|
||||
QSPI_DEBUG_PRINTF("QSPI: Initializing (base=0x%lx)...\n",
|
||||
(unsigned long)VERSAL_QSPI_BASE);
|
||||
|
||||
/* Read initial state left by PLM */
|
||||
cfg = GQSPI_CFG;
|
||||
QSPI_DEBUG_PRINTF("QSPI: PLM state - CFG=0x%08x ISR=0x%08x\n",
|
||||
cfg, GQSPI_ISR);
|
||||
|
||||
/* Disable controller during reconfiguration */
|
||||
GQSPI_EN = 0;
|
||||
dsb();
|
||||
|
||||
/* Select GQSPI mode (not linear LQSPI) */
|
||||
GQSPI_SEL = GQSPI_SEL_GQSPI;
|
||||
dsb();
|
||||
|
||||
/* Don't reset FIFOs - just drain any stale data by reading RXD */
|
||||
while (GQSPI_ISR & GQSPI_IXR_RX_FIFO_NOT_EMPTY) {
|
||||
(void)GQSPI_RXD; /* Discard any stale RX data */
|
||||
}
|
||||
|
||||
/* Clear all interrupt status bits */
|
||||
GQSPI_ISR = GQSPI_IXR_ALL_MASK;
|
||||
dsb();
|
||||
|
||||
/* Preserve PLM's CFG but switch to IO mode for our transfers
|
||||
* PLM: 0xA0080010 = DMA mode | manual start | WP_HOLD | CLK_POL
|
||||
* Key: Keep manual start mode (bit 29) and clock settings */
|
||||
cfg = (cfg & ~GQSPI_CFG_MODE_EN_MASK); /* Clear mode bits */
|
||||
cfg |= GQSPI_CFG_MODE_EN_IO; /* Set IO mode */
|
||||
GQSPI_CFG = cfg;
|
||||
dsb();
|
||||
|
||||
/* Set thresholds */
|
||||
GQSPI_TX_THRESH = 1;
|
||||
GQSPI_RX_THRESH = 1;
|
||||
GQSPI_GF_THRESH = 16;
|
||||
|
||||
QSPI_DEBUG_PRINTF("QSPI: After config - CFG=0x%08x\n", GQSPI_CFG);
|
||||
|
||||
/* Configure device for single flash (lower) first */
|
||||
qspiDev.mode = GQSPI_GEN_FIFO_MODE_SPI;
|
||||
qspiDev.bus = GQSPI_GEN_FIFO_BUS_LOW;
|
||||
qspiDev.cs = GQSPI_GEN_FIFO_CS_LOWER;
|
||||
qspiDev.stripe = 0;
|
||||
|
||||
memset(id, 0, sizeof(id));
|
||||
ret = qspi_read_id(&qspiDev, id, 3);
|
||||
wolfBoot_printf("QSPI: Lower ID: %02x %02x %02x\n", id[0], id[1], id[2]);
|
||||
|
||||
/* Enter 4-byte address mode for lower flash */
|
||||
ret = qspi_enter_4byte_addr(&qspiDev);
|
||||
if (ret != 0) {
|
||||
QSPI_DEBUG_PRINTF("QSPI: 4-byte mode failed (lower)\n");
|
||||
}
|
||||
|
||||
/* Read ID from upper flash */
|
||||
qspiDev.bus = GQSPI_GEN_FIFO_BUS_UP;
|
||||
qspiDev.cs = GQSPI_GEN_FIFO_CS_UPPER;
|
||||
|
||||
memset(id, 0, sizeof(id));
|
||||
ret = qspi_read_id(&qspiDev, id, 3);
|
||||
wolfBoot_printf("QSPI: Upper ID: %02x %02x %02x\n", id[0], id[1], id[2]);
|
||||
|
||||
/* Enter 4-byte address mode for upper flash */
|
||||
ret = qspi_enter_4byte_addr(&qspiDev);
|
||||
if (ret != 0) {
|
||||
QSPI_DEBUG_PRINTF("QSPI: 4-byte mode failed (upper)\n");
|
||||
}
|
||||
|
||||
/* Configure for dual parallel operation */
|
||||
qspiDev.mode = GQSPI_GEN_FIFO_MODE_SPI;
|
||||
qspiDev.bus = GQSPI_GEN_FIFO_BUS_BOTH;
|
||||
qspiDev.cs = GQSPI_GEN_FIFO_CS_BOTH;
|
||||
qspiDev.stripe = GQSPI_GEN_FIFO_STRIPE;
|
||||
|
||||
/* QSPI bare-metal driver */
|
||||
wolfBoot_printf("QSPI Init: Ref=%dMHz, Div=%d, Bus=%d, IO=%s\n",
|
||||
GQSPI_CLK_REF/1000000,
|
||||
(2 << GQSPI_CLK_DIV),
|
||||
(GQSPI_CLK_REF / (2 << GQSPI_CLK_DIV)),
|
||||
#ifdef GQSPI_MODE_IO
|
||||
"Poll"
|
||||
#else
|
||||
"DMA"
|
||||
#endif
|
||||
);
|
||||
|
||||
qspi_initialized = 1;
|
||||
|
||||
#ifdef TEST_EXT_FLASH
|
||||
test_ext_flash(&qspiDev);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* EXT_FLASH */
|
||||
|
||||
|
||||
/* ============================================================================
|
||||
* HAL Public Interface
|
||||
* ============================================================================
|
||||
|
|
@ -279,7 +982,9 @@ void hal_init(void)
|
|||
wolfBoot_printf("Current EL: %d\n", current_el());
|
||||
wolfBoot_printf("Timer Freq: %lu Hz\n", (unsigned long)timer_get_freq());
|
||||
|
||||
/* TODO: Initialize flash controller (OSPI/SD) */
|
||||
#ifdef EXT_FLASH
|
||||
qspi_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void hal_prepare_boot(void)
|
||||
|
|
@ -373,7 +1078,7 @@ int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
|
|||
|
||||
|
||||
/* ============================================================================
|
||||
* External Flash Support (STUBS)
|
||||
* External Flash Interface
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
|
|
@ -381,41 +1086,249 @@ int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
|
|||
|
||||
void ext_flash_lock(void)
|
||||
{
|
||||
/* Stub */
|
||||
/* No-op - flash protection handled elsewhere */
|
||||
}
|
||||
|
||||
void ext_flash_unlock(void)
|
||||
{
|
||||
/* Stub */
|
||||
/* No-op - flash protection handled elsewhere */
|
||||
}
|
||||
|
||||
/* Write to a single flash chip - used for dual parallel writes */
|
||||
static int ext_flash_write_chip(uintptr_t addr, const uint8_t *data, int len,
|
||||
uint32_t bus, uint32_t cs)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t cmd[5];
|
||||
uint32_t xferSz, page, pages;
|
||||
QspiDev_t tmpDev;
|
||||
|
||||
tmpDev.mode = GQSPI_GEN_FIFO_MODE_SPI;
|
||||
tmpDev.bus = bus;
|
||||
tmpDev.cs = cs;
|
||||
tmpDev.stripe = 0;
|
||||
|
||||
/* Write by page */
|
||||
pages = ((len + (FLASH_PAGE_SIZE - 1)) / FLASH_PAGE_SIZE);
|
||||
for (page = 0; page < pages && ret == 0; page++) {
|
||||
ret = qspi_write_enable(&tmpDev);
|
||||
if (ret != 0) break;
|
||||
|
||||
xferSz = len;
|
||||
if (xferSz > FLASH_PAGE_SIZE)
|
||||
xferSz = FLASH_PAGE_SIZE;
|
||||
|
||||
/* Page Program with 4-byte address */
|
||||
cmd[0] = FLASH_CMD_PAGE_PROG_4B;
|
||||
cmd[1] = ((addr + page * FLASH_PAGE_SIZE) >> 24) & 0xFF;
|
||||
cmd[2] = ((addr + page * FLASH_PAGE_SIZE) >> 16) & 0xFF;
|
||||
cmd[3] = ((addr + page * FLASH_PAGE_SIZE) >> 8) & 0xFF;
|
||||
cmd[4] = (addr + page * FLASH_PAGE_SIZE) & 0xFF;
|
||||
|
||||
/* Send command + data */
|
||||
ret = qspi_write_page(&tmpDev, cmd, 5,
|
||||
data + (page * FLASH_PAGE_SIZE), xferSz);
|
||||
if (ret != 0) break;
|
||||
|
||||
ret = qspi_wait_ready(&tmpDev);
|
||||
qspi_write_disable(&tmpDev);
|
||||
len -= xferSz;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
||||
{
|
||||
(void)address;
|
||||
(void)data;
|
||||
(void)len;
|
||||
int ret = 0;
|
||||
uint8_t cmd[5];
|
||||
uint32_t xferSz, page, pages;
|
||||
uintptr_t addr;
|
||||
uint32_t i;
|
||||
|
||||
wolfBoot_printf("ext_flash_write: STUB\n");
|
||||
return -1;
|
||||
if (!qspi_initialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (qspiDev.stripe) {
|
||||
/* For dual parallel: split data and write to each flash separately */
|
||||
uint8_t *lower_data = NULL;
|
||||
uint8_t *upper_data = NULL;
|
||||
uint32_t half_len = (len + 1) / 2;
|
||||
uintptr_t flash_addr = address / 2;
|
||||
|
||||
/* Allocate temp buffers for split data */
|
||||
/* Note: For production, would use static buffer or stack */
|
||||
static uint8_t lower_buf[FLASH_PAGE_SIZE * 4];
|
||||
static uint8_t upper_buf[FLASH_PAGE_SIZE * 4];
|
||||
|
||||
if (len > (int)sizeof(lower_buf) * 2) {
|
||||
QSPI_DEBUG_PRINTF("ext_flash_write: len too large\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
lower_data = lower_buf;
|
||||
upper_data = upper_buf;
|
||||
|
||||
/* De-interleave data: even bytes to lower, odd bytes to upper */
|
||||
for (i = 0; i < (uint32_t)len; i++) {
|
||||
if (i & 1) {
|
||||
upper_data[i / 2] = data[i];
|
||||
} else {
|
||||
lower_data[i / 2] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Write to lower flash */
|
||||
ret = ext_flash_write_chip(flash_addr, lower_data, half_len,
|
||||
GQSPI_GEN_FIFO_BUS_LOW, GQSPI_GEN_FIFO_CS_LOWER);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* Write to upper flash */
|
||||
ret = ext_flash_write_chip(flash_addr, upper_data, half_len,
|
||||
GQSPI_GEN_FIFO_BUS_UP, GQSPI_GEN_FIFO_CS_UPPER);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Single flash write */
|
||||
pages = ((len + (FLASH_PAGE_SIZE - 1)) / FLASH_PAGE_SIZE);
|
||||
for (page = 0; page < pages && ret == 0; page++) {
|
||||
ret = qspi_write_enable(&qspiDev);
|
||||
if (ret != 0) break;
|
||||
|
||||
xferSz = len;
|
||||
if (xferSz > FLASH_PAGE_SIZE)
|
||||
xferSz = FLASH_PAGE_SIZE;
|
||||
|
||||
addr = address + (page * FLASH_PAGE_SIZE);
|
||||
|
||||
/* Page Program with 4-byte address */
|
||||
cmd[0] = FLASH_CMD_PAGE_PROG_4B;
|
||||
cmd[1] = (addr >> 24) & 0xFF;
|
||||
cmd[2] = (addr >> 16) & 0xFF;
|
||||
cmd[3] = (addr >> 8) & 0xFF;
|
||||
cmd[4] = addr & 0xFF;
|
||||
|
||||
/* Send command + data */
|
||||
ret = qspi_write_page(&qspiDev, cmd, 5,
|
||||
data + (page * FLASH_PAGE_SIZE), xferSz);
|
||||
if (ret != 0) break;
|
||||
|
||||
ret = qspi_wait_ready(&qspiDev);
|
||||
qspi_write_disable(&qspiDev);
|
||||
len -= xferSz;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext_flash_read(uintptr_t address, uint8_t *data, int len)
|
||||
{
|
||||
(void)address;
|
||||
(void)data;
|
||||
(void)len;
|
||||
uint8_t cmd[5];
|
||||
int ret;
|
||||
uintptr_t addr = address;
|
||||
uint32_t i;
|
||||
|
||||
wolfBoot_printf("ext_flash_read: STUB\n");
|
||||
return -1;
|
||||
if (!qspi_initialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (qspiDev.stripe) {
|
||||
/* For dual parallel: read from each chip separately and interleave */
|
||||
static uint8_t lower_buf[FLASH_PAGE_SIZE * 4];
|
||||
static uint8_t upper_buf[FLASH_PAGE_SIZE * 4];
|
||||
uint32_t half_len = (len + 1) / 2;
|
||||
QspiDev_t tmpDev;
|
||||
|
||||
if (len > (int)sizeof(lower_buf) * 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr = address / 2; /* Flash address for each chip */
|
||||
|
||||
cmd[0] = FLASH_CMD_READ_4B;
|
||||
cmd[1] = (addr >> 24) & 0xFF;
|
||||
cmd[2] = (addr >> 16) & 0xFF;
|
||||
cmd[3] = (addr >> 8) & 0xFF;
|
||||
cmd[4] = addr & 0xFF;
|
||||
|
||||
/* Read from lower chip */
|
||||
tmpDev.mode = GQSPI_GEN_FIFO_MODE_SPI;
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_LOW;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_LOWER;
|
||||
tmpDev.stripe = 0;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 5, lower_buf, half_len, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* Read from upper chip */
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_UP;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_UPPER;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 5, upper_buf, half_len, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* Interleave: even positions from lower, odd positions from upper */
|
||||
for (i = 0; i < (uint32_t)len; i++) {
|
||||
if (i & 1) {
|
||||
data[i] = upper_buf[i / 2];
|
||||
} else {
|
||||
data[i] = lower_buf[i / 2];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Single flash read */
|
||||
cmd[0] = FLASH_CMD_READ_4B;
|
||||
cmd[1] = (addr >> 24) & 0xFF;
|
||||
cmd[2] = (addr >> 16) & 0xFF;
|
||||
cmd[3] = (addr >> 8) & 0xFF;
|
||||
cmd[4] = addr & 0xFF;
|
||||
|
||||
ret = qspi_transfer(&qspiDev, cmd, 5, data, len, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ext_flash_erase(uintptr_t address, int len)
|
||||
{
|
||||
(void)address;
|
||||
(void)len;
|
||||
int ret = 0;
|
||||
uint8_t cmd[5];
|
||||
uintptr_t addr;
|
||||
|
||||
wolfBoot_printf("ext_flash_erase: STUB\n");
|
||||
return -1;
|
||||
if (!qspi_initialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (len > 0 && ret == 0) {
|
||||
addr = address;
|
||||
if (qspiDev.stripe) {
|
||||
/* For dual parallel the address divide by 2 */
|
||||
addr /= 2;
|
||||
}
|
||||
|
||||
ret = qspi_write_enable(&qspiDev);
|
||||
if (ret != 0) break;
|
||||
|
||||
/* Sector Erase with 4-byte address */
|
||||
cmd[0] = FLASH_CMD_SECTOR_ERASE_4B;
|
||||
cmd[1] = (addr >> 24) & 0xFF;
|
||||
cmd[2] = (addr >> 16) & 0xFF;
|
||||
cmd[3] = (addr >> 8) & 0xFF;
|
||||
cmd[4] = addr & 0xFF;
|
||||
|
||||
ret = qspi_transfer(&qspiDev, cmd, 5, NULL, 0, 0);
|
||||
QSPI_DEBUG_PRINTF("ext_flash_erase: addr=0x%lx\n", (unsigned long)address);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = qspi_wait_ready(&qspiDev);
|
||||
}
|
||||
qspi_write_disable(&qspiDev);
|
||||
|
||||
address += FLASH_SECTOR_SIZE;
|
||||
len -= FLASH_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* EXT_FLASH */
|
||||
|
|
|
|||
163
hal/versal.h
163
hal/versal.h
|
|
@ -291,12 +291,167 @@
|
|||
|
||||
|
||||
/* ============================================================================
|
||||
* OSPI (Octal SPI) Flash Controller
|
||||
* QSPI (Quad SPI) Flash Controller - GQSPI
|
||||
* ============================================================================
|
||||
* Versal uses OSPI instead of QSPI (though QSPI mode is supported)
|
||||
* Stub definitions - to be implemented
|
||||
* Versal QSPI is similar to ZynqMP but at different base address.
|
||||
* VMK180 uses dual parallel MT25QU01GBBB (128MB each, 256MB total)
|
||||
*/
|
||||
#define VERSAL_OSPI_BASE 0xF1010000UL
|
||||
#define VERSAL_QSPI_BASE 0xF1030000UL
|
||||
|
||||
/* QSPI Enable Register (at base, not +0x100) */
|
||||
#define QSPI_EN_REG (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x14)))
|
||||
|
||||
/* GQSPI Registers (at offset 0x100 from QSPI base) */
|
||||
#define GQSPI_CFG (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x100)))
|
||||
#define GQSPI_ISR (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x104)))
|
||||
#define GQSPI_IER (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x108)))
|
||||
#define GQSPI_IDR (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x10C)))
|
||||
#define GQSPI_IMR (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x110)))
|
||||
#define GQSPI_EN (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x114)))
|
||||
#define GQSPI_TXD (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x11C)))
|
||||
#define GQSPI_RXD (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x120)))
|
||||
#define GQSPI_TX_THRESH (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x128)))
|
||||
#define GQSPI_RX_THRESH (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x12C)))
|
||||
#define GQSPI_GPIO (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x130)))
|
||||
#define GQSPI_LPBK_DLY_ADJ (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x138)))
|
||||
#define GQSPI_GEN_FIFO (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x140)))
|
||||
#define GQSPI_SEL (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x144)))
|
||||
#define GQSPI_FIFO_CTRL (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x14C)))
|
||||
#define GQSPI_GF_THRESH (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x150)))
|
||||
#define GQSPI_POLL_CFG (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x154)))
|
||||
#define GQSPI_P_TIMEOUT (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x158)))
|
||||
#define GQSPI_XFER_STS (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x15C)))
|
||||
#define GQSPI_DATA_DLY_ADJ (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x1F8)))
|
||||
#define GQSPI_MOD_ID (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x1FC)))
|
||||
|
||||
/* DMA Registers (at offset 0x800 from QSPI base) */
|
||||
#define GQSPIDMA_DST (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x800)))
|
||||
#define GQSPIDMA_SIZE (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x804)))
|
||||
#define GQSPIDMA_STS (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x808)))
|
||||
#define GQSPIDMA_CTRL (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x80C)))
|
||||
#define GQSPIDMA_ISR (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x814)))
|
||||
#define GQSPIDMA_IER (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x818)))
|
||||
#define GQSPIDMA_IDR (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x81C)))
|
||||
#define GQSPIDMA_IMR (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x820)))
|
||||
#define GQSPIDMA_CTRL2 (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x824)))
|
||||
#define GQSPIDMA_DST_MSB (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x828)))
|
||||
|
||||
/* Tap Delay Bypass Register - Versal specific location */
|
||||
#define IOU_TAPDLY_BYPASS (*((volatile uint32_t*)(VERSAL_QSPI_BASE + 0x03C)))
|
||||
#define IOU_TAPDLY_BYPASS_LQSPI_RX (1UL << 2)
|
||||
|
||||
/* GQSPI_CFG: Configuration register bits */
|
||||
#define GQSPI_CFG_CLK_POL (1UL << 1)
|
||||
#define GQSPI_CFG_CLK_PH (1UL << 2)
|
||||
#define GQSPI_CFG_BAUD_RATE_DIV_MASK (7UL << 3)
|
||||
#define GQSPI_CFG_BAUD_RATE_DIV(d) (((d) << 3) & GQSPI_CFG_BAUD_RATE_DIV_MASK)
|
||||
#define GQSPI_CFG_WP_HOLD (1UL << 19)
|
||||
#define GQSPI_CFG_EN_POLL_TIMEOUT (1UL << 20)
|
||||
#define GQSPI_CFG_ENDIAN (1UL << 26)
|
||||
#define GQSPI_CFG_START_GEN_FIFO (1UL << 28)
|
||||
#define GQSPI_CFG_GEN_FIFO_START_MODE (1UL << 29)
|
||||
#define GQSPI_CFG_MODE_EN_MASK (3UL << 30)
|
||||
#define GQSPI_CFG_MODE_EN_IO (0UL << 30)
|
||||
#define GQSPI_CFG_MODE_EN_DMA (2UL << 30)
|
||||
|
||||
/* GQSPI_ISR/IER/IDR: Interrupt bits */
|
||||
#define GQSPI_IXR_POLL_TIME_EXPIRE (1UL << 1)
|
||||
#define GQSPI_IXR_TX_FIFO_NOT_FULL (1UL << 2)
|
||||
#define GQSPI_IXR_TX_FIFO_FULL (1UL << 3)
|
||||
#define GQSPI_IXR_RX_FIFO_NOT_EMPTY (1UL << 4)
|
||||
#define GQSPI_IXR_RX_FIFO_FULL (1UL << 5)
|
||||
#define GQSPI_IXR_GEN_FIFO_EMPTY (1UL << 7)
|
||||
#define GQSPI_IXR_TX_FIFO_EMPTY (1UL << 8)
|
||||
#define GQSPI_IXR_GEN_FIFO_NOT_FULL (1UL << 9)
|
||||
#define GQSPI_IXR_GEN_FIFO_FULL (1UL << 10)
|
||||
#define GQSPI_IXR_RX_FIFO_EMPTY (1UL << 11)
|
||||
#define GQSPI_IXR_ALL_MASK 0x0FBEU
|
||||
#define GQSPI_ISR_WR_TO_CLR_MASK 0x02U
|
||||
|
||||
/* GenFIFO Entry bits */
|
||||
#define GQSPI_GEN_FIFO_IMM_MASK 0xFFU
|
||||
#define GQSPI_GEN_FIFO_IMM(x) ((x) & GQSPI_GEN_FIFO_IMM_MASK)
|
||||
#define GQSPI_GEN_FIFO_DATA_XFER (1UL << 8)
|
||||
#define GQSPI_GEN_FIFO_EXP (1UL << 9)
|
||||
#define GQSPI_GEN_FIFO_MODE_SPI (1UL << 10)
|
||||
#define GQSPI_GEN_FIFO_MODE_DSPI (2UL << 10)
|
||||
#define GQSPI_GEN_FIFO_MODE_QSPI (3UL << 10)
|
||||
#define GQSPI_GEN_FIFO_MODE_MASK (3UL << 10)
|
||||
#define GQSPI_GEN_FIFO_CS_LOWER (1UL << 12)
|
||||
#define GQSPI_GEN_FIFO_CS_UPPER (1UL << 13)
|
||||
#define GQSPI_GEN_FIFO_CS_MASK (3UL << 12)
|
||||
#define GQSPI_GEN_FIFO_CS_BOTH (3UL << 12)
|
||||
#define GQSPI_GEN_FIFO_BUS_LOW (1UL << 14)
|
||||
#define GQSPI_GEN_FIFO_BUS_UP (1UL << 15)
|
||||
#define GQSPI_GEN_FIFO_BUS_BOTH (3UL << 14)
|
||||
#define GQSPI_GEN_FIFO_BUS_MASK (3UL << 14)
|
||||
#define GQSPI_GEN_FIFO_TX (1UL << 16)
|
||||
#define GQSPI_GEN_FIFO_RX (1UL << 17)
|
||||
#define GQSPI_GEN_FIFO_STRIPE (1UL << 18)
|
||||
#define GQSPI_GEN_FIFO_POLL (1UL << 19)
|
||||
|
||||
/* DMA Control bits */
|
||||
#define GQSPIDMA_CTRL_DEF 0x403FFA00UL
|
||||
#define GQSPIDMA_CTRL2_DEF 0x081BFFF8UL
|
||||
#define GQSPIDMA_CTRL_ENDIANNESS (1UL << 23)
|
||||
|
||||
/* DMA Status bits */
|
||||
#define GQSPIDMA_STS_BUSY (1UL << 0)
|
||||
#define GQSPIDMA_STS_WTC (7UL << 13)
|
||||
|
||||
/* DMA Interrupt bits */
|
||||
#define GQSPIDMA_ISR_DONE (1UL << 1)
|
||||
#define GQSPIDMA_ISR_ALL_MASK 0xFEU
|
||||
|
||||
/* FIFO Control bits */
|
||||
#define GQSPI_FIFO_CTRL_RST_GEN (1UL << 0)
|
||||
#define GQSPI_FIFO_CTRL_RST_TX (1UL << 1)
|
||||
#define GQSPI_FIFO_CTRL_RST_RX (1UL << 2)
|
||||
|
||||
/* QSPI Select */
|
||||
#define GQSPI_SEL_GQSPI (1UL << 0)
|
||||
|
||||
/* Flash Commands */
|
||||
#define FLASH_CMD_READ_ID 0x9F
|
||||
#define FLASH_CMD_READ_STATUS 0x05
|
||||
#define FLASH_CMD_READ_FLAG_STATUS 0x70
|
||||
#define FLASH_CMD_WRITE_ENABLE 0x06
|
||||
#define FLASH_CMD_WRITE_DISABLE 0x04
|
||||
#define FLASH_CMD_READ 0x03
|
||||
#define FLASH_CMD_FAST_READ 0x0B
|
||||
#define FLASH_CMD_QUAD_READ 0x6B
|
||||
#define FLASH_CMD_READ_4B 0x13
|
||||
#define FLASH_CMD_FAST_READ_4B 0x0C
|
||||
#define FLASH_CMD_QUAD_READ_4B 0x6C
|
||||
#define FLASH_CMD_PAGE_PROG 0x02
|
||||
#define FLASH_CMD_PAGE_PROG_4B 0x12
|
||||
#define FLASH_CMD_SECTOR_ERASE 0xD8
|
||||
#define FLASH_CMD_SECTOR_ERASE_4B 0xDC
|
||||
#define FLASH_CMD_ENTER_4B_MODE 0xB7
|
||||
#define FLASH_CMD_EXIT_4B_MODE 0xE9
|
||||
|
||||
/* Flash Status Register bits */
|
||||
#define FLASH_SR_WIP (1UL << 0) /* Write In Progress */
|
||||
#define FLASH_SR_WEL (1UL << 1) /* Write Enable Latch */
|
||||
#define FLASH_FSR_READY (1UL << 7) /* Flag Status Ready */
|
||||
|
||||
/* Flash Configuration for MT25QU01GBBB */
|
||||
#define FLASH_JEDEC_MICRON 0x20
|
||||
#define FLASH_JEDEC_MT25QU01G 0x20BB21
|
||||
#define FLASH_PAGE_SIZE 256
|
||||
#define FLASH_SECTOR_SIZE 0x10000 /* 64KB */
|
||||
#define FLASH_DEVICE_SIZE 0x8000000 /* 128MB per chip */
|
||||
|
||||
/* QSPI Timing */
|
||||
#define GQSPI_CLK_DIV 2 /* Divide by 8 (300MHz / 8 = 37.5MHz) */
|
||||
#define GQSPI_DUMMY_CLOCKS 8
|
||||
#define GQSPI_TIMEOUT_TRIES 100000
|
||||
#define GQSPIDMA_TIMEOUT_TRIES 100000000
|
||||
#define GQSPI_FLASH_READY_TRIES 1000000 /* Erase can take seconds */
|
||||
|
||||
/* QSPI DMA alignment */
|
||||
#define GQSPI_DMA_ALIGN 32
|
||||
#define XALIGNED(x) __attribute__((aligned(x)))
|
||||
|
||||
|
||||
/* ============================================================================
|
||||
|
|
|
|||
|
|
@ -681,10 +681,19 @@ MMUTableL2:
|
|||
.set SECT, SECT+0x200000
|
||||
.endr
|
||||
|
||||
#ifdef TARGET_versal
|
||||
/* Versal: LPD/PMC peripherals at 0xF0000000 - 0xF7FFFFFF (includes QSPI @ 0xF1030000) */
|
||||
.rept 0x040 /* 0xF000_0000 - 0xF7FF_FFFF */
|
||||
.8byte SECT + Device /* 128MB LPD peripherals (QSPI, I2C, etc) */
|
||||
.set SECT, SECT+0x200000
|
||||
.endr
|
||||
#else
|
||||
/* ZynqMP: This region is reserved */
|
||||
.rept 0x040 /* 0xF000_0000 - 0xF7FF_FFFF */
|
||||
.8byte SECT + reserved /* 128MB Reserved */
|
||||
.set SECT, SECT+0x200000
|
||||
.endr
|
||||
#endif
|
||||
|
||||
.rept 0x8 /* 0xF800_0000 - 0xF8FF_FFFF */
|
||||
.8byte SECT + Device /* 16MB coresight */
|
||||
|
|
|
|||
Loading…
Reference in New Issue