Versal QSPI support

pull/679/head
David Garske 2026-01-14 15:54:45 -08:00 committed by Daniele Lacamera
parent ae32d5b6f3
commit 90a96e411b
4 changed files with 1103 additions and 22 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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)))
/* ============================================================================

View File

@ -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 */