| // ---------------------------------------------------------------------------- |
| // Second stage boot code |
| // Copyright (c) 2019-2021 Raspberry Pi (Trading) Ltd. |
| // SPDX-License-Identifier: BSD-3-Clause |
| // |
| // Device: Adesto AT25SF128A |
| // Based on W25Q080 code: main difference is the QE bit is being set |
| // via command 0x31 |
| // |
| // Description: Configures AT25SF128A to run in Quad I/O continuous read XIP mode |
| // |
| // Details: * Check status register 2 to determine if QSPI mode is enabled, |
| // and perform an SR2 programming cycle if necessary. |
| // * Use SSI to perform a dummy 0xEB read command, with the mode |
| // continuation bits set, so that the flash will not require |
| // 0xEB instruction prefix on subsequent reads. |
| // * Configure SSI to write address, mode bits, but no instruction. |
| // SSI + flash are now jointly in a state where continuous reads |
| // can take place. |
| // * Jump to exit pointer passed in via lr. Bootrom passes null, |
| // in which case this code uses a default 256 byte flash offset |
| // |
| // Building: * This code must be position-independent, and use stack only |
| // * The code will be padded to a size of 256 bytes, including a |
| // 4-byte checksum. Therefore code size cannot exceed 252 bytes. |
| // ---------------------------------------------------------------------------- |
| |
| #include "pico/asm_helper.S" |
| #include "hardware/regs/addressmap.h" |
| #include "hardware/regs/ssi.h" |
| #include "hardware/regs/pads_qspi.h" |
| |
| // todo port to QMI |
| |
| // ---------------------------------------------------------------------------- |
| // Config section |
| // ---------------------------------------------------------------------------- |
| // It should be possible to support most flash devices by modifying this section |
| |
| // The serial flash interface will run at clk_sys/PICO_FLASH_SPI_CLKDIV. |
| // This must be a positive, even integer. |
| // The bootrom is very conservative with SPI frequency, but here we should be |
| // as aggressive as possible. |
| |
| #ifndef PICO_FLASH_SPI_CLKDIV |
| #define PICO_FLASH_SPI_CLKDIV 4 |
| #endif |
| #if PICO_FLASH_SPI_CLKDIV & 1 |
| #error PICO_FLASH_SPI_CLKDIV must be even |
| #endif |
| |
| // Define interface width: single/dual/quad IO |
| #define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_QUAD |
| |
| // For W25Q080 this is the "Read data fast quad IO" instruction: |
| #define CMD_READ 0xeb |
| |
| // "Mode bits" are 8 special bits sent immediately after |
| // the address bits in a "Read Data Fast Quad I/O" command sequence. |
| // On W25Q080, the four LSBs are don't care, and if MSBs == 0xa, the |
| // next read does not require the 0xeb instruction prefix. |
| #define MODE_CONTINUOUS_READ 0x20 |
| |
| // The number of address + mode bits, divided by 4 (always 4, not function of |
| // interface width). |
| #define ADDR_L 8 |
| |
| // How many clocks of Hi-Z following the mode bits. For W25Q080, 4 dummy cycles |
| // are required. |
| #define WAIT_CYCLES 4 |
| |
| // If defined, we will read status reg, compare to SREG_DATA, and overwrite |
| // with our value if the SR doesn't match. |
| // We do a two-byte write to SR1 (01h cmd) rather than a one-byte write to |
| // SR2 (31h cmd) as the latter command isn't supported by WX25Q080. |
| // This isn't great because it will remove block protections. |
| // A better solution is to use a volatile SR write if your device supports it. |
| #define PROGRAM_STATUS_REG |
| |
| #define CMD_WRITE_ENABLE 0x06 |
| #define CMD_READ_STATUS 0x05 |
| #define CMD_READ_STATUS2 0x35 |
| #define CMD_WRITE_STATUS 0x01 |
| #define CMD_WRITE_STATUS2 0x31 |
| #define SREG_DATA 0x02 // Enable quad-SPI mode |
| |
| // ---------------------------------------------------------------------------- |
| // Start of 2nd Stage Boot Code |
| // ---------------------------------------------------------------------------- |
| |
| pico_default_asm_setup |
| |
| .section .text |
| |
| // On RP2350 boot stage2 is always called as a regular function, and should return normally |
| regular_func _stage2_boot |
| push {lr} |
| |
| // Set pad configuration: |
| // - SCLK 8mA drive, no slew limiting |
| // - SDx disable input Schmitt to reduce delay |
| |
| ldr r3, =PADS_QSPI_BASE |
| movs r0, #(2 << PADS_QSPI_GPIO_QSPI_SCLK_DRIVE_LSB | PADS_QSPI_GPIO_QSPI_SCLK_SLEWFAST_BITS) |
| str r0, [r3, #PADS_QSPI_GPIO_QSPI_SCLK_OFFSET] |
| ldr r0, [r3, #PADS_QSPI_GPIO_QSPI_SD0_OFFSET] |
| movs r1, #PADS_QSPI_GPIO_QSPI_SD0_SCHMITT_BITS |
| bics r0, r1 |
| str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD0_OFFSET] |
| str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD1_OFFSET] |
| str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD2_OFFSET] |
| str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD3_OFFSET] |
| |
| ldr r3, =XIP_SSI_BASE |
| |
| // Disable SSI to allow further config |
| movs r1, #0 |
| str r1, [r3, #SSI_SSIENR_OFFSET] |
| |
| // Set baud rate |
| movs r1, #PICO_FLASH_SPI_CLKDIV |
| str r1, [r3, #SSI_BAUDR_OFFSET] |
| |
| // Set 1-cycle sample delay. If PICO_FLASH_SPI_CLKDIV == 2 then this means, |
| // if the flash launches data on SCLK posedge, we capture it at the time that |
| // the next SCLK posedge is launched. This is shortly before that posedge |
| // arrives at the flash, so data hold time should be ok. For |
| // PICO_FLASH_SPI_CLKDIV > 2 this pretty much has no effect. |
| |
| movs r1, #1 |
| movs r2, #SSI_RX_SAMPLE_DLY_OFFSET // == 0xf0 so need 8 bits of offset significance |
| str r1, [r3, r2] |
| |
| |
| // On QSPI parts we usually need a 01h SR-write command to enable QSPI mode |
| // (i.e. turn WPn and HOLDn into IO2/IO3) |
| #ifdef PROGRAM_STATUS_REG |
| program_sregs: |
| #define CTRL0_SPI_TXRX \ |
| (7 << SSI_CTRLR0_DFS_32_LSB) | /* 8 bits per data frame */ \ |
| (SSI_CTRLR0_TMOD_VALUE_TX_AND_RX << SSI_CTRLR0_TMOD_LSB) |
| |
| ldr r1, =(CTRL0_SPI_TXRX) |
| str r1, [r3, #SSI_CTRLR0_OFFSET] |
| |
| // Enable SSI and select slave 0 |
| movs r1, #1 |
| str r1, [r3, #SSI_SSIENR_OFFSET] |
| |
| // Check whether SR needs updating |
| movs r0, #CMD_READ_STATUS2 |
| bl read_flash_sreg |
| movs r2, #SREG_DATA |
| cmp r0, r2 |
| beq skip_sreg_programming |
| |
| // Send write enable command |
| movs r1, #CMD_WRITE_ENABLE |
| str r1, [r3, #SSI_DR0_OFFSET] |
| |
| // Poll for completion and discard RX |
| bl wait_ssi_ready |
| ldr r1, [r3, #SSI_DR0_OFFSET] |
| |
| // Send status write command followed by data bytes |
| movs r1, #CMD_WRITE_STATUS2 |
| str r1, [r3, #SSI_DR0_OFFSET] |
| str r2, [r3, #SSI_DR0_OFFSET] |
| |
| bl wait_ssi_ready |
| ldr r1, [r3, #SSI_DR0_OFFSET] |
| ldr r1, [r3, #SSI_DR0_OFFSET] |
| ldr r1, [r3, #SSI_DR0_OFFSET] |
| |
| // Poll status register for write completion |
| 1: |
| movs r0, #CMD_READ_STATUS |
| bl read_flash_sreg |
| movs r1, #1 |
| tst r0, r1 |
| bne 1b |
| |
| skip_sreg_programming: |
| |
| // Disable SSI again so that it can be reconfigured |
| movs r1, #0 |
| str r1, [r3, #SSI_SSIENR_OFFSET] |
| #endif |
| |
| // Currently the flash expects an 8 bit serial command prefix on every |
| // transfer, which is a waste of cycles. Perform a dummy Fast Read Quad I/O |
| // command, with mode bits set such that the flash will not expect a serial |
| // command prefix on *subsequent* transfers. We don't care about the results |
| // of the read, the important part is the mode bits. |
| |
| dummy_read: |
| #define CTRLR0_ENTER_XIP \ |
| (FRAME_FORMAT /* Quad I/O mode */ \ |
| << SSI_CTRLR0_SPI_FRF_LSB) | \ |
| (31 << SSI_CTRLR0_DFS_32_LSB) | /* 32 data bits */ \ |
| (SSI_CTRLR0_TMOD_VALUE_EEPROM_READ /* Send INST/ADDR, Receive Data */ \ |
| << SSI_CTRLR0_TMOD_LSB) |
| |
| ldr r1, =(CTRLR0_ENTER_XIP) |
| str r1, [r3, #SSI_CTRLR0_OFFSET] |
| |
| movs r1, #0x0 // NDF=0 (single 32b read) |
| str r1, [r3, #SSI_CTRLR1_OFFSET] |
| |
| #define SPI_CTRLR0_ENTER_XIP \ |
| (ADDR_L << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Address + mode bits */ \ |
| (WAIT_CYCLES << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | /* Hi-Z dummy clocks following address + mode */ \ |
| (SSI_SPI_CTRLR0_INST_L_VALUE_8B \ |
| << SSI_SPI_CTRLR0_INST_L_LSB) | /* 8-bit instruction */ \ |
| (SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_1C2A /* Send Command in serial mode then address in Quad I/O mode */ \ |
| << SSI_SPI_CTRLR0_TRANS_TYPE_LSB) |
| |
| ldr r1, =(SPI_CTRLR0_ENTER_XIP) |
| ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET) // SPI_CTRL0 Register |
| str r1, [r0] |
| |
| movs r1, #1 // Re-enable SSI |
| str r1, [r3, #SSI_SSIENR_OFFSET] |
| |
| movs r1, #CMD_READ |
| str r1, [r3, #SSI_DR0_OFFSET] // Push SPI command into TX FIFO |
| movs r1, #MODE_CONTINUOUS_READ // 32-bit: 24 address bits (we don't care, so 0) and M[7:4]=1010 |
| str r1, [r3, #SSI_DR0_OFFSET] // Push Address into TX FIFO - this will trigger the transaction |
| |
| // Poll for completion |
| bl wait_ssi_ready |
| |
| // The flash is in a state where we can blast addresses in parallel, and get |
| // parallel data back. Now configure the SSI to translate XIP bus accesses |
| // into QSPI transfers of this form. |
| |
| movs r1, #0 |
| str r1, [r3, #SSI_SSIENR_OFFSET] // Disable SSI (and clear FIFO) to allow further config |
| |
| // Note that the INST_L field is used to select what XIP data gets pushed into |
| // the TX FIFO: |
| // INST_L_0_BITS {ADDR[23:0],XIP_CMD[7:0]} Load "mode bits" into XIP_CMD |
| // Anything else {XIP_CMD[7:0],ADDR[23:0]} Load SPI command into XIP_CMD |
| configure_ssi: |
| #define SPI_CTRLR0_XIP \ |
| (MODE_CONTINUOUS_READ /* Mode bits to keep flash in continuous read mode */ \ |
| << SSI_SPI_CTRLR0_XIP_CMD_LSB) | \ |
| (ADDR_L << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Total number of address + mode bits */ \ |
| (WAIT_CYCLES << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | /* Hi-Z dummy clocks following address + mode */ \ |
| (SSI_SPI_CTRLR0_INST_L_VALUE_NONE /* Do not send a command, instead send XIP_CMD as mode bits after address */ \ |
| << SSI_SPI_CTRLR0_INST_L_LSB) | \ |
| (SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_2C2A /* Send Address in Quad I/O mode (and Command but that is zero bits long) */ \ |
| << SSI_SPI_CTRLR0_TRANS_TYPE_LSB) |
| |
| ldr r1, =(SPI_CTRLR0_XIP) |
| ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET) |
| str r1, [r0] |
| |
| movs r1, #1 |
| str r1, [r3, #SSI_SSIENR_OFFSET] // Re-enable SSI |
| |
| // Bus accesses to the XIP window will now be transparently serviced by the |
| // external flash on cache miss. We are ready to run code from flash. |
| |
| // Pull in standard exit routine |
| #include "boot2_helpers/exit_from_boot2.S" |
| |
| // Common functions |
| #include "boot2_helpers/wait_ssi_ready.S" |
| #ifdef PROGRAM_STATUS_REG |
| #include "boot2_helpers/read_flash_sreg.S" |
| #endif |
| |
| .global literals |
| literals: |
| .ltorg |
| |
| .end |