From 8de1b45c85686749659e46712b658f83c4b6f35f Mon Sep 17 00:00:00 2001 From: newbrain Date: Fri, 22 Jun 2018 15:44:17 +0200 Subject: [PATCH] Kinetis KE04: Flash and debug support Support for Kinetis KE04 8KiB, 64KiB and 128KiB variants in nxpke04.c Target monitor commands for sector and mass erase. Changes to kinetis.c MDM-AP target to support KE04. Only KE04Z8 tested in HW. --- src/Makefile | 1 + src/target/cortexm.c | 1 + src/target/kinetis.c | 25 ++- src/target/nxpke04.c | 375 +++++++++++++++++++++++++++++++++++ src/target/target_internal.h | 2 +- 5 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 src/target/nxpke04.c diff --git a/src/Makefile b/src/Makefile index c4e0bd7..def78d5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -48,6 +48,7 @@ SRC = \ morse.c \ msp432.c \ nrf51.c \ + nxpke04.c \ platform.c \ sam3x.c \ sam4l.c \ diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 120dbc2..6ffdfb4 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -344,6 +344,7 @@ bool cortexm_probe(ADIv5_AP_t *ap, bool forced) PROBE(kinetis_probe); PROBE(efm32_probe); PROBE(msp432_probe); + PROBE(ke04_probe); PROBE(lpc17xx_probe); #undef PROBE diff --git a/src/target/kinetis.c b/src/target/kinetis.c index d9f4d0a..f1f39c5 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -335,9 +335,11 @@ static int kl_gen_flash_done(struct target_flash *f) #define KINETIS_MDM_IDR_KZ03 0x1c0020 static bool kinetis_mdm_cmd_erase_mass(target *t); +static bool kinetis_mdm_cmd_ke04_mode(target *t); 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} }; @@ -355,7 +357,7 @@ enum target_halt_reason mdm_halt_poll(target *t, target_addr *watch) void kinetis_mdm_probe(ADIv5_AP_t *ap) { switch(ap->idr) { - case KINETIS_MDM_IDR_KZ03: + case KINETIS_MDM_IDR_KZ03: /* Also valid for KE04, no way to check! */ case KINETIS_MDM_IDR_K22F: break; default: @@ -392,21 +394,40 @@ void kinetis_mdm_probe(ADIv5_AP_t *ap) #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 ke04_mode = false; +static bool kinetis_mdm_cmd_ke04_mode(target *t) +{ + /* Set a flag to ignore part of the status and assert reset */ + ke04_mode = true; + tc_printf(t, "Mass erase for KE04 now allowed\n"); + return true; +} static bool kinetis_mdm_cmd_erase_mass(target *t) { ADIv5_AP_t *ap = t->priv; + /* Keep the MCU in reset as stated in KL25PxxM48SF0RM */ + if(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); - if (!(status & MDM_STATUS_MASS_ERASE_ENABLED)) { + /* This flag does not exist on KE04 */ + if (!(status & MDM_STATUS_MASS_ERASE_ENABLED) && !ke04_mode) { tc_printf(t, "ERROR: Mass erase disabled!\n"); return false; } + /* Flag is not persistent */ + ke04_mode = false; + if (!(status & MDM_STATUS_FLASH_READY)) { tc_printf(t, "ERROR: Flash not ready!\n"); return false; diff --git a/src/target/nxpke04.c b/src/target/nxpke04.c new file mode 100644 index 0000000..25ee1a4 --- /dev/null +++ b/src/target/nxpke04.c @@ -0,0 +1,375 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2015 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * Copyright (C) 2018 newbrain + * + * 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 KE04 target specific functions providing + * the XML memory map and Flash memory programming. + * + * An additional command to manually erase a single sector is also provided + * + * While very similar to other Kinetis, the differences in the Flash Module + * registers and the security byte warrant a separate set of routines. + * + * According to Freescale document MKE04P24M48SF0RM and MKE04P80M48SF0RM: + * KE04 Sub-Family Reference Manual + * + * And document MKE04P24M48SF0 and MKE04P80M48SF0: + * KE04 Sub-Family Data Sheet + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" + +/* KE04 registers and constants */ + +/* Memory base addresses */ +#define RAM_BASE_ADDR 0x20000000u +#define FLASH_BASE_ADDR 0x00000000u + +/* ID register and related constants */ +#define SIM_SRSID 0x40048000u +#define SRSID_KE04_MASK 0xFF00u +#define SRSID_KE04_FAMILY 0x0400u +#define SRSID_PIN_MASK 0x000Fu +#define SRSID_PIN__8 0x0000u +#define SRSID_PIN_16 0x0001u +#define SRSID_PIN_20 0x0002u +#define SRSID_PIN_24 0x0003u +#define SRSID_PIN_32 0x0004u +#define SRSID_PIN_44 0x0005u +#define SRSID_PIN_48 0x0006u +#define SRSID_PIN_64 0x0007u +#define SRSID_PIN_80 0x0008u +#define SRSID_PIN100 0x000Au + +/* Flash Memory Module registers */ +#define FTMRE_BASE 0x40020000u +#define FTMRE_FCCOBIX (FTMRE_BASE + 0x01u) +#define FTMRE_FSEC (FTMRE_BASE + 0x02u) +#define FTMRE_FCLKDIV (FTMRE_BASE + 0x03u) +#define FTMRE_FSTAT (FTMRE_BASE + 0x05u) +#define FTMRE_FCNFG (FTMRE_BASE + 0x07u) +#define FTMRE_FCCOB (FTMRE_BASE + 0x08u) +#define FTMRE_FCCOBLO (FTMRE_BASE + 0x08u) +#define FTMRE_FCCOBHI (FTMRE_BASE + 0x09u) +#define FTMRE_FPROT (FTMRE_BASE + 0x0Bu) +#define FTMRE_FOPT (FTMRE_BASE + 0x0Fu) + +/* FTMRE_FSTAT flags */ +#define FTMRE_FSTAT_CCIF 0x80u +#define FTMRE_FSTAT_ACCERR 0x20u +#define FTMRE_FSTAT_FPVIOL 0x10u +#define FTMRE_FSTAT_MGBUSY 0x08u +#define FTMRE_FSTAT_MGSTAT1 0x02u +#define FTMRE_FSTAT_MGSTAT0 0x01u + +/* Flash Memory Module commands */ +#define CMD_PROGRAM_FLASH_32 0x00u /* Special placeholder */ +#define CMD_ERASE_VERIFY_ALL_BLOCKS 0x01u /* Unused */ +#define CMD_ERASE_VERIFY_BLOCK 0x02u /* Unused */ +#define CMD_ERASE_VERIFY_SECTION 0x03u /* Unused */ +#define CMD_READ_ONCE 0x04u /* Unused */ +#define CMD_PROGRAM_FLASH 0x06u /* Used */ +#define CMD_PROGRAM_ONCE 0x07u /* Unused */ +#define CMD_ERASE_ALL_BLOCKS 0x08u /* Unused */ +#define CMD_ERASE_FLASH_BLOCK 0x09u /* Unused */ +#define CMD_ERASE_FLASH_SECTOR 0x0Au /* Used */ +#define CMD_UNSECURE_FLASH 0x0Bu /* Used */ +#define CMD_VERIFY_BACKDOOR_ACCESS_KEY 0x0Cu /* Unused */ +#define CMD_SET_USER_MARGIN_LEVEL 0x0Du /* Unused */ +#define CMD_SET_FACTORY_MARGIN_LEVEL 0x0Eu /* Unused */ + +/* Flash Memory Module write and erase sizes */ +#define KE04_WRITE_LEN 8 +#define KE04_SECTOR_SIZE 0x200u + +/* Security byte */ +#define FLASH_SECURITY_BYTE_ADDRESS 0x0000040Eu +#define FLASH_SECURITY_BYTE_UNSECURED 0xFEu +#define FLASH_SECURITY_WORD_ADDRESS 0x0000040Cu +#define FLASH_SECURITY_WORD_UNSECURED 0xFFFEFFFFu + + +/* Length in 16bit words of flash commands */ +static const uint8_t cmdLen[] = { + 4, 1, 2, 3, 6, 0, 6, 6, 1, 2, 2, 1, 5, 3, 3}; + +/* Flash routines */ +static bool ke04_command(target *t, uint8_t cmd, uint32_t addr, const uint8_t data[8]); +static int ke04_flash_erase(struct target_flash *f, target_addr addr, size_t len); +static int ke04_flash_write(struct target_flash *f, + target_addr dest, const void *src, size_t len); + +static int ke04_flash_done(struct target_flash *f); + +/* Target specific commands */ +static bool kinetis_cmd_unsafe(target *t, int argc, char *argv[]); +static bool ke04_cmd_sector_erase(target *t, int argc, char *argv[]); +static bool ke04_cmd_mass_erase(target *t, int argc, char *argv[]); +static bool unsafe_enabled; + +const struct command_s ke_cmd_list[] = { + {"unsafe", (cmd_handler)kinetis_cmd_unsafe, "Allow programming security byte (enable|disable)"}, + {"sector_erase", (cmd_handler)ke04_cmd_sector_erase, "Erase sector containing given address"}, + {"mass_erase", (cmd_handler)ke04_cmd_mass_erase, "Erase the whole flash"}, + {NULL, NULL, NULL} +}; + +static bool ke04_cmd_sector_erase(target *t, int argc, char *argv[]) +{ + if (argc < 2) + tc_printf(t, "usage: monitor sector_erase \n"); + + char *eos; + struct target_flash *f = t->flash; + uint32_t addr = strtoul(argv[1], &eos, 0); + + /* check that addr is a valid number and inside the flash range */ + if ((*eos != 0) || (addr < f->start) || (addr >= f->start+f->length)) { + /* Address not in range */ + tc_printf(t, "Invalid sector address\n"); + return false; + } + + /* Erase and verify the given sector */ + ke04_command(t, CMD_ERASE_FLASH_SECTOR, addr, NULL); + /* Adjust security byte if needed */ + ke04_flash_done(f); + return true; +} + +static bool ke04_cmd_mass_erase(target *t, int argc, char *argv[]) +{ + (void)argc; + (void)argv; + /* Erase and verify the whole flash */ + ke04_command(t, CMD_ERASE_ALL_BLOCKS, 0, NULL); + /* Adjust security byte if needed */ + ke04_flash_done(t->flash); + return true; +} + +static bool kinetis_cmd_unsafe(target *t, int argc, char *argv[]) +{ + if (argc == 1) + tc_printf(t, "Allow programming security byte: %s\n", + unsafe_enabled ? "enabled" : "disabled"); + else + unsafe_enabled = argv[1][0] == 'e'; + return true; +} + +bool ke04_probe(target *t) +{ + uint32_t ramsize; + uint32_t flashsize; + volatile uint32_t dummy; + + /* Read the higher 16bits of System Reset Status and ID Register */ + uint32_t srsid = target_mem_read32(t, SIM_SRSID) >> 16; + + /* Is this a Kinetis KE04 family MCU? */ + if ((srsid & SRSID_KE04_MASK) != SRSID_KE04_FAMILY) + return false; + + /* KE04Z8 only comes in 16, 20, and 24 pins */ + switch (srsid & SRSID_PIN_MASK) { + case SRSID_PIN_16: + case SRSID_PIN_20: + case SRSID_PIN_24: + /* We have a KE04Z8 */ + t->driver = "Kinetis KE04Z8Vxxxx"; + flashsize = 0x2000u; /* 8 kilobytes */ + ramsize = 0x400u; /* 1 kilobyte */ + break; + + /* KE04Z64 and KE04Z128 only come in 44, 64, and 80 pins */ + case SRSID_PIN_44: + case SRSID_PIN_64: + case SRSID_PIN_80: + /* We have either a KE04Z64 or 128 */ + /* Try to read a flash address not available in a Z64 */ + dummy = target_mem_read32(t, 0x00010000u); + (void)dummy; + if (target_check_error(t)) { + /* Read failed: we have a 64 */ + t->driver = "Kinetis KE04Z64Vxxxx"; + flashsize = 0x10000u; /* 64 kilobytes */ + ramsize = 0x2000u; /* 8 kilobytes */ + } else { + /* Read succeeded: we have a 128 */ + t->driver = "Kinetis KE04Z128Vxxxx"; + flashsize = 0x20000u; /* 128 kilobytes */ + ramsize = 0x4000u; /* 16 kilobytes */ + } + break; + + /* Unknown number of pins, not a supported KE04 */ + default: + return false; + } + + /* Add low (1/4) and high (3/4) RAM */ + ramsize /= 4; /* Amount before RAM_BASE_ADDR */ + target_add_ram(t, RAM_BASE_ADDR - ramsize, ramsize); /* Lower RAM */ + ramsize *= 3; /* Amount after RAM_BASE_ADDR */ + target_add_ram(t, RAM_BASE_ADDR, ramsize); /* Higher RAM */ + + /* Add flash, all KE04 have same write and erase size */ + struct target_flash *f = calloc(1, sizeof(*f)); + f->start = FLASH_BASE_ADDR; + f->length = flashsize; + f->blocksize = KE04_SECTOR_SIZE; + f->erase = ke04_flash_erase; + f->write = ke04_flash_write; + f->done = ke04_flash_done; + f->erased = 0xFFu; + target_add_flash(t, f); + + /* Add target specific commands */ + unsafe_enabled = false; + target_add_commands(t, ke_cmd_list, t->driver); + + return true; +} + +static bool +ke04_command(target *t, uint8_t cmd, uint32_t addr, const uint8_t data[8]) +{ + uint8_t fstat; + + /* Set FCLKDIV to 0x17 for 24MHz (default at reset) */ + uint8_t fclkdiv = target_mem_read8(t, FTMRE_FCLKDIV); + if( (fclkdiv & 0x1Fu) != 0x17u ) { + /* Wait for CCIF to be high */ + do { + fstat = target_mem_read8(t, FTMRE_FSTAT); + } while (!(fstat & FTMRE_FSTAT_CCIF)); + /* Write correct value */ + target_mem_write8(t, FTMRE_FCLKDIV, 0x17u); + } + + /* clear errors unconditionally, so we can start a new operation */ + target_mem_write8(t,FTMRE_FSTAT,(FTMRE_FSTAT_ACCERR | FTMRE_FSTAT_FPVIOL)); + + /* Wait for CCIF to be high */ + do { + fstat = target_mem_read8(t, FTMRE_FSTAT); + } while (!(fstat & FTMRE_FSTAT_CCIF)); + + /* Write the flash command and the needed parameters */ + uint8_t fccobix = 0; + /* Trim address, probably not needed */ + addr &= 0x00FFFFFF; + uint8_t cmdL = cmdLen[cmd]; + /* Special case: single 32bits word flashing */ + if(cmd == CMD_PROGRAM_FLASH_32) + cmd = CMD_PROGRAM_FLASH; + uint16_t cmdV = (cmd << 8) | (addr >> 16); + /* Write command to FCCOB array */ + target_mem_write8(t, FTMRE_FCCOBIX, fccobix++); + target_mem_write16(t, FTMRE_FCCOB, cmdV); + + /* Write first argument (low partof address) */ + if (cmdL >= 1) { + target_mem_write8(t, FTMRE_FCCOBIX, fccobix++); + target_mem_write16(t, FTMRE_FCCOB, addr & 0xFFFFu); + } + + /* Write one or two 32 bit words of data */ + uint8_t dataix = 0; + for ( ; fccobix < cmdL; fccobix++) { + target_mem_write8(t, FTMRE_FCCOBIX, fccobix); + target_mem_write16(t, FTMRE_FCCOB, *(uint16_t *)&data[dataix]); + dataix += 2; + } + + /* Enable execution by clearing CCIF */ + target_mem_write8(t, FTMRE_FSTAT, FTMRE_FSTAT_CCIF); + + /* Wait for execution to complete */ + do { + fstat = target_mem_read8(t, FTMRE_FSTAT); + /* Check ACCERR and FPVIOL are zero in FSTAT */ + if (fstat & (FTMRE_FSTAT_ACCERR | FTMRE_FSTAT_FPVIOL)) { + return false; + } + } while (!(fstat & FTMRE_FSTAT_CCIF)); + + return true; +} + +static int ke04_flash_erase(struct target_flash *f, target_addr addr, size_t len) +{ + while (len) { + if (ke04_command(f->t, CMD_ERASE_FLASH_SECTOR, addr, NULL)) { + /* Different targets have different flash erase sizes */ + len -= f->blocksize; + addr += f->blocksize; + } else { + return 1; + } + } + return 0; +} + +static int ke04_flash_write(struct target_flash *f, + target_addr dest, const void *src, size_t len) +{ + /* Ensure we don't write something horrible over the security byte */ + if (!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; + } + + while (len) { + if (ke04_command(f->t, CMD_PROGRAM_FLASH, dest, src)) { + len -= KE04_WRITE_LEN; + dest += KE04_WRITE_LEN; + src += KE04_WRITE_LEN; + } else { + return 1; + } + } + return 0; +} + +static int ke04_flash_done(struct target_flash *f) +{ + if (unsafe_enabled) + return 0; + + if (target_mem_read8(f->t, FLASH_SECURITY_BYTE_ADDRESS) == + FLASH_SECURITY_BYTE_UNSECURED) + return 0; + + /* Load the security byte from its field */ + /* Note: Cumulative programming is not allowed according to the RM */ + uint32_t val = target_mem_read32(f->t, FLASH_SECURITY_WORD_ADDRESS); + val = (val & 0xff00ffff) | (FLASH_SECURITY_BYTE_UNSECURED << 16); + ke04_command(f->t, CMD_PROGRAM_FLASH_32, + FLASH_SECURITY_WORD_ADDRESS, (uint8_t *)&val); + + return 0; +} diff --git a/src/target/target_internal.h b/src/target/target_internal.h index a81581b..6c18d07 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -178,5 +178,5 @@ bool samd_probe(target *t); bool kinetis_probe(target *t); bool efm32_probe(target *t); bool msp432_probe(target *t); - +bool ke04_probe(target *t); #endif