mirror of https://github.com/wolfSSL/wolfBoot.git
PolarFire SoC: QSPI clock divider knob and word-mode FIFO reads
parent
14a9081f3c
commit
e5badf5ae5
|
|
@ -965,6 +965,7 @@ These flags apply to `polarfire_mpfs250_m_qspi.config` and are added via `CFLAGS
|
|||
| `WATCHDOG` | undefined (disabled) | When defined, the E51 watchdog timer is **kept enabled** during wolfBoot operation with a generous timeout. When undefined, the WDT is **disabled** in `hal_init()` and re-enabled with the boot ROM default in `hal_prepare_boot()` before jumping to the application. Either way, the application receives a normal WDT. |
|
||||
| `WATCHDOG_TIMEOUT_MS` | `30000` (30 s) | Watchdog timeout in milliseconds when `WATCHDOG` is defined. ECDSA P-384 verification on E51 with portable C math is bounded at ~5 s; the default 30 s avoids any need to refresh the WDT during the long verify call. |
|
||||
| `MPFS_WDT_NODES_KEEP` (make variable, `=1`) | undefined | Linux boot only: keep the five MSS watchdog nodes enabled in the dtb and stop the parked E51 from refreshing them, so the OS owns the watchdogs. See "Watchdog policy" below. |
|
||||
| `MPFS_QSPI_CLK_DIV` (make variable) | `QSPI_CLK_DIV_8` | QSPI SCK divider, a `QSPI_CLK_DIV_*` name from `hal/mpfs250.h`. See "PolarFire SoC QSPI". |
|
||||
|
||||
#### Stack overflow detection
|
||||
|
||||
|
|
@ -1091,6 +1092,14 @@ Example single-shot build: `cp config/examples/polarfire_mpfs250_qspi.config .co
|
|||
|
||||
Notes:
|
||||
- Both modes support full read, write, and erase operations.
|
||||
- SCK is the controller clock divided by the CoreQSPI `CLKRATE` field, SCK = PCLK / (2 * CLKRATE).
|
||||
`MPFS_QSPI_CLK_DIV` selects the divider by its `QSPI_CLK_DIV_*` name from `hal/mpfs250.h`
|
||||
(`QSPI_CLK_DIV_2` through `QSPI_CLK_DIV_30`, CLKRATE 1..15). The default `QSPI_CLK_DIV_8` is
|
||||
about 18.75 MHz from the 150 MHz APB clock, where read throughput stops improving: from there on
|
||||
the polled FIFO transfer rather than SCK sets the rate, so a faster SCK buys nothing. The MSS QSPI
|
||||
PCLK is the MSS APB clock, which is 40 MHz at the M-mode reset clock until wolfBoot's PLL
|
||||
bring-up, so an M-mode build that starts QSPI before `mss_pll_init()` runs SCK that much slower;
|
||||
the SC QSPI is clocked by the System Controller and is unaffected by the MSS PLL.
|
||||
- For QSPI-based boot flows, disable SD/eMMC in the config (`DISK_SDCARD=0`, `DISK_EMMC=0`) unless you
|
||||
explicitly want wolfBoot to load from disk and the application from QSPI.
|
||||
- The MSS QSPI path expects external flash on the MSS QSPI pins; the SC QSPI path is for
|
||||
|
|
|
|||
|
|
@ -2127,13 +2127,13 @@ int qspi_init(void)
|
|||
QSPI_IEN = 0;
|
||||
|
||||
/* Configure QSPI Control Register:
|
||||
* - Clock divider for ~5MHz (conservative)
|
||||
* - Clock divider (MPFS_QSPI_CLK_DIV, ~5 MHz by default)
|
||||
* - CPOL=1 (clock idle high) for SPI Mode 3
|
||||
* - Sample on SCK edge
|
||||
* - Enable controller
|
||||
*/
|
||||
QSPI_CONTROL =
|
||||
(QSPI_CLK_DIV_30 << QSPI_CTRL_CLKRATE_OFFSET) |
|
||||
(MPFS_QSPI_CLK_DIV << QSPI_CTRL_CLKRATE_OFFSET) |
|
||||
QSPI_CTRL_CLKIDLE |
|
||||
QSPI_CTRL_SAMPLE_SCK |
|
||||
QSPI_CTRL_EN;
|
||||
|
|
@ -2180,6 +2180,8 @@ static int qspi_transfer_block(uint8_t read_mode, const uint8_t *cmd,
|
|||
uint32_t i;
|
||||
uint32_t timeout;
|
||||
uint32_t frame_cmd;
|
||||
uint32_t rx_word;
|
||||
int word_read;
|
||||
|
||||
/* Wait for controller to be ready before starting */
|
||||
timeout = QSPI_TIMEOUT_TRIES;
|
||||
|
|
@ -2216,10 +2218,18 @@ static int qspi_transfer_block(uint8_t read_mode, const uint8_t *cmd,
|
|||
* command phase avoids this. The flash determines command vs data
|
||||
* boundaries from the opcode, not the controller's phase. */
|
||||
frame_cmd = read_mode ? cmd_len : total_bytes;
|
||||
/* FBYTE makes RXAVAIL/TXAVAIL count single bytes; FWORD makes them count
|
||||
* 32-bit words, so a read polls STATUS once per four bytes instead of
|
||||
* once per byte and pulls them through QSPI_X4_RX_DATA in one access.
|
||||
* The polled loop, not SCK, is what caps the byte-mode read rate (it
|
||||
* measures the same from CLKRATE /8 down to /2). Word mode needs a data
|
||||
* phase that is a whole number of words; anything else stays byte-mode. */
|
||||
word_read = (read_mode && data_len >= 4u && (data_len & 3u) == 0u) ? 1 : 0;
|
||||
frames = ((total_bytes & 0xFFFF) << QSPI_FRAMES_TOTALBYTES_OFFSET) |
|
||||
((frame_cmd & 0x1FF) << QSPI_FRAMES_CMDBYTES_OFFSET) |
|
||||
((dummy_cycles & 0xF) << QSPI_FRAMES_IDLE_OFFSET) |
|
||||
(1u << QSPI_FRAMES_FBYTE_OFFSET);
|
||||
(word_read ? (1u << QSPI_FRAMES_FWORD_OFFSET)
|
||||
: (1u << QSPI_FRAMES_FBYTE_OFFSET));
|
||||
|
||||
QSPI_FRAMES = frames;
|
||||
|
||||
|
|
@ -2243,18 +2253,42 @@ static int qspi_transfer_block(uint8_t read_mode, const uint8_t *cmd,
|
|||
}
|
||||
|
||||
if (read_mode) {
|
||||
/* Read mode: poll RXAVAIL for each data byte. */
|
||||
for (i = 0; i < data_len; i++) {
|
||||
timeout = QSPI_RX_TIMEOUT_TRIES;
|
||||
while (!(QSPI_STATUS & QSPI_STATUS_RXAVAIL) && --timeout);
|
||||
if (timeout == 0) {
|
||||
#ifdef DEBUG_QSPI
|
||||
wolfBoot_printf("QSPI: RX timeout at byte %d, status=0x%x\n",
|
||||
i, QSPI_STATUS);
|
||||
#endif
|
||||
return -3;
|
||||
if (word_read) {
|
||||
/* One STATUS poll and one 32-bit read per four bytes. The
|
||||
* destination is not necessarily word-aligned, so the word is
|
||||
* stored a byte at a time; the saving is in the polling and the
|
||||
* FIFO accesses, not the stores. */
|
||||
for (i = 0; i < data_len; i += 4u) {
|
||||
timeout = QSPI_RX_TIMEOUT_TRIES;
|
||||
while (!(QSPI_STATUS & QSPI_STATUS_RXAVAIL) && --timeout);
|
||||
if (timeout == 0) {
|
||||
#ifdef DEBUG_QSPI
|
||||
wolfBoot_printf("QSPI: RX timeout at word %d, status=0x%x\n",
|
||||
i >> 2, QSPI_STATUS);
|
||||
#endif
|
||||
return -3;
|
||||
}
|
||||
rx_word = QSPI_X4_RX_DATA;
|
||||
data[i] = (uint8_t)(rx_word & 0xFFu);
|
||||
data[i + 1] = (uint8_t)((rx_word >> 8) & 0xFFu);
|
||||
data[i + 2] = (uint8_t)((rx_word >> 16) & 0xFFu);
|
||||
data[i + 3] = (uint8_t)((rx_word >> 24) & 0xFFu);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Byte mode: poll RXAVAIL for each data byte. */
|
||||
for (i = 0; i < data_len; i++) {
|
||||
timeout = QSPI_RX_TIMEOUT_TRIES;
|
||||
while (!(QSPI_STATUS & QSPI_STATUS_RXAVAIL) && --timeout);
|
||||
if (timeout == 0) {
|
||||
#ifdef DEBUG_QSPI
|
||||
wolfBoot_printf("QSPI: RX timeout at byte %d, status=0x%x\n",
|
||||
i, QSPI_STATUS);
|
||||
#endif
|
||||
return -3;
|
||||
}
|
||||
data[i] = QSPI_RX_DATA;
|
||||
}
|
||||
data[i] = QSPI_RX_DATA;
|
||||
}
|
||||
/* Wait for receive complete */
|
||||
timeout = QSPI_RX_TIMEOUT_TRIES;
|
||||
|
|
@ -2353,7 +2387,10 @@ static int qspi_enter_4byte_mode(void)
|
|||
/* Read from QSPI flash (4-byte addressing) */
|
||||
static int qspi_flash_read(uint32_t address, uint8_t *data, uint32_t len)
|
||||
{
|
||||
const uint32_t max_chunk = 0xFFFF - 5; /* total_bytes is 16-bit, cmd is 5 */
|
||||
/* total_bytes is 16-bit and the command takes 5 of it; round down to a
|
||||
* whole number of words so a bulk read stays on the word-mode FIFO path
|
||||
* in qspi_transfer_block instead of falling back to byte mode. */
|
||||
const uint32_t max_chunk = (0xFFFFu - 5u) & ~3u;
|
||||
uint8_t cmd[5];
|
||||
uint32_t remaining = len;
|
||||
uint32_t chunk_len;
|
||||
|
|
|
|||
|
|
@ -789,8 +789,10 @@ int mpfs_pdma_memcpy(void *dst, const void *src, uint32_t bytes);
|
|||
/* QSPI Flash Controller
|
||||
*
|
||||
* Two CoreQSPI v2 controllers with identical register layouts:
|
||||
* SC QSPI (MPFS_SC_SPI=1, default): 0x37020100 -- fabric-connected flash
|
||||
* MSS QSPI (MPFS_SC_SPI=0): 0x21000000 -- MSS QSPI pins
|
||||
* SC QSPI (-DMPFS_SC_SPI, every shipped QSPI config): 0x37020100,
|
||||
* the System Controller's flash
|
||||
* MSS QSPI (MPFS_SC_SPI undefined): 0x21000000,
|
||||
* the MSS QSPI pins
|
||||
*/
|
||||
|
||||
/* QSPI Controller Base Address */
|
||||
|
|
@ -851,14 +853,34 @@ int mpfs_pdma_memcpy(void *dst, const void *src, uint32_t bytes);
|
|||
#define QSPI_STATUS_TXAVAIL (1u << 3)
|
||||
#define QSPI_STATUS_READY (1u << 7)
|
||||
|
||||
/* QSPI Clock Configuration */
|
||||
/* QSPI Clock Configuration: SCK = PCLK / (2 * CLKRATE). The MSS QSPI PCLK
|
||||
* is the APB clock, 150 MHz under HSS and 40 MHz at the M-mode reset clock
|
||||
* (MSS_APB_AHB_CLK) until mss_pll_init() raises it; the SC QSPI has the
|
||||
* System Controller's own clock. MPFS_QSPI_CLK_DIV picks the divider. */
|
||||
#define QSPI_CLK_DIV_2 0x01u
|
||||
#define QSPI_CLK_DIV_4 0x02u
|
||||
#define QSPI_CLK_DIV_6 0x03u
|
||||
#define QSPI_CLK_DIV_8 0x04u
|
||||
#define QSPI_CLK_DIV_8 0x04u /* ~18.75 MHz from 150 MHz APB */
|
||||
#define QSPI_CLK_DIV_10 0x05u
|
||||
#define QSPI_CLK_DIV_12 0x06u
|
||||
#define QSPI_CLK_DIV_14 0x07u
|
||||
#define QSPI_CLK_DIV_16 0x08u
|
||||
#define QSPI_CLK_DIV_18 0x09u
|
||||
#define QSPI_CLK_DIV_20 0x0Au
|
||||
#define QSPI_CLK_DIV_22 0x0Bu
|
||||
#define QSPI_CLK_DIV_24 0x0Cu
|
||||
#define QSPI_CLK_DIV_26 0x0Du
|
||||
#define QSPI_CLK_DIV_28 0x0Eu
|
||||
#define QSPI_CLK_DIV_30 0x0Fu /* ~5 MHz from 150 MHz APB */
|
||||
/* Divide by 8 is where read throughput stops improving: the polled FIFO
|
||||
* transfer, not SCK, is the limit from there on. Anything slower only costs
|
||||
* time, anything faster buys nothing. */
|
||||
#ifndef MPFS_QSPI_CLK_DIV
|
||||
#define MPFS_QSPI_CLK_DIV QSPI_CLK_DIV_8
|
||||
#endif
|
||||
#if (MPFS_QSPI_CLK_DIV < 0x01u) || (MPFS_QSPI_CLK_DIV > 0x0Fu)
|
||||
#error MPFS_QSPI_CLK_DIV must be a CLKRATE of 1..15 (QSPI_CLK_DIV_2..QSPI_CLK_DIV_30)
|
||||
#endif
|
||||
|
||||
/* Micron MT25QL01G Flash Commands */
|
||||
#define QSPI_CMD_READ_ID_OPCODE 0x9Fu /* JEDEC ID Read */
|
||||
|
|
|
|||
|
|
@ -194,6 +194,11 @@ ifeq ($(DISK_FAILOVER_ZEROIZE),1)
|
|||
endif
|
||||
CFLAGS+=-D"DISK_FAILOVER_ZEROIZE"
|
||||
endif
|
||||
# QSPI SCK divider: a QSPI_CLK_DIV_* name from hal/mpfs250.h, default QSPI_CLK_DIV_8.
|
||||
ifneq ($(MPFS_QSPI_CLK_DIV),)
|
||||
CFLAGS+=-DMPFS_QSPI_CLK_DIV=$(MPFS_QSPI_CLK_DIV)
|
||||
endif
|
||||
|
||||
# Leave the MSS watchdog nodes in the dtb and stop the parked E51 refreshing them, so the OS owns
|
||||
# the watchdogs after the hand-off and must service them itself.
|
||||
ifeq ($(MPFS_WDT_NODES_KEEP),1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue