mirror of
https://github.com/openocd-org/openocd.git
synced 2025-10-14 02:58:23 +08:00
Compare commits
24 Commits
2aa0592e0f
...
4d4c45cfd2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4d4c45cfd2 | ||
![]() |
bf66d95be2 | ||
![]() |
5bb7dbc231 | ||
![]() |
376d11c2e3 | ||
![]() |
f039fe7f9d | ||
![]() |
a960887169 | ||
![]() |
d29c1c6d6d | ||
![]() |
9fce121366 | ||
![]() |
63f94bbab8 | ||
![]() |
c914cfceab | ||
![]() |
26729aa8b0 | ||
![]() |
20d1d4405d | ||
![]() |
2e1a76368e | ||
![]() |
22dfd0efad | ||
![]() |
69ee445786 | ||
![]() |
2e49c99b1f | ||
![]() |
15d92076b0 | ||
![]() |
ba03d13c29 | ||
![]() |
8f92e520bb | ||
![]() |
ca966d3d7f | ||
![]() |
2e8e1a3da3 | ||
![]() |
eb4a634248 | ||
![]() |
1ed49addc5 | ||
![]() |
ea775d49fc |
@@ -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}
|
||||
|
@@ -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 \
|
||||
|
@@ -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;
|
||||
|
@@ -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,
|
||||
|
@@ -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
1442
src/flash/nor/rp2xxx.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
234
tcl/target/rp2350.cfg
Normal 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
|
||||
}
|
Reference in New Issue
Block a user