blob: 89d2cd10ecca7c2ab4396301a5a0f351fe22ccf6 [file] [log] [blame]
// ----------------------------------------------------------------------------
// Second stage boot code
// Copyright (c) 2019-2021 Raspberry Pi (Trading) Ltd.
// SPDX-License-Identifier: BSD-3-Clause
//
// Device: Winbond W25X10CL
//
// Description: Configures W25X10CL to run in Dual I/O continuous read XIP mode
//
// Details: * Disable SSI
// * Configure SSI to generate 8b command + 28b address + 2 wait,
// with address and data using dual SPI mode
// * Enable SSI
// * Generate dummy read with command = 0xBB, top 24b of address
// of 0x000000 followed by M[7:0]=0010zzzz (with the HiZ being
// generated by 2 wait cycles). This leaves the W25X10CL in
// continuous read mode
// * Disable SSI
// * Configure SSI to generate 0b command + 28b address + 2 wait,
// with the extra 4 bits of address LSB being 0x2 to keep the
// W25X10CL in continuous read mode forever
// * Enable SSI
// * Set VTOR = 0x10000100
// * Read MSP reset vector from 0x10000100 and write to MSP (this
// will also enable XIP mode in the SSI wrapper)
// * Read PC reset vector from 0x10000104 and jump to it
//
// Building: * This code must be linked to run at 0x20000000
// * 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"
// The serial flash interface will run at clk_sys/PICO_FLASH_SPI_CLKDIV.
// This must be an even number.
#ifndef PICO_FLASH_SPI_CLKDIV
#define PICO_FLASH_SPI_CLKDIV 4
#endif
pico_default_asm_setup
// ----------------------------------------------------------------------------
// The "System Control Block" is a set of internal Cortex-M0+ control registers
// that are memory mapped and accessed like any other H/W register. They have
// fixed addresses in the address map of every Cortex-M0+ system.
// ----------------------------------------------------------------------------
.equ SCB_VTOR, 0xE000ED08 // RW Vector Table Offset Register
// ----------------------------------------------------------------------------
// Winbond W25X10CL Supported Commands
// Taken from "w25x10cl_reg_021714.pdf"
// ----------------------------------------------------------------------------
.equ W25X10CL_CMD_READ_DATA_FAST_DUAL_IO, 0xbb
// ----------------------------------------------------------------------------
// Winbond W25X10CL "Mode bits" are 8 special bits sent immediately after
// the address bits in a "Read Data Fast Dual I/O" command sequence.
// Of M[7:4], they say M[7:6] are reserved (set to zero), and bits M[3:0]
// are don't care (we HiZ). Only M[5:4] are used, and they must be set
// to M[5:4] = 2'b10 to enable continuous read mode.
// ----------------------------------------------------------------------------
.equ W25X10CL_MODE_CONTINUOUS_READ, 0x20
// ----------------------------------------------------------------------------
// Start of 2nd Stage Boot Code
// ----------------------------------------------------------------------------
.org 0
.section .text
// This code will get copied to 0x20000000 and then executed
regular_func _stage2_boot
push {lr}
ldr r3, =XIP_SSI_BASE // Use as base address where possible
// We are primarily interested in setting up Flash for DSPI XIP w/ continuous read
movs r1, #0
str r1, [r3, #SSI_SSIENR_OFFSET] // Disable SSI to allow further config
// The Boot ROM sets a very conservative SPI clock frequency to be sure it can
// read the initial 256 bytes from any device. Here we can be more aggressive.
movs r1, #PICO_FLASH_SPI_CLKDIV
str r1, [r3, #SSI_BAUDR_OFFSET] // Set SSI Clock
// First we need to send the initial command to get us in to Fast Read Dual I/O
// mode. As this transaction requires a command, we can't send it in XIP mode.
// To enter Continuous Read mode as well we need to append 4'b0010 to the address
// bits and then add a further 4 don't care bits. We will construct this by
// specifying a 28-bit address, with the least significant bits being 4'b0010.
// This is just a dummy transaction so we'll perform a read from address zero
// and then discard what comes back. All we really care about is that at the
// end of the transaction, the Winbond W25X10CL device is in Continuous Read mode
// and from then on will only expect to receive addresses.
#define CTRLR0_ENTER_XIP \
(SSI_CTRLR0_SPI_FRF_VALUE_DUAL /* Dual 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 \
(7 << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Send 28 bits (24 address + 4 mode) */ \
(2 << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | /* Hi-Z the other 4 mode bits (2 cycles @ dual I/O = 4 bits) */ \
(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 Dual 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, #W25X10CL_CMD_READ_DATA_FAST_DUAL_IO // 8b command = 0xBB
str r1, [r3, #SSI_DR0_OFFSET] // Push SPI command into TX FIFO
movs r1, #0x0000002 // 28-bit Address for dummy read = 0x000000 + 0x2 Mode bits to set M[5:4]=10
str r1, [r3, #SSI_DR0_OFFSET] // Push Address into TX FIFO - this will trigger the transaction
// Now we wait for the read transaction to complete by monitoring the SSI
// status register and checking for the "RX FIFO Not Empty" flag to assert.
movs r1, #SSI_SR_RFNE_BITS
00:
ldr r0, [r3, #SSI_SR_OFFSET] // Read status register
tst r0, r1 // RFNE status flag set?
beq 00b // If not then wait
// At this point CN# will be deasserted and the SPI clock will not be running.
// The Winbond WX25X10CL device will be in continuous read, dual I/O mode and
// only expecting address bits after the next CN# assertion. So long as we
// send 4'b0010 (and 4 more dummy HiZ bits) after every subsequent 24b address
// then the Winbond device will remain in continuous read mode. This is the
// ideal mode for Execute-In-Place.
// (If we want to exit continuous read mode then we will need to switch back
// to APM mode and generate a 28-bit address phase with the extra nibble set
// to 4'b0000).
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
#define SPI_CTRLR0_XIP \
(W25X10CL_MODE_CONTINUOUS_READ /* Mode bits to keep Winbond in continuous read mode */ \
<< SSI_SPI_CTRLR0_XIP_CMD_LSB) | \
(7 << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Send 28 bits (24 address + 4 mode) */ \
(2 << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | /* Hi-Z the other 4 mode bits (2 cycles @ dual I/O = 4 bits) */ \
(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 Dual 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
// We are now in XIP mode, with all transactions using Dual I/O and only
// needing to send 24-bit addresses (plus mode bits) for each read transaction.
// Pull in standard exit routine
#include "boot2_helpers/exit_from_boot2.S"
.global literals
literals:
.ltorg
.end