-
-
Notifications
You must be signed in to change notification settings - Fork 781
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
s32k3xx: support probing and flash erase/write for S32K344
- Loading branch information
Showing
1 changed file
with
224 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <[email protected]> | ||
* Copyright (C) 2023 1BitSquared <[email protected]> | ||
* Written by Matthew Via <[email protected]> | ||
* | ||
* 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 |= CORTEXM_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); | ||
} |