mirror of https://github.com/wolfSSL/wolfBoot.git
Added QSPI with DMA support
parent
90a96e411b
commit
e966941296
|
|
@ -93,6 +93,17 @@ 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
|
||||
# QSPI Reference Clock: Ref (300MHz default for Versal)
|
||||
#CFLAGS_EXTRA+=-DGQSPI_CLK_REF=300000000
|
||||
|
||||
# QSPI Bus Divisor: (2 << div) = BUS (0=div2, 1=div4, 2=div8)
|
||||
# MT25QU01G max: 133MHz Quad Read (0x6C) with 8 dummy cycles
|
||||
# div=0: 300MHz/2 = 150MHz (above spec but tested working)
|
||||
# div=1: 300MHz/4 = 75MHz (within spec, default)
|
||||
# div=2: 300MHz/8 = 37.5MHz (conservative)
|
||||
#CFLAGS_EXTRA+=-DGQSPI_CLK_DIV=1
|
||||
|
||||
# QSPI flash options (uncomment to enable)
|
||||
#CFLAGS_EXTRA+=-DDEBUG_QSPI # Enable QSPI debug logging
|
||||
#CFLAGS_EXTRA+=-DGQSPI_MODE_IO # Use polling instead of DMA (slower)
|
||||
#CFLAGS_EXTRA+=-DTEST_EXT_FLASH # Run flash erase/write/read test
|
||||
|
|
|
|||
482
hal/versal.c
482
hal/versal.c
|
|
@ -380,6 +380,33 @@ static int qspi_gen_fifo_write(uint32_t entry)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Calculate EXP mode for large transfers (returns actual transfer size)
|
||||
* For transfers > 255 bytes, use exponent mode where IMM = power of 2
|
||||
* Pattern from zynq.c qspi_calc_exp() */
|
||||
static uint32_t qspi_calc_exp(uint32_t xferSz, uint32_t *reg_genfifo)
|
||||
{
|
||||
uint32_t expval;
|
||||
|
||||
*reg_genfifo &= ~(GQSPI_GEN_FIFO_IMM_MASK | GQSPI_GEN_FIFO_EXP);
|
||||
|
||||
if (xferSz > GQSPI_GEN_FIFO_IMM_MASK) {
|
||||
/* Use exponent mode (max is 2^28 for DMA) */
|
||||
for (expval = 28; expval >= 8; expval--) {
|
||||
/* Find highest power of 2 that fits */
|
||||
if (xferSz >= (1UL << expval)) {
|
||||
*reg_genfifo |= GQSPI_GEN_FIFO_EXP;
|
||||
*reg_genfifo |= GQSPI_GEN_FIFO_IMM(expval);
|
||||
xferSz = (1UL << expval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Use immediate length mode */
|
||||
*reg_genfifo |= GQSPI_GEN_FIFO_IMM(xferSz);
|
||||
}
|
||||
return xferSz;
|
||||
}
|
||||
|
||||
/* Chip select control */
|
||||
static int qspi_cs(QspiDev_t *dev, int assert)
|
||||
{
|
||||
|
|
@ -395,6 +422,41 @@ static int qspi_cs(QspiDev_t *dev, int assert)
|
|||
return qspi_gen_fifo_write(entry);
|
||||
}
|
||||
|
||||
/* DMA temporary buffer for unaligned transfers (DMA is default, IO is optional) */
|
||||
#ifndef GQSPI_MODE_IO
|
||||
static uint8_t XALIGNED(GQSPI_DMA_ALIGN) dma_tmpbuf[GQSPI_DMA_TMPSZ];
|
||||
|
||||
/* Flush data cache for DMA coherency */
|
||||
static void flush_dcache_range(uintptr_t start, uintptr_t end)
|
||||
{
|
||||
/* ARM64: Clean and invalidate by virtual address to PoC */
|
||||
uintptr_t addr;
|
||||
for (addr = (start & ~(GQSPI_DMA_ALIGN - 1)); addr < end;
|
||||
addr += GQSPI_DMA_ALIGN) {
|
||||
__asm__ volatile("dc civac, %0" : : "r"(addr) : "memory");
|
||||
}
|
||||
__asm__ volatile("dsb sy" : : : "memory");
|
||||
}
|
||||
|
||||
/* Wait for DMA completion */
|
||||
static int qspi_dma_wait(void)
|
||||
{
|
||||
uint32_t timeout = GQSPIDMA_TIMEOUT_TRIES;
|
||||
|
||||
while (!(GQSPIDMA_ISR & GQSPIDMA_ISR_DONE) && --timeout)
|
||||
;
|
||||
|
||||
if (timeout == 0) {
|
||||
QSPI_DEBUG_PRINTF("QSPI: DMA timeout\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Clear DMA done interrupt */
|
||||
GQSPIDMA_ISR = GQSPIDMA_ISR_DONE;
|
||||
return 0;
|
||||
}
|
||||
#endif /* !GQSPI_MODE_IO */
|
||||
|
||||
/* TX via FIFO (polling mode) */
|
||||
static int qspi_fifo_tx(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
|
|
@ -545,6 +607,223 @@ static int qspi_transfer(QspiDev_t *dev, const uint8_t *txData, uint32_t txLen,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* QSPI Read transfer - uses Quad mode (4-bit) for data phase
|
||||
* Command and address are sent in SPI mode, data received in QSPI mode */
|
||||
static int qspi_transfer_qread(QspiDev_t *dev, const uint8_t *cmd, uint32_t cmdLen,
|
||||
uint8_t *rxData, uint32_t rxLen, uint32_t dummyClocks)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t entry, rxEntry;
|
||||
uint32_t i;
|
||||
|
||||
/* Enable GQSPI controller */
|
||||
GQSPI_EN = 1;
|
||||
dsb();
|
||||
|
||||
/* Base entry for command phase: 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 assertion */
|
||||
ret = qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
|
||||
/* TX Phase - send command + address bytes in SPI 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 TX to complete */
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
|
||||
/* Dummy clocks phase (required for Fast/Quad Read)
|
||||
* Send dummy clocks: DATA_XFER with no TX or RX, IMM = clock count */
|
||||
if (ret == 0 && dummyClocks > 0) {
|
||||
uint32_t dummyEntry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_QSPI_MODE |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
GQSPI_GEN_FIFO_IMM(dummyClocks);
|
||||
ret = qspi_gen_fifo_push(dummyEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
}
|
||||
|
||||
/* RX Phase - receive data in QSPI mode (4-bit)
|
||||
* Use EXP mode for large transfers (pattern from zynq.c) */
|
||||
rxEntry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_QSPI_MODE |
|
||||
GQSPI_GEN_FIFO_RX |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
(dev->stripe & GQSPI_GEN_FIFO_STRIPE);
|
||||
|
||||
{
|
||||
uint32_t remaining = rxLen;
|
||||
uint32_t offset = 0;
|
||||
uint32_t xferSz;
|
||||
|
||||
while (ret == 0 && remaining > 0) {
|
||||
xferSz = qspi_calc_exp(remaining, &rxEntry);
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = qspi_fifo_rx(&rxData[offset], xferSz);
|
||||
}
|
||||
offset += xferSz;
|
||||
remaining -= xferSz;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
#ifndef GQSPI_MODE_IO
|
||||
/* DMA-enabled QSPI Read transfer
|
||||
* Uses DMA for RX phase for better performance on large reads */
|
||||
static int qspi_transfer_qread_dma(QspiDev_t *dev, const uint8_t *cmd, uint32_t cmdLen,
|
||||
uint8_t *rxData, uint32_t rxLen, uint32_t dummyClocks)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t entry, rxEntry;
|
||||
uint32_t i;
|
||||
uint8_t *dmaPtr;
|
||||
uint32_t dmaLen;
|
||||
int useTemp = 0;
|
||||
|
||||
/* Enable GQSPI controller in DMA mode */
|
||||
GQSPI_CFG = (GQSPI_CFG & ~GQSPI_CFG_MODE_EN_MASK) | GQSPI_CFG_MODE_EN_DMA;
|
||||
GQSPI_EN = 1;
|
||||
dsb();
|
||||
|
||||
/* Base entry for command phase: 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 assertion */
|
||||
ret = qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
|
||||
/* TX Phase - send command + address bytes in SPI 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 TX to complete */
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
|
||||
/* Dummy clocks phase */
|
||||
if (ret == 0 && dummyClocks > 0) {
|
||||
uint32_t dummyEntry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_QSPI_MODE |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
GQSPI_GEN_FIFO_IMM(dummyClocks);
|
||||
ret = qspi_gen_fifo_push(dummyEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
}
|
||||
|
||||
/* DMA RX Phase */
|
||||
if (ret == 0 && rxLen > 0) {
|
||||
uint32_t remaining = rxLen;
|
||||
uint32_t xferSz;
|
||||
|
||||
/* Check alignment - DMA requires cache-line aligned buffer */
|
||||
if (((uintptr_t)rxData & (GQSPI_DMA_ALIGN - 1)) || (rxLen & 3)) {
|
||||
/* Use temp buffer for unaligned data */
|
||||
dmaPtr = dma_tmpbuf;
|
||||
dmaLen = (rxLen + GQSPI_DMA_ALIGN - 1) & ~(GQSPI_DMA_ALIGN - 1);
|
||||
if (dmaLen > sizeof(dma_tmpbuf)) {
|
||||
dmaLen = sizeof(dma_tmpbuf);
|
||||
}
|
||||
useTemp = 1;
|
||||
} else {
|
||||
dmaPtr = rxData;
|
||||
dmaLen = rxLen;
|
||||
}
|
||||
|
||||
/* Setup DMA destination */
|
||||
GQSPIDMA_DST = ((uintptr_t)dmaPtr & 0xFFFFFFFFUL);
|
||||
GQSPIDMA_DST_MSB = ((uintptr_t)dmaPtr >> 32);
|
||||
GQSPIDMA_SIZE = dmaLen;
|
||||
|
||||
/* Enable DMA done interrupt */
|
||||
GQSPIDMA_IER = GQSPIDMA_ISR_DONE;
|
||||
|
||||
/* Flush dcache for DMA coherency */
|
||||
flush_dcache_range((uintptr_t)dmaPtr, (uintptr_t)dmaPtr + dmaLen);
|
||||
|
||||
/* Setup GenFIFO for RX with DMA - use EXP mode for large transfers */
|
||||
rxEntry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_QSPI_MODE |
|
||||
GQSPI_GEN_FIFO_RX |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
(dev->stripe & GQSPI_GEN_FIFO_STRIPE);
|
||||
|
||||
/* Use qspi_calc_exp for large transfers (pattern from zynq.c) */
|
||||
while (ret == 0 && remaining > 0) {
|
||||
xferSz = qspi_calc_exp(remaining, &rxEntry);
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
remaining -= xferSz;
|
||||
}
|
||||
|
||||
/* Trigger GenFIFO */
|
||||
if (ret == 0) {
|
||||
GQSPI_CFG |= GQSPI_CFG_START_GEN_FIFO;
|
||||
dsb();
|
||||
}
|
||||
|
||||
/* Wait for DMA completion */
|
||||
if (ret == 0) {
|
||||
ret = qspi_dma_wait();
|
||||
}
|
||||
|
||||
/* Invalidate cache after DMA */
|
||||
flush_dcache_range((uintptr_t)dmaPtr, (uintptr_t)dmaPtr + dmaLen);
|
||||
|
||||
/* Copy from temp buffer if needed */
|
||||
if (ret == 0 && useTemp) {
|
||||
memcpy(rxData, dmaPtr, rxLen);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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();
|
||||
|
||||
/* Switch back to IO mode and disable controller */
|
||||
GQSPI_CFG = (GQSPI_CFG & ~GQSPI_CFG_MODE_EN_MASK) | GQSPI_CFG_MODE_EN_IO;
|
||||
GQSPI_EN = 0;
|
||||
dsb();
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* !GQSPI_MODE_IO */
|
||||
|
||||
/* 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)
|
||||
|
|
@ -579,10 +858,9 @@ static int qspi_write_page(QspiDev_t *dev, const uint8_t *cmd, uint32_t cmdLen,
|
|||
|
||||
/* 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;
|
||||
uint32_t txEntry = entry | GQSPI_GEN_FIFO_TX | GQSPI_GEN_FIFO_DATA_XFER |
|
||||
(dev->stripe & GQSPI_GEN_FIFO_STRIPE);
|
||||
|
||||
/* 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);
|
||||
|
|
@ -774,6 +1052,7 @@ static int qspi_write_disable(QspiDev_t *dev)
|
|||
return qspi_transfer(dev, cmd, 1, NULL, 0, 0);
|
||||
}
|
||||
|
||||
#if GQPI_USE_4BYTE_ADDR == 1
|
||||
/* Enter 4-byte address mode */
|
||||
static int qspi_enter_4byte_addr(QspiDev_t *dev)
|
||||
{
|
||||
|
|
@ -795,6 +1074,27 @@ static int qspi_enter_4byte_addr(QspiDev_t *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Exit 4-byte address mode */
|
||||
static int qspi_exit_4byte_addr(QspiDev_t *dev)
|
||||
{
|
||||
uint8_t cmd[1];
|
||||
int ret;
|
||||
|
||||
ret = qspi_write_enable(dev);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
cmd[0] = FLASH_CMD_EXIT_4B_MODE;
|
||||
ret = qspi_transfer(dev, cmd, 1, NULL, 0, 0);
|
||||
QSPI_DEBUG_PRINTF("QSPI: Exit 4-byte mode: ret=%d\n", ret);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = qspi_wait_ready(dev);
|
||||
}
|
||||
qspi_write_disable(dev);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST_EXT_FLASH
|
||||
#ifndef TEST_EXT_ADDRESS
|
||||
#define TEST_EXT_ADDRESS 0x2800000 /* 40MB */
|
||||
|
|
@ -914,12 +1214,15 @@ static void qspi_init(void)
|
|||
ret = qspi_read_id(&qspiDev, id, 3);
|
||||
wolfBoot_printf("QSPI: Lower ID: %02x %02x %02x\n", id[0], id[1], id[2]);
|
||||
|
||||
#if GQPI_USE_4BYTE_ADDR == 1
|
||||
/* 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");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if GQPI_USE_DUAL_PARALLEL == 1
|
||||
/* Read ID from upper flash */
|
||||
qspiDev.bus = GQSPI_GEN_FIFO_BUS_UP;
|
||||
qspiDev.cs = GQSPI_GEN_FIFO_CS_UPPER;
|
||||
|
|
@ -928,23 +1231,32 @@ static void qspi_init(void)
|
|||
ret = qspi_read_id(&qspiDev, id, 3);
|
||||
wolfBoot_printf("QSPI: Upper ID: %02x %02x %02x\n", id[0], id[1], id[2]);
|
||||
|
||||
#if GQPI_USE_4BYTE_ADDR == 1
|
||||
/* 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");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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;
|
||||
#endif
|
||||
|
||||
/* 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)),
|
||||
/* QSPI bare-metal driver info */
|
||||
wolfBoot_printf("QSPI: %dMHz, %s mode, %s\n",
|
||||
(GQSPI_CLK_REF / (2 << GQSPI_CLK_DIV)) / 1000000,
|
||||
#if GQSPI_QSPI_MODE == GQSPI_GEN_FIFO_MODE_QSPI
|
||||
"Quad"
|
||||
#elif GQSPI_QSPI_MODE == GQSPI_GEN_FIFO_MODE_DSPI
|
||||
"Dual"
|
||||
#else
|
||||
"SPI"
|
||||
#endif
|
||||
,
|
||||
#ifdef GQSPI_MODE_IO
|
||||
"Poll"
|
||||
#else
|
||||
|
|
@ -989,6 +1301,11 @@ void hal_init(void)
|
|||
|
||||
void hal_prepare_boot(void)
|
||||
{
|
||||
#if defined(EXT_FLASH) && GQPI_USE_4BYTE_ADDR == 1
|
||||
/* Exit 4-byte address mode before handing off to application */
|
||||
qspi_exit_4byte_addr(&qspiDev);
|
||||
#endif
|
||||
|
||||
/* Flush any pending UART output (with timeout) */
|
||||
#ifdef DEBUG_UART
|
||||
{
|
||||
|
|
@ -1094,103 +1411,18 @@ void ext_flash_unlock(void)
|
|||
/* 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)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t cmd[5];
|
||||
uint32_t xferSz, page, pages;
|
||||
uintptr_t addr;
|
||||
uint32_t i;
|
||||
|
||||
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 */
|
||||
/* Write by page */
|
||||
pages = ((len + (FLASH_PAGE_SIZE - 1)) / FLASH_PAGE_SIZE);
|
||||
for (page = 0; page < pages && ret == 0; page++) {
|
||||
ret = qspi_write_enable(&qspiDev);
|
||||
|
|
@ -1201,6 +1433,10 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|||
xferSz = FLASH_PAGE_SIZE;
|
||||
|
||||
addr = address + (page * FLASH_PAGE_SIZE);
|
||||
if (qspiDev.stripe) {
|
||||
/* For dual parallel the address is divided by 2 */
|
||||
addr /= 2;
|
||||
}
|
||||
|
||||
/* Page Program with 4-byte address */
|
||||
cmd[0] = FLASH_CMD_PAGE_PROG_4B;
|
||||
|
|
@ -1209,9 +1445,10 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|||
cmd[3] = (addr >> 8) & 0xFF;
|
||||
cmd[4] = addr & 0xFF;
|
||||
|
||||
/* Send command + data */
|
||||
/* Send command + data - hardware handles striping */
|
||||
ret = qspi_write_page(&qspiDev, cmd, 5,
|
||||
data + (page * FLASH_PAGE_SIZE), xferSz);
|
||||
QSPI_DEBUG_PRINTF("Flash Page %d Write: Ret %d\n", page, ret);
|
||||
if (ret != 0) break;
|
||||
|
||||
ret = qspi_wait_ready(&qspiDev);
|
||||
|
|
@ -1227,64 +1464,29 @@ int ext_flash_read(uintptr_t address, uint8_t *data, int len)
|
|||
uint8_t cmd[5];
|
||||
int ret;
|
||||
uintptr_t addr = address;
|
||||
uint32_t i;
|
||||
|
||||
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;
|
||||
/* For dual parallel the address is divided by 2 */
|
||||
addr /= 2;
|
||||
}
|
||||
|
||||
/* Single flash read */
|
||||
cmd[0] = FLASH_CMD_READ_4B;
|
||||
/* Use Quad Read command (0x6C) with 4-byte address */
|
||||
cmd[0] = FLASH_CMD_QUAD_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);
|
||||
/* Hardware handles striping via GQSPI_GEN_FIFO_STRIPE flag */
|
||||
#ifdef GQSPI_MODE_IO
|
||||
ret = qspi_transfer_qread(&qspiDev, cmd, 5, data, len, GQSPI_DUMMY_READ);
|
||||
#else
|
||||
ret = qspi_transfer_qread_dma(&qspiDev, cmd, 5, data, len, GQSPI_DUMMY_READ);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
33
hal/versal.h
33
hal/versal.h
|
|
@ -442,15 +442,38 @@
|
|||
#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
|
||||
/* QSPI Configuration (bare-metal driver) */
|
||||
#ifndef GQSPI_CLK_REF
|
||||
#define GQSPI_CLK_REF 300000000 /* 300 MHz */
|
||||
#endif
|
||||
#ifndef GQSPI_CLK_DIV
|
||||
#define GQSPI_CLK_DIV 1 /* Divide by 4 (300MHz / 4 = 75MHz) */
|
||||
#endif
|
||||
#define GQSPI_CS_ASSERT_CLOCKS 5 /* CS Setup Time (tCSS) */
|
||||
#define GQSPI_CS_DEASSERT_CLOCKS 4 /* CS Hold Time */
|
||||
#define GQSPI_FIFO_WORD_SZ 4
|
||||
#define GQSPI_DMA_ALIGN 64 /* L1 cache size */
|
||||
#ifndef GQSPI_DMA_TMPSZ
|
||||
#define GQSPI_DMA_TMPSZ 4096
|
||||
#endif
|
||||
#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
|
||||
/* QSPI Mode Configuration */
|
||||
#ifndef GQSPI_QSPI_MODE
|
||||
#define GQSPI_QSPI_MODE GQSPI_GEN_FIFO_MODE_QSPI /* 4-bit data */
|
||||
#endif
|
||||
#ifndef GQPI_USE_DUAL_PARALLEL
|
||||
#define GQPI_USE_DUAL_PARALLEL 1 /* 0=single, 1=dual parallel (striped) */
|
||||
#endif
|
||||
#ifndef GQPI_USE_4BYTE_ADDR
|
||||
#define GQPI_USE_4BYTE_ADDR 1 /* 0=3-byte addr, 1=4-byte addr */
|
||||
#endif
|
||||
#ifndef GQSPI_DUMMY_READ
|
||||
#define GQSPI_DUMMY_READ 8 /* Dummy clocks for Fast/Quad Read */
|
||||
#endif
|
||||
|
||||
#define XALIGNED(x) __attribute__((aligned(x)))
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue