From a782475bfa741afe2e50a0460a1da198f670b314 Mon Sep 17 00:00:00 2001 From: Vegard Storheil Eriksen Date: Mon, 2 Dec 2024 21:55:35 +0100 Subject: [PATCH] nrf54l: Initial support --- src/target/adi.c | 9 +- src/target/adiv5.c | 37 +++---- src/target/cortexm.c | 1 + src/target/meson.build | 1 + src/target/nrf54l.c | 196 ++++++++++++++++++++++++++++++++++++++ src/target/target_probe.c | 2 + src/target/target_probe.h | 2 + 7 files changed, 230 insertions(+), 18 deletions(-) create mode 100644 src/target/nrf54l.c diff --git a/src/target/adi.c b/src/target/adi.c index 096a717ca39..25194ffa1c7 100644 --- a/src/target/adi.c +++ b/src/target/adi.c @@ -371,6 +371,7 @@ bool adi_configure_mem_ap(adiv5_access_port_s *const ap) /* Check the Debug Base Address register for not-present. See ADIv5 Specification C2.6.1 */ if (base_flags == (ADIV5_AP_BASE_FORMAT_ADIV5 | ADIV5_AP_BASE_PRESENT_NO_ENTRY) || (!(ap->flags & ADIV5_AP_FLAGS_64BIT) && (uint32_t)ap->base == ADIV5_AP_BASE_NOT_PRESENT)) { + bool ignore_not_present = false; /* * Debug Base Address not present in this MEM-AP * No debug entries... useless AP @@ -380,7 +381,13 @@ bool adi_configure_mem_ap(adiv5_access_port_s *const ap) * valid debug components on AP0, so we have to have an exception * for this part family. */ - if (ap->dp->target_designer_code != JEP106_MANUFACTURER_TEXAS || ap->base != 0xf0000002U) { + if (ap->dp->target_designer_code == JEP106_MANUFACTURER_TEXAS && ap->base == 0xf0000002U) + ignore_not_present = true; + + else if (ap->dp->target_designer_code == JEP106_MANUFACTURER_NORDIC && ap->base != 0x00000002U) + ignore_not_present = true; + + if (!ignore_not_present) { DEBUG_INFO(" -> Not Present\n"); return false; } diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 7407344ab9f..91d7b6520dc 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -514,28 +514,31 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp) kinetis_mdm_probe(ap); nrf51_mdm_probe(ap); + nrf54l_ctrl_ap_probe(ap); efm32_aap_probe(ap); lpc55_dmap_probe(ap); - /* Try to prepare the AP if it seems to be a AHB3 MEM-AP */ - if (!ap->apsel && ADIV5_AP_IDR_CLASS(ap->idr) == 8U && ADIV5_AP_IDR_TYPE(ap->idr) == ARM_AP_TYPE_AHB3) { - if (!cortexm_prepare(ap)) - DEBUG_WARN("adiv5: Failed to prepare AP, results may be unpredictable\n"); - } + if (ADIV5_AP_IDR_CLASS(ap->idr) == ADIV5_AP_IDR_CLASS_MEM) { + /* Try to prepare the AP if it seems to be a AHB3 MEM-AP */ + if (!ap->apsel && ADIV5_AP_IDR_TYPE(ap->idr) == ARM_AP_TYPE_AHB3) { + if (!cortexm_prepare(ap)) + DEBUG_WARN("adiv5: Failed to prepare AP, results may be unpredictable\n"); + } - /* The rest should only be added after checking ROM table */ - adi_ap_component_probe(ap, ap->base, 0, 0); - /* Having completed discovery on this AP, try to resume any halted cores */ - adi_ap_resume_cores(ap); + /* The rest should only be added after checking ROM table */ + adi_ap_component_probe(ap, ap->base, 0, 0); + /* Having completed discovery on this AP, try to resume any halted cores */ + adi_ap_resume_cores(ap); - /* - * Due to the Tiva TM4C1294KCDT (among others) repeating the single AP ad-nauseum, - * this check is needed so that we bail rather than repeating the same AP ~256 times. - */ - if (ap->dp->quirks & ADIV5_DP_QUIRK_DUPED_AP) { - adiv5_ap_unref(ap); - adiv5_dp_unref(dp); - return; + /* + * Due to the Tiva TM4C1294KCDT (among others) repeating the single AP ad-nauseum, + * this check is needed so that we bail rather than repeating the same AP ~256 times. + */ + if (ap->dp->quirks & ADIV5_DP_QUIRK_DUPED_AP) { + adiv5_ap_unref(ap); + adiv5_dp_unref(dp); + return; + } } adiv5_ap_unref(ap); diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 7bc252044f6..b652eda110d 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -404,6 +404,7 @@ bool cortexm_probe(adiv5_access_port_s *ap) break; case JEP106_MANUFACTURER_NORDIC: PROBE(nrf51_probe); + PROBE(nrf54l_probe); PROBE(nrf91_probe); break; case JEP106_MANUFACTURER_ATMEL: diff --git a/src/target/meson.build b/src/target/meson.build index 6d1e714c239..4e11cb99ef0 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -201,6 +201,7 @@ target_lpc = declare_dependency( target_nrf = declare_dependency( sources: files( 'nrf51.c', + 'nrf54l.c', 'nrf91.c', ), dependencies: target_cortexm, diff --git a/src/target/nrf54l.c b/src/target/nrf54l.c new file mode 100644 index 00000000000..099fcb6dce4 --- /dev/null +++ b/src/target/nrf54l.c @@ -0,0 +1,196 @@ +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" +#include "adiv5.h" + +#define NRF54L_PARTNO 0x1c0U + +#define NRF54L_FICR_INFO_RAM 0x00ffc328U +#define NRF54L_FICR_INFO_RRAM 0x00ffc32cU + +#define NRF54L_RRAM 0x00000000U +#define NRF54L_UICR 0x00ffd000U +#define NRF54L_RAM 0x20000000U + +#define NRF54L_RRAMC_READY 0x5004b400U +#define NRF54L_RRAMC_READYNEXT 0x5004b404U +#define NRF54L_RRAMC_BUFSTATUS_WRITEBUFEMPTY 0x5004b418U +#define NRF54L_RRAMC_CONFIG 0x5004b500U +#define NRF54L_RRAMC_ERASE_ERASEALL 0x5004b540U + +#define NRF54L_RRAMC_READY_BUSY 0U +#define NRF54L_RRAMC_READYNEXT_READY 1U +#define NRF54L_RRAMC_BUFSTATUS_WRITEBUFEMPTY_EMPTY 1U +#define NRF54L_RRAMC_CONFIG_WRITE_DISABLED (0U << 0U) +#define NRF54L_RRAMC_CONFIG_WRITE_ENABLED (1U << 0U) +#define NRF54L_RRAMC_CONFIG_WRITEBUFSIZE(size) (size << 8U) +#define NRF54L_RRAMC_ERASE_ERASEALL_ERASE 1U + +#define NRF54L_CTRL_AP_IDR_VALUE 0x32880000U + +#define NRF54L_CTRL_AP_RESET ADIV5_AP_REG(0x00U) +#define NRF54L_CTRL_AP_ERASEALL ADIV5_AP_REG(0x04U) +#define NRF54L_CTRL_AP_ERASEALLSTATUS ADIV5_AP_REG(0x08U) +#define NRF54L_CTRL_AP_APPROTECT_STATUS ADIV5_AP_REG(0x14U) + +#define NRF54L_CTRL_AP_RESET_NORESET 0U +#define NRF54L_CTRL_AP_RESET_HARDRESET 2U +#define NRF54L_CTRL_AP_ERASEALL_ERASE 1U +#define NRF54L_CTRL_AP_ERASEALLSTATUS_BUSY 2U +#define NRF54L_CTRL_AP_APPROTECT_STATUS_APPROTECT_ENABLED (1U << 0U) +#define NRF54L_CTRL_AP_APPROTECT_STATUS_SECUREAPPROTECT_ENABLED (1U << 1U) + +static bool rram_erase(target_flash_s *flash, target_addr_t addr, size_t len) +{ + (void)flash; + (void)addr; + (void)len; + // RRAM doesn't need to be erased before being written, so we just return ok. + return true; +} + +static bool rram_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len) +{ + // Wait for rram to be ready for next write. + while (target_mem32_read32(flash->t, NRF54L_RRAMC_READYNEXT) != NRF54L_RRAMC_READYNEXT_READY) + continue; + target_mem32_write(flash->t, dest, src, len); + return true; +} + +static bool rram_prepare(target_flash_s *flash) +{ + uint32_t writebufsize = flash->writesize / 16U; + target_mem32_write32(flash->t, NRF54L_RRAMC_CONFIG, + NRF54L_RRAMC_CONFIG_WRITEBUFSIZE(writebufsize) | NRF54L_RRAMC_CONFIG_WRITE_ENABLED); + return true; +} + +static bool rram_done(target_flash_s *flash) +{ + // Wait for writebuf to flush. + while (target_mem32_read32(flash->t, NRF54L_RRAMC_BUFSTATUS_WRITEBUFEMPTY) != + NRF54L_RRAMC_BUFSTATUS_WRITEBUFEMPTY_EMPTY) + continue; + target_mem32_write32(flash->t, NRF54L_RRAMC_CONFIG, NRF54L_RRAMC_CONFIG_WRITE_DISABLED); + return true; +} + +static bool rram_mass_erase(target_s *const target, platform_timeout_s *const print_progess) +{ + target_mem32_write32(target, NRF54L_RRAMC_ERASE_ERASEALL, NRF54L_RRAMC_ERASE_ERASEALL_ERASE); + + uint32_t ready; + do { + ready = target_mem32_read32(target, NRF54L_RRAMC_READY); + target_print_progress(print_progess); + } while (ready == NRF54L_RRAMC_READY_BUSY); + + return true; +} + +static void add_rram(target_s *target, uint32_t addr, size_t length, uint32_t writesize) +{ + target_flash_s *flash = calloc(1, sizeof(*flash)); + if (!flash) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + + flash->start = addr; + flash->length = length; + flash->blocksize = writesize; + flash->writesize = writesize; + flash->erase = rram_erase; + flash->write = rram_write; + flash->prepare = rram_prepare; + flash->done = rram_done; + flash->erased = 0xffU; + target_add_flash(target, flash); +} + +bool nrf54l_probe(target_s *target) +{ + adiv5_access_port_s *ap = cortex_ap(target); + + if (ap->dp->version < 2U) + return false; + + switch (ap->dp->target_partno) { + case NRF54L_PARTNO: + target->driver = "Nordic nRF54L"; + target->target_options |= TOPT_INHIBIT_NRST; + break; + default: + return false; + } + + target->mass_erase = rram_mass_erase; + + const uint32_t info_ram = target_mem32_read32(target, NRF54L_FICR_INFO_RAM); + const uint32_t info_rram = target_mem32_read32(target, NRF54L_FICR_INFO_RRAM); + + target_add_ram32(target, NRF54L_RAM, info_ram * 1024U); + add_rram(target, NRF54L_RRAM, info_rram * 1024U, 512U); + add_rram(target, NRF54L_UICR, 0x1000U, 4U); + + return true; +} + +static bool nrf54l_ctrl_ap_mass_erase(target_s *target, platform_timeout_s *print_progess); + +bool nrf54l_ctrl_ap_probe(adiv5_access_port_s *ap) +{ + switch (ap->idr) { + case NRF54L_CTRL_AP_IDR_VALUE: + break; + default: + return false; + } + + target_s *target = target_new(); + if (!target) + return false; + + target->mass_erase = nrf54l_ctrl_ap_mass_erase; + adiv5_ap_ref(ap); + target->priv = ap; + target->priv_free = (void *)adiv5_ap_unref; + + uint32_t status = adiv5_ap_read(ap, NRF54L_CTRL_AP_APPROTECT_STATUS); + + if (!(status & + (NRF54L_CTRL_AP_APPROTECT_STATUS_APPROTECT_ENABLED | + NRF54L_CTRL_AP_APPROTECT_STATUS_SECUREAPPROTECT_ENABLED))) + target->driver = "Nordic nRF54L Access Port"; + else + target->driver = "Nordic nRF54L Access Port (protected)"; + target->regs_size = 0U; + + return true; +} + +static bool nrf54l_ctrl_ap_mass_erase(target_s *const target, platform_timeout_s *const print_progess) +{ + adiv5_access_port_s *const ap = target->priv; + + const uint32_t ctrl = adiv5_dp_read(ap->dp, ADIV5_DP_CTRLSTAT); + adiv5_dp_write(ap->dp, ADIV5_DP_CTRLSTAT, ctrl | ADIV5_DP_CTRLSTAT_CDBGPWRUPREQ); + + adiv5_ap_write(ap, NRF54L_CTRL_AP_ERASEALL, NRF54L_CTRL_AP_ERASEALL_ERASE); + + uint32_t status; + do { + status = adiv5_ap_read(ap, NRF54L_CTRL_AP_ERASEALLSTATUS); + target_print_progress(print_progess); + } while (status == NRF54L_CTRL_AP_ERASEALLSTATUS_BUSY); + + // Assert reset. + adiv5_ap_write(ap, NRF54L_CTRL_AP_RESET, NRF54L_CTRL_AP_RESET_HARDRESET); + + // Deassert reset. + adiv5_ap_write(ap, NRF54L_CTRL_AP_RESET, NRF54L_CTRL_AP_RESET_NORESET); + + return true; +} diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 689ba88228a..d4a6a4cacd5 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -98,6 +98,7 @@ CORTEXM_PROBE_WEAK_NOP(efm32_aap_probe) CORTEXM_PROBE_WEAK_NOP(kinetis_mdm_probe) CORTEXM_PROBE_WEAK_NOP(lpc55_dmap_probe) CORTEXM_PROBE_WEAK_NOP(nrf51_mdm_probe) +CORTEXM_PROBE_WEAK_NOP(nrf54l_ctrl_ap_probe) CORTEXM_PROBE_WEAK_NOP(rp2040_rescue_probe) TARGET_PROBE_WEAK_NOP(apollo_3_probe) @@ -127,6 +128,7 @@ TARGET_PROBE_WEAK_NOP(msp432e4_probe) TARGET_PROBE_WEAK_NOP(msp432p4_probe) TARGET_PROBE_WEAK_NOP(mspm0_probe) TARGET_PROBE_WEAK_NOP(nrf51_probe) +TARGET_PROBE_WEAK_NOP(nrf54l_probe) TARGET_PROBE_WEAK_NOP(nrf91_probe) TARGET_PROBE_WEAK_NOP(puya_probe) TARGET_PROBE_WEAK_NOP(renesas_ra_probe) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 009fa06a763..842211a16ba 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -49,6 +49,7 @@ bool efm32_aap_probe(adiv5_access_port_s *ap); bool kinetis_mdm_probe(adiv5_access_port_s *ap); bool lpc55_dmap_probe(adiv5_access_port_s *ap); bool nrf51_mdm_probe(adiv5_access_port_s *ap); +bool nrf54l_ctrl_ap_probe(adiv5_access_port_s *ap); bool rp2040_rescue_probe(adiv5_access_port_s *ap); bool at32f40x_probe(target_s *target); // STM32 clones from Artery @@ -78,6 +79,7 @@ bool msp432e4_probe(target_s *target); bool msp432p4_probe(target_s *target); bool mspm0_probe(target_s *target); bool nrf51_probe(target_s *target); +bool nrf54l_probe(target_s *target); bool nrf91_probe(target_s *target); bool puya_probe(target_s *target); bool renesas_ra_probe(target_s *target);