From 78ad1886d1736d6d84971b57cf0b7c74d66055d7 Mon Sep 17 00:00:00 2001 From: Matthew Via Date: Fri, 20 Oct 2023 20:01:50 -0400 Subject: [PATCH 1/3] adiv5: use explicit AP discovery procedure for s32k3xx These chips will hard fault if we probe for APs that are not present. Instead, lets detect the specific chip and explicitly initialize the APs we know it has. Additionally, the S32K3 requires specific bits to be set in the SDA_AP registers to allow debug access, so we explicitly initialize that as well. --- src/target/adiv5.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/target/adiv5.c b/src/target/adiv5.c index b08560b034b..18a2a5223d0 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -807,6 +807,63 @@ uint32_t adiv5_dp_read_dpidr(adiv5_debug_port_s *const dp) return dpidr; } +#define S32K344_TARGET_PARTNO 0x995cU +#define S32K3xx_APB_AP 1U +#define S32K3xx_AHB_AP 4U +#define S32K3xx_MDM_AP 6U +#define S32K3xx_SDA_AP 7U +#define S32K3xx_SDA_AP_DBGENCTR ADIV5_AP_REG(0x80U) +#define S32K3xx_SDA_AP_DBGENCTR_MASK 0x300000f0U + +static bool s32k3xx_dp_prepare(adiv5_debug_port_s *const dp) +{ + /* Is this an S32K344? */ + if (dp->target_partno != S32K344_TARGET_PARTNO) + return false; + + adiv5_dp_abort(dp, ADIV5_DP_ABORT_DAPABORT); + + /* SDA_AP has various flags we must enable before we can have debug access, so + * start with it and enable them */ + adiv5_access_port_s *sda_ap = adiv5_new_ap(dp, S32K3xx_SDA_AP); + if (!sda_ap) + return false; + adiv5_ap_write(sda_ap, S32K3xx_SDA_AP_DBGENCTR, S32K3xx_SDA_AP_DBGENCTR_MASK); + adiv5_ap_unref(sda_ap); + + /* If we try to access an invalid AP the S32K3 will hard fault, so we must + * statically enumerate the APs we expect */ + adiv5_access_port_s *apb_ap = adiv5_new_ap(dp, S32K3xx_APB_AP); + if (!apb_ap) + return false; + adiv5_component_probe(apb_ap, apb_ap->base, 0, 0); + adiv5_ap_unref(apb_ap); + + adiv5_access_port_s *ahb_ap = adiv5_new_ap(dp, S32K3xx_AHB_AP); + if (!ahb_ap) + return false; + adiv5_component_probe(ahb_ap, ahb_ap->base, 0, 0); + + cortexm_prepare(ahb_ap); + for (target_s *target = target_list; target; target = target->next) { + if (!connect_assert_nrst && target->priv_free == cortex_priv_free) { + adiv5_access_port_s *target_ap = cortex_ap(target); + if (target_ap == ahb_ap) + target_halt_resume(target, false); + } + } + + adiv5_ap_unref(ahb_ap); + + adiv5_access_port_s *mdm_ap = adiv5_new_ap(dp, S32K3xx_MDM_AP); + if (!mdm_ap) + return false; + adiv5_component_probe(mdm_ap, mdm_ap->base, 0, 0); + adiv5_ap_unref(mdm_ap); + + return true; +} + void adiv5_dp_init(adiv5_debug_port_s *const dp) { /* @@ -949,6 +1006,15 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp) /* Probe for APs on this DP */ size_t invalid_aps = 0; dp->refcnt++; + + if (dp->target_designer_code == JEP106_MANUFACTURER_FREESCALE) { + /* S32K3XX will requires special handling, do so and skip the AP enumeration */ + if (s32k3xx_dp_prepare(dp)) { + adiv5_dp_unref(dp); + return; + } + } + for (size_t i = 0; i < 256U && invalid_aps < 8U; ++i) { adiv5_access_port_s *ap = adiv5_new_ap(dp, i); if (ap == NULL) { From 6de9088cba283ba1aff7bf264409c00842090a15 Mon Sep 17 00:00:00 2001 From: Matthew Via Date: Fri, 20 Oct 2023 20:14:58 -0400 Subject: [PATCH 2/3] s32k3xx: implement skeleton for s32k3xx --- src/Makefile | 1 + src/target/cortexm.c | 1 + src/target/meson.build | 1 + src/target/s32k3xx.c | 52 +++++++++++++++++++++++++++++++++++++++ src/target/target_probe.c | 1 + src/target/target_probe.h | 1 + 6 files changed, 57 insertions(+) create mode 100644 src/target/s32k3xx.c diff --git a/src/Makefile b/src/Makefile index 72d695cf17e..e6708dee92b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -79,6 +79,7 @@ SRC = \ semihosting.c \ sfdp.c \ spi.c \ + s32k3xx.c \ stm32f1.c \ ch32f1.c \ stm32f4.c \ diff --git a/src/target/cortexm.c b/src/target/cortexm.c index dd4cfad9325..315b22285d0 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -569,6 +569,7 @@ bool cortexm_probe(adiv5_access_port_s *ap) case JEP106_MANUFACTURER_FREESCALE: PROBE(imxrt_probe); PROBE(kinetis_probe); + PROBE(s32k3xx_probe); break; case JEP106_MANUFACTURER_GIGADEVICE: PROBE(gd32f1_probe); diff --git a/src/target/meson.build b/src/target/meson.build index 565acdb9293..d237dcd40e6 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -182,6 +182,7 @@ target_nxp = declare_dependency( 'imxrt.c', 'kinetis.c', 'nxpke04.c', + 's32k3xx.c', ), dependencies: target_cortexm, ) diff --git a/src/target/s32k3xx.c b/src/target/s32k3xx.c new file mode 100644 index 00000000000..da68793cd14 --- /dev/null +++ b/src/target/s32k3xx.c @@ -0,0 +1,52 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2015 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * This file implements S32K3xx target specific functions providing + * the XML memory map and Flash memory programming. + */ + +#include + +#include "command.h" +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "adiv5.h" +#include "cortexm.h" + +#define SIUL2_MIDR1 0x40290004U + +bool s32k3xx_probe(target_s *const target) +{ + uint32_t midr1 = target_mem_read32(target, SIUL2_MIDR1); + char product_letter = (midr1 >> 26U) & 0x3fU; + uint32_t part_no = (midr1 >> 16U) & 0x3ffU; + + if (product_letter != 0xbU) + return false; + + switch (part_no) { + default: + return false; + } + + return true; +} diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 355d39f2049..eb44e984b79 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -128,6 +128,7 @@ TARGET_PROBE_WEAK_NOP(nrf91_probe) TARGET_PROBE_WEAK_NOP(renesas_ra_probe) TARGET_PROBE_WEAK_NOP(renesas_rz_probe) TARGET_PROBE_WEAK_NOP(rp_probe) +TARGET_PROBE_WEAK_NOP(s32k3xx_probe) TARGET_PROBE_WEAK_NOP(sam3x_probe) TARGET_PROBE_WEAK_NOP(sam4l_probe) TARGET_PROBE_WEAK_NOP(samd_probe) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 6418c6e3682..78e89eafad8 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -79,6 +79,7 @@ bool nrf91_probe(target_s *target); bool renesas_ra_probe(target_s *target); bool renesas_rz_probe(target_s *target); bool rp_probe(target_s *target); +bool s32k3xx_probe(target_s *target); bool sam3x_probe(target_s *target); bool sam4l_probe(target_s *target); bool samd_probe(target_s *target); From d0234cdbd571fdeae6b2c78ee4ade6c5b7f48be2 Mon Sep 17 00:00:00 2001 From: Matthew Via Date: Fri, 20 Oct 2023 20:15:22 -0400 Subject: [PATCH 3/3] s32k3xx: support probing and flash erase/write for S32K344 --- src/target/s32k3xx.c | 229 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 224 insertions(+), 5 deletions(-) diff --git a/src/target/s32k3xx.c b/src/target/s32k3xx.c index da68793cd14..afdde965746 100644 --- a/src/target/s32k3xx.c +++ b/src/target/s32k3xx.c @@ -1,8 +1,8 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2015 Black Sphere Technologies Ltd. - * Written by Gareth McMullin + * Copyright (C) 2023 1BitSquared + * Written by Matthew Via * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,6 +33,88 @@ #include "cortexm.h" #define SIUL2_MIDR1 0x40290004U +#define MDMAPCTL 0x40250604U + +#define C40ASF_MCR 0x402ec000U +#define C40ASF_MCR_PGM (1U << 8U) +#define C40ASF_MCR_ERS (1U << 4U) +#define C40ASF_MCR_EHV (1U << 0U) + +#define C40ASF_MCRS 0x402ec004U +#define C40ASF_MCRS_PEP (1U << 17U) +#define C40ASF_MCRS_PES (1U << 16U) +#define C40ASF_MCRS_DONE (1U << 15U) +#define C40ASF_MCRS_PEG (1U << 14U) + +#define C40ASF_PEADR 0x402ec014U +#define C40ASF_DATA0 0x402ec100U +#define C40ASF_DATA1 0x402ec104U +#define PFCPGM_PEADR_L 0x40268300U +#define PFCBLKU_SPELOCK 0x40268358U + +#define PFCBLK0_SSPELOCK 0x4026835cU +#define PFCBLK1_SSPELOCK 0x40268360U +#define PFCBLK2_SSPELOCK 0x40268364U +#define PFCBLK3_SSPELOCK 0x40268368U + +#define PFCBLK0_SPELOCK 0x40268340U +#define PFCBLK1_SPELOCK 0x40268344U +#define PFCBLK2_SPELOCK 0x40268348U +#define PFCBLK3_SPELOCK 0x4026834cU +#define PFCBLK4_SPELOCK 0x40268350U + +#define PAGE_SIZE 32U +#define QUAD_PAGE_SIZE 128U +#define SECTOR_SIZE 8192U +#define SUPER_SECTOR_SIZE 65536U + +static inline uint32_t C40ASF_DATA_REG(const uint32_t x) +{ + return 0x402ec100U + (4U * x); +} + +static inline uint32_t C40ASF_SSPELOCK_REG(const uint32_t block) +{ + return PFCBLK0_SSPELOCK + (4U * block); +} + +static inline uint32_t C40ASF_SPELOCK_REG(const uint32_t block) +{ + return PFCBLK0_SPELOCK + (4U * block); +} + +static bool s32k3xx_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len); +static bool s32k3xx_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len); +static bool s32k3xx_unlock_address(target_flash_s *const flash, target_addr_t addr); +static bool s32k3xx_flash_trigger_mcr(target_flash_s *const flash, uint32_t mcr_bits); +static void s32k3xx_flash_prepare(target_flash_s *const flash); +static void s32k3xx_reset(target_s *target); + +typedef struct s32k3xx_flash { + target_flash_s flash; + uint8_t block; +} s32k3xx_flash_s; + +static void s32k3xx_add_flash( + target_s *const target, const uint32_t addr, const size_t length, const size_t erasesize, const uint8_t block) +{ + s32k3xx_flash_s *s32_flash = calloc(1, sizeof(*s32_flash)); + if (!s32_flash) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + + target_flash_s *flash = &s32_flash->flash; + flash->start = addr; + flash->length = length; + flash->blocksize = erasesize; + flash->writesize = 128U; + flash->erase = s32k3xx_flash_erase; + flash->write = s32k3xx_flash_write; + flash->erased = 0xffU; + s32_flash->block = block; + target_add_flash(target, flash); +} bool s32k3xx_probe(target_s *const target) { @@ -44,9 +126,146 @@ bool s32k3xx_probe(target_s *const target) return false; switch (part_no) { - default: - return false; - } + case 0x158U: /* S32K344 */ + target->driver = "S32K344"; + target_add_ram(target, 0x20400000U, 0x00050000U); + target_add_ram(target, 0x00000000U, 0x00010000U); + target_add_ram(target, 0x20000000U, 0x00020000U); + s32k3xx_add_flash(target, 0x00400000U, 0x00100000U, 0x2000U, 0U); + s32k3xx_add_flash(target, 0x00500000U, 0x00100000U, 0x2000U, 1U); + s32k3xx_add_flash(target, 0x00600000U, 0x00100000U, 0x2000U, 2U); + s32k3xx_add_flash(target, 0x00700000U, 0x00100000U, 0x2000U, 3U); + s32k3xx_add_flash(target, 0x10000000U, 0x00020000U, 0x2000U, 4U); + break; + } + target->unsafe_enabled = false; + target->target_options |= TOPT_INHIBIT_NRST; + target->extended_reset = s32k3xx_reset; + return true; +} + +static bool s32k3xx_unlock_address(target_flash_s *const flash, target_addr_t addr) +{ + s32k3xx_flash_s *const s32flash = (s32k3xx_flash_s *)flash; + + /* Single (8KB) sector size locks are used only for the last 256 KB of a + * block, and are the only type of lock if the block is less than 256 KB */ + target_addr_t start_of_single_sectors; + if (flash->length < (256U * 1024U)) + start_of_single_sectors = flash->start; + else + start_of_single_sectors = flash->start + flash->length - (256U * 1024U); + + if (addr >= start_of_single_sectors) { + /* Use 8KB sectors */ + uint8_t sector = (addr - start_of_single_sectors) / SECTOR_SIZE; + uint32_t spelock_reg = C40ASF_SPELOCK_REG(s32flash->block); + + uint32_t spelock_val = target_mem_read32(flash->t, spelock_reg); + spelock_val &= ~(1U << sector); + target_mem_write32(flash->t, spelock_reg, spelock_val); + } else { + /* Use super sector unlock */ + uint8_t supersector = (addr - flash->start) / SUPER_SECTOR_SIZE; + uint32_t sspelock_reg = C40ASF_SSPELOCK_REG(s32flash->block); + uint32_t sspelock_val = target_mem_read32(flash->t, sspelock_reg); + sspelock_val &= ~(1U << supersector); + target_mem_write32(flash->t, sspelock_reg, sspelock_val); + } return true; } + +static bool s32k3xx_flash_trigger_mcr(target_flash_s *const flash, uint32_t mcr_bits) +{ + uint32_t mcr = target_mem_read32(flash->t, C40ASF_MCR); + mcr |= mcr_bits; + target_mem_write32(flash->t, C40ASF_MCR, mcr); + + /* Set EVH to trigger operation */ + mcr |= C40ASF_MCR_EHV; + target_mem_write32(flash->t, C40ASF_MCR, mcr); + + /* Wait for DONE to be set. + * According to section 9.1 of S32KXX DS, lifetime max times for: + * Quad-page program: 450 uS + * 8 KB sector erase: 30 ms (typ 8.5), + * First wait 1 ms, then wait 10 ms at a time until we timeout + */ + platform_timeout_s wait_timeout; + platform_timeout_set(&wait_timeout, 60); + platform_delay(1); + while ( + !(target_mem_read32(flash->t, C40ASF_MCRS) & C40ASF_MCRS_DONE) && !platform_timeout_is_expired(&wait_timeout)) + platform_delay(10); + + if (!(target_mem_read32(flash->t, C40ASF_MCRS) & C40ASF_MCRS_DONE)) { + DEBUG_ERROR("MCRS[DONE] not set after operation\n"); + return false; + } + + /* Clear the EVH bit first */ + mcr = target_mem_read32(flash->t, C40ASF_MCR); + mcr &= ~C40ASF_MCR_EHV; + target_mem_write32(flash->t, C40ASF_MCR, mcr); + + uint32_t mcrs = target_mem_read32(flash->t, C40ASF_MCRS); + + /* Then clear the operation bits */ + mcr &= ~mcr_bits; + target_mem_write32(flash->t, C40ASF_MCR, mcr); + + if ((mcrs & C40ASF_MCRS_PEG) == 0U) { + DEBUG_ERROR("MCRS[PEG] not set after operation\n"); + return false; + } + + if ((mcrs & 0xffff0000U) > 0U) { + DEBUG_ERROR("Operation failed, MCRS: %x\n", mcrs); + return false; + } + return true; +} + +static void s32k3xx_flash_prepare(target_flash_s *const flash) +{ + uint32_t mcrs = target_mem_read32(flash->t, C40ASF_MCRS); + mcrs |= C40ASF_MCRS_PEP | C40ASF_MCRS_PES; + target_mem_write32(flash->t, C40ASF_MCRS, mcrs); +} + +static bool s32k3xx_flash_erase(target_flash_s *const flash, target_addr_t addr, size_t len) +{ + (void)len; + s32k3xx_flash_prepare(flash); + s32k3xx_unlock_address(flash, addr); + + target_mem_write32(flash->t, PFCPGM_PEADR_L, addr); + target_mem_write32(flash->t, C40ASF_DATA0, 0U); + if (!s32k3xx_flash_trigger_mcr(flash, C40ASF_MCR_ERS)) + return false; + + return true; +} + +static bool s32k3xx_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len) +{ + assert(len == flash->writesize); + + const uint32_t *const s_data = src; + s32k3xx_flash_prepare(flash); + target_mem_write32(flash->t, PFCPGM_PEADR_L, dest); + for (size_t i = 0; i < len; i += 4) { + const size_t word = i / 4; + target_mem_write32(flash->t, C40ASF_DATA_REG(word), s_data[word]); + } + + if (!s32k3xx_flash_trigger_mcr(flash, C40ASF_MCR_PGM)) + return false; + return true; +} + +static void s32k3xx_reset(target_s *target) +{ + target_mem_write32(target, CORTEXM_AIRCR, CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_VECTRESET); +}