This is an automated email from Gerrit. "Tomas Vanek <van...@fbl.cz>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/8451
-- gerrit commit c355c8b429401fdd992d5b5a43a00bd102c7b3aa Author: Tomas Vanek <van...@fbl.cz> Date: Wed Aug 14 09:10:31 2024 +0200 flash/nor/rp2040: detect flash size including SFDP Also keep size override by FLASHSIZE Tcl variable possible. Signed-off-by: Tomas Vanek <van...@fbl.cz> Change-Id: I224c3644450e8b46e35714bfc5436219ffdee563 diff --git a/src/flash/nor/rp2040.c b/src/flash/nor/rp2040.c index e5028303c0..1858c2ffda 100644 --- a/src/flash/nor/rp2040.c +++ b/src/flash/nor/rp2040.c @@ -9,6 +9,7 @@ #include <target/algorithm.h> #include <target/armv7m.h> #include "spi.h" +#include "sfdp.h" #include <target/cortex_m.h> /* this is 'M' 'u', 1 (version) */ @@ -43,6 +44,25 @@ #define ACCESSCTRL_CFGRESET_OFFSET 0x40060008u #define ACCESSCTRL_WRITE_PASSWORD 0xacce0000u +#define RP2350_QMI_DIRECT_CSR 0x400d0000 +#define RP2350_QMI_DIRECT_TX 0x400d0004 +#define RP2350_QMI_DIRECT_RX 0x400d0008 + +#define RP2350_QMI_DIRECT_CSR_EN BIT(0) +#define RP2350_QMI_DIRECT_CSR_ASSERT_CS0N BIT(2) +#define RP2350_QMI_DIRECT_TX_NOPUSH BIT(20) +#define RP2350_QMI_DIRECT_TX_OE BIT(19) + +#define RP2XXX_SYSINFO_CHIP_ID 0x40000000 +#define RP2XXX_CHIP_ID_PART_MANUFACTURER(id) ((id) & 0x0fffffff) +#define RP2XXX_CHIP_ID_MANUFACTURER 0x493 +#define RP2XXX_MK_PART(part) (((part) << 12) | (RP2XXX_CHIP_ID_MANUFACTURER << 1) | 1) +#define RP2040_CHIP_ID_PART 0x0002 +#define IS_RP2040(id) (RP2XXX_CHIP_ID_PART_MANUFACTURER(id) == RP2XXX_MK_PART(RP2040_CHIP_ID_PART)) +#define RP2350_CHIP_ID_PART 0x0004 +#define IS_RP2350(id) (RP2XXX_CHIP_ID_PART_MANUFACTURER(id) == RP2XXX_MK_PART(RP2350_CHIP_ID_PART)) +#define RP2XXX_CHIP_ID_REVISION(id) ((id) >> 28) + #define RP2XXX_MAX_ALGO_STACK_USAGE 1024 #define RP2XXX_MAX_RAM_ALGO_SIZE 1024 @@ -156,10 +176,9 @@ typedef struct rp2xxx_rom_call_batch_record { } rp2xxx_rom_call_batch_record_t; struct rp2040_flash_bank { - /* flag indicating successful flash probe */ - bool probed; - /* stack used by Boot ROM calls */ - struct working_area *stack; + bool probed; /* flag indicating successful flash probe */ + uint32_t id; /* cached SYSINFO CHIP_ID */ + struct working_area *stack; /* stack used by Boot ROM calls */ /* static code scratchpad used for RAM algorithms -- allocated in advance so that higher-level calls can just grab all remaining workarea: */ struct working_area *ram_algo_space; @@ -172,6 +191,11 @@ struct rp2040_flash_bank { uint16_t jump_flash_reset_address_trans; uint16_t jump_enter_cmd_xip; uint16_t jump_bootrom_reset_state; + + char dev_name[20]; + bool size_override; + struct flash_device spi_dev; /* detected model of SPI flash */ + unsigned int sfdp_dummy, sfdp_dummy_detect; }; #ifndef LOG_ROM_SYMBOL_DEBUG @@ -869,37 +893,279 @@ cleanup_and_return: /* ----------------------------------------------------------------------------- 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_tx_rx(struct target *target, + const uint8_t *tx, unsigned int tx_len, + unsigned int dummy_len, + uint8_t *rx, unsigned int rx_len) +{ + const target_addr_t ssi_dr0 = 0x18000060; + + int retval = rp2040_ssel_active(target, true); + if (retval != ERROR_OK) { + LOG_ERROR("QSPI select failed"); + goto deselect; + } + + unsigned int tx_cnt = 0; + unsigned int rx_cnt = 0; + unsigned int xfer_len = tx_len + dummy_len + rx_len; + while (rx_cnt < xfer_len) { + int in_flight = tx_cnt - rx_cnt; + if (tx_cnt < xfer_len && in_flight < 14) { + uint32_t dr = tx_cnt < tx_len ? tx[tx_cnt] : 0; + retval = target_write_u32(target, ssi_dr0, dr); + if (retval != ERROR_OK) + break; + + tx_cnt++; + continue; + } + uint32_t dr; + retval = target_read_u32(target, ssi_dr0, &dr); + if (retval != ERROR_OK) + break; + + if (rx_cnt >= tx_len + dummy_len) + rx[rx_cnt - tx_len - dummy_len] = (uint8_t)dr; + + rx_cnt++; + } + +deselect: + int retval2 = rp2040_ssel_active(target, false); + + if (retval != ERROR_OK) { + LOG_ERROR("QSPI Tx/Rx failed"); + return retval; + } + if (retval2 != ERROR_OK) + LOG_ERROR("QSPI deselect failed"); + + return retval2; +} + +static int rp2350_spi_tx_rx(struct target *target, + const uint8_t *tx, unsigned int tx_len, + unsigned int dummy_len, + uint8_t *rx, unsigned int rx_len) +{ + uint32_t direct_csr; + int retval = target_read_u32(target, RP2350_QMI_DIRECT_CSR, &direct_csr); + if (retval != ERROR_OK) { + LOG_ERROR("QMI DIRECT_CSR read failed"); + return retval; + } + direct_csr |= RP2350_QMI_DIRECT_CSR_EN | RP2350_QMI_DIRECT_CSR_ASSERT_CS0N; + retval = target_write_u32(target, RP2350_QMI_DIRECT_CSR, direct_csr); + if (retval != ERROR_OK) { + LOG_ERROR("QMI DIRECT mode enable failed"); + goto deselect; + } + + unsigned int tx_cnt = 0; + unsigned int rx_cnt = 0; + unsigned int xfer_len = tx_len + dummy_len + rx_len; + while (tx_cnt < xfer_len || rx_cnt < rx_len) { + int in_flight = tx_cnt - tx_len - dummy_len - rx_cnt; + if (tx_cnt < xfer_len && in_flight < 4) { + uint32_t tx_cmd; + if (tx_cnt < tx_len) + tx_cmd = tx[tx_cnt] | RP2350_QMI_DIRECT_TX_NOPUSH | RP2350_QMI_DIRECT_TX_OE; + else if (tx_cnt < tx_len + dummy_len) + tx_cmd = RP2350_QMI_DIRECT_TX_NOPUSH; + else + tx_cmd = 0; + + retval = target_write_u32(target, RP2350_QMI_DIRECT_TX, tx_cmd); + if (retval != ERROR_OK) + break; + + tx_cnt++; + continue; + } + if (rx_cnt < rx_len) { + uint32_t dr; + retval = target_read_u32(target, RP2350_QMI_DIRECT_RX, &dr); + if (retval != ERROR_OK) + break; + + rx[rx_cnt] = (uint8_t)dr; + rx_cnt++; + } + } + +deselect: + direct_csr &= ~(RP2350_QMI_DIRECT_CSR_EN | RP2350_QMI_DIRECT_CSR_ASSERT_CS0N); + int retval2 = target_write_u32(target, RP2350_QMI_DIRECT_CSR, direct_csr); + + if (retval != ERROR_OK) { + LOG_ERROR("QSPI Tx/Rx failed"); + return retval; + } + if (retval2 != ERROR_OK) + LOG_ERROR("QMI DIRECT mode disable failed"); + + return retval2; +} + +static int rp2xxx_spi_tx_rx(struct flash_bank *bank, + const uint8_t *tx, unsigned int tx_len, + unsigned int dummy_len, + uint8_t *rx, unsigned int rx_len) +{ + struct rp2040_flash_bank *priv = bank->driver_priv; + struct target *target = bank->target; + + if (IS_RP2040(priv->id)) + return rp2040_spi_tx_rx(target, tx, tx_len, dummy_len, rx, rx_len); + else if (IS_RP2350(priv->id)) + return rp2350_spi_tx_rx(target, tx, tx_len, dummy_len, rx, rx_len); + else + return ERROR_FAIL; +} + +static int rp2xxx_read_sfdp_block(struct flash_bank *bank, uint32_t addr, + unsigned int words, uint32_t *buffer) +{ + struct rp2040_flash_bank *priv = bank->driver_priv; + + uint8_t cmd[4] = { SPIFLASH_READ_SFDP }; + uint8_t data[4 * words + priv->sfdp_dummy_detect]; + + h_u24_to_be(&cmd[1], addr); + + int retval = rp2xxx_spi_tx_rx(bank, cmd, sizeof(cmd), priv->sfdp_dummy, + data, 4 * words + priv->sfdp_dummy_detect); + if (retval != ERROR_OK) + return retval; + + if (priv->sfdp_dummy_detect) { + for (unsigned int i = 0; i < priv->sfdp_dummy_detect; i++) + if (le_to_h_u32(&data[i]) == SFDP_MAGIC) { + priv->sfdp_dummy_detect = 0; + priv->sfdp_dummy = i; + break; + } + for (unsigned int i = 0; i < words; i++) + buffer[i] = le_to_h_u32(&data[4 * i + priv->sfdp_dummy]); + } else { + for (unsigned int i = 0; i < words; i++) + buffer[i] = le_to_h_u32(&data[4 * i]); + } + return retval; +} + static int rp2040_flash_probe(struct flash_bank *bank) { struct rp2040_flash_bank *priv = bank->driver_priv; struct target *target = bank->target; - int err = rp2xxx_populate_rom_pointer_cache(target, priv); - if (err != ERROR_OK) - return err; + int retval = target_read_u32(target, RP2XXX_SYSINFO_CHIP_ID, &priv->id); + if (retval != ERROR_OK) { + LOG_ERROR("SYSINFO CHIP_ID read failed"); + return retval; + } + if (!IS_RP2040(priv->id) && !IS_RP2350(priv->id)) { + LOG_ERROR("Unknown SYSINFO CHIP_ID 0x%08" PRIx32, priv->id); + return ERROR_FLASH_BANK_INVALID; + } + + retval = rp2xxx_populate_rom_pointer_cache(target, priv); + if (retval != ERROR_OK) + return retval; /* the Boot ROM flash_range_program() routine requires page alignment */ bank->write_start_alignment = 256; bank->write_end_alignment = 256; - // Max size -- up to two devices (two chip selects) in adjacent 24-bit address windows + uint32_t flash_id = 0; + if (priv->size_override) { + priv->spi_dev.name = "size override"; + LOG_DEBUG("SPI flash autodetection disabled, using configured size"); + } else { + bank->size = 0; + + (void)setup_for_raw_flash_cmd(target, priv); + /* ignore error, flash size detection could work anyway */ + + const uint8_t cmd[] = { SPIFLASH_READ_ID }; + uint8_t data[3]; + retval = rp2xxx_spi_tx_rx(bank, cmd, sizeof(cmd), 0, data, sizeof(data)); + if (retval == ERROR_OK) { + flash_id = le_to_h_u24(data); + + /* search for a SPI flash Device ID match */ + for (const struct flash_device *p = flash_devices; p->name ; p++) { + if (p->device_id == flash_id) { + priv->spi_dev = *p; + bank->size = p->size_in_bytes; + break; + } + } + } + + if (bank->size == 0) { + priv->sfdp_dummy_detect = 8; + priv->sfdp_dummy = 0; + retval = spi_sfdp(bank, &priv->spi_dev, &rp2xxx_read_sfdp_block); + if (retval == ERROR_OK) + bank->size = priv->spi_dev.size_in_bytes; + } + + cleanup_after_raw_flash_cmd(target, priv); + } + + snprintf(priv->dev_name, sizeof(priv->dev_name), "%s rev %u", + IS_RP2350(priv->id) ? "RP2350" : "RP2040", + RP2XXX_CHIP_ID_REVISION(priv->id)); + if (bank->size == 0) { - /* TODO: get real flash size */ - bank->size = 32 * 1024 * 1024; + LOG_ERROR("%s, QSPI Flash id = 0x%06" PRIx32 " not recognised", + priv->dev_name, flash_id); + return ERROR_FLASH_BANK_INVALID; } bank->num_sectors = bank->size / 4096; - LOG_INFO("RP2040 Flash Probe: %d bytes @" TARGET_ADDR_FMT ", in %d sectors\n", - bank->size, bank->base, bank->num_sectors); + if (priv->size_override) { + LOG_INFO("%s, QSPI Flash size override = %u KiB in %u sectors", + priv->dev_name, bank->size / 1024, bank->num_sectors); + } else { + LOG_INFO("%s, QSPI Flash %s id = 0x%06" PRIx32 " size = %u KiB in %u sectors", + priv->dev_name, priv->spi_dev.name, flash_id, + bank->size / 1024, bank->num_sectors); + } + + free(bank->sectors); bank->sectors = alloc_block_array(0, 4096, bank->num_sectors); if (!bank->sectors) return ERROR_FAIL; - if (err == ERROR_OK) - priv->probed = true; + priv->probed = true; - return err; + return ERROR_OK; } static int rp2040_flash_auto_probe(struct flash_bank *bank) @@ -927,6 +1193,7 @@ FLASH_BANK_COMMAND_HANDLER(rp2040_flash_bank_command) priv = malloc(sizeof(struct rp2040_flash_bank)); memset(priv, 0, sizeof(struct rp2040_flash_bank)); priv->probed = false; + priv->size_override = bank->size != 0; /* Set up driver_priv */ bank->driver_priv = priv; diff --git a/tcl/target/rp2350.cfg b/tcl/target/rp2350.cfg index 7ae5eb9fcb..0a5a987e7f 100644 --- a/tcl/target/rp2350.cfg +++ b/tcl/target/rp2350.cfg @@ -19,6 +19,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 { @@ -155,13 +163,15 @@ if { ![info exists _FLASH_TARGET] && [info exists _TARGETNAME_1] } { } } if { [info exists _FLASH_TARGET] } { - $_FLASH_TARGET configure -work-area-phys 0x20010000 -work-area-size $_WORKAREASIZE + # QSPI flash size detection during gdb connect requires to back-up RAM + set _WKA_BACKUP [expr { $_WORKAREASIZE == 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 + $_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 - set _FLASHSIZE 0x00400000 flash bank $_FLASHNAME rp2040_flash 0x10000000 $_FLASHSIZE 0 0 $_FLASH_TARGET } --