Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for nRF54L #2009

Merged
merged 2 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
39 changes: 21 additions & 18 deletions src/target/adiv5.c
Original file line number Diff line number Diff line change
Expand Up @@ -513,29 +513,32 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp)
}

kinetis_mdm_probe(ap);
nrf51_mdm_probe(ap);
nrf51_ctrl_ap_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
40 changes: 20 additions & 20 deletions src/target/nrf51.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,20 +400,20 @@ static bool nrf51_cmd_read(target_s *t, int argc, const char **argv)
return nrf51_cmd_read_help(t, 0, NULL);
}

#define NRF52_MDM_IDR 0x02880000U
#define NRF52_CTRL_AP_IDR 0x02880000U

static bool nrf51_mdm_mass_erase(target_s *t, platform_timeout_s *print_progess);
static bool nrf51_ctrl_ap_mass_erase(target_s *t, platform_timeout_s *print_progess);

#define MDM_POWER_EN ADIV5_DP_REG(0x01U)
#define MDM_SELECT_AP ADIV5_DP_REG(0x02U)
#define MDM_STATUS ADIV5_AP_REG(0x08U)
#define MDM_CONTROL ADIV5_AP_REG(0x04U)
#define MDM_PROT_EN ADIV5_AP_REG(0x0cU)
#define CTRL_AP_POWER_EN ADIV5_DP_REG(0x01U)
#define CTRL_AP_SELECT_AP ADIV5_DP_REG(0x02U)
#define CTRL_AP_STATUS ADIV5_AP_REG(0x08U)
#define CTRL_AP_CONTROL ADIV5_AP_REG(0x04U)
#define CTRL_AP_PROT_EN ADIV5_AP_REG(0x0cU)

bool nrf51_mdm_probe(adiv5_access_port_s *ap)
bool nrf51_ctrl_ap_probe(adiv5_access_port_s *ap)
{
switch (ap->idr) {
case NRF52_MDM_IDR:
case NRF52_CTRL_AP_IDR:
break;
default:
return false;
Expand All @@ -424,13 +424,13 @@ bool nrf51_mdm_probe(adiv5_access_port_s *ap)
return false;

t->enter_flash_mode = target_enter_flash_mode_stub;
t->mass_erase = nrf51_mdm_mass_erase;
t->mass_erase = nrf51_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, MDM_PROT_EN);
status = adiv5_ap_read(ap, MDM_PROT_EN);
uint32_t status = adiv5_ap_read(ap, CTRL_AP_PROT_EN);
status = adiv5_ap_read(ap, CTRL_AP_PROT_EN);
if (status)
t->driver = "Nordic nRF52 Access Port";
else
Expand All @@ -440,24 +440,24 @@ bool nrf51_mdm_probe(adiv5_access_port_s *ap)
return true;
}

static bool nrf51_mdm_mass_erase(target_s *const t, platform_timeout_s *const print_progess)
static bool nrf51_ctrl_ap_mass_erase(target_s *const t, platform_timeout_s *const print_progess)
{
adiv5_access_port_s *const ap = t->priv;

uint32_t status = adiv5_ap_read(ap, MDM_STATUS);
adiv5_dp_write(ap->dp, MDM_POWER_EN, 0x50000000U);
adiv5_dp_write(ap->dp, MDM_SELECT_AP, 0x01000000U);
adiv5_ap_write(ap, MDM_CONTROL, 0x00000001U);
uint32_t status = adiv5_ap_read(ap, CTRL_AP_STATUS);
adiv5_dp_write(ap->dp, CTRL_AP_POWER_EN, 0x50000000U);
adiv5_dp_write(ap->dp, CTRL_AP_SELECT_AP, 0x01000000U);
adiv5_ap_write(ap, CTRL_AP_CONTROL, 0x00000001U);

// Read until 0, probably should have a timeout here...
do {
status = adiv5_ap_read(ap, MDM_STATUS);
status = adiv5_ap_read(ap, CTRL_AP_STATUS);
target_print_progress(print_progess);
} while (status);

// The second read will provide true prot status
status = adiv5_ap_read(ap, MDM_PROT_EN);
status = adiv5_ap_read(ap, MDM_PROT_EN);
status = adiv5_ap_read(ap, CTRL_AP_PROT_EN);
status = adiv5_ap_read(ap, CTRL_AP_PROT_EN);

// Should we return the prot status here?
return true;
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;
}
4 changes: 3 additions & 1 deletion src/target/target_probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ TARGET_PROBE_WEAK_NOP(riscv64_probe)
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(nrf51_ctrl_ap_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
Loading
Loading