Skip to content

Commit 3cd6781

Browse files
MichaelBellGadgetoid
authored andcommitted
ports/rp2: Set flash divisor appropriately.
Signed-off-by: Mike Bell <[email protected]>
1 parent f10906d commit 3cd6781

File tree

4 files changed

+92
-0
lines changed

4 files changed

+92
-0
lines changed

ports/rp2/main.c

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <stdio.h>
2828

2929
#include "rp2_psram.h"
30+
#include "rp2_flash.h"
3031
#include "py/compile.h"
3132
#include "py/cstack.h"
3233
#include "py/runtime.h"
@@ -91,6 +92,9 @@ int main(int argc, char **argv) {
9192
// Set the MCU frequency and as a side effect the peripheral clock to 48 MHz.
9293
set_sys_clock_khz(125000, false);
9394

95+
// Set the flash divisor to an appropriate value
96+
rp2_flash_set_timing();
97+
9498
#if MICROPY_HW_ENABLE_UART_REPL
9599
bi_decl(bi_program_feature("UART REPL"))
96100
setup_default_uart();

ports/rp2/modmachine.c

+12
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "modmachine.h"
3333
#include "uart.h"
3434
#include "rp2_psram.h"
35+
#include "rp2_flash.h"
3536
#include "clocks_extra.h"
3637
#include "hardware/pll.h"
3738
#include "hardware/structs/rosc.h"
@@ -95,6 +96,11 @@ static mp_obj_t mp_machine_get_freq(void) {
9596

9697
static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
9798
mp_int_t freq = mp_obj_get_int(args[0]);
99+
100+
// If necessary, increase the flash divider before increasing the clock speed
101+
const int old_freq = clock_get_hz(clk_sys);
102+
rp2_flash_set_timing_for_freq(MAX(freq, old_freq));
103+
98104
if (!set_sys_clock_khz(freq / 1000, false)) {
99105
mp_raise_ValueError(MP_ERROR_TEXT("cannot change frequency"));
100106
}
@@ -112,6 +118,12 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
112118
}
113119
}
114120
}
121+
122+
// If clock speed was reduced, maybe we can reduce the flash divider
123+
if (freq < old_freq) {
124+
rp2_flash_set_timing_for_freq(freq);
125+
}
126+
115127
#if MICROPY_HW_ENABLE_UART_REPL
116128
setup_default_uart();
117129
mp_uart_init();

ports/rp2/rp2_flash.c

+69
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@
3333
#include "modrp2.h"
3434
#include "hardware/flash.h"
3535
#include "pico/binary_info.h"
36+
#ifdef PICO_RP2350
37+
#include "hardware/structs/ioqspi.h"
38+
#include "hardware/structs/qmi.h"
39+
#else
40+
#include "hardware/structs/ssi.h"
41+
#endif
3642

3743
#define BLOCK_SIZE_BYTES (FLASH_SECTOR_SIZE)
3844

@@ -71,6 +77,48 @@ bi_decl(bi_block_device(
7177
BINARY_INFO_BLOCK_DEV_FLAG_WRITE |
7278
BINARY_INFO_BLOCK_DEV_FLAG_PT_UNKNOWN));
7379

80+
// Function to set the flash divisor to the correct divisor, assumes interrupts disabled
81+
// and core1 locked out if relevant.
82+
static void __no_inline_not_in_flash_func(rp2_flash_set_timing_internal)(int clock_hz) {
83+
84+
// Use the minimum divisor assuming a 133MHz flash.
85+
const int max_flash_freq = 133000000;
86+
int divisor = (clock_hz + max_flash_freq - 1) / max_flash_freq;
87+
88+
#if PICO_RP2350
89+
// Make sure flash is deselected - QMI doesn't appear to have a busy flag(!)
90+
while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) {
91+
;
92+
}
93+
94+
// RX delay equal to the divisor means sampling at the same time as the next falling edge of SCK after the
95+
// falling edge that generated the data. This is pretty tight at 133MHz but seems to work with the Winbond flash chips.
96+
const int rxdelay = divisor;
97+
qmi_hw->m[0].timing = (1 << QMI_M0_TIMING_COOLDOWN_LSB) |
98+
rxdelay << QMI_M1_TIMING_RXDELAY_LSB |
99+
divisor << QMI_M1_TIMING_CLKDIV_LSB;
100+
101+
// Force a read through XIP to ensure the timing is applied
102+
volatile uint32_t *ptr = (volatile uint32_t *)0x14000000;
103+
(void)*ptr;
104+
#else
105+
// RP2040 SSI hardware only supports even divisors
106+
if (divisor & 1) {
107+
divisor += 1;
108+
}
109+
110+
// Wait for SSI not busy
111+
while (ssi_hw->sr & SSI_SR_BUSY_BITS) {
112+
;
113+
}
114+
115+
// Disable, set the new divisor, and re-enable
116+
hw_clear_bits(&ssi_hw->ssienr, SSI_SSIENR_SSI_EN_BITS);
117+
ssi_hw->baudr = divisor;
118+
hw_set_bits(&ssi_hw->ssienr, SSI_SSIENR_SSI_EN_BITS);
119+
#endif
120+
}
121+
74122
// Flash erase and write must run with interrupts disabled and the other core suspended,
75123
// because the XIP bit gets disabled.
76124
static uint32_t begin_critical_flash_section(void) {
@@ -94,6 +142,7 @@ static void end_critical_flash_section(uint32_t state) {
94142
#if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM
95143
psram_init(MICROPY_HW_PSRAM_CS_PIN);
96144
#endif
145+
rp2_flash_set_timing_internal(clock_get_hz(clk_sys));
97146
restore_interrupts(state);
98147
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
99148
multicore_lockout_end_blocking();
@@ -250,3 +299,23 @@ MP_DEFINE_CONST_OBJ_TYPE(
250299
make_new, rp2_flash_make_new,
251300
locals_dict, &rp2_flash_locals_dict
252301
);
302+
303+
// Modify the flash timing. Ensure flash access is suspended while
304+
// the timings are altered.
305+
void rp2_flash_set_timing_for_freq(int clock_hz) {
306+
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
307+
multicore_lockout_start_blocking();
308+
}
309+
uint32_t state = save_and_disable_interrupts();
310+
311+
rp2_flash_set_timing_internal(clock_hz);
312+
313+
restore_interrupts(state);
314+
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
315+
multicore_lockout_end_blocking();
316+
}
317+
}
318+
319+
void rp2_flash_set_timing() {
320+
rp2_flash_set_timing_for_freq(clock_get_hz(clk_sys));
321+
}

ports/rp2/rp2_flash.h

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifndef MICROPY_INCLUDED_RP2_MACHINE_FLASH_H
2+
#define MICROPY_INCLUDED_RP2_MACHINE_FLASH_H
3+
4+
extern void rp2_flash_set_timing_for_freq(int clock_hz);
5+
extern void rp2_flash_set_timing();
6+
7+
#endif

0 commit comments

Comments
 (0)