#include "sdk/src/rp2040/hardware_structs/include/hardware/structs/ssi.h" #include "sdk/src/rp2040/hardware_structs/include/hardware/structs/pads_qspi.h" #include "sdk/src/rp2040/hardware_regs/include/hardware/regs/addressmap.h" #include "sdk/src/rp2040/hardware_regs/include/hardware/regs/m0plus.h" // "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 0xa0 // Define interface width: single/dual/quad IO {% if quad_ok %} #define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_QUAD #define TRANSACTION_TYPE SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_2C2A // 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 #define INSTRUCTION_LENGTH SSI_SPI_CTRLR0_INST_L_VALUE_NONE #define READ_INSTRUCTION MODE_CONTINUOUS_READ #define ADDR_L 8 // 6 for address, 2 for mode {% else %} #define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_STD #define TRANSACTION_TYPE SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_1C1A #define INSTRUCTION_LENGTH SSI_SPI_CTRLR0_INST_L_VALUE_8B #define READ_INSTRUCTION (0x{{ '%02x' % read_command }}) #define ADDR_L 6 // * 4 = 24 {% endif %} #define CMD_READ_STATUS1 0x05 #define CMD_READ_STATUS2 0x35 #define CMD_WRITE_ENABLE 0x06 #define CMD_WRITE_STATUS1 0x01 #define CMD_WRITE_STATUS2 0x31 #define SREG_DATA 0x02 static uint32_t wait_and_read(uint8_t); static uint8_t read_flash_sreg(uint8_t status_command); // This function is use by the bootloader to enable the XIP flash. It is also // used by the SDK to reinit XIP after doing non-read flash interactions such as // writing or erasing. This code must compile down to position independent // assembly because we don't know where in RAM it'll be when run. // This must be the first defined function so that it is placed at the start of // memory where the bootloader jumps to! extern void _stage2_boot(void); void __attribute__((section(".entry._stage2_boot"), used)) _stage2_boot(void) { uint32_t lr; asm ("MOV %0, LR\n" : "=r" (lr) ); // Set aggressive pad configuration for QSPI // - SCLK 8mA drive, no slew limiting // - SDx disable input Schmitt to reduce delay // SCLK pads_qspi_hw->io[0] = PADS_QSPI_GPIO_QSPI_SCLK_DRIVE_VALUE_8MA << PADS_QSPI_GPIO_QSPI_SCLK_DRIVE_LSB | PADS_QSPI_GPIO_QSPI_SCLK_SLEWFAST_BITS; // Data lines uint32_t data_settings = pads_qspi_hw->io[1]; data_settings &= ~PADS_QSPI_GPIO_QSPI_SCLK_SCHMITT_BITS; pads_qspi_hw->io[2] = data_settings; {% if quad_ok %} pads_qspi_hw->io[1] = data_settings; pads_qspi_hw->io[3] = data_settings; pads_qspi_hw->io[4] = data_settings; {% endif %} // Disable the SSI so we can change the settings. ssi_hw->ssienr = 0; // QSPI config ssi_hw->baudr = {{ clock_divider }}; // 125 mhz / clock divider // 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. ssi_hw->rx_sample_dly = 1; // Set a temporary mode for doing simple commands. ssi_hw->ctrlr0 = (7 << SSI_CTRLR0_DFS_32_LSB) | // 8 bits per data frame (SSI_CTRLR0_TMOD_VALUE_TX_AND_RX << SSI_CTRLR0_TMOD_LSB); ssi_hw->ssienr = 0x1; {% if quad_ok %} // Program status register. // Enable SSI and select slave 0 {% if quad_enable_status_byte == 1 %} uint8_t result = read_flash_sreg(CMD_READ_STATUS1); {% elif quad_enable_status_byte == 2 %} uint8_t result = read_flash_sreg(CMD_READ_STATUS2); {% endif %} if (result != {{ quad_enable_bit_mask }}) { ssi_hw->dr0 = (uint8_t) CMD_WRITE_ENABLE; wait_and_read(1); {% if split_status_write %} {% if quad_enable_status_byte == 1 %} ssi_hw->dr0 = (uint8_t) CMD_WRITE_STATUS1; {% elif quad_enable_status_byte == 2 %} ssi_hw->dr0 = (uint8_t) CMD_WRITE_STATUS2; {% endif %} ssi_hw->dr0 = {{ quad_enable_bit_mask }}; wait_and_read(2); {% else %} ssi_hw->dr0 = (uint8_t) CMD_WRITE_STATUS1; {% if quad_enable_status_byte == 2 %} ssi_hw->dr0 = 0x0; {% endif %} ssi_hw->dr0 = {{ quad_enable_bit_mask }}; wait_and_read({{ quad_enable_status_byte + 1 }}); {% endif %} // Wait for the write to complete. while ((read_flash_sreg(CMD_READ_STATUS1) & 0x1) != 0) {} } {% endif %} // Disable SSI again so that it can be reconfigured ssi_hw->ssienr = 0; // Do a single read to get us in continuous mode. // Final SSI ctrlr0 settings. We only change the SPI specific settings later. ssi_hw->ctrlr0 = (FRAME_FORMAT << SSI_CTRLR0_SPI_FRF_LSB) | // Quad I/O mode (31 << SSI_CTRLR0_DFS_32_LSB) | // 32 data bits (SSI_CTRLR0_TMOD_VALUE_EEPROM_READ << SSI_CTRLR0_TMOD_LSB); // Send INST/ADDR, Receive Data ssi_hw->ctrlr1 = 0; // Single 32b read {% if quad_ok %} ssi_hw->spi_ctrlr0 = (ADDR_L << SSI_SPI_CTRLR0_ADDR_L_LSB) | // Address + mode bits // Hi-Z dummy clocks following address + mode ({{ wait_cycles }} << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | // 8-bit instruction (SSI_SPI_CTRLR0_INST_L_VALUE_8B << SSI_SPI_CTRLR0_INST_L_LSB) | // Send Command in serial mode then address in Quad I/O mode (SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_1C2A << SSI_SPI_CTRLR0_TRANS_TYPE_LSB); // Re-enable the SSI ssi_hw->ssienr = 1; // Do a single read to get us in continuous mode. ssi_hw->dr0 = 0x{{ '%02x' % read_command }}; ssi_hw->dr0 = MODE_CONTINUOUS_READ; wait_and_read(2); // Disable the SSI to switch to no-command mode (because we're setup for continuous.) ssi_hw->ssienr = 0; {% endif %} // Final SPI ctrlr0 settings. ssi_hw->spi_ctrlr0 = (READ_INSTRUCTION << SSI_SPI_CTRLR0_XIP_CMD_LSB) | // Mode bits to keep flash in continuous read mode (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 (INSTRUCTION_LENGTH << SSI_SPI_CTRLR0_INST_L_LSB) | // Do not send a command, instead send XIP_CMD as mode bits after address (TRANSACTION_TYPE << SSI_SPI_CTRLR0_TRANS_TYPE_LSB); // Send Address in Quad I/O mode (and Command but that is zero bits long) // Re-enable the SSI ssi_hw->ssienr = 1; // If lr is 0, then we came from the bootloader. if (lr == 0) { uint32_t* vector_table = (uint32_t*) (XIP_BASE + 0x100); // Switch the vector table to immediately after the stage 2 area. *((uint32_t *) (PPB_BASE + M0PLUS_VTOR_OFFSET)) = (uint32_t) vector_table; // Set the top of the stack according to the vector table. asm volatile ("MSR msp, %0" : : "r" (vector_table[0]) : ); // The reset handler is the second entry in the vector table asm volatile ("bx %0" : : "r" (vector_table[1]) : ); // Doesn't return. It jumps to the reset handler instead. } // Otherwise we return. } static uint32_t wait_and_read(uint8_t count) { while ((ssi_hw->sr & SSI_SR_TFE_BITS) == 0) {} while ((ssi_hw->sr & SSI_SR_BUSY_BITS) != 0) {} uint32_t result = 0; while (count > 0) { result = ssi_hw->dr0; count--; } return result; } static uint8_t read_flash_sreg(uint8_t status_command) { ssi_hw->dr0 = status_command; ssi_hw->dr0 = status_command; return wait_and_read(2); }