1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
#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);
}
|