586 lines
18 KiB
C
586 lines
18 KiB
C
/*
|
|
* This file is part of the Black Magic Debug project.
|
|
*
|
|
* Copyright (C) 2015 Black Sphere Technologies Ltd.
|
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* This file implements KL25 target specific functions providing
|
|
* the XML memory map and Flash memory programming.
|
|
*
|
|
* According to Freescale doc KL25P80M48SF0RM:
|
|
* KL25 Sub-family Reference Manual
|
|
*
|
|
* Extended with support for KL02 family
|
|
*
|
|
* Extended with support for K64 family with info from K22P64M50SF4RM:
|
|
* K22 Sub-Family Reference Manual
|
|
*
|
|
* Extended with support for K64 family with info from K64P144M120SF5RM:
|
|
* K64 Sub-Family Reference Manual, Rev. 2,
|
|
*/
|
|
|
|
#include "command.h"
|
|
#include "general.h"
|
|
#include "target.h"
|
|
#include "target_internal.h"
|
|
|
|
#define SIM_SDID 0x40048024
|
|
#define SIM_FCFG1 0x4004804C
|
|
|
|
#define FTFA_BASE 0x40020000
|
|
#define FTFA_FSTAT (FTFA_BASE + 0x00)
|
|
#define FTFA_FCNFG (FTFA_BASE + 0x01)
|
|
#define FTFA_FSEC (FTFA_BASE + 0x02)
|
|
#define FTFA_FOPT (FTFA_BASE + 0x03)
|
|
#define FTFA_FCCOB_0 (FTFA_BASE + 0x04)
|
|
#define FTFA_FCCOB_1 (FTFA_BASE + 0x08)
|
|
#define FTFA_FCCOB_2 (FTFA_BASE + 0x0C)
|
|
|
|
#define FTFA_FSTAT_CCIF (1 << 7)
|
|
#define FTFA_FSTAT_RDCOLERR (1 << 6)
|
|
#define FTFA_FSTAT_ACCERR (1 << 5)
|
|
#define FTFA_FSTAT_FPVIOL (1 << 4)
|
|
#define FTFA_FSTAT_MGSTAT0 (1 << 0)
|
|
|
|
#define FTFA_CMD_CHECK_ERASE 0x01
|
|
#define FTFA_CMD_PROGRAM_CHECK 0x02
|
|
#define FTFA_CMD_READ_RESOURCE 0x03
|
|
#define FTFA_CMD_PROGRAM_LONGWORD 0x06
|
|
/* Part of the FTFE module for K64 */
|
|
#define FTFE_CMD_PROGRAM_PHRASE 0x07
|
|
#define FTFA_CMD_ERASE_SECTOR 0x09
|
|
#define FTFA_CMD_CHECK_ERASE_ALL 0x40
|
|
#define FTFA_CMD_READ_ONCE 0x41
|
|
#define FTFA_CMD_PROGRAM_ONCE 0x43
|
|
#define FTFA_CMD_ERASE_ALL 0x44
|
|
#define FTFA_CMD_BACKDOOR_ACCESS 0x45
|
|
|
|
#define KL_WRITE_LEN 4
|
|
/* 8 byte phrases need to be written to the k64 flash */
|
|
#define K64_WRITE_LEN 8
|
|
|
|
static bool kinetis_cmd_unsafe(target *t, int argc, char *argv[]);
|
|
|
|
const struct command_s kinetis_cmd_list[] = {
|
|
{"unsafe", (cmd_handler)kinetis_cmd_unsafe, "Allow programming security byte (enable|disable)"},
|
|
{NULL, NULL, NULL}
|
|
};
|
|
|
|
static bool kinetis_cmd_unsafe(target *t, int argc, char *argv[])
|
|
{
|
|
if (argc == 1) {
|
|
tc_printf(t, "Allow programming security byte: %s\n",
|
|
t->unsafe_enabled ? "enabled" : "disabled");
|
|
} else {
|
|
parse_enable_or_disable(argv[1], &t->unsafe_enabled);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t len);
|
|
static int kl_gen_flash_write(struct target_flash *f,
|
|
target_addr dest, const void *src, size_t len);
|
|
static int kl_gen_flash_done(struct target_flash *f);
|
|
|
|
struct kinetis_flash {
|
|
struct target_flash f;
|
|
uint8_t write_len;
|
|
};
|
|
|
|
static void kl_gen_add_flash(target *t, uint32_t addr, size_t length,
|
|
size_t erasesize, size_t write_len)
|
|
{
|
|
struct kinetis_flash *kf = calloc(1, sizeof(*kf));
|
|
struct target_flash *f;
|
|
|
|
if (!kf) { /* calloc failed: heap exhaustion */
|
|
DEBUG_WARN("calloc: failed in %s\n", __func__);
|
|
return;
|
|
}
|
|
|
|
f = &kf->f;
|
|
f->start = addr;
|
|
f->length = length;
|
|
f->blocksize = erasesize;
|
|
f->erase = kl_gen_flash_erase;
|
|
f->write = kl_gen_flash_write;
|
|
f->done = kl_gen_flash_done;
|
|
f->erased = 0xff;
|
|
kf->write_len = write_len;
|
|
target_add_flash(t, f);
|
|
}
|
|
|
|
bool kinetis_probe(target *t)
|
|
{
|
|
uint32_t sdid = target_mem_read32(t, SIM_SDID);
|
|
uint32_t fcfg1 = target_mem_read32(t, SIM_FCFG1);
|
|
|
|
switch (sdid >> 20) {
|
|
case 0x161:
|
|
/* sram memory size */
|
|
switch((sdid >> 16) & 0x0f) {
|
|
case 0x03:/* 4 KB */
|
|
target_add_ram(t, 0x1ffffc00, 0x0400);
|
|
target_add_ram(t, 0x20000000, 0x0C00);
|
|
break;
|
|
case 0x04:/* 8 KB */
|
|
target_add_ram(t, 0x1ffff800, 0x0800);
|
|
target_add_ram(t, 0x20000000, 0x1800);
|
|
break;
|
|
case 0x05:/* 16 KB */
|
|
target_add_ram(t, 0x1ffff000, 0x1000);
|
|
target_add_ram(t, 0x20000000, 0x3000);
|
|
break;
|
|
case 0x06:/* 32 KB */
|
|
target_add_ram(t, 0x1fffe000, 0x2000);
|
|
target_add_ram(t, 0x20000000, 0x6000);
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
/* flash memory size */
|
|
switch((fcfg1 >> 24) & 0x0f) {
|
|
case 0x03: /* 32 KB */
|
|
t->driver = "KL16Z32Vxxx";
|
|
kl_gen_add_flash(t, 0x00000000, 0x08000, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
|
|
case 0x05: /* 64 KB */
|
|
t->driver = "KL16Z64Vxxx";
|
|
kl_gen_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
|
|
case 0x07: /* 128 KB */
|
|
t->driver = "KL16Z128Vxxx";
|
|
kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
|
|
case 0x09: /* 256 KB */
|
|
t->driver = "KL16Z256Vxxx";
|
|
kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x251:
|
|
t->driver = "KL25";
|
|
target_add_ram(t, 0x1ffff000, 0x1000);
|
|
target_add_ram(t, 0x20000000, 0x3000);
|
|
kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
case 0x231:
|
|
t->driver = "KL27x128"; // MKL27 >=128kb
|
|
target_add_ram(t, 0x1fffe000, 0x2000);
|
|
target_add_ram(t, 0x20000000, 0x6000);
|
|
kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
case 0x271:
|
|
switch((sdid >> 16) & 0x0f) {
|
|
case 4:
|
|
t->driver = "KL27x32";
|
|
target_add_ram(t, 0x1ffff800, 0x0800);
|
|
target_add_ram(t, 0x20000000, 0x1800);
|
|
kl_gen_add_flash(t, 0x00000000, 0x8000, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
case 5:
|
|
t->driver = "KL27x64";
|
|
target_add_ram(t, 0x1ffff000, 0x1000);
|
|
target_add_ram(t, 0x20000000, 0x3000);
|
|
kl_gen_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case 0x021: /* KL02 family */
|
|
switch((sdid >> 16) & 0x0f) {
|
|
case 3:
|
|
t->driver = "KL02x32";
|
|
target_add_ram(t, 0x1FFFFC00, 0x400);
|
|
target_add_ram(t, 0x20000000, 0xc00);
|
|
kl_gen_add_flash(t, 0x00000000, 0x7FFF, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
case 2:
|
|
t->driver = "KL02x16";
|
|
target_add_ram(t, 0x1FFFFE00, 0x200);
|
|
target_add_ram(t, 0x20000000, 0x600);
|
|
kl_gen_add_flash(t, 0x00000000, 0x3FFF, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
case 1:
|
|
t->driver = "KL02x8";
|
|
target_add_ram(t, 0x1FFFFF00, 0x100);
|
|
target_add_ram(t, 0x20000000, 0x300);
|
|
kl_gen_add_flash(t, 0x00000000, 0x1FFF, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case 0x031: /* KL03 family */
|
|
t->driver = "KL03";
|
|
target_add_ram(t, 0x1ffffe00, 0x200);
|
|
target_add_ram(t, 0x20000000, 0x600);
|
|
kl_gen_add_flash(t, 0, 0x8000, 0x400, KL_WRITE_LEN);
|
|
break;
|
|
case 0x220: /* K22F family */
|
|
t->driver = "K22F";
|
|
target_add_ram(t, 0x1c000000, 0x4000000);
|
|
target_add_ram(t, 0x20000000, 0x100000);
|
|
kl_gen_add_flash(t, 0, 0x40000, 0x800, KL_WRITE_LEN);
|
|
kl_gen_add_flash(t, 0x40000, 0x40000, 0x800, KL_WRITE_LEN);
|
|
break;
|
|
case 0x620: /* K64F family. */
|
|
/* This should be 0x640, but according to the errata sheet
|
|
* (KINETIS_1N83J) K64 and K24's will show up with the
|
|
* subfamily nibble as 2
|
|
*/
|
|
t->driver = "K64";
|
|
target_add_ram(t, 0x1FFF0000, 0x10000);
|
|
target_add_ram(t, 0x20000000, 0x30000);
|
|
kl_gen_add_flash(t, 0, 0x80000, 0x1000, K64_WRITE_LEN);
|
|
kl_gen_add_flash(t, 0x80000, 0x80000, 0x1000, K64_WRITE_LEN);
|
|
break;
|
|
case 0x000: /* Older K-series */
|
|
switch(sdid & 0xff0) {
|
|
case 0x000: /* K10 Family, DIEID=0x0 */
|
|
case 0x080: /* K10 Family, DIEID=0x1 */
|
|
case 0x100: /* K10 Family, DIEID=0x2 */
|
|
case 0x180: /* K10 Family, DIEID=0x3 */
|
|
case 0x220: /* K11 Family, DIEID=0x4 */
|
|
return false;
|
|
case 0x200: /* K12 Family, DIEID=0x4 */
|
|
switch((fcfg1 >> 24) & 0x0f) {
|
|
/* K12 Sub-Family Reference Manual, K12P80M50SF4RM, Rev. 4, February 2013 */
|
|
case 0x7:
|
|
t->driver = "MK12DX128Vxx5";
|
|
target_add_ram(t, 0x1fffc000, 0x00004000); /* SRAM_L, 16 KB */
|
|
target_add_ram(t, 0x20000000, 0x00004000); /* SRAM_H, 16 KB */
|
|
kl_gen_add_flash(t, 0x00000000, 0x00020000, 0x800, KL_WRITE_LEN); /* P-Flash, 128 KB, 2 KB Sectors */
|
|
kl_gen_add_flash(t, 0x10000000, 0x00010000, 0x800, KL_WRITE_LEN); /* FlexNVM, 64 KB, 2 KB Sectors */
|
|
break;
|
|
case 0x9:
|
|
t->driver = "MK12DX256Vxx5";
|
|
target_add_ram(t, 0x1fffc000, 0x00004000); /* SRAM_L, 16 KB */
|
|
target_add_ram(t, 0x20000000, 0x00004000); /* SRAM_H, 16 KB */
|
|
kl_gen_add_flash(t, 0x00000000, 0x00040000, 0x800, KL_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
|
kl_gen_add_flash(t, 0x10000000, 0x00010000, 0x800, KL_WRITE_LEN); /* FlexNVM, 64 KB, 2 KB Sectors */
|
|
break;
|
|
case 0xb:
|
|
t->driver = "MK12DN512Vxx5";
|
|
target_add_ram(t, 0x1fff8000, 0x00008000); /* SRAM_L, 32 KB */
|
|
target_add_ram(t, 0x20000000, 0x00008000); /* SRAM_H, 32 KB */
|
|
kl_gen_add_flash(t, 0x00000000, 0x00040000, 0x800, KL_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
|
kl_gen_add_flash(t, 0x00040000, 0x00040000, 0x800, KL_WRITE_LEN); /* FlexNVM, 256 KB, 2 KB Sectors */
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case 0x010: /* K20 Family, DIEID=0x0 */
|
|
case 0x090: /* K20 Family, DIEID=0x1 */
|
|
case 0x110: /* K20 Family, DIEID=0x2 */
|
|
case 0x190: /* K20 Family, DIEID=0x3 */
|
|
case 0x230: /* K21 Family, DIEID=0x4 */
|
|
case 0x330: /* K21 Family, DIEID=0x6 */
|
|
case 0x210: /* K22 Family, DIEID=0x4 */
|
|
case 0x310: /* K22 Family, DIEID=0x6 */
|
|
case 0x0a0: /* K30 Family, DIEID=0x1 */
|
|
case 0x120: /* K30 Family, DIEID=0x2 */
|
|
case 0x0b0: /* K40 Family, DIEID=0x1 */
|
|
case 0x130: /* K40 Family, DIEID=0x2 */
|
|
case 0x0e0: /* K50 Family, DIEID=0x1 */
|
|
case 0x0f0: /* K51 Family, DIEID=0x1 */
|
|
case 0x170: /* K53 Family, DIEID=0x2 */
|
|
case 0x140: /* K60 Family, DIEID=0x2 */
|
|
case 0x1c0: /* K60 Family, DIEID=0x3 */
|
|
case 0x1d0: /* K70 Family, DIEID=0x3 */
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case 0x118: /* S32K118 */
|
|
t->driver = "S32K118";
|
|
target_add_ram(t, 0x1ffffc00, 0x00000400); /* SRAM_L, 1 KB */
|
|
target_add_ram(t, 0x20000000, 0x00005800); /* SRAM_H, 22 KB */
|
|
kl_gen_add_flash(t, 0x00000000, 0x00040000, 0x800, K64_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
|
kl_gen_add_flash(t, 0x10000000, 0x00008000, 0x800, K64_WRITE_LEN); /* FlexNVM, 32 KB, 2 KB Sectors */
|
|
break;
|
|
case 0x148: /* S32K148 */
|
|
t->driver = "S32K148";
|
|
target_add_ram(t, 0x1FFE0000, 0x20000); /* SRAM_L, 128 KB */
|
|
target_add_ram(t, 0x20000000, 0x1f000); /* SRAM_H, 124 KB */
|
|
kl_gen_add_flash(t, 0x00000000, 0x00180000, 0x1000, K64_WRITE_LEN); /* P-Flash, 1536 KB, 4 KB Sectors */
|
|
kl_gen_add_flash(t, 0x10000000, 0x80000, 0x1000, K64_WRITE_LEN); /* FlexNVM, 512 KB, 4 KB Sectors */
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
t->unsafe_enabled = false;
|
|
target_add_commands(t, kinetis_cmd_list, t->driver);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
kl_gen_command(target *t, uint8_t cmd, uint32_t addr, const uint8_t data[8])
|
|
{
|
|
uint8_t fstat;
|
|
|
|
/* clear errors unconditionally, so we can start a new operation */
|
|
target_mem_write8(t,FTFA_FSTAT,(FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL));
|
|
|
|
/* Wait for CCIF to be high */
|
|
do {
|
|
fstat = target_mem_read8(t, FTFA_FSTAT);
|
|
} while (!(fstat & FTFA_FSTAT_CCIF));
|
|
|
|
/* Write command to FCCOB */
|
|
addr &= 0xffffff;
|
|
addr |= (uint32_t)cmd << 24;
|
|
target_mem_write32(t, FTFA_FCCOB_0, addr);
|
|
if (data) {
|
|
target_mem_write32(t, FTFA_FCCOB_1, *(uint32_t*)&data[0]);
|
|
target_mem_write32(t, FTFA_FCCOB_2, *(uint32_t*)&data[4]);
|
|
}
|
|
|
|
/* Enable execution by clearing CCIF */
|
|
target_mem_write8(t, FTFA_FSTAT, FTFA_FSTAT_CCIF);
|
|
|
|
/* Wait for execution to complete */
|
|
do {
|
|
fstat = target_mem_read8(t, FTFA_FSTAT);
|
|
/* Check ACCERR and FPVIOL are zero in FSTAT */
|
|
if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL))
|
|
return false;
|
|
} while (!(fstat & FTFA_FSTAT_CCIF));
|
|
|
|
return true;
|
|
}
|
|
|
|
static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t len)
|
|
{
|
|
while (len) {
|
|
if (kl_gen_command(f->t, FTFA_CMD_ERASE_SECTOR, addr, NULL)) {
|
|
/* Different targets have different flash erase sizes */
|
|
if (len > f->blocksize)
|
|
len -= f->blocksize;
|
|
else
|
|
len = 0;
|
|
addr += f->blocksize;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define FLASH_SECURITY_BYTE_ADDRESS 0x40C
|
|
#define FLASH_SECURITY_BYTE_UNSECURED 0xFE
|
|
|
|
static int kl_gen_flash_write(struct target_flash *f,
|
|
target_addr dest, const void *src, size_t len)
|
|
{
|
|
struct kinetis_flash *kf = (struct kinetis_flash *)f;
|
|
|
|
/* Ensure we don't write something horrible over the security byte */
|
|
if (!f->t->unsafe_enabled &&
|
|
(dest <= FLASH_SECURITY_BYTE_ADDRESS) &&
|
|
((dest + len) > FLASH_SECURITY_BYTE_ADDRESS)) {
|
|
((uint8_t*)src)[FLASH_SECURITY_BYTE_ADDRESS - dest] =
|
|
FLASH_SECURITY_BYTE_UNSECURED;
|
|
}
|
|
|
|
/* Determine write command based on the alignment. */
|
|
uint8_t write_cmd;
|
|
if (kf->write_len == K64_WRITE_LEN) {
|
|
write_cmd = FTFE_CMD_PROGRAM_PHRASE;
|
|
} else {
|
|
write_cmd = FTFA_CMD_PROGRAM_LONGWORD;
|
|
}
|
|
|
|
while (len) {
|
|
if (kl_gen_command(f->t, write_cmd, dest, src)) {
|
|
if (len > kf->write_len)
|
|
len -= kf->write_len;
|
|
else
|
|
len = 0;
|
|
dest += kf->write_len;
|
|
src += kf->write_len;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int kl_gen_flash_done(struct target_flash *f)
|
|
{
|
|
struct kinetis_flash *kf = (struct kinetis_flash *)f;
|
|
|
|
if (f->t->unsafe_enabled)
|
|
return 0;
|
|
|
|
if (target_mem_read8(f->t, FLASH_SECURITY_BYTE_ADDRESS) ==
|
|
FLASH_SECURITY_BYTE_UNSECURED)
|
|
return 0;
|
|
|
|
/* Load the security byte based on the alignment (determine 8 byte phrases
|
|
* vs 4 byte phrases).
|
|
*/
|
|
if (kf->write_len == 8) {
|
|
uint32_t vals[2];
|
|
vals[0] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS-4);
|
|
vals[1] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS);
|
|
vals[1] = (vals[1] & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED;
|
|
kl_gen_command(f->t, FTFE_CMD_PROGRAM_PHRASE,
|
|
FLASH_SECURITY_BYTE_ADDRESS - 4, (uint8_t*)vals);
|
|
} else {
|
|
uint32_t val = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS);
|
|
val = (val & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED;
|
|
kl_gen_command(f->t, FTFA_CMD_PROGRAM_LONGWORD,
|
|
FLASH_SECURITY_BYTE_ADDRESS, (uint8_t*)&val);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*** Kinetis recovery mode using the MDM-AP ***/
|
|
|
|
/* Kinetis security bits are stored in regular flash, so it is possible
|
|
* to enable protection by accident when flashing a bad binary.
|
|
* a backdoor AP is provided which may allow a mass erase to recover the
|
|
* device. This provides a fake target to allow a monitor command interface
|
|
*/
|
|
#include "adiv5.h"
|
|
|
|
#define KINETIS_MDM_IDR_K22F 0x1c0000
|
|
#define KINETIS_MDM_IDR_KZ03 0x1c0020
|
|
|
|
static bool kinetis_mdm_cmd_erase_mass(target *t, int argc, const char **argv);
|
|
static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv);
|
|
|
|
const struct command_s kinetis_mdm_cmd_list[] = {
|
|
{"erase_mass", (cmd_handler)kinetis_mdm_cmd_erase_mass, "Erase entire flash memory"},
|
|
{"ke04_mode", (cmd_handler)kinetis_mdm_cmd_ke04_mode, "Allow erase for KE04"},
|
|
{NULL, NULL, NULL}
|
|
};
|
|
|
|
enum target_halt_reason mdm_halt_poll(target *t, target_addr *watch)
|
|
{
|
|
(void)t; (void)watch;
|
|
return TARGET_HALT_REQUEST;
|
|
}
|
|
|
|
void kinetis_mdm_probe(ADIv5_AP_t *ap)
|
|
{
|
|
switch(ap->idr) {
|
|
case KINETIS_MDM_IDR_KZ03: /* Also valid for KE04, no way to check! */
|
|
case KINETIS_MDM_IDR_K22F:
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
target *t = target_new();
|
|
if (!t) {
|
|
return;
|
|
}
|
|
|
|
adiv5_ap_ref(ap);
|
|
t->priv = ap;
|
|
t->priv_free = (void*)adiv5_ap_unref;
|
|
|
|
t->driver = "Kinetis Recovery (MDM-AP)";
|
|
t->regs_size = 4;
|
|
target_add_commands(t, kinetis_mdm_cmd_list, t->driver);
|
|
}
|
|
|
|
#define MDM_STATUS ADIV5_AP_REG(0x00)
|
|
#define MDM_CONTROL ADIV5_AP_REG(0x04)
|
|
|
|
#define MDM_STATUS_MASS_ERASE_ACK (1 << 0)
|
|
#define MDM_STATUS_FLASH_READY (1 << 1)
|
|
#define MDM_STATUS_MASS_ERASE_ENABLED (1 << 5)
|
|
|
|
#define MDM_CONTROL_MASS_ERASE (1 << 0)
|
|
#define MDM_CONTROL_SYS_RESET (1 << 3)
|
|
|
|
/* This is needed as a separate command, as there's no way to *
|
|
* tell a KE04 from other kinetis in kinetis_mdm_probe() */
|
|
static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv)
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
/* Set a flag to ignore part of the status and assert reset */
|
|
t->ke04_mode = true;
|
|
tc_printf(t, "Mass erase for KE04 now allowed\n");
|
|
return true;
|
|
}
|
|
static bool kinetis_mdm_cmd_erase_mass(target *t, int argc, const char **argv)
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
ADIv5_AP_t *ap = t->priv;
|
|
|
|
/* Keep the MCU in reset as stated in KL25PxxM48SF0RM */
|
|
if(t->ke04_mode)
|
|
adiv5_ap_write(ap, MDM_CONTROL, MDM_CONTROL_SYS_RESET);
|
|
|
|
uint32_t status, control;
|
|
status = adiv5_ap_read(ap, MDM_STATUS);
|
|
control = adiv5_ap_read(ap, MDM_CONTROL);
|
|
tc_printf(t, "Requesting mass erase (status = 0x%"PRIx32")\n", status);
|
|
|
|
/* This flag does not exist on KE04 */
|
|
if (!(status & MDM_STATUS_MASS_ERASE_ENABLED) && !t->ke04_mode) {
|
|
tc_printf(t, "ERROR: Mass erase disabled!\n");
|
|
return false;
|
|
}
|
|
|
|
/* Flag is not persistent */
|
|
t->ke04_mode = false;
|
|
|
|
if (!(status & MDM_STATUS_FLASH_READY)) {
|
|
tc_printf(t, "ERROR: Flash not ready!\n");
|
|
return false;
|
|
}
|
|
|
|
if (status & MDM_STATUS_MASS_ERASE_ACK) {
|
|
tc_printf(t, "ERROR: Mass erase already in progress!\n");
|
|
return false;
|
|
}
|
|
|
|
adiv5_ap_write(ap, MDM_CONTROL, MDM_CONTROL_MASS_ERASE);
|
|
|
|
do {
|
|
status = adiv5_ap_read(ap, MDM_STATUS);
|
|
} while (!(status & MDM_STATUS_MASS_ERASE_ACK));
|
|
tc_printf(t, "Mass erase acknowledged\n");
|
|
|
|
do {
|
|
control = adiv5_ap_read(ap, MDM_CONTROL);
|
|
} while (!(control & MDM_CONTROL_MASS_ERASE));
|
|
tc_printf(t, "Mass erase complete\n");
|
|
|
|
return true;
|
|
}
|