Compare commits

...

24 Commits

Author SHA1 Message Date
Tomas Vanek
4d4c45cfd2 flash/nor/rp2xxx: define macro BOOTROM_MAGIC_MASK
and use it instead of magic value.

Change-Id: I5d006aaf990d4ef3a82e622b1e41cd2bfec359f7
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reported-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8810
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Tested-by: jenkins
2025-04-25 10:26:15 +00:00
Tomas Vanek
bf66d95be2 flash/nor/rp2xxx: fix LOG_xxx messages
Use proper format specifiers for uint16_t and uint32_t arguments.
Use LOG_TARGET_DEBUG instead of target->cmd_name as a parameter.
Use command_print() in command handler.
Drop dots and new lines at end of messages.

Change-Id: I37c7d3680a352210b1d7e69f2c9b4ba0efe6ec15
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reported-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8809
Tested-by: jenkins
2025-04-25 10:25:52 +00:00
Luke Wren
5bb7dbc231 flash/nor/rp2xxx: fix flash operation after halt in RISC-V bootsel
Calling ROM API set_bootrom_stack() function allows ROM API functionality
after OpenOCD halt or reset halt in RISC-V bootloder (emulated ARM code)

Change-Id: I3b255738d61876e876a94207804d9cbe1a7593c2
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Signed-off-by: Luke Wren <luke@raspberrypi.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8729
Tested-by: jenkins
2025-04-25 09:45:51 +00:00
Tomas Vanek
376d11c2e3 tcl/target/rp2040: add flash size override and reset init event
Allow flash size override and suppress flash size detection
by setting FLASHSIZE Tcl variable.

reset-init event calls 'connect XIP' ROM API function to make
flash content accessible at the XIP mapping memory area.

Ported from rp2350.cfg

Change-Id: I9b352b1ef6d4c6d4b78a6b61e900ce01355c8eff
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: https://review.openocd.org/c/openocd/+/8461
Reviewed-by: Jonathan Bell <jonathan@raspberrypi.com>
Tested-by: jenkins
2025-04-25 09:45:33 +00:00
Tomas Vanek
f039fe7f9d flash/nor/rp2xxx: fix endianness error
struct rp2xxx_rom_call_batch_record consists of uint32_t in the host
endianness. Therefore it should be converted to the target endianness
not just simply copied by target_write_buffer().

Concatenate algo code, converted batch records and terminator
to the host resident buffer and copy it at once to the target and
save some adapter turnaround times.

While on it remove typedef rp2xxx_rom_call_batch_record_t

Change-Id: I0e698396003869bee5dde4141d48ddd7d62b3cbc
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: https://review.openocd.org/c/openocd/+/8460
Reviewed-by: Jonathan Bell <jonathan@raspberrypi.com>
Tested-by: jenkins
2025-04-25 09:45:06 +00:00
Tomas Vanek
a960887169 flash/nor/rp2xxx: drop couple of Java-like const
The compiler knows what variable remains constant during its lifetime
and there is no need to emphasise constantness.

Change-Id: Ib515f96a3c77afea87274f33b8ccac7a71bfb932
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: https://review.openocd.org/c/openocd/+/8459
Tested-by: jenkins
Reviewed-by: Jonathan Bell <jonathan@raspberrypi.com>
2025-04-25 09:44:28 +00:00
Tomas Vanek
d29c1c6d6d flash/nor/rp2xxx: minor code improvements
Add error messages and proper error propagation.
Type cleaning.
Use saved chip id.
Cosmetics: separating lines added.

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: I151e684e1fbfc9476ec429036caf85f4c9329547
Reviewed-on: https://review.openocd.org/c/openocd/+/8457
Tested-by: jenkins
Reviewed-by: Jonathan Bell <jonathan@raspberrypi.com>
2025-04-25 09:44:00 +00:00
Tomas Vanek
9fce121366 doc: document changes in rp2xxx flash driver
Namely the driver name changed from rp2040_flash and added RP2350 support.

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: I2ec9e62786002d71f655dbe0edc9f2e9ac4141b7
Reviewed-on: https://review.openocd.org/c/openocd/+/8456
Tested-by: jenkins
2025-04-25 09:42:07 +00:00
Tomas Vanek
63f94bbab8 flash/nor/rp2040: refactoring: change rp2040 to rp2xxx
While on it use calloc() instead of malloc()/memset()
Drop useless implementation of rp2040_flash_free_driver_priv()
- exactly same as default_flash_free_driver_priv()
Code style fixes forced by checkpatch

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: I5c56c4a7d586c0dcab164a45e8f6200ea9a3bd1d
Reviewed-on: https://review.openocd.org/c/openocd/+/8455
Tested-by: jenkins
2025-04-25 09:41:44 +00:00
Tomas Vanek
c914cfceab flash/nor/rp2040: refactor finalizing calls and use them after erase
Invalidate cache and restore flash XIP mode after erase and also
in error cleanup after write/erase.

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: If7e0c2d75f50f923e6bcbf0aa7bab53fe91b6cc8
Reviewed-on: https://review.openocd.org/c/openocd/+/8454
Tested-by: jenkins
2025-04-25 09:41:01 +00:00
Tomas Vanek
26729aa8b0 flash/nor/rp2040: improve flash write buffer size computation
While on it:
Define the names for the fixed flash page/sector sizes and use them
instead of magic values.
Fix memory leak on error return.

Partially backported from former upstream rp2040.c

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: If51c912f4d381ee47756a70f616ecdbee1ac0da7
Reviewed-on: https://review.openocd.org/c/openocd/+/8453
Tested-by: jenkins
2025-04-25 09:40:30 +00:00
Tomas Vanek
20d1d4405d flash/nor/rp2040: add missing TARGET_HALTED checks
Flash erase and write require this guard, unfortunately it is also
partially needed in the flash probe.

Partially backported from former upstream rp2040.c

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: Ie8a240e66c3ed68e08f872cbbfdd90a6d80e1f1e
Reviewed-on: https://review.openocd.org/c/openocd/+/8452
Tested-by: jenkins
2025-04-25 09:40:02 +00:00
Tomas Vanek
2e1a76368e flash/nor/rp2040: detect flash size including SFDP
Also keep size override by FLASHSIZE Tcl variable possible.

Partially backported from former upstream rp2040.c

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: I224c3644450e8b46e35714bfc5436219ffdee563
Reviewed-on: https://review.openocd.org/c/openocd/+/8451
Tested-by: jenkins
2025-04-25 09:36:20 +00:00
Tomas Vanek
22dfd0efad tcl/target/rp2350: workarounds for ROM API issues
A0 chip: remove pad isolation
A2 chip: instead of reset init fixes we will fix the flash driver
with the following patch by Luke Wren:
8729: flash/nor/rp2xxx: fix flash operation after halt in RISC-V bootsel
https://review.openocd.org/c/openocd/+/8729

I don't have A1 version to test.

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: I9e9fab04ead929fe6e0a17c6c2f32a6f02e9beb9
Reviewed-on: https://review.openocd.org/c/openocd/+/8450
Tested-by: jenkins
2025-04-25 09:35:37 +00:00
Tomas Vanek
69ee445786 tcl/target/rp2350: universal config for any combination of CM/RV cores
RP2350 has 2 slots where either Cortex-M33 or RISC-V can be selected.
Tcl variable USE_CORE selects what cores will be configured for debug.

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: I56fe1aa94304bdfd1ec98bba57cc3fa792a35f69
Reviewed-on: https://review.openocd.org/c/openocd/+/8449
Tested-by: jenkins
2025-04-25 09:34:04 +00:00
Tomas Vanek
2e49c99b1f flash/nor/rp2040: flash bank target switching for RP2350
RP2350 can switch either core to Cortex-M33 or RISC-V.
The different architectures have to be supported as
distinct targets in OpenOCD.

Introduce 'rp2xxx _switch target' Tcl command to adapt flash
bank to architecture changes.

Keep the target and priv pointers intact until a flash operation
is finished to prevent sudden change in the middle of write/erase.

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: I764354ab469e253042128958dfe70c09d04d6411
Reviewed-on: https://review.openocd.org/c/openocd/+/8448
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
2025-04-25 09:32:11 +00:00
Tomas Vanek
15d92076b0 flash/nor/rp2040: allow arbitrary ROM API call from Tcl
The new flash command could be handy for a reboot to BOOTSEL mode
and for making (Q)SPI flash content visible at 0x10xxxxxx
address mapping area after a rescue reset.

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: I1b532afcc41a4051298313e685658e86c02c53f9
Reviewed-on: https://review.openocd.org/c/openocd/+/8447
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Tested-by: jenkins
2025-04-25 09:30:21 +00:00
Tomas Vanek
ba03d13c29 flash/nor/rp2040: allow flash size override from cfg
Do not enforce hard-wired size 32 MiB

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Change-Id: I54608f75cc13996fda38ebd5d330e3b1893c2fd9
Reviewed-on: https://review.openocd.org/c/openocd/+/8446
Tested-by: jenkins
Reviewed-by: Jonathan Bell <jonathan@raspberrypi.com>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
2025-04-25 09:27:59 +00:00
Luke Wren
8f92e520bb flash/nor/rp2040: Fix incorrect erase bounds calculation
when erase region does not start at 0

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Signed-off-by: Luke Wren <luke@raspberrypi.com>
Change-Id: I2b9db61e8ac837b6c6431aacf3b73ed3a1772fbc
Reviewed-on: https://review.openocd.org/c/openocd/+/8445
Tested-by: jenkins
Reviewed-by: Jonathan Bell <jonathan@raspberrypi.com>
2025-04-25 09:27:40 +00:00
Luke Wren
ca966d3d7f flash/nor/rp2040: Avoid ROM call timeout on long erases by splitting into chunks
Also add keep_alive() to erase/program to avoid nasty GDB message.

TV: Fixed style problems.

Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Signed-off-by: Luke Wren <luke@raspberrypi.com>
Change-Id: Ibb18775aeed192361ae1585bfdaad03760583cf3
Reviewed-on: https://review.openocd.org/c/openocd/+/8444
Tested-by: jenkins
Reviewed-by: Jonathan Bell <jonathan@raspberrypi.com>
2025-04-25 09:27:20 +00:00
Luke Wren
2e8e1a3da3 flash/nor/rp2040: Fix up ROM table lookup for RP2350 A2
which has 16-bit well-known pointers.

Change-Id: Ia0838a0b062f73a9c5751abb48f1b4d55100bd1d
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Signed-off-by: Luke Wren <luke@raspberrypi.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8443
Reviewed-by: Jonathan Bell <jonathan@raspberrypi.com>
Tested-by: jenkins
2025-04-25 09:26:46 +00:00
graham sanderson
eb4a634248 flash/nor/rp2040: RP2350 A1 changes
TV: cortex_m.c changes removed.

Change-Id: I85830f2d64f8afb86690737f9ae70dde5e6143e1
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Signed-off-by: graham sanderson <graham.sanderson@raspberrypi.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8442
Tested-by: jenkins
2025-04-25 09:26:27 +00:00
Luke Wren
1ed49addc5 flash/nor/rp2040: Add RISC-V ROM algorithm batch call support
And add support for A1 ROM table.

TV: cortex_m smp change removed.
Fixed style problems.
'uint' replaced by unsigned int

Change-Id: Iff2710fa0734dc7074d8d490d8fae43dc27c0c2a
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Signed-off-by: Luke Wren <wren6991@gmail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8441
Tested-by: jenkins
2025-04-25 09:25:57 +00:00
graham sanderson
ea775d49fc flash/nor/rp2040: add RP2350 support
TV: Extracted RP2040/2350 flash driver part only.
Fixed style problems.

Change-Id: I88a7d5aa0a239ae93d72bd5671686b19c6ca11ad
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Signed-off-by: graham sanderson <graham.sanderson@raspberrypi.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8440
Tested-by: jenkins
2025-04-25 09:25:32 +00:00
8 changed files with 1715 additions and 546 deletions

View File

@@ -7872,15 +7872,29 @@ locked, but can still mass erase the whole flash.
@end deffn
@end deffn
@deffn {Flash Driver} {rp2040}
Supports RP2040 "Raspberry Pi Pico" microcontroller.
RP2040 is a dual-core device with two CM0+ cores. Both cores share the same
Flash/RAM/MMIO address space. Non-volatile storage is achieved with an
external QSPI flash; a Boot ROM provides helper functions.
@deffn {Flash Driver} {rp2xxx}
Supports RP2040 "Raspberry Pi Pico" microcontroller and RP2350 Pico 2
RP2040 is a dual-core device with two CM0+ cores.
RP2350 is a dual-core device with two slots switched to either Cortex-M33
or Hazard3 RISC-V core.
Both cores share the same Flash/RAM/MMIO address space.
Non-volatile storage is achieved with an external QSPI flash;
a Boot ROM provides helper functions.
@example
flash bank $_FLASHNAME rp2040_flash $_FLASHBASE $_FLASHSIZE 1 32 $_TARGETNAME
flash bank $_FLASHNAME rp2xxx $_FLASHBASE $_FLASHSIZE 0 0 $_TARGETNAME
@end example
@deffn {Command} {rp2xxx rom_api_call} fc [p0 [p1 [p2 [p3]]]]
A utility for calling ROM API function with two characters lookup code
@var{fc} and up to 4 optional parameters @var{p0 p1 p2 p3}.
The call is supported on the target where the flash bank is configured
(core0).
@end deffn
@deffn {Command} {rp2xxx _switch_target} old_target new_target
A command used internally by rp2350.cfg when the core type is switched.
@end deffn
@end deffn
@deffn {Flash Driver} {rsl10}

View File

@@ -59,7 +59,7 @@ NOR_DRIVERS = \
%D%/psoc6.c \
%D%/qn908x.c \
%D%/renesas_rpchf.c \
%D%/rp2040.c \
%D%/rp2xxx.c \
%D%/rsl10.c \
%D%/sfdp.c \
%D%/sh_qspi.c \

View File

@@ -289,7 +289,7 @@ extern const struct flash_driver psoc5lp_nvl_flash;
extern const struct flash_driver psoc6_flash;
extern const struct flash_driver qn908x_flash;
extern const struct flash_driver renesas_rpchf_flash;
extern const struct flash_driver rp2040_flash;
extern const struct flash_driver rp2xxx_flash;
extern const struct flash_driver rsl10_flash;
extern const struct flash_driver sh_qspi_flash;
extern const struct flash_driver sim3x_flash;

View File

@@ -66,7 +66,7 @@ static const struct flash_driver * const flash_drivers[] = {
&psoc6_flash,
&qn908x_flash,
&renesas_rpchf_flash,
&rp2040_flash,
&rp2xxx_flash,
&sh_qspi_flash,
&sim3x_flash,
&stellaris_flash,

View File

@@ -1,533 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
#include "spi.h"
/* NOTE THAT THIS CODE REQUIRES FLASH ROUTINES in BOOTROM WITH FUNCTION TABLE PTR AT 0x00000010
Your gdbinit should load the bootrom.elf if appropriate */
/* this is 'M' 'u', 1 (version) */
#define BOOTROM_MAGIC 0x01754d
#define BOOTROM_MAGIC_ADDR 0x00000010
/* Call a ROM function via the debug trampoline
Up to four arguments passed in r0...r3 as per ABI
Function address is passed in r7
the trampoline is needed because OpenOCD "algorithm" code insists on sw breakpoints. */
#define MAKE_TAG(a, b) (((b)<<8) | a)
#define FUNC_DEBUG_TRAMPOLINE MAKE_TAG('D', 'T')
#define FUNC_DEBUG_TRAMPOLINE_END MAKE_TAG('D', 'E')
#define FUNC_FLASH_EXIT_XIP MAKE_TAG('E', 'X')
#define FUNC_CONNECT_INTERNAL_FLASH MAKE_TAG('I', 'F')
#define FUNC_FLASH_RANGE_ERASE MAKE_TAG('R', 'E')
#define FUNC_FLASH_RANGE_PROGRAM MAKE_TAG('R', 'P')
#define FUNC_FLASH_FLUSH_CACHE MAKE_TAG('F', 'C')
#define FUNC_FLASH_ENTER_CMD_XIP MAKE_TAG('C', 'X')
struct rp2040_flash_bank {
/* flag indicating successful flash probe */
bool probed;
/* stack used by Boot ROM calls */
struct working_area *stack;
/* function jump table populated by rp2040_flash_probe() */
uint16_t jump_debug_trampoline;
uint16_t jump_debug_trampoline_end;
uint16_t jump_flash_exit_xip;
uint16_t jump_connect_internal_flash;
uint16_t jump_flash_range_erase;
uint16_t jump_flash_range_program;
uint16_t jump_flush_cache;
uint16_t jump_enter_cmd_xip;
/* detected model of SPI flash */
const struct flash_device *dev;
};
/* guessed SPI flash description if autodetection disabled (same as win w25q16jv) */
static const struct flash_device rp2040_default_spi_device =
FLASH_ID("autodetect disabled", 0x03, 0x00, 0x02, 0xd8, 0xc7, 0, 0x100, 0x10000, 0);
static uint32_t rp2040_lookup_symbol(struct target *target, uint32_t tag, uint16_t *symbol)
{
uint32_t magic;
int err = target_read_u32(target, BOOTROM_MAGIC_ADDR, &magic);
if (err != ERROR_OK)
return err;
magic &= 0xffffff; /* ignore bootrom version */
if (magic != BOOTROM_MAGIC) {
if (!((magic ^ BOOTROM_MAGIC)&0xffff))
LOG_ERROR("Incorrect RP2040 BOOT ROM version");
else
LOG_ERROR("RP2040 BOOT ROM not found");
return ERROR_FAIL;
}
/* dereference the table pointer */
uint16_t table_entry;
err = target_read_u16(target, BOOTROM_MAGIC_ADDR + 4, &table_entry);
if (err != ERROR_OK)
return err;
uint16_t entry_tag;
do {
err = target_read_u16(target, table_entry, &entry_tag);
if (err != ERROR_OK)
return err;
if (entry_tag == tag) {
/* 16 bit symbol is next */
return target_read_u16(target, table_entry + 2, symbol);
}
table_entry += 4;
} while (entry_tag);
return ERROR_FAIL;
}
static int rp2040_call_rom_func(struct target *target, struct rp2040_flash_bank *priv,
uint16_t func_offset, uint32_t argdata[], unsigned int n_args, unsigned int timeout_ms)
{
char *regnames[4] = { "r0", "r1", "r2", "r3" };
assert(n_args <= ARRAY_SIZE(regnames)); /* only allow register arguments */
if (!priv->stack) {
LOG_ERROR("no stack for flash programming code");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
target_addr_t stacktop = priv->stack->address + priv->stack->size;
LOG_TARGET_DEBUG(target, "Calling ROM func @0x%" PRIx16 " with %u arguments", func_offset, n_args);
struct reg_param args[ARRAY_SIZE(regnames) + 2];
struct armv7m_algorithm alg_info;
for (unsigned int i = 0; i < n_args; ++i) {
init_reg_param(&args[i], regnames[i], 32, PARAM_OUT);
buf_set_u32(args[i].value, 0, 32, argdata[i]);
}
/* Pass function pointer in r7 */
init_reg_param(&args[n_args], "r7", 32, PARAM_OUT);
buf_set_u32(args[n_args].value, 0, 32, func_offset);
/* Setup stack */
init_reg_param(&args[n_args + 1], "sp", 32, PARAM_OUT);
buf_set_u32(args[n_args + 1].value, 0, 32, stacktop);
unsigned int n_reg_params = n_args + 2; /* User arguments + r7 + sp */
for (unsigned int i = 0; i < n_reg_params; ++i)
LOG_DEBUG("Set %s = 0x%" PRIx32, args[i].reg_name, buf_get_u32(args[i].value, 0, 32));
/* Actually call the function */
alg_info.common_magic = ARMV7M_COMMON_MAGIC;
alg_info.core_mode = ARM_MODE_THREAD;
int err = target_run_algorithm(
target,
0, NULL, /* No memory arguments */
n_reg_params, args, /* User arguments + r7 + sp */
priv->jump_debug_trampoline, priv->jump_debug_trampoline_end,
timeout_ms,
&alg_info
);
for (unsigned int i = 0; i < n_reg_params; ++i)
destroy_reg_param(&args[i]);
if (err != ERROR_OK)
LOG_ERROR("Failed to invoke ROM function @0x%" PRIx16, func_offset);
return err;
}
/* Finalize flash write/erase/read ID
* - flush cache
* - enters memory-mapped (XIP) mode to make flash data visible
* - deallocates target ROM func stack if previously allocated
*/
static int rp2040_finalize_stack_free(struct flash_bank *bank)
{
struct rp2040_flash_bank *priv = bank->driver_priv;
struct target *target = bank->target;
/* Always flush before returning to execute-in-place, to invalidate stale
* cache contents. The flush call also restores regular hardware-controlled
* chip select following a rp2040_flash_exit_xip().
*/
LOG_DEBUG("Flushing flash cache after write behind");
int err = rp2040_call_rom_func(target, priv, priv->jump_flush_cache, NULL, 0, 1000);
if (err != ERROR_OK) {
LOG_ERROR("Failed to flush flash cache");
/* Intentionally continue after error and try to setup xip anyway */
}
LOG_DEBUG("Configuring SSI for execute-in-place");
err = rp2040_call_rom_func(target, priv, priv->jump_enter_cmd_xip, NULL, 0, 1000);
if (err != ERROR_OK)
LOG_ERROR("Failed to set SSI to XIP mode");
target_free_working_area(target, priv->stack);
priv->stack = NULL;
return err;
}
/* Prepare flash write/erase/read ID
* - allocates a stack for target ROM func
* - switches the SPI interface from memory-mapped mode to direct command mode
* Always pair with a call of rp2040_finalize_stack_free()
* after flash operation finishes or fails.
*/
static int rp2040_stack_grab_and_prep(struct flash_bank *bank)
{
struct rp2040_flash_bank *priv = bank->driver_priv;
struct target *target = bank->target;
/* target_alloc_working_area always allocates multiples of 4 bytes, so no worry about alignment */
const int STACK_SIZE = 256;
int err = target_alloc_working_area(target, STACK_SIZE, &priv->stack);
if (err != ERROR_OK) {
LOG_ERROR("Could not allocate stack for flash programming code");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
LOG_DEBUG("Connecting internal flash");
err = rp2040_call_rom_func(target, priv, priv->jump_connect_internal_flash, NULL, 0, 1000);
if (err != ERROR_OK) {
LOG_ERROR("Failed to connect internal flash");
return err;
}
LOG_DEBUG("Kicking flash out of XIP mode");
err = rp2040_call_rom_func(target, priv, priv->jump_flash_exit_xip, NULL, 0, 1000);
if (err != ERROR_OK) {
LOG_ERROR("Failed to exit flash XIP mode");
return err;
}
return ERROR_OK;
}
static int rp2040_flash_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
{
LOG_DEBUG("Writing %d bytes starting at 0x%" PRIx32, count, offset);
struct rp2040_flash_bank *priv = bank->driver_priv;
struct target *target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
struct working_area *bounce = NULL;
int err = rp2040_stack_grab_and_prep(bank);
if (err != ERROR_OK)
goto cleanup;
unsigned int avail_pages = target_get_working_area_avail(target) / priv->dev->pagesize;
/* We try to allocate working area rounded down to device page size,
* al least 1 page, at most the write data size
*/
unsigned int chunk_size = MIN(MAX(avail_pages, 1) * priv->dev->pagesize, count);
err = target_alloc_working_area(target, chunk_size, &bounce);
if (err != ERROR_OK) {
LOG_ERROR("Could not allocate bounce buffer for flash programming. Can't continue");
goto cleanup;
}
LOG_DEBUG("Allocated flash bounce buffer @" TARGET_ADDR_FMT, bounce->address);
while (count > 0) {
uint32_t write_size = count > chunk_size ? chunk_size : count;
LOG_DEBUG("Writing %d bytes to offset 0x%" PRIx32, write_size, offset);
err = target_write_buffer(target, bounce->address, write_size, buffer);
if (err != ERROR_OK) {
LOG_ERROR("Could not load data into target bounce buffer");
break;
}
uint32_t args[3] = {
offset, /* addr */
bounce->address, /* data */
write_size /* count */
};
err = rp2040_call_rom_func(target, priv, priv->jump_flash_range_program,
args, ARRAY_SIZE(args), 3000);
if (err != ERROR_OK) {
LOG_ERROR("Failed to invoke flash programming code on target");
break;
}
buffer += write_size;
offset += write_size;
count -= write_size;
}
cleanup:
target_free_working_area(target, bounce);
rp2040_finalize_stack_free(bank);
return err;
}
static int rp2040_flash_erase(struct flash_bank *bank, unsigned int first, unsigned int last)
{
struct rp2040_flash_bank *priv = bank->driver_priv;
struct target *target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
uint32_t start_addr = bank->sectors[first].offset;
uint32_t length = bank->sectors[last].offset + bank->sectors[last].size - start_addr;
LOG_DEBUG("RP2040 erase %d bytes starting at 0x%" PRIx32, length, start_addr);
int err = rp2040_stack_grab_and_prep(bank);
if (err != ERROR_OK)
goto cleanup;
LOG_DEBUG("Remote call flash_range_erase");
uint32_t args[4] = {
bank->sectors[first].offset, /* addr */
bank->sectors[last].offset + bank->sectors[last].size - bank->sectors[first].offset, /* count */
priv->dev->sectorsize, /* block_size */
priv->dev->erase_cmd /* block_cmd */
};
/*
The RP2040 Boot ROM provides a _flash_range_erase() API call documented in Section 2.8.3.1.3:
https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
and the particular source code for said Boot ROM function can be found here:
https://github.com/raspberrypi/pico-bootrom/blob/master/bootrom/program_flash_generic.c
In theory, the function algorithm provides for erasing both a smaller "sector" (4096 bytes) and
an optional larger "block" (size and command provided in args).
*/
unsigned int timeout_ms = 2000 * (last - first) + 1000;
err = rp2040_call_rom_func(target, priv, priv->jump_flash_range_erase,
args, ARRAY_SIZE(args), timeout_ms);
cleanup:
rp2040_finalize_stack_free(bank);
return err;
}
/* -----------------------------------------------------------------------------
Driver probing etc */
static int rp2040_ssel_active(struct target *target, bool active)
{
const target_addr_t qspi_ctrl_addr = 0x4001800c;
const uint32_t qspi_ctrl_outover_low = 2UL << 8;
const uint32_t qspi_ctrl_outover_high = 3UL << 8;
uint32_t state = (active) ? qspi_ctrl_outover_low : qspi_ctrl_outover_high;
uint32_t val;
int err = target_read_u32(target, qspi_ctrl_addr, &val);
if (err != ERROR_OK)
return err;
val = (val & ~qspi_ctrl_outover_high) | state;
err = target_write_u32(target, qspi_ctrl_addr, val);
if (err != ERROR_OK)
return err;
return ERROR_OK;
}
static int rp2040_spi_read_flash_id(struct target *target, uint32_t *devid)
{
uint32_t device_id = 0;
const target_addr_t ssi_dr0 = 0x18000060;
int err = rp2040_ssel_active(target, true);
/* write RDID request into SPI peripheral's FIFO */
for (int count = 0; (count < 4) && (err == ERROR_OK); count++)
err = target_write_u32(target, ssi_dr0, SPIFLASH_READ_ID);
/* by this time, there is a receive FIFO entry for every write */
for (int count = 0; (count < 4) && (err == ERROR_OK); count++) {
uint32_t status;
err = target_read_u32(target, ssi_dr0, &status);
device_id >>= 8;
device_id |= (status & 0xFF) << 24;
}
if (err == ERROR_OK)
*devid = device_id >> 8;
int err2 = rp2040_ssel_active(target, false);
if (err2 != ERROR_OK)
LOG_ERROR("SSEL inactive failed");
return err;
}
static int rp2040_flash_probe(struct flash_bank *bank)
{
struct rp2040_flash_bank *priv = bank->driver_priv;
struct target *target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
int err = rp2040_lookup_symbol(target, FUNC_DEBUG_TRAMPOLINE, &priv->jump_debug_trampoline);
if (err != ERROR_OK) {
LOG_ERROR("Debug trampoline not found in RP2040 ROM.");
return err;
}
priv->jump_debug_trampoline &= ~1u; /* mask off thumb bit */
err = rp2040_lookup_symbol(target, FUNC_DEBUG_TRAMPOLINE_END, &priv->jump_debug_trampoline_end);
if (err != ERROR_OK) {
LOG_ERROR("Debug trampoline end not found in RP2040 ROM.");
return err;
}
priv->jump_debug_trampoline_end &= ~1u; /* mask off thumb bit */
err = rp2040_lookup_symbol(target, FUNC_FLASH_EXIT_XIP, &priv->jump_flash_exit_xip);
if (err != ERROR_OK) {
LOG_ERROR("Function FUNC_FLASH_EXIT_XIP not found in RP2040 ROM.");
return err;
}
err = rp2040_lookup_symbol(target, FUNC_CONNECT_INTERNAL_FLASH, &priv->jump_connect_internal_flash);
if (err != ERROR_OK) {
LOG_ERROR("Function FUNC_CONNECT_INTERNAL_FLASH not found in RP2040 ROM.");
return err;
}
err = rp2040_lookup_symbol(target, FUNC_FLASH_RANGE_ERASE, &priv->jump_flash_range_erase);
if (err != ERROR_OK) {
LOG_ERROR("Function FUNC_FLASH_RANGE_ERASE not found in RP2040 ROM.");
return err;
}
err = rp2040_lookup_symbol(target, FUNC_FLASH_RANGE_PROGRAM, &priv->jump_flash_range_program);
if (err != ERROR_OK) {
LOG_ERROR("Function FUNC_FLASH_RANGE_PROGRAM not found in RP2040 ROM.");
return err;
}
err = rp2040_lookup_symbol(target, FUNC_FLASH_FLUSH_CACHE, &priv->jump_flush_cache);
if (err != ERROR_OK) {
LOG_ERROR("Function FUNC_FLASH_FLUSH_CACHE not found in RP2040 ROM.");
return err;
}
err = rp2040_lookup_symbol(target, FUNC_FLASH_ENTER_CMD_XIP, &priv->jump_enter_cmd_xip);
if (err != ERROR_OK) {
LOG_ERROR("Function FUNC_FLASH_ENTER_CMD_XIP not found in RP2040 ROM.");
return err;
}
if (bank->size) {
/* size overridden, suppress reading SPI flash ID */
priv->dev = &rp2040_default_spi_device;
LOG_DEBUG("SPI flash autodetection disabled, using configured size");
} else {
/* zero bank size in cfg, read SPI flash ID and autodetect */
err = rp2040_stack_grab_and_prep(bank);
uint32_t device_id = 0;
if (err == ERROR_OK)
err = rp2040_spi_read_flash_id(target, &device_id);
rp2040_finalize_stack_free(bank);
if (err != ERROR_OK)
return err;
/* search for a SPI flash Device ID match */
priv->dev = NULL;
for (const struct flash_device *p = flash_devices; p->name ; p++)
if (p->device_id == device_id) {
priv->dev = p;
break;
}
if (!priv->dev) {
LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", device_id);
return ERROR_FAIL;
}
LOG_INFO("Found flash device '%s' (ID 0x%08" PRIx32 ")",
priv->dev->name, priv->dev->device_id);
bank->size = priv->dev->size_in_bytes;
}
/* the Boot ROM flash_range_program() routine requires page alignment */
bank->write_start_alignment = priv->dev->pagesize;
bank->write_end_alignment = priv->dev->pagesize;
bank->num_sectors = bank->size / priv->dev->sectorsize;
LOG_INFO("RP2040 B0 Flash Probe: %" PRIu32 " bytes @" TARGET_ADDR_FMT ", in %u sectors\n",
bank->size, bank->base, bank->num_sectors);
bank->sectors = alloc_block_array(0, priv->dev->sectorsize, bank->num_sectors);
if (!bank->sectors)
return ERROR_FAIL;
if (err == ERROR_OK)
priv->probed = true;
return err;
}
static int rp2040_flash_auto_probe(struct flash_bank *bank)
{
struct rp2040_flash_bank *priv = bank->driver_priv;
if (priv->probed)
return ERROR_OK;
return rp2040_flash_probe(bank);
}
static void rp2040_flash_free_driver_priv(struct flash_bank *bank)
{
free(bank->driver_priv);
bank->driver_priv = NULL;
}
/* -----------------------------------------------------------------------------
Driver boilerplate */
FLASH_BANK_COMMAND_HANDLER(rp2040_flash_bank_command)
{
struct rp2040_flash_bank *priv;
priv = malloc(sizeof(struct rp2040_flash_bank));
priv->probed = false;
/* Set up driver_priv */
bank->driver_priv = priv;
return ERROR_OK;
}
const struct flash_driver rp2040_flash = {
.name = "rp2040_flash",
.flash_bank_command = rp2040_flash_bank_command,
.erase = rp2040_flash_erase,
.write = rp2040_flash_write,
.read = default_flash_read,
.probe = rp2040_flash_probe,
.auto_probe = rp2040_flash_auto_probe,
.erase_check = default_flash_blank_check,
.free_driver_priv = rp2040_flash_free_driver_priv
};

1442
src/flash/nor/rp2xxx.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,14 @@ if { [info exists WORKAREASIZE] } {
set _WORKAREASIZE 0x10000
}
# Nonzero FLASHSIZE supresses QSPI flash size detection
if { [info exists FLASHSIZE] } {
set _FLASHSIZE $FLASHSIZE
} else {
# Detect QSPI flash size based on flash ID or SFDP
set _FLASHSIZE 0
}
if { [info exists CPUTAPID] } {
set _CPUTAPID $CPUTAPID
} else {
@@ -74,6 +82,10 @@ if { $_USE_CORE != 1 } {
target create $_TARGETNAME_0 cortex_m -dap $_CHIPNAME.dap0 -coreid 0
# srst does not exist; use SYSRESETREQ to perform a soft reset
$_TARGETNAME_0 cortex_m reset_config sysresetreq
# After a rescue reset and fi BOOTSEL is halted connect the flash to enable
# reads from the XIP cached mapping area
$_TARGETNAME_0 configure -event reset-init { rp2xxx rom_api_call 0 CX }
}
# core 1
@@ -95,11 +107,11 @@ if { $_USE_CORE == 1 } {
} else {
set _FLASH_TARGET $_TARGETNAME_0
}
# Backup the work area. The flash probe runs an algorithm on the target CPU.
# The flash is probed during gdb connect if gdb memory_map is enabled (by default).
$_FLASH_TARGET configure -work-area-phys 0x20010000 -work-area-size $_WORKAREASIZE -work-area-backup 1
# QSPI flash size detection during gdb connect requires to back-up RAM
set _WKA_BACKUP [expr { $_FLASHSIZE == 0 }]
$_FLASH_TARGET configure -work-area-phys 0x20010000 -work-area-size $_WORKAREASIZE -work-area-backup $_WKA_BACKUP
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME rp2040_flash 0x10000000 0 0 0 $_FLASH_TARGET
flash bank $_FLASHNAME rp2xxx 0x10000000 $_FLASHSIZE 0 0 $_FLASH_TARGET
if { $_BOTH_CORES } {
# Alias to ensure gdb connecting to core 1 gets the correct memory map

234
tcl/target/rp2350.cfg Normal file
View File

@@ -0,0 +1,234 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# RP2350 is a microcontroller with dual Cortex-M33 cores or dual Hazard3 RISC-V cores.
# https://www.raspberrypi.com/documentation/microcontrollers/rp2350.html
transport select swd
source [find target/swj-dp.tcl]
if { [info exists CHIPNAME] } {
set _CHIPNAME $CHIPNAME
} else {
set _CHIPNAME rp2350
}
if { [info exists WORKAREASIZE] } {
set _WORKAREASIZE $WORKAREASIZE
} else {
set _WORKAREASIZE 0x10000
}
# Nonzero FLASHSIZE supresses QSPI flash size detection
if { [info exists FLASHSIZE] } {
set _FLASHSIZE $FLASHSIZE
} else {
# Detect QSPI flash size based on flash ID or SFDP
set _FLASHSIZE 0
}
if { [info exists CPUTAPID] } {
set _CPUTAPID $CPUTAPID
} else {
set _CPUTAPID 0x00040927
}
# Set to '1' to start rescue mode
if { [info exists RESCUE] } {
set _RESCUE $RESCUE
} else {
set _RESCUE 0
}
# Set to 'cm0' or 'cm1' for Cortex-M33 single core configuration
# To keep compatibility with RP2040 aliases '0' and '1' are provided for Cortex-M33 cores
# Use 'rv0' or 'rv1' for RISC-V single core configuration
# List more for a multicore configuration
if { [info exists USE_CORE] } {
set _USE_CORE $USE_CORE
} else {
# defaults to both Cortex-M33 cores
set _USE_CORE { cm0 cm1 }
}
swj_newdap $_CHIPNAME cpu -expected-id $_CPUTAPID
if { [info exists SWD_MULTIDROP] } {
dap create $_CHIPNAME.dap -adiv6 -chain-position $_CHIPNAME.cpu -dp-id 0x0040927 -instance-id 0
} else {
dap create $_CHIPNAME.dap -adiv6 -chain-position $_CHIPNAME.cpu
}
# Cortex-M33 core 0
if { [lsearch $_USE_CORE cm0] >= 0 || [lsearch $_USE_CORE 0] >= 0 } {
set _TARGETNAME_CM0 $_CHIPNAME.cm0
set _TARGETNAME_0 $_TARGETNAME_CM0
}
# RISC-V core 0
if { [lsearch $_USE_CORE rv0] >= 0 } {
set _TARGETNAME_RV0 $_CHIPNAME.rv0
if { ![info exists _TARGETNAME_0] } {
set _TARGETNAME_0 $_TARGETNAME_RV0
}
}
# Cortex-M33 core 1
if { [lsearch $_USE_CORE cm1] >= 0 || [lsearch $_USE_CORE 1] >= 0 } {
set _TARGETNAME_CM1 $_CHIPNAME.cm1
set _TARGETNAME_1 $_TARGETNAME_CM1
}
# RISC-V core 1
if { [lsearch $_USE_CORE rv1] >= 0 } {
set _TARGETNAME_RV1 $_CHIPNAME.rv1
if { ![info exists _TARGETNAME_1] } {
set _TARGETNAME_1 $_TARGETNAME_RV1
}
}
if { [info exists _TARGETNAME_CM0] } {
target create $_TARGETNAME_CM0 cortex_m -dap $_CHIPNAME.dap -ap-num 0x2000
# srst does not exist; use SYSRESETREQ to perform a soft reset
$_TARGETNAME_CM0 cortex_m reset_config sysresetreq
# After a rescue reset the cache requires invalidate to allow SPI flash
# reads from the XIP cached mapping area
$_TARGETNAME_CM0 configure -event reset-init { rp2xxx rom_api_call FC }
}
if { [info exists _TARGETNAME_RV0] } {
target create $_TARGETNAME_RV0 riscv -dap $_CHIPNAME.dap -ap-num 0xa000 -coreid 0
$_TARGETNAME_RV0 riscv set_enable_virt2phys off
$_TARGETNAME_RV0 configure -event reset-init "_rv_reset_init"
if { [info exists _TARGETNAME_CM0] } {
# just for setting after init when the event become-available is not fired
$_TARGETNAME_RV0 configure -event examine-end "rp2xxx _switch_target $_TARGETNAME_CM0 $_TARGETNAME_RV0"
}
}
if { [info exists _TARGETNAME_CM1] } {
target create $_TARGETNAME_CM1 cortex_m -dap $_CHIPNAME.dap -ap-num 0x4000
$_TARGETNAME_CM1 cortex_m reset_config sysresetreq
}
if { [info exists _TARGETNAME_RV1] } {
target create $_TARGETNAME_RV1 riscv -dap $_CHIPNAME.dap -ap-num 0xa000 -coreid 1
$_TARGETNAME_RV1 riscv set_enable_virt2phys off
}
if { [info exists USE_SMP] } {
set _USE_SMP $USE_SMP
} elseif { [info exists _TARGETNAME_CM0] == [info exists _TARGETNAME_CM1]
&& [info exists _TARGETNAME_RV0] == [info exists _TARGETNAME_RV1] } {
set _USE_SMP 1
} else {
set _USE_SMP 0
}
if { $_USE_SMP } {
if { [info exists _TARGETNAME_CM0] && [info exists _TARGETNAME_CM1] } {
$_TARGETNAME_CM0 configure -rtos hwthread
$_TARGETNAME_CM1 configure -rtos hwthread
target smp $_TARGETNAME_CM0 $_TARGETNAME_CM1
}
if { [info exists _TARGETNAME_RV0] && [info exists _TARGETNAME_RV1] } {
$_TARGETNAME_RV0 configure -rtos hwthread
$_TARGETNAME_RV1 configure -rtos hwthread
target smp $_TARGETNAME_RV0 $_TARGETNAME_RV1
}
}
if { [info exists _TARGETNAME_0] } {
set _FLASH_TARGET $_TARGETNAME_0
}
if { ![info exists _FLASH_TARGET] && [info exists _TARGETNAME_1] } {
set _FLASH_TARGET $_TARGETNAME_1
if { [info exists _TARGETNAME_CM1] && [info exists _TARGETNAME_RV1] } {
echo "Info : $_CHIPNAME.flash will be handled by $_TARGETNAME_1 without switching"
}
}
if { [info exists _FLASH_TARGET] } {
# QSPI flash size detection during gdb connect requires to back-up RAM
set _WKA_BACKUP [expr { $_FLASHSIZE == 0 }]
$_FLASH_TARGET configure -work-area-phys 0x20010000 -work-area-size $_WORKAREASIZE -work-area-backup $_WKA_BACKUP
if { [info exists _TARGETNAME_CM0] && [info exists _TARGETNAME_RV0] } {
$_TARGETNAME_RV0 configure -work-area-phys 0x20010000 \
-work-area-size $_WORKAREASIZE -work-area-backup $_WKA_BACKUP
echo "Info : $_CHIPNAME.flash will be handled by the active one of $_FLASH_TARGET and $_TARGETNAME_RV0 cores"
}
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME rp2xxx 0x10000000 $_FLASHSIZE 0 0 $_FLASH_TARGET
}
if { [info exists _TARGETNAME_1] } {
# Alias to ensure gdb connecting to core 1 gets the correct memory map
flash bank $_CHIPNAME.alias virtual 0x10000000 0 0 0 $_TARGETNAME_1 $_FLASHNAME
}
if { [info exists _TARGETNAME_0] } {
# Select core 0
targets $_TARGETNAME_0
}
# Cold reset resets everything except DP
proc cold_reset { { __CHIPNAME "" } } {
if { $__CHIPNAME == "" } {
global _CHIPNAME
set __CHIPNAME $_CHIPNAME
}
poll off
# set CDBGRSTREQ (and keep set CSYSPWRUPREQ and CDBGPWRUPREQ)
$__CHIPNAME.dap dpreg 4 0x54000000
set dpstat [$__CHIPNAME.dap dpreg 4]
if { [expr { $dpstat & 0xcc000000 }] != 0xcc000000 } {
echo "Warn : dpstat_reset failed, DP STAT $dpstat"
}
$__CHIPNAME.dap dpreg 4 0x50000000
dap init
poll on
}
# Rescue reset resets everything except DP and RP_AP
# Both Cortex-M33 cores stop in bootrom
proc rescue_reset { { __CHIPNAME "" } } {
if { $__CHIPNAME == "" } {
global _CHIPNAME
set __CHIPNAME $_CHIPNAME
}
poll off
# set bit RESCUE_RESTART in RP_AP: CTRL register
$__CHIPNAME.dap apreg 0x80000 0 0x80000000
$__CHIPNAME.dap apreg 0x80000 0 0
dap init
poll on
if { [lsearch [target names] $__CHIPNAME.cm0] < 0 } {
echo "Info : restart OpenOCD with 'set USE_CORE { cm0 cm1 }' to debug after rescue"
}
}
if { $_RESCUE } {
init
rescue_reset
}
proc _rv_reset_init { } {
set chip_id [format 0x%08x [read_memory 0x40000000 32 1]]
# Version related workarounds
switch $chip_id {
0x00004927 { # A0
# remove IO_QSPI isolation
mww 0x40030014 0
mww 0x4003001c 0
mww 0x40030024 0
mww 0x4003002c 0
mww 0x40030034 0
mww 0x4003003c 0
}
}
rp2xxx rom_api_call FC
}