Skip to content

Commit

Permalink
nrf54l: Initial support
Browse files Browse the repository at this point in the history
  • Loading branch information
zyp committed Dec 3, 2024
1 parent b0eb8d0 commit a782475
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 18 deletions.
9 changes: 8 additions & 1 deletion src/target/adi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
Expand Down
37 changes: 20 additions & 17 deletions src/target/adiv5.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/target/cortexm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions src/target/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ target_lpc = declare_dependency(
target_nrf = declare_dependency(
sources: files(
'nrf51.c',
'nrf54l.c',
'nrf91.c',
),
dependencies: target_cortexm,
Expand Down
196 changes: 196 additions & 0 deletions src/target/nrf54l.c
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 2 additions & 0 deletions src/target/target_probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/target/target_probe.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit a782475

Please sign in to comment.