/* * This file is part of the Black Magic Debug project. * * Copyright (C) 2021 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de) * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* This file implements Raspberry Pico (RP2040) target specific functions * for detecting the device, providing the XML memory map and * Flash memory programming. */ #include "general.h" #include "target.h" #include "target_internal.h" #include "cortexm.h" #include "sfdp.h" #define RP_ID "Raspberry RP2040" #define RP_MAX_TABLE_SIZE 0x80U #define BOOTROM_MAGIC_ADDR 0x00000010U #define BOOTROM_MAGIC ('M' | ('u' << 8) | (0x01 << 16)) #define BOOTROM_MAGIC_MASK 0x00ffffffU #define BOOTROM_VERSION_SHIFT 24U #define RP_XIP_FLASH_BASE 0x10000000U #define RP_SRAM_BASE 0x20000000U #define RP_SRAM_SIZE 0x42000U #define RP_GPIO_QSPI_BASE_ADDR 0x40018000U #define RP_GPIO_QSPI_CS_CTRL (RP_GPIO_QSPI_BASE_ADDR + 0x0cU) #define RP_GPIO_QSPI_CS_DRIVE_LOW (2U << 8U) #define RP_GPIO_QSPI_CS_DRIVE_HIGH (3U << 8U) #define RP_GPIO_QSPI_CS_DRIVE_MASK 0x00000300U #define RP_SSI_BASE_ADDR 0x18000000U #define RP_SSI_CTRL0 (RP_SSI_BASE_ADDR + 0x00U) #define RP_SSI_CTRL1 (RP_SSI_BASE_ADDR + 0x04U) #define RP_SSI_ENABLE (RP_SSI_BASE_ADDR + 0x08U) #define RP_SSI_DR0 (RP_SSI_BASE_ADDR + 0x60U) #define RP_SSI_XIP_SPI_CTRL0 (RP_SSI_BASE_ADDR + 0xf4U) #define RP_SSI_CTRL0_FRF_MASK 0x00600000U #define RP_SSI_CTRL0_FRF_SERIAL (0U << 21U) #define RP_SSI_CTRL0_FRF_DUAL (1U << 21U) #define RP_SSI_CTRL0_FRF_QUAD (2U << 21U) #define RP_SSI_CTRL0_TMOD_MASK 0x00000300U #define RP_SSI_CTRL0_TMOD_BIDI (0U << 8U) #define RP_SSI_CTRL0_TMOD_TX_ONLY (1U << 8U) #define RP_SSI_CTRL0_TMOD_RX_ONLY (2U << 8U) #define RP_SSI_CTRL0_TMOD_EEPROM (3U << 8U) #define RP_SSI_CTRL0_DATA_BIT_MASK 0x001f0000U #define RP_SSI_CTRL0_DATA_BIT_SHIFT 16U #define RP_SSI_CTRL0_DATA_BITS(x) (((x) - 1U) << RP_SSI_CTRL0_DATA_BIT_SHIFT) #define RP_SSI_CTRL0_MASK (RP_SSI_CTRL0_FRF_MASK | RP_SSI_CTRL0_TMOD_MASK | RP_SSI_CTRL0_DATA_BIT_MASK) #define RP_SSI_ENABLE_SSI (1U << 0U) #define RP_SSI_XIP_SPI_CTRL0_FORMAT_STD_SPI (0U << 0U) #define RP_SSI_XIP_SPI_CTRL0_FORMAT_SPLIT (1U << 0U) #define RP_SSI_XIP_SPI_CTRL0_FORMAT_FRF (2U << 0U) #define RP_SSI_XIP_SPI_CTRL0_ADDRESS_LENGTH(x) (((x) * 2U) << 2U) #define RP_SSI_XIP_SPI_CTRL0_INSTR_LENGTH_8b (2U << 8U) #define RP_SSI_XIP_SPI_CTRL0_WAIT_CYCLES(x) (((x) * 8U) << 11U) #define BOOTROM_FUNC_TABLE_ADDR 0x00000014U #define BOOTROM_FUNC_TABLE_TAG(x, y) ((uint8_t)(x) | ((uint8_t)(y) << 8U)) #define FLASHSIZE_4K_SECTOR (4U * 1024U) #define FLASHSIZE_32K_BLOCK (32U * 1024U) #define FLASHSIZE_64K_BLOCK (64U * 1024U) #define FLASHSIZE_32K_BLOCK_MASK ~(FLASHSIZE_32K_BLOCK - 1U) #define FLASHSIZE_64K_BLOCK_MASK ~(FLASHSIZE_64K_BLOCK - 1U) #define MAX_FLASH (16U * 1024U * 1024U) #define RP_SPI_OPCODE(x) (x) #define RP_SPI_OPCODE_MASK 0x00ffU #define RP_SPI_INTER_SHIFT 8U #define RP_SPI_INTER_LENGTH(x) (((x) & 7U) << RP_SPI_INTER_SHIFT) #define RP_SPI_INTER_MASK 0x0700U #define RP_SPI_FRAME_OPCODE_ONLY (1 << 11U) #define RP_SPI_FRAME_OPCODE_3B_ADDR (2 << 11U) #define RP_SPI_FRAME_MASK 0x1800U /* Instruction codes taken from Winbond W25Q16JV datasheet, as used on the * original Pico board from Raspberry Pi. * https://www.winbond.com/resource-files/w25q16jv%20spi%20revd%2008122016.pdf * All dev boards supported by Pico SDK V1.3.1 use SPI flash chips which support * these commands. Other custom boards using different SPI flash chips might * not support these commands */ #define FLASHCMD_SECTOR_ERASE 0x20 #define FLASHCMD_BLOCK32K_ERASE 0x52 #define FLASHCMD_BLOCK64K_ERASE 0xd8 #define FLASHCMD_CHIP_ERASE 0x60 #define SPI_FLASH_CMD_READ_JEDEC_ID (RP_SPI_OPCODE(0x9fU) | RP_SPI_INTER_LENGTH(0) | RP_SPI_FRAME_OPCODE_ONLY) #define SPI_FLASH_CMD_READ_SFDP (RP_SPI_OPCODE(0x5aU) | RP_SPI_INTER_LENGTH(1) | RP_SPI_FRAME_OPCODE_3B_ADDR) typedef struct rp_priv { uint16_t rom_debug_trampoline_begin; uint16_t rom_debug_trampoline_end; uint16_t rom_connect_internal_flash; uint16_t rom_flash_enter_xip; uint16_t rom_flash_exit_xip; uint16_t rom_flash_range_erase; uint16_t rom_flash_range_program; uint16_t rom_flash_flush_cache; uint16_t rom_reset_usb_boot; bool is_prepared; bool is_monitor; uint32_t regs[0x20]; /* Register playground*/ } rp_priv_s; static bool rp_cmd_erase_sector(target *t, int argc, const char **argv); static bool rp_cmd_reset_usb_boot(target *t, int argc, const char **argv); const struct command_s rp_cmd_list[] = { {"erase_sector", rp_cmd_erase_sector, "Erase a sector: [start address] length"}, {"reset_usb_boot", rp_cmd_reset_usb_boot, "Reboot the device into BOOTSEL mode"}, {NULL, NULL, NULL} }; static int rp_flash_erase(struct target_flash *f, target_addr addr, size_t len); static int rp_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len); static bool rp_read_rom_func_table(target *t); static bool rp_attach(target *t); static void rp_flash_prepare(target *t); static void rp_flash_resume(target *t); static void rp_spi_read(target *t, uint16_t command, target_addr address, void *buffer, size_t length); static uint32_t rp_get_flash_length(target *t); static bool rp_mass_erase(target *t); static void rp_spi_read_sfdp(target *const t, const uint32_t address, void *const buffer, const size_t length) { rp_spi_read(t, SPI_FLASH_CMD_READ_SFDP, address, buffer, length); #if ENABLE_DEBUG DEBUG_INFO("%" PRIu32 " byte SFDP read at 0x%" PRIx32 ":\n", (uint32_t)length, address); const uint8_t *const data = buffer; for (size_t i = 0; i < length; i += 8U) { DEBUG_INFO("\t%02x %02x %02x %02x %02x %02x %02x %02x\n", data[i + 0], data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5], data[i + 6], data[i + 7]); } #endif } static void rp_add_flash(target *t, uint32_t addr, size_t length) { struct target_flash *f = calloc(1, sizeof(*f)); if (!f) { /* calloc failed: heap exhaustion */ DEBUG_WARN("calloc: failed in %s\n", __func__); return; } f->start = addr; f->length = length; f->blocksize = 0x1000; f->erase = rp_flash_erase; f->write = rp_flash_write; f->buf_size = 2048; /* Max buffer size used otherwise */ f->erased = 0xFF; target_add_flash(t, f); } bool rp_probe(target *t) { /* Check bootrom magic*/ uint32_t boot_magic = target_mem_read32(t, BOOTROM_MAGIC_ADDR); if ((boot_magic & BOOTROM_MAGIC_MASK) != BOOTROM_MAGIC) { DEBUG_WARN("Wrong Bootmagic %08" PRIx32 " found!\n", boot_magic); return false; } #if defined(ENABLE_DEBUG) if ((boot_magic >> BOOTROM_VERSION_SHIFT) == 1) DEBUG_WARN("Old Bootrom Version 1!\n"); #endif rp_priv_s *priv_storage = calloc(1, sizeof(rp_priv_s)); if (!priv_storage) { /* calloc failed: heap exhaustion */ DEBUG_WARN("calloc: failed in %s\n", __func__); return false; } t->target_storage = (void *)priv_storage; t->mass_erase = rp_mass_erase; t->driver = RP_ID; t->target_options |= CORTEXM_TOPT_INHIBIT_NRST; t->attach = rp_attach; target_add_commands(t, rp_cmd_list, RP_ID); return true; } static bool rp_attach(target *t) { if (!cortexm_attach(t) || !rp_read_rom_func_table(t)) return false; /* Free previously loaded memory map */ target_mem_map_free(t); spi_parameters_s spi_parameters; rp_flash_prepare(t); if (!sfdp_read_parameters(t, &spi_parameters, rp_spi_read_sfdp)) { rp_flash_resume(t); /* SFDP readout failed, so make some assumptions and hope for the best. */ spi_parameters.capacity = rp_get_flash_length(t); } else rp_flash_resume(t); DEBUG_INFO("Flash size: %zu MB\n", spi_parameters.capacity / (1024U * 1024U)); rp_add_flash(t, RP_XIP_FLASH_BASE, spi_parameters.capacity); target_add_ram(t, RP_SRAM_BASE, RP_SRAM_SIZE); return true; } /* * Parse out the ROM function table for routines we need. * Entries in the table are in pairs of 16-bit integers: * * A two character tag for the routine (see section 2.8.3 of the datasheet) * * The 16-bit pointer associated with that routine */ static bool rp_read_rom_func_table(target *const t) { rp_priv_s *const priv = (rp_priv_s *)t->target_storage; /* We have to do a 32-bit read here but the pointer contained is only 16-bit. */ const uint16_t table_offset = target_mem_read32(t, BOOTROM_FUNC_TABLE_ADDR) & 0x0000ffffU; uint16_t table[RP_MAX_TABLE_SIZE]; if (target_mem_read(t, table, table_offset, RP_MAX_TABLE_SIZE)) return false; size_t check = 0; for (size_t i = 0; i < RP_MAX_TABLE_SIZE; i += 2) { const uint16_t tag = table[i]; const uint16_t addr = table[i + 1]; switch (tag) { case BOOTROM_FUNC_TABLE_TAG('D', 'T'): priv->rom_debug_trampoline_begin = addr; break; case BOOTROM_FUNC_TABLE_TAG('D', 'E'): priv->rom_debug_trampoline_end = addr; break; case BOOTROM_FUNC_TABLE_TAG('I', 'F'): priv->rom_connect_internal_flash = addr; break; case BOOTROM_FUNC_TABLE_TAG('C', 'X'): priv->rom_flash_enter_xip = addr; break; case BOOTROM_FUNC_TABLE_TAG('E', 'X'): priv->rom_flash_exit_xip = addr; break; case BOOTROM_FUNC_TABLE_TAG('R', 'E'): priv->rom_flash_range_erase = addr; break; case BOOTROM_FUNC_TABLE_TAG('R', 'P'): priv->rom_flash_range_program = addr; break; case BOOTROM_FUNC_TABLE_TAG('F', 'C'): priv->rom_flash_flush_cache = addr; break; case BOOTROM_FUNC_TABLE_TAG('U', 'B'): priv->rom_reset_usb_boot = addr; break; default: continue; } ++check; } DEBUG_TARGET("RP ROM routines connect %04x debug_trampoline %04x end %04x\n", priv->rom_connect_internal_flash, priv->rom_debug_trampoline_begin, priv->rom_debug_trampoline_end); return check == 9; } /* RP ROM functions calls * * timout == 0: Do not wait for poll, use for rom_reset_usb_boot() * timeout > 500 (ms) : display spinner */ static bool rp_rom_call(target *t, uint32_t *regs, uint32_t cmd, uint32_t timeout) { const char spinner[] = "|/-\\"; int spinindex = 0; rp_priv_s *ps = (rp_priv_s *)t->target_storage; regs[7] = cmd; regs[REG_LR] = ps->rom_debug_trampoline_end; regs[REG_PC] = ps->rom_debug_trampoline_begin; regs[REG_MSP] = 0x20042000; regs[REG_XPSR] = CORTEXM_XPSR_THUMB; uint32_t dbg_regs[t->regs_size / sizeof(uint32_t)]; target_regs_write(t, regs); /* start the target and wait for it to halt again */ target_halt_resume(t, false); if (!timeout) return false; DEBUG_INFO("Call cmd %04" PRIx32 "\n", cmd); platform_timeout operation_timeout; platform_timeout_set(&operation_timeout, timeout); platform_timeout spinner_timeout; if (timeout > 500) platform_timeout_set(&spinner_timeout, 500); else /* never trigger if timeout is short */ platform_timeout_set(&spinner_timeout, timeout + 1); do { if (platform_timeout_is_expired(&spinner_timeout)) { if (ps->is_monitor) tc_printf(t, "\b%c", spinner[spinindex++ % 4]); platform_timeout_set(&spinner_timeout, 500); } if (platform_timeout_is_expired(&operation_timeout)) { DEBUG_WARN("RP Run timout %d ms reached: ", (int)timeout); break; } } while (!target_halt_poll(t, NULL)); /* Debug */ target_regs_read(t, dbg_regs); bool ret = ((dbg_regs[REG_PC] & ~1) != (ps->rom_debug_trampoline_end & ~1)); if (ret) { DEBUG_WARN("rp_rom_call cmd %04" PRIx32 " failed, PC %08" PRIx32 "\n", cmd, dbg_regs[REG_PC]); } return ret; } static void rp_flash_prepare(target *t) { rp_priv_s *ps = (rp_priv_s *)t->target_storage; if (!ps->is_prepared) { DEBUG_INFO("rp_flash_prepare\n"); /* connect*/ rp_rom_call(t, ps->regs, ps->rom_connect_internal_flash, 100); /* exit_xip */ rp_rom_call(t, ps->regs, ps->rom_flash_exit_xip, 100); ps->is_prepared = true; } } static void rp_flash_resume(target *t) { rp_priv_s *ps = (rp_priv_s *)t->target_storage; if (ps->is_prepared) { DEBUG_INFO("rp_flash_resume\n"); /* flush */ rp_rom_call(t, ps->regs, ps->rom_flash_flush_cache, 100); /* enter_cmd_xip */ rp_rom_call(t, ps->regs, ps->rom_flash_enter_xip, 100); ps->is_prepared = false; } } /* FLASHCMD_SECTOR_ERASE 45/ 400 ms * 32k block erase 120/ 1600 ms * 64k block erase 150/ 2000 ms * chip erase 5000/25000 ms * page programm 0.4/ 3 ms */ static int rp_flash_erase(struct target_flash *f, target_addr addr, size_t len) { DEBUG_INFO("Erase addr 0x%08" PRIx32 " len 0x%" PRIx32 "\n", addr, (uint32_t)len); target *t = f->t; if (addr & (FLASHSIZE_4K_SECTOR - 1)) { DEBUG_WARN("Unaligned erase\n"); return -1; } if ((addr < t->flash->start) || (addr >= t->flash->start + t->flash->length)) { DEBUG_WARN("Address is invalid\n"); return -1; } addr -= t->flash->start; len = ALIGN(len, FLASHSIZE_4K_SECTOR); len = MIN(len, t->flash->length - addr); rp_priv_s *ps = (rp_priv_s *)t->target_storage; const bool full_erase = addr == f->start && len == f->length; platform_timeout timeout; platform_timeout_set(&timeout, 500); /* erase */ rp_flash_prepare(t); bool ret = 0; while (len) { if (len >= FLASHSIZE_64K_BLOCK) { const uint32_t chunk = len & FLASHSIZE_64K_BLOCK_MASK; ps->regs[0] = addr; ps->regs[1] = chunk; ps->regs[2] = FLASHSIZE_64K_BLOCK; ps->regs[3] = FLASHCMD_BLOCK64K_ERASE; DEBUG_WARN("64k_ERASE addr 0x%08" PRIx32 " len 0x%" PRIx32 "\n", addr, chunk); ret = rp_rom_call(t, ps->regs, ps->rom_flash_range_erase, 25100); len -= chunk; addr += chunk; } else if (len >= FLASHSIZE_32K_BLOCK) { const uint32_t chunk = len & FLASHSIZE_32K_BLOCK_MASK; ps->regs[0] = addr; ps->regs[1] = chunk; ps->regs[2] = FLASHSIZE_32K_BLOCK; ps->regs[3] = FLASHCMD_BLOCK32K_ERASE; DEBUG_WARN("32k_ERASE addr 0x%08" PRIx32 " len 0x%" PRIx32 "\n", addr, chunk); ret = rp_rom_call(t, ps->regs, ps->rom_flash_range_erase, 1700); len -= chunk; addr += chunk; } else { ps->regs[0] = addr; ps->regs[1] = len; ps->regs[2] = FLASHSIZE_4K_SECTOR; ps->regs[3] = FLASHCMD_SECTOR_ERASE; DEBUG_WARN("Sector_ERASE addr 0x%08" PRIx32 " len 0x%" PRIx32 "\n", addr, (uint32_t)len); ret = rp_rom_call(t, ps->regs, ps->rom_flash_range_erase, 410); len = 0; } if (ret) { DEBUG_WARN("Erase failed!\n"); break; } if (full_erase) target_print_progress(&timeout); } rp_flash_resume(t); DEBUG_INFO("Erase done!\n"); return ret; } static int rp_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len) { DEBUG_INFO("RP Write 0x%08" PRIx32 " len 0x%" PRIx32 "\n", dest, (uint32_t)len); target *t = f->t; if ((dest & 0xff) || (len & 0xff)) { DEBUG_WARN("Unaligned write\n"); return -1; } dest -= t->flash->start; rp_priv_s *ps = (rp_priv_s *)t->target_storage; /* Write payload to target ram */ rp_flash_prepare(t); bool ret = 0; #define MAX_WRITE_CHUNK 0x1000 while (len) { uint32_t chunksize = (len <= MAX_WRITE_CHUNK) ? len : MAX_WRITE_CHUNK; target_mem_write(t, RP_SRAM_BASE, src, chunksize); /* Programm range */ ps->regs[0] = dest; ps->regs[1] = RP_SRAM_BASE; ps->regs[2] = chunksize; /* Loading takes 3 ms per 256 byte page * however it takes much longer if the XOSC is not enabled * so lets give ourselves a little bit more time (x10) */ ret |= rp_rom_call(t, ps->regs, ps->rom_flash_range_program, (3 * chunksize * 10) >> 8); if (ret) { DEBUG_WARN("Write failed!\n"); break; } len -= chunksize; src += chunksize; dest += chunksize; } rp_flash_resume(t); DEBUG_INFO("Write done!\n"); return ret; } static bool rp_mass_erase(target *t) { rp_priv_s *ps = (rp_priv_s *)t->target_storage; ps->is_monitor = true; const bool result = rp_flash_erase(t->flash, t->flash->start, t->flash->length) == 0; ps->is_monitor = false; return result; } static void rp_spi_chip_select(target *const t, const bool active) { const uint32_t state = active ? RP_GPIO_QSPI_CS_DRIVE_LOW : RP_GPIO_QSPI_CS_DRIVE_HIGH; const uint32_t value = target_mem_read32(t, RP_GPIO_QSPI_CS_CTRL); target_mem_write32(t, RP_GPIO_QSPI_CS_CTRL, (value & ~RP_GPIO_QSPI_CS_DRIVE_MASK) | state); } static void rp_spi_read( target *const t, const uint16_t command, const target_addr address, void *const buffer, const size_t length) { /* Ensure the controller is in the correct serial SPI mode and select the Flash */ const uint32_t ssi_enabled = target_mem_read32(t, RP_SSI_ENABLE); target_mem_write32(t, RP_SSI_ENABLE, 0); const uint32_t ctrl0 = target_mem_read32(t, RP_SSI_CTRL0); const uint32_t ctrl1 = target_mem_read32(t, RP_SSI_CTRL1); const uint32_t xpi_ctrl0 = target_mem_read32(t, RP_SSI_XIP_SPI_CTRL0); target_mem_write32(t, RP_SSI_CTRL0, (ctrl0 & RP_SSI_CTRL0_MASK) | RP_SSI_CTRL0_FRF_SERIAL | RP_SSI_CTRL0_TMOD_BIDI | RP_SSI_CTRL0_DATA_BITS(8)); target_mem_write32(t, RP_SSI_XIP_SPI_CTRL0, RP_SSI_XIP_SPI_CTRL0_FORMAT_FRF | RP_SSI_XIP_SPI_CTRL0_ADDRESS_LENGTH(0) | RP_SSI_XIP_SPI_CTRL0_INSTR_LENGTH_8b | RP_SSI_XIP_SPI_CTRL0_WAIT_CYCLES(0)); target_mem_write32(t, RP_SSI_CTRL1, length); target_mem_write32(t, RP_SSI_ENABLE, RP_SSI_ENABLE_SSI); rp_spi_chip_select(t, true); /* Set up the instruction */ const uint8_t opcode = command & RP_SPI_OPCODE_MASK; target_mem_write32(t, RP_SSI_DR0, opcode); target_mem_read32(t, RP_SSI_DR0); const uint16_t addr_mode = command & RP_SPI_FRAME_MASK; if (addr_mode == RP_SPI_FRAME_OPCODE_3B_ADDR) { /* For each byte sent here, we have to manually clean up from the controller with a read */ target_mem_write32(t, RP_SSI_DR0, (address >> 16U) & 0xffU); target_mem_read32(t, RP_SSI_DR0); target_mem_write32(t, RP_SSI_DR0, (address >> 8U) & 0xffU); target_mem_read32(t, RP_SSI_DR0); target_mem_write32(t, RP_SSI_DR0, address & 0xffU); target_mem_read32(t, RP_SSI_DR0); } const size_t inter_length = (command & RP_SPI_INTER_MASK) >> RP_SPI_INTER_SHIFT; for (size_t i = 0; i < inter_length; ++i) { /* For each byte sent here, we have to manually clean up from the controller with a read */ target_mem_write32(t, RP_SSI_DR0, 0); target_mem_read32(t, RP_SSI_DR0); } /* Now read back the data that elicited */ uint8_t *const data = (uint8_t *const)buffer; for (size_t i = 0; i < length; ++i) { /* Do a write to read */ target_mem_write32(t, RP_SSI_DR0, 0); data[i] = target_mem_read32(t, RP_SSI_DR0) & 0xffU; } /* Deselect the Flash and put things back to how they were */ rp_spi_chip_select(t, false); target_mem_write32(t, RP_SSI_ENABLE, 0); target_mem_write32(t, RP_SSI_CTRL1, ctrl1); target_mem_write32(t, RP_SSI_CTRL0, ctrl0); target_mem_write32(t, RP_SSI_XIP_SPI_CTRL0, xpi_ctrl0); target_mem_write32(t, RP_SSI_ENABLE, ssi_enabled); } static uint32_t rp_get_flash_length(target *t) { uint32_t size = MAX_FLASH; uint32_t bootsec[16]; size_t i; target_mem_read(t, bootsec, RP_XIP_FLASH_BASE, sizeof(bootsec)); for (i = 0; i < 16; i++) { if ((bootsec[i] != 0x00) && (bootsec[i] != 0xff)) break; } if (i < 16) { // We have some data (hopefully a valid program) stored in the start // of the flash memory. We can check if the start of this data is // mirrored anywhere else in the flash as the flash region will repeat // when we try to read out of bounds. uint32_t mirrorsec[16]; while (size > FLASHSIZE_4K_SECTOR) { target_mem_read(t, mirrorsec, RP_XIP_FLASH_BASE + size, sizeof(bootsec)); if (memcmp(bootsec, mirrorsec, sizeof(bootsec)) != 0) return size << 1U; size >>= 1U; } } // That approach didn't work. Most likely because there was no data found in // at the start of the flash memory. If we have no valid program it's ok to // interrupt the flash execution to check the JEDEC ID of the flash chip. size = MAX_FLASH; rp_flash_prepare(t); spi_flash_id_s flash_id; rp_spi_read(t, SPI_FLASH_CMD_READ_JEDEC_ID, 0, &flash_id, sizeof(flash_id)); rp_flash_resume(t); DEBUG_INFO("Flash device ID: %02x %02x %02x\n", flash_id.manufacturer, flash_id.type, flash_id.capacity); if (flash_id.capacity >= 8 && flash_id.capacity <= 34) size = 1 << flash_id.capacity; return size; } static bool rp_cmd_erase_sector(target *t, int argc, const char **argv) { uint32_t start = t->flash->start; uint32_t length; if (argc == 3) { start = strtoul(argv[1], NULL, 0); length = strtoul(argv[2], NULL, 0); } else if (argc == 2) length = strtoul(argv[1], NULL, 0); else return -1; rp_priv_s *ps = (rp_priv_s *)t->target_storage; ps->is_monitor = true; const bool result = rp_flash_erase(t->flash, start, length) == 0; ps->is_monitor = false; return result; } static bool rp_cmd_reset_usb_boot(target *t, int argc, const char **argv) { rp_priv_s *ps = (rp_priv_s *)t->target_storage; if (argc > 2) { ps->regs[1] = strtoul(argv[2], NULL, 0); } else if (argc < 3) { ps->regs[0] = strtoul(argv[1], NULL, 0); } else { ps->regs[0] = 0; ps->regs[1] = 0; } rp_rom_call(t, ps->regs, ps->rom_reset_usb_boot, 0); return true; } static bool rp_rescue_do_reset(target *t) { ADIv5_AP_t *ap = (ADIv5_AP_t *)t->priv; ap->dp->low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_DP_CTRLSTAT, ADIV5_DP_CTRLSTAT_CDBGPWRUPREQ); ap->dp->low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_DP_CTRLSTAT, 0); return false; } /* The RP Pico rescue DP provides no AP, so we need special handling * * Attach to this DP will do the reset, but will fail to attach! */ bool rp_rescue_probe(ADIv5_AP_t *ap) { target *t = target_new(); if (!t) { return false; } adiv5_ap_ref(ap); t->attach = (void *)rp_rescue_do_reset; t->priv = ap; t->priv_free = (void *)adiv5_ap_unref; t->driver = "Raspberry RP2040 Rescue (Attach to reset!)"; return true; }