blob: 3c8715f0778434335b15fba5c4dbdd84585ca379 [file] [log] [blame]
// ----------------------------------------------------------------------------
// Second stage boot code
// Copyright (c) 2019-2021 Raspberry Pi (Trading) Ltd.
// SPDX-License-Identifier: BSD-3-Clause
//
// Device: Anything which responds to 03h serial read command
//
// Details: * Configure SSI to translate each APB read into a 03h command
// * 8 command clocks, 24 address clocks and 32 data clocks
// * This enables you to boot from almost anything: you can pretty
// much solder a potato to your PCB, or a piece of cheese
// * The tradeoff is performance around 3x worse than QSPI XIP
//
// 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"
pico_default_asm_setup
// ----------------------------------------------------------------------------
// 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
#define CMD_READ 0x03
// Value is number of address bits divided by 4
#define ADDR_L 6
#define CTRLR0_XIP \
(SSI_CTRLR0_SPI_FRF_VALUE_STD << SSI_CTRLR0_SPI_FRF_LSB) | /* Standard 1-bit SPI serial frames */ \
(31 << SSI_CTRLR0_DFS_32_LSB) | /* 32 clocks per data frame */ \
(SSI_CTRLR0_TMOD_VALUE_EEPROM_READ << SSI_CTRLR0_TMOD_LSB) /* Send instr + addr, receive data */
#define SPI_CTRLR0_XIP \
(CMD_READ << SSI_SPI_CTRLR0_XIP_CMD_LSB) | /* Value of instruction prefix */ \
(ADDR_L << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Total number of address + mode bits */ \
(2 << SSI_SPI_CTRLR0_INST_L_LSB) | /* 8 bit command prefix (field value is bits divided by 4) */ \
(SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_1C1A << SSI_SPI_CTRLR0_TRANS_TYPE_LSB) /* command and address both in serial format */
// ----------------------------------------------------------------------------
// Start of 2nd Stage Boot Code
// ----------------------------------------------------------------------------
.section .text
regular_func _stage2_boot
push {lr}
ldr r3, =XIP_SSI_BASE // Use as base address where possible
// 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]
ldr r1, =(CTRLR0_XIP)
str r1, [r3, #SSI_CTRLR0_OFFSET]
ldr r1, =(SPI_CTRLR0_XIP)
ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET)
str r1, [r0]
// NDF=0 (single 32b read)
movs r1, #0x0
str r1, [r3, #SSI_CTRLR1_OFFSET]
// Re-enable SSI
movs r1, #1
str r1, [r3, #SSI_SSIENR_OFFSET]
// We are now in XIP mode. Any bus accesses to the XIP address window will be
// translated by the SSI into 03h read commands to the external flash (if cache is missed),
// and the data will be returned to the bus.
// Pull in standard exit routine
#include "boot2_helpers/exit_from_boot2.S"
.global literals
literals:
.ltorg
.end