-/* SPDX-License-Identifier: GPL-2.0-or-later */
+// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef HAVE_CONFIG_H
#include "config.h"
}
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)
+ uint16_t func_offset, uint32_t argdata[], unsigned int n_args, int timeout_ms)
{
char *regnames[4] = { "r0", "r1", "r2", "r3" };
}
target_addr_t stacktop = priv->stack->address + priv->stack->size;
- LOG_DEBUG("Calling ROM func @0x%" PRIx16 " with %d arguments", func_offset, n_args);
- LOG_DEBUG("Calling on core \"%s\"", target->cmd_name);
+ 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;
/* 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_args + 2; ++i)
+ 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 */
int err = target_run_algorithm(
target,
0, NULL, /* No memory arguments */
- n_args + 1, args, /* User arguments + r7 */
+ n_reg_params, args, /* User arguments + r7 + sp */
priv->jump_debug_trampoline, priv->jump_debug_trampoline_end,
- 3000, /* 3s timeout */
+ timeout_ms,
&alg_info
);
- for (unsigned int i = 0; i < n_args + 2; ++i)
+
+ 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 "\n", func_offset);
+ 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;
}
-static int stack_grab_and_prep(struct flash_bank *bank)
+/* 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(bank->target, STACK_SIZE, &priv->stack);
+ 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(bank->target, priv, priv->jump_connect_internal_flash, NULL, 0);
+ err = rp2040_call_rom_func(target, priv, priv->jump_connect_internal_flash, NULL, 0, 1000);
if (err != ERROR_OK) {
- LOG_ERROR("RP2040 erase: failed to connect internal flash");
+ LOG_ERROR("Failed to connect internal flash");
return err;
}
LOG_DEBUG("Kicking flash out of XIP mode");
- err = rp2040_call_rom_func(bank->target, priv, priv->jump_flash_exit_xip, NULL, 0);
+ err = rp2040_call_rom_func(target, priv, priv->jump_flash_exit_xip, NULL, 0, 1000);
if (err != ERROR_OK) {
- LOG_ERROR("RP2040 erase: failed to exit flash XIP mode");
+ LOG_ERROR("Failed to exit flash XIP mode");
return err;
}
struct rp2040_flash_bank *priv = bank->driver_priv;
struct target *target = bank->target;
- struct working_area *bounce;
- int err = stack_grab_and_prep(bank);
- if (err != ERROR_OK)
- return err;
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ struct working_area *bounce = NULL;
- const unsigned int chunk_size = target_get_working_area_avail(target);
- if (target_alloc_working_area(target, chunk_size, &bounce) != ERROR_OK) {
+ 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");
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ goto cleanup;
}
LOG_DEBUG("Allocated flash bounce buffer @" TARGET_ADDR_FMT, bounce->address);
bounce->address, /* data */
write_size /* count */
};
- err = rp2040_call_rom_func(target, priv, priv->jump_flash_range_program, args, ARRAY_SIZE(args));
+ 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;
offset += write_size;
count -= write_size;
}
+
+cleanup:
target_free_working_area(target, bounce);
- if (err != ERROR_OK)
- return err;
+ rp2040_finalize_stack_free(bank);
- /* Flash is successfully programmed. We can now do a bit of poking to make the flash
- contents visible to us via memory-mapped (XIP) interface in the 0x1... memory region */
- LOG_DEBUG("Flushing flash cache after write behind");
- err = rp2040_call_rom_func(bank->target, priv, priv->jump_flush_cache, NULL, 0);
- if (err != ERROR_OK) {
- LOG_ERROR("RP2040 write: failed to flush flash cache");
- return err;
- }
- LOG_DEBUG("Configuring SSI for execute-in-place");
- err = rp2040_call_rom_func(bank->target, priv, priv->jump_enter_cmd_xip, NULL, 0);
- if (err != ERROR_OK)
- LOG_ERROR("RP2040 write: failed to flush flash cache");
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 = stack_grab_and_prep(bank);
+ int err = rp2040_stack_grab_and_prep(bank);
if (err != ERROR_OK)
- return err;
+ goto cleanup;
LOG_DEBUG("Remote call flash_range_erase");
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). OpenOCD's spi.c only uses "block" sizes.
+ an optional larger "block" (size and command provided in args).
*/
- err = rp2040_call_rom_func(bank->target, priv, priv->jump_flash_range_erase, args, ARRAY_SIZE(args));
+ 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;
}
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;
}
- err = stack_grab_and_prep(bank);
- if (err != ERROR_OK)
- return err;
+ err = rp2040_stack_grab_and_prep(bank);
uint32_t device_id = 0;
- const target_addr_t ssi_dr0 = 0x18000060;
-
- 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);
+ if (err == ERROR_OK)
+ err = rp2040_spi_read_flash_id(target, &device_id);
- device_id >>= 8;
- device_id |= (status & 0xFF) << 24;
- }
- device_id >>= 8;
+ rp2040_finalize_stack_free(bank);
- err = rp2040_ssel_active(target, false);
- if (err != ERROR_OK) {
- LOG_ERROR("SSEL inactive failed");
+ if (err != ERROR_OK)
return err;
- }
/* search for a SPI flash Device ID match */
priv->dev = NULL;