From 5eaa6df04f984785b99c241d7bd82fbaddb99250 Mon Sep 17 00:00:00 2001 From: Vegard Storheil Eriksen Date: Sun, 1 Dec 2024 02:26:28 +0100 Subject: [PATCH] target/nrf54l: Initial support --- src/target/adi.c | 9 ++- src/target/adiv5.c | 12 ++- src/target/cortexm.c | 1 + src/target/meson.build | 1 + src/target/nrf54l.c | 166 ++++++++++++++++++++++++++++++++++++++ src/target/target_probe.c | 2 + src/target/target_probe.h | 2 + 7 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 src/target/nrf54l.c diff --git a/src/target/adi.c b/src/target/adi.c index 096a717ca39..33d0cfa6a30 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; + + 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..ea1a30301cf 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -512,10 +512,14 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp) continue; } - kinetis_mdm_probe(ap); - nrf51_mdm_probe(ap); - efm32_aap_probe(ap); - lpc55_dmap_probe(ap); + /* Check if this is a vendor specific AP */ + bool vendor_specific_ap = kinetis_mdm_probe(ap) || nrf51_mdm_probe(ap) || nrf54l_ctrl_ap_probe(ap) || + efm32_aap_probe(ap) || lpc55_dmap_probe(ap); + + if (vendor_specific_ap) { + adiv5_ap_unref(ap); + continue; + } /* 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) { 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..c568f7143d2 --- /dev/null +++ b/src/target/nrf54l.c @@ -0,0 +1,166 @@ +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" +#include "adiv5.h" + +#define NRF54L_FICR_INFO_RAM 0x00ffc328U +#define NRF54L_FICR_INFO_RRAM 0x00ffc32cU + +#define NRF54L_UICR 0x00ffd000U + +#define NRF54L_RRAMC_READY 0x5004b400U +#define NRF54L_RRAMC_CONFIG 0x5004b500U +#define NRF54L_RRAMC_ERASE_ERASEALL 0x5004b540U + +#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) + +static bool rram_erase(target_flash_s *f, target_addr_t addr, size_t len) +{ + (void)f; + (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 *f, target_addr_t dest, const void *src, size_t len) +{ + target_mem32_write(f->t, dest, src, len); + return true; +} + +static bool rram_prepare(target_flash_s *f) +{ + target_mem32_write32(f->t, NRF54L_RRAMC_CONFIG, 1); + return true; +} + +static bool rram_done(target_flash_s *f) +{ + target_mem32_write32(f->t, NRF54L_RRAMC_CONFIG, 0); + return true; +} + +static bool rram_mass_erase(target_s *const t, platform_timeout_s *const print_progess) +{ + target_mem32_write32(t, NRF54L_RRAMC_ERASE_ERASEALL, 1); + + uint32_t ready; + do { + ready = target_mem32_read32(t, NRF54L_RRAMC_READY); + target_print_progress(print_progess); + } while (ready == 0); + + return true; +} + +static void add_rram(target_s *t, uint32_t addr, size_t length) +{ + target_flash_s *f = calloc(1, sizeof(*f)); + if (!f) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + + f->start = addr; + f->length = length; + f->blocksize = 1024U; + f->writesize = 1024U; + f->erase = rram_erase; + f->write = rram_write; + f->prepare = rram_prepare; + f->done = rram_done; + f->erased = 0xff; + target_add_flash(t, f); +} + +bool nrf54l_probe(target_s *target) +{ + DEBUG_INFO("nrf54l_probe:\n"); + + adiv5_access_port_s *ap = cortex_ap(target); + + if (ap->dp->version < 2U) + return false; + + switch (ap->dp->target_partno) { + case 0x1c0: + target->driver = "Nordic nRF54L"; + target->target_options |= TOPT_INHIBIT_NRST; + break; + default: + return false; + } + + target->mass_erase = rram_mass_erase; + + uint32_t info_ram = target_mem32_read32(target, NRF54L_FICR_INFO_RAM); + uint32_t info_rram = target_mem32_read32(target, NRF54L_FICR_INFO_RRAM); + + target_add_ram32(target, 0x20000000U, info_ram * 1024U); + add_rram(target, 0x0, info_rram * 1024U); + add_rram(target, NRF54L_UICR, 0x1000U); + + return true; +} + +static bool nrf54l_ctrl_ap_mass_erase(target_s *t, 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 *t = target_new(); + if (!t) + return false; + + t->mass_erase = nrf54l_ctrl_ap_mass_erase; + adiv5_ap_ref(ap); + t->priv = ap; + t->priv_free = (void *)adiv5_ap_unref; + + uint32_t status = adiv5_ap_read(ap, NRF54L_CTRL_AP_APPROTECT_STATUS); + + if ((status & 0x3) == 0) + t->driver = "Nordic nRF54L Access Port"; + else + t->driver = "Nordic nRF54L Access Port (protected)"; + t->regs_size = 0; + + return true; +} + +static bool nrf54l_ctrl_ap_mass_erase(target_s *const t, platform_timeout_s *const print_progess) +{ + adiv5_access_port_s *const ap = t->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, 0x00000001U); + + uint32_t status; + // Read until 0, probably should have a timeout here... + do { + status = adiv5_ap_read(ap, NRF54L_CTRL_AP_ERASEALLSTATUS); + target_print_progress(print_progess); + } while (status == 2); + + // Assert reset. + adiv5_ap_write(ap, NRF54L_CTRL_AP_RESET, 0x2U); + + // Deassert reset. + adiv5_ap_write(ap, NRF54L_CTRL_AP_RESET, 0x0U); + + 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);