#include "jtag/interface.h"
#include "imp.h"
#include <helper/binarybuffer.h>
+#include <helper/time_support.h>
#include <target/target_type.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
*/
/* Addressess */
+#define FCF_ADDRESS 0x00000400
+#define FCF_FPROT 0x8
+#define FCF_FSEC 0xc
+#define FCF_FOPT 0xd
+#define FCF_FDPROT 0xf
+#define FCF_SIZE 0x10
+
#define FLEXRAM 0x14000000
#define FMC_PFB01CR 0x4001f004
#define WDOG_STCTRH 0x40052000
#define SMC_PMCTRL 0x4007E001
#define SMC_PMSTAT 0x4007E003
+#define MCM_PLACR 0xF000300C
/* Values */
#define PM_STAT_RUN 0x01
FS_PROGRAM_SECTOR = 1,
FS_PROGRAM_LONGWORD = 2,
FS_PROGRAM_PHRASE = 4, /* Unsupported */
- FS_INVALIDATE_CACHE = 8,
+ FS_INVALIDATE_CACHE_K = 8,
+ FS_INVALIDATE_CACHE_L = 0x10,
} flash_support;
};
+#define MDM_AP 1
+
#define MDM_REG_STAT 0x00
#define MDM_REG_CTRL 0x04
#define MDM_REG_ID 0xfc
#define MDM_CTRL_VLLSX_DBG_ACK (1<<6)
#define MDM_CTRL_VLLSX_STAT_ACK (1<<7)
-#define MDM_ACCESS_TIMEOUT 3000 /* iterations */
+#define MDM_ACCESS_TIMEOUT 500 /* msec */
+
+
+static bool allow_fcf_writes;
+static uint8_t fcf_fopt = 0xff;
+
+
+struct flash_driver kinetis_flash;
+static int kinetis_write_inner(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count);
+static int kinetis_auto_probe(struct flash_bank *bank);
+
static int kinetis_mdm_write_register(struct adiv5_dap *dap, unsigned reg, uint32_t value)
{
int retval;
LOG_DEBUG("MDM_REG[0x%02x] <- %08" PRIX32, reg, value);
- retval = dap_queue_ap_write(dap_ap(dap, 1), reg, value);
+ retval = dap_queue_ap_write(dap_ap(dap, MDM_AP), reg, value);
if (retval != ERROR_OK) {
LOG_DEBUG("MDM: failed to queue a write request");
return retval;
{
int retval;
- retval = dap_queue_ap_read(dap_ap(dap, 1), reg, result);
+ retval = dap_queue_ap_read(dap_ap(dap, MDM_AP), reg, result);
if (retval != ERROR_OK) {
LOG_DEBUG("MDM: failed to queue a read request");
return retval;
return ERROR_OK;
}
-static int kinetis_mdm_poll_register(struct adiv5_dap *dap, unsigned reg, uint32_t mask, uint32_t value)
+static int kinetis_mdm_poll_register(struct adiv5_dap *dap, unsigned reg,
+ uint32_t mask, uint32_t value, uint32_t timeout_ms)
{
uint32_t val;
int retval;
- int timeout = MDM_ACCESS_TIMEOUT;
+ int64_t ms_timeout = timeval_ms() + timeout_ms;
do {
retval = kinetis_mdm_read_register(dap, reg, &val);
return retval;
alive_sleep(1);
- } while (timeout--);
+ } while (timeval_ms() < ms_timeout);
LOG_DEBUG("MDM: polling timed out");
return ERROR_FAIL;
int retval;
int tries = 0;
uint32_t stat;
+ int64_t ms_timeout = timeval_ms() + MDM_ACCESS_TIMEOUT;
if (!dap) {
LOG_ERROR("Cannot perform halt with a high-level adapter");
== (MDM_STAT_FREADY | MDM_STAT_SYSRES))
break;
- if (tries > MDM_ACCESS_TIMEOUT) {
+ if (timeval_ms() >= ms_timeout) {
LOG_ERROR("MDM: halt timed out");
return ERROR_FAIL;
}
return retval;
}
- retval = kinetis_mdm_poll_register(dap, MDM_REG_STAT, MDM_STAT_SYSRES, 0);
+ retval = kinetis_mdm_poll_register(dap, MDM_REG_STAT, MDM_STAT_SYSRES, 0, 500);
if (retval != ERROR_OK) {
LOG_ERROR("MDM: failed to assert reset");
return retval;
}
/*
- * ... Read the MDM-AP status register Mass Erase Enable bit to
- * determine if the mass erase command is enabled. If Mass Erase
- * Enable = 0, then mass erase is disabled and the processor
- * cannot be erased or unsecured. If Mass Erase Enable = 1, then
- * the mass erase command can be used...
+ * ... Read the MDM-AP status register repeatedly and wait for
+ * stable conditions suitable for mass erase:
+ * - mass erase is enabled
+ * - flash is ready
+ * - reset is finished
+ *
+ * Mass erase is started as soon as all conditions are met in 32
+ * subsequent status reads.
+ *
+ * In case of not stable conditions (RESET/WDOG loop in secured device)
+ * the user is asked for manual pressing of RESET button
+ * as a last resort.
*/
- uint32_t stat;
+ int cnt_mass_erase_disabled = 0;
+ int cnt_ready = 0;
+ int64_t ms_start = timeval_ms();
+ bool man_reset_requested = false;
- retval = kinetis_mdm_read_register(dap, MDM_REG_STAT, &stat);
- if (retval != ERROR_OK) {
- LOG_ERROR("MDM: failed to read MDM_REG_STAT");
- goto deassert_reset_and_exit;
- }
+ do {
+ uint32_t stat = 0;
+ int64_t ms_elapsed = timeval_ms() - ms_start;
- if (!(stat & MDM_STAT_FMEEN)) {
- LOG_ERROR("MDM: mass erase is disabled");
- goto deassert_reset_and_exit;
- }
+ if (!man_reset_requested && ms_elapsed > 100) {
+ LOG_INFO("MDM: Press RESET button now if possible.");
+ man_reset_requested = true;
+ }
- if ((stat & MDM_STAT_SYSSEC) && !(jtag_get_reset_config() & RESET_HAS_SRST)) {
- LOG_ERROR("Mass erase of a secured MCU is not possible without hardware reset.");
- LOG_INFO("Connect SRST and use 'reset_config srst_only'.");
- goto deassert_reset_and_exit;
- }
+ if (ms_elapsed > 3000) {
+ LOG_ERROR("MDM: waiting for mass erase conditions timed out.");
+ LOG_INFO("Mass erase of a secured MCU is not possible without hardware reset.");
+ LOG_INFO("Connect SRST, use 'reset_config srst_only' and retry.");
+ goto deassert_reset_and_exit;
+ }
+ retval = kinetis_mdm_read_register(dap, MDM_REG_STAT, &stat);
+ if (retval != ERROR_OK) {
+ cnt_ready = 0;
+ continue;
+ }
- /*
- * ... Read the MDM-AP status register until the Flash Ready bit sets
- * and System Reset is asserted...
- */
- retval = kinetis_mdm_poll_register(dap, MDM_REG_STAT,
- MDM_STAT_FREADY | MDM_STAT_SYSRES,
- MDM_STAT_FREADY);
- if (retval != ERROR_OK) {
- LOG_ERROR("MDM: flash ready / system reset timeout");
- goto deassert_reset_and_exit;
- }
+ if (!(stat & MDM_STAT_FMEEN)) {
+ cnt_ready = 0;
+ cnt_mass_erase_disabled++;
+ if (cnt_mass_erase_disabled > 10) {
+ LOG_ERROR("MDM: mass erase is disabled");
+ goto deassert_reset_and_exit;
+ }
+ continue;
+ }
+
+ if ((stat & (MDM_STAT_FREADY | MDM_STAT_SYSRES)) == MDM_STAT_FREADY)
+ cnt_ready++;
+ else
+ cnt_ready = 0;
+
+ } while (cnt_ready < 32);
/*
* ... Write the MDM-AP control register to set the Flash Mass
/*
* ... Read the MDM-AP control register until the Flash Mass
* Erase in Progress bit clears...
+ * Data sheed defines erase time <3.6 sec/512kB flash block.
+ * The biggest device has 4 pflash blocks => timeout 16 sec.
*/
- retval = kinetis_mdm_poll_register(dap, MDM_REG_CTRL, MDM_CTRL_FMEIP, 0);
+ retval = kinetis_mdm_poll_register(dap, MDM_REG_CTRL, MDM_CTRL_FMEIP, 0, 16000);
if (retval != ERROR_OK) {
LOG_ERROR("MDM: mass erase timeout");
goto deassert_reset_and_exit;
return ERROR_OK;
}
+ if (!dap->ops)
+ return ERROR_OK; /* too early to check, in JTAG mode ops may not be initialised */
+
uint32_t val;
int retval;
retval = kinetis_mdm_read_register(dap, MDM_REG_ID, &val);
if (retval != ERROR_OK) {
LOG_ERROR("MDM: failed to read ID register");
- goto fail;
+ return ERROR_OK;
}
+ if (val == 0)
+ return ERROR_OK; /* dap not yet initialised */
+
bool found = false;
for (size_t i = 0; i < ARRAY_SIZE(kinetis_known_mdm_ids); i++) {
if (val == kinetis_known_mdm_ids[i]) {
if (!found)
LOG_WARNING("MDM: unknown ID %08" PRIX32, val);
- /*
- * ... Read the MDM-AP status register until the Flash Ready bit sets...
- */
- retval = kinetis_mdm_poll_register(dap, MDM_REG_STAT,
- MDM_STAT_FREADY,
- MDM_STAT_FREADY);
- if (retval != ERROR_OK) {
- LOG_ERROR("MDM: flash ready timeout");
- goto fail;
- }
-
/*
* ... Read the System Security bit to determine if security is enabled.
* If System Security = 0, then proceed. If System Security = 1, then
retval = kinetis_mdm_read_register(dap, MDM_REG_STAT, &val);
if (retval != ERROR_OK) {
LOG_ERROR("MDM: failed to read MDM_REG_STAT");
- goto fail;
+ return ERROR_OK;
}
- if ((val & (MDM_STAT_SYSSEC | MDM_STAT_CORE_HALTED)) == MDM_STAT_SYSSEC) {
- LOG_WARNING("MDM: Secured MCU state detected however it may be a false alarm");
- LOG_WARNING("MDM: Halting target to detect secured state reliably");
+ /*
+ * System Security bit is also active for short time during reset.
+ * If a MCU has blank flash and runs in RESET/WDOG loop,
+ * System Security bit is active most of time!
+ * We should observe Flash Ready bit and read status several times
+ * to avoid false detection of secured MCU
+ */
+ int secured_score = 0, flash_not_ready_score = 0;
- retval = target_halt(target);
- if (retval == ERROR_OK)
- retval = target_wait_state(target, TARGET_HALTED, 100);
+ if ((val & (MDM_STAT_SYSSEC | MDM_STAT_FREADY)) != MDM_STAT_FREADY) {
+ uint32_t stats[32];
+ int i;
- if (retval != ERROR_OK) {
- LOG_WARNING("MDM: Target not halted, trying reset halt");
- target->reset_halt = true;
- target->type->assert_reset(target);
- target->type->deassert_reset(target);
+ for (i = 0; i < 32; i++) {
+ stats[i] = MDM_STAT_FREADY;
+ dap_queue_ap_read(dap_ap(dap, MDM_AP), MDM_REG_STAT, &stats[i]);
}
-
- /* re-read status */
- retval = kinetis_mdm_read_register(dap, MDM_REG_STAT, &val);
+ retval = dap_run(dap);
if (retval != ERROR_OK) {
- LOG_ERROR("MDM: failed to read MDM_REG_STAT");
- goto fail;
+ LOG_DEBUG("MDM: dap_run failed when validating secured state");
+ return ERROR_OK;
+ }
+ for (i = 0; i < 32; i++) {
+ if (stats[i] & MDM_STAT_SYSSEC)
+ secured_score++;
+ if (!(stats[i] & MDM_STAT_FREADY))
+ flash_not_ready_score++;
}
}
- if (val & MDM_STAT_SYSSEC) {
+ if (flash_not_ready_score <= 8 && secured_score > 24) {
jtag_poll_set_enabled(false);
LOG_WARNING("*********** ATTENTION! ATTENTION! ATTENTION! ATTENTION! **********");
LOG_WARNING("**** command, power cycle the MCU and restart OpenOCD. ****");
LOG_WARNING("**** ****");
LOG_WARNING("*********** ATTENTION! ATTENTION! ATTENTION! ATTENTION! **********");
+
+ } else if (flash_not_ready_score > 24) {
+ jtag_poll_set_enabled(false);
+ LOG_WARNING("**** Your Kinetis MCU is probably locked-up in RESET/WDOG loop. ****");
+ LOG_WARNING("**** Common reason is a blank flash (at least a reset vector). ****");
+ LOG_WARNING("**** Issue 'kinetis mdm halt' command or if SRST is connected ****");
+ LOG_WARNING("**** and configured, use 'reset halt' ****");
+ LOG_WARNING("**** If MCU cannot be halted, it is likely secured and running ****");
+ LOG_WARNING("**** in RESET/WDOG loop. Issue 'kinetis mdm mass_erase' ****");
+
} else {
LOG_INFO("MDM: Chip is unsecured. Continuing.");
jtag_poll_set_enabled(true);
}
return ERROR_OK;
-
-fail:
- LOG_ERROR("MDM: Failed to check security status of the MCU. Cannot proceed further");
- jtag_poll_set_enabled(false);
- return retval;
}
FLASH_BANK_COMMAND_HANDLER(kinetis_flash_bank_command)
}
+static int kinetis_ftfx_decode_error(uint8_t fstat)
+{
+ if (fstat & 0x20) {
+ LOG_ERROR("Flash operation failed, illegal command");
+ return ERROR_FLASH_OPER_UNSUPPORTED;
+
+ } else if (fstat & 0x10)
+ LOG_ERROR("Flash operation failed, protection violated");
+
+ else if (fstat & 0x40)
+ LOG_ERROR("Flash operation failed, read collision");
+
+ else if (fstat & 0x80)
+ return ERROR_OK;
+
+ else
+ LOG_ERROR("Flash operation timed out");
+
+ return ERROR_FLASH_OPERATION_FAILED;
+}
+
+
+static int kinetis_ftfx_prepare(struct target *target)
+{
+ int result, i;
+ uint8_t fstat;
+
+ /* wait until busy */
+ for (i = 0; i < 50; i++) {
+ result = target_read_u8(target, FTFx_FSTAT, &fstat);
+ if (result != ERROR_OK)
+ return result;
+
+ if (fstat & 0x80)
+ break;
+ }
+
+ if ((fstat & 0x80) == 0) {
+ LOG_ERROR("Flash controller is busy");
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ if (fstat != 0x80) {
+ /* reset error flags */
+ result = target_write_u8(target, FTFx_FSTAT, 0x70);
+ }
+ return result;
+}
+
/* Kinetis Program-LongWord Microcodes */
static const uint8_t kinetis_flash_write_code[] = {
- /* Params:
- * r0 - workarea buffer
- * r1 - target address
- * r2 - wordcount
- * Clobbered:
- * r4 - tmp
- * r5 - tmp
- * r6 - tmp
- * r7 - tmp
- */
-
- /* .L1: */
- /* for(register uint32_t i=0;i<wcount;i++){ */
- 0x04, 0x1C, /* mov r4, r0 */
- 0x00, 0x23, /* mov r3, #0 */
- /* .L2: */
- 0x0E, 0x1A, /* sub r6, r1, r0 */
- 0xA6, 0x19, /* add r6, r4, r6 */
- 0x93, 0x42, /* cmp r3, r2 */
- 0x16, 0xD0, /* beq .L9 */
- /* .L5: */
- /* while((FTFx_FSTAT&FTFA_FSTAT_CCIF_MASK) != FTFA_FSTAT_CCIF_MASK){}; */
- 0x0B, 0x4D, /* ldr r5, .L10 */
- 0x2F, 0x78, /* ldrb r7, [r5] */
- 0x7F, 0xB2, /* sxtb r7, r7 */
- 0x00, 0x2F, /* cmp r7, #0 */
- 0xFA, 0xDA, /* bge .L5 */
- /* FTFx_FSTAT = FTFA_FSTAT_ACCERR_MASK|FTFA_FSTAT_FPVIOL_MASK|FTFA_FSTAT_RDCO */
- 0x70, 0x27, /* mov r7, #112 */
- 0x2F, 0x70, /* strb r7, [r5] */
- /* FTFx_FCCOB3 = faddr; */
- 0x09, 0x4F, /* ldr r7, .L10+4 */
- 0x3E, 0x60, /* str r6, [r7] */
- 0x06, 0x27, /* mov r7, #6 */
- /* FTFx_FCCOB0 = 0x06; */
- 0x08, 0x4E, /* ldr r6, .L10+8 */
- 0x37, 0x70, /* strb r7, [r6] */
- /* FTFx_FCCOB7 = *pLW; */
- 0x80, 0xCC, /* ldmia r4!, {r7} */
- 0x08, 0x4E, /* ldr r6, .L10+12 */
- 0x37, 0x60, /* str r7, [r6] */
- /* FTFx_FSTAT = FTFA_FSTAT_CCIF_MASK; */
- 0x80, 0x27, /* mov r7, #128 */
- 0x2F, 0x70, /* strb r7, [r5] */
- /* .L4: */
- /* while((FTFx_FSTAT&FTFA_FSTAT_CCIF_MASK) != FTFA_FSTAT_CCIF_MASK){}; */
- 0x2E, 0x78, /* ldrb r6, [r5] */
- 0x77, 0xB2, /* sxtb r7, r6 */
- 0x00, 0x2F, /* cmp r7, #0 */
- 0xFB, 0xDA, /* bge .L4 */
- 0x01, 0x33, /* add r3, r3, #1 */
- 0xE4, 0xE7, /* b .L2 */
- /* .L9: */
- 0x00, 0xBE, /* bkpt #0 */
- /* .L10: */
- 0x00, 0x00, 0x02, 0x40, /* .word 1073872896 */
- 0x04, 0x00, 0x02, 0x40, /* .word 1073872900 */
- 0x07, 0x00, 0x02, 0x40, /* .word 1073872903 */
- 0x08, 0x00, 0x02, 0x40, /* .word 1073872904 */
+#include "../../../contrib/loaders/flash/kinetis/kinetis_flash.inc"
};
/* Program LongWord Block Write */
struct working_area *source;
struct kinetis_flash_bank *kinfo = bank->driver_priv;
uint32_t address = kinfo->prog_base + offset;
- struct reg_param reg_params[3];
+ uint32_t end_address;
+ struct reg_param reg_params[5];
struct armv7m_algorithm armv7m_info;
- int retval = ERROR_OK;
-
- /* Params:
- * r0 - workarea buffer
- * r1 - target address
- * r2 - wordcount
- * Clobbered:
- * r4 - tmp
- * r5 - tmp
- * r6 - tmp
- * r7 - tmp
- */
+ int retval;
+ uint8_t fstat;
/* Increase buffer_size if needed */
if (buffer_size < (target->working_area_size/2))
buffer_size = (target->working_area_size/2);
- LOG_INFO("Kinetis: FLASH Write ...");
-
- /* check code alignment */
- if (offset & 0x1) {
- LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
- return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
- }
-
/* allocate working area with flash programming code */
if (target_alloc_working_area(target, sizeof(kinetis_flash_write_code),
&write_algorithm) != ERROR_OK) {
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
- init_reg_param(®_params[0], "r0", 32, PARAM_OUT); /* *pLW (*buffer) */
- init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* faddr */
- init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* number of words to program */
+ init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); /* address */
+ init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* word count */
+ init_reg_param(®_params[2], "r2", 32, PARAM_OUT);
+ init_reg_param(®_params[3], "r3", 32, PARAM_OUT);
+ init_reg_param(®_params[4], "r4", 32, PARAM_OUT);
- /* write code buffer and use Flash programming code within kinetis */
- /* Set breakpoint to 0 with time-out of 1000 ms */
- while (wcount > 0) {
- uint32_t thisrun_count = (wcount > (buffer_size / 4)) ? (buffer_size / 4) : wcount;
+ buf_set_u32(reg_params[0].value, 0, 32, address);
+ buf_set_u32(reg_params[1].value, 0, 32, wcount);
+ buf_set_u32(reg_params[2].value, 0, 32, source->address);
+ buf_set_u32(reg_params[3].value, 0, 32, source->address + source->size);
+ buf_set_u32(reg_params[4].value, 0, 32, FTFx_FSTAT);
- retval = target_write_buffer(target, source->address, thisrun_count * 4, buffer);
- if (retval != ERROR_OK)
- break;
+ retval = target_run_flash_async_algorithm(target, buffer, wcount, 4,
+ 0, NULL,
+ 5, reg_params,
+ source->address, source->size,
+ write_algorithm->address, 0,
+ &armv7m_info);
- buf_set_u32(reg_params[0].value, 0, 32, source->address);
- buf_set_u32(reg_params[1].value, 0, 32, address);
- buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
+ if (retval == ERROR_FLASH_OPERATION_FAILED) {
+ end_address = buf_get_u32(reg_params[0].value, 0, 32);
- retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
- write_algorithm->address, 0, 100000, &armv7m_info);
- if (retval != ERROR_OK) {
- LOG_ERROR("Error executing kinetis Flash programming algorithm");
- retval = ERROR_FLASH_OPERATION_FAILED;
- break;
- }
+ LOG_ERROR("Error writing flash at %08" PRIx32, end_address);
- buffer += thisrun_count * 4;
- address += thisrun_count * 4;
- wcount -= thisrun_count;
- }
+ retval = target_read_u8(target, FTFx_FSTAT, &fstat);
+ if (retval == ERROR_OK) {
+ retval = kinetis_ftfx_decode_error(fstat);
+
+ /* reset error flags */
+ target_write_u8(target, FTFx_FSTAT, 0x70);
+ }
+ } else if (retval != ERROR_OK)
+ LOG_ERROR("Error executing kinetis Flash programming algorithm");
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
destroy_reg_param(®_params[0]);
destroy_reg_param(®_params[1]);
destroy_reg_param(®_params[2]);
+ destroy_reg_param(®_params[3]);
+ destroy_reg_param(®_params[4]);
return retval;
}
static int kinetis_protect(struct flash_bank *bank, int set, int first, int last)
{
- LOG_WARNING("kinetis_protect not supported yet");
- /* FIXME: TODO */
+ int i;
- if (bank->target->state != TARGET_HALTED) {
- LOG_ERROR("Target not halted");
- return ERROR_TARGET_NOT_HALTED;
+ if (allow_fcf_writes) {
+ LOG_ERROR("Protection setting is possible with 'kinetis fcf_source protection' only!");
+ return ERROR_FAIL;
+ }
+
+ if (!bank->prot_blocks || bank->num_prot_blocks == 0) {
+ LOG_ERROR("No protection possible for current bank!");
+ return ERROR_FLASH_BANK_INVALID;
}
- return ERROR_FLASH_BANK_INVALID;
+ for (i = first; i < bank->num_prot_blocks && i <= last; i++)
+ bank->prot_blocks[i].is_protected = set;
+
+ LOG_INFO("Protection bits will be written at the next FCF sector erase or write.");
+ LOG_INFO("Do not issue 'flash info' command until protection is written,");
+ LOG_INFO("doing so would re-read protection status from MCU.");
+
+ return ERROR_OK;
}
static int kinetis_protect_check(struct flash_bank *bank)
struct kinetis_flash_bank *kinfo = bank->driver_priv;
int result;
int i, b;
- uint32_t fprot, psec;
-
- if (bank->target->state != TARGET_HALTED) {
- LOG_ERROR("Target not halted");
- return ERROR_TARGET_NOT_HALTED;
- }
+ uint32_t fprot;
if (kinfo->flash_class == FC_PFLASH) {
- uint8_t buffer[4];
/* read protection register */
- result = target_read_memory(bank->target, FTFx_FPROT3, 1, 4, buffer);
-
+ result = target_read_u32(bank->target, FTFx_FPROT3, &fprot);
if (result != ERROR_OK)
return result;
- fprot = target_buffer_get_u32(bank->target, buffer);
/* Every bit protects 1/32 of the full flash (not necessarily just this bank) */
} else if (kinfo->flash_class == FC_FLEX_NVM) {
uint8_t fdprot;
/* read protection register */
- result = target_read_memory(bank->target, FTFx_FDPROT, 1, 1, &fdprot);
-
+ result = target_read_u8(bank->target, FTFx_FDPROT, &fdprot);
if (result != ERROR_OK)
return result;
}
b = kinfo->protection_block;
- for (psec = 0, i = 0; i < bank->num_sectors; i++) {
+ for (i = 0; i < bank->num_prot_blocks; i++) {
if ((fprot >> b) & 1)
- bank->sectors[i].is_protected = 0;
+ bank->prot_blocks[i].is_protected = 0;
else
- bank->sectors[i].is_protected = 1;
+ bank->prot_blocks[i].is_protected = 1;
+
+ b++;
+ }
+
+ return ERROR_OK;
+}
- psec += bank->sectors[i].size;
- if (psec >= kinfo->protection_size) {
- psec = 0;
- b++;
+static int kinetis_fill_fcf(struct flash_bank *bank, uint8_t *fcf)
+{
+ uint32_t fprot = 0xffffffff;
+ uint8_t fsec = 0xfe; /* set MCU unsecure */
+ uint8_t fdprot = 0xff;
+ int i;
+ uint32_t pflash_bit;
+ uint8_t dflash_bit;
+ struct flash_bank *bank_iter;
+ struct kinetis_flash_bank *kinfo;
+
+ memset(fcf, 0xff, FCF_SIZE);
+
+ pflash_bit = 1;
+ dflash_bit = 1;
+
+ /* iterate over all kinetis banks */
+ /* current bank is bank 0, it contains FCF */
+ for (bank_iter = bank; bank_iter; bank_iter = bank_iter->next) {
+ if (bank_iter->driver != &kinetis_flash
+ || bank_iter->target != bank->target)
+ continue;
+
+ kinetis_auto_probe(bank_iter);
+
+ kinfo = bank->driver_priv;
+ if (!kinfo)
+ continue;
+
+ if (kinfo->flash_class == FC_PFLASH) {
+ for (i = 0; i < bank_iter->num_prot_blocks; i++) {
+ if (bank_iter->prot_blocks[i].is_protected == 1)
+ fprot &= ~pflash_bit;
+
+ pflash_bit <<= 1;
+ }
+
+ } else if (kinfo->flash_class == FC_FLEX_NVM) {
+ for (i = 0; i < bank_iter->num_prot_blocks; i++) {
+ if (bank_iter->prot_blocks[i].is_protected == 1)
+ fdprot &= ~dflash_bit;
+
+ dflash_bit <<= 1;
+ }
+
}
}
+ target_buffer_set_u32(bank->target, fcf + FCF_FPROT, fprot);
+ fcf[FCF_FSEC] = fsec;
+ fcf[FCF_FOPT] = fcf_fopt;
+ fcf[FCF_FDPROT] = fdprot;
return ERROR_OK;
}
uint8_t command[12] = {faddr & 0xff, (faddr >> 8) & 0xff, (faddr >> 16) & 0xff, fcmd,
fccob7, fccob6, fccob5, fccob4,
fccobb, fccoba, fccob9, fccob8};
- int result, i;
- uint8_t buffer;
-
- /* wait for done */
- for (i = 0; i < 50; i++) {
- result =
- target_read_memory(target, FTFx_FSTAT, 1, 1, &buffer);
-
- if (result != ERROR_OK)
- return result;
-
- if (buffer & 0x80)
- break;
-
- buffer = 0x00;
- }
-
- if (buffer != 0x80) {
- /* reset error flags */
- buffer = 0x30;
- result =
- target_write_memory(target, FTFx_FSTAT, 1, 1, &buffer);
- if (result != ERROR_OK)
- return result;
- }
+ int result;
+ uint8_t fstat;
+ int64_t ms_timeout = timeval_ms() + 250;
result = target_write_memory(target, FTFx_FCCOB3, 4, 3, command);
-
if (result != ERROR_OK)
return result;
/* start command */
- buffer = 0x80;
- result = target_write_memory(target, FTFx_FSTAT, 1, 1, &buffer);
+ result = target_write_u8(target, FTFx_FSTAT, 0x80);
if (result != ERROR_OK)
return result;
/* wait for done */
- for (i = 0; i < 240; i++) { /* Need longtime for "Mass Erase" Command Nemui Changed */
- result =
- target_read_memory(target, FTFx_FSTAT, 1, 1, ftfx_fstat);
+ do {
+ result = target_read_u8(target, FTFx_FSTAT, &fstat);
if (result != ERROR_OK)
return result;
- if (*ftfx_fstat & 0x80)
+ if (fstat & 0x80)
break;
- }
- if ((*ftfx_fstat & 0xf0) != 0x80) {
- LOG_ERROR
- ("ftfx command failed FSTAT: %02X FCCOB: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
- *ftfx_fstat, command[3], command[2], command[1], command[0],
+ } while (timeval_ms() < ms_timeout);
+
+ if (ftfx_fstat)
+ *ftfx_fstat = fstat;
+
+ if ((fstat & 0xf0) != 0x80) {
+ LOG_DEBUG("ftfx command failed FSTAT: %02X FCCOB: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
+ fstat, command[3], command[2], command[1], command[0],
command[7], command[6], command[5], command[4],
command[11], command[10], command[9], command[8]);
- return ERROR_FLASH_OPERATION_FAILED;
+
+ return kinetis_ftfx_decode_error(fstat);
}
return ERROR_OK;
static void kinetis_invalidate_flash_cache(struct flash_bank *bank)
{
struct kinetis_flash_bank *kinfo = bank->driver_priv;
- uint8_t pfb01cr_byte2 = 0xf0;
- if (!(kinfo->flash_support & FS_INVALIDATE_CACHE))
- return;
+ if (kinfo->flash_support & FS_INVALIDATE_CACHE_K)
+ target_write_u8(bank->target, FMC_PFB01CR + 2, 0xf0);
+
+ else if (kinfo->flash_support & FS_INVALIDATE_CACHE_L)
+ target_write_u8(bank->target, MCM_PLACR + 1, 0x04);
- target_write_memory(bank->target, FMC_PFB01CR + 2, 1, 1, &pfb01cr_byte2);
return;
}
if (result != ERROR_OK)
return result;
+ /* reset error flags */
+ result = kinetis_ftfx_prepare(bank->target);
+ if (result != ERROR_OK)
+ return result;
+
if ((first > bank->num_sectors) || (last > bank->num_sectors))
return ERROR_FLASH_OPERATION_FAILED;
* block. Should be quicker.
*/
for (i = first; i <= last; i++) {
- uint8_t ftfx_fstat;
/* set command and sector address */
result = kinetis_ftfx_command(bank->target, FTFx_CMD_SECTERASE, kinfo->prog_base + bank->sectors[i].offset,
- 0, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
+ 0, 0, 0, 0, 0, 0, 0, 0, NULL);
if (result != ERROR_OK) {
LOG_WARNING("erase sector %d failed", i);
}
bank->sectors[i].is_erased = 1;
+
+ if (bank->base == 0
+ && bank->sectors[i].offset <= FCF_ADDRESS
+ && bank->sectors[i].offset + bank->sectors[i].size > FCF_ADDRESS + FCF_SIZE) {
+ if (allow_fcf_writes) {
+ LOG_WARNING("Flash Configuration Field erased, DO NOT reset or power off the device");
+ LOG_WARNING("until correct FCF is programmed or MCU gets security lock.");
+ } else {
+ uint8_t fcf_buffer[FCF_SIZE];
+
+ kinetis_fill_fcf(bank, fcf_buffer);
+ result = kinetis_write_inner(bank, fcf_buffer, FCF_ADDRESS, FCF_SIZE);
+ if (result != ERROR_OK)
+ LOG_WARNING("Flash Configuration Field write failed");
+ bank->sectors[i].is_erased = 0;
+ }
+ }
}
kinetis_invalidate_flash_cache(bank);
- if (first == 0) {
- LOG_WARNING
- ("flash configuration field erased, please reset the device");
- }
-
return ERROR_OK;
}
static int kinetis_make_ram_ready(struct target *target)
{
int result;
- uint8_t ftfx_fstat;
uint8_t ftfx_fcnfg;
/* check if ram ready */
- result = target_read_memory(target, FTFx_FCNFG, 1, 1, &ftfx_fcnfg);
+ result = target_read_u8(target, FTFx_FCNFG, &ftfx_fcnfg);
if (result != ERROR_OK)
return result;
/* make flex ram available */
result = kinetis_ftfx_command(target, FTFx_CMD_SETFLEXRAM, 0x00ff0000,
- 0, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
+ 0, 0, 0, 0, 0, 0, 0, 0, NULL);
if (result != ERROR_OK)
return ERROR_FLASH_OPERATION_FAILED;
/* check again */
- result = target_read_memory(target, FTFx_FCNFG, 1, 1, &ftfx_fcnfg);
+ result = target_read_u8(target, FTFx_FCNFG, &ftfx_fcnfg);
if (result != ERROR_OK)
return result;
return ERROR_FLASH_OPERATION_FAILED;
}
-static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer,
+
+static int kinetis_write_sections(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
- unsigned int i, result, fallback = 0;
- uint32_t wc;
+ int result = ERROR_OK;
struct kinetis_flash_bank *kinfo = bank->driver_priv;
- uint8_t *new_buffer = NULL;
+ uint8_t *buffer_aligned = NULL;
+ /*
+ * Kinetis uses different terms for the granularity of
+ * sector writes, e.g. "phrase" or "128 bits". We use
+ * the generic term "chunk". The largest possible
+ * Kinetis "chunk" is 16 bytes (128 bits).
+ */
+ uint32_t prog_section_chunk_bytes = kinfo->sector_size >> 8;
+ uint32_t prog_size_bytes = kinfo->max_flash_prog_size;
+
+ while (count > 0) {
+ uint32_t size = prog_size_bytes - offset % prog_size_bytes;
+ uint32_t align_begin = offset % prog_section_chunk_bytes;
+ uint32_t align_end;
+ uint32_t size_aligned;
+ uint16_t chunk_count;
+ uint8_t ftfx_fstat;
- result = kinetis_check_run_mode(bank->target);
- if (result != ERROR_OK)
- return result;
+ if (size > count)
+ size = count;
+
+ align_end = (align_begin + size) % prog_section_chunk_bytes;
+ if (align_end)
+ align_end = prog_section_chunk_bytes - align_end;
+
+ size_aligned = align_begin + size + align_end;
+ chunk_count = size_aligned / prog_section_chunk_bytes;
+
+ if (size != size_aligned) {
+ /* aligned section: the first, the last or the only */
+ if (!buffer_aligned)
+ buffer_aligned = malloc(prog_size_bytes);
+
+ memset(buffer_aligned, 0xff, size_aligned);
+ memcpy(buffer_aligned + align_begin, buffer, size);
+
+ result = target_write_memory(bank->target, FLEXRAM,
+ 4, size_aligned / 4, buffer_aligned);
+
+ LOG_DEBUG("section @ %08" PRIx32 " aligned begin %" PRIu32 ", end %" PRIu32,
+ bank->base + offset, align_begin, align_end);
+ } else
+ result = target_write_memory(bank->target, FLEXRAM,
+ 4, size_aligned / 4, buffer);
+
+ LOG_DEBUG("write section @ %08" PRIx32 " with length %" PRIu32 " bytes",
+ bank->base + offset, size);
+
+ if (result != ERROR_OK) {
+ LOG_ERROR("target_write_memory failed");
+ break;
+ }
+
+ /* execute section-write command */
+ result = kinetis_ftfx_command(bank->target, FTFx_CMD_SECTWRITE,
+ kinfo->prog_base + offset - align_begin,
+ chunk_count>>8, chunk_count, 0, 0,
+ 0, 0, 0, 0, &ftfx_fstat);
+
+ if (result != ERROR_OK) {
+ LOG_ERROR("Error writing section at %08" PRIx32, bank->base + offset);
+ break;
+ }
+
+ if (ftfx_fstat & 0x01)
+ LOG_ERROR("Flash write error at %08" PRIx32, bank->base + offset);
+
+ buffer += size;
+ offset += size;
+ count -= size;
+ }
+
+ free(buffer_aligned);
+ return result;
+}
+
+
+static int kinetis_write_inner(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ int result, fallback = 0;
+ struct kinetis_flash_bank *kinfo = bank->driver_priv;
if (!(kinfo->flash_support & FS_PROGRAM_SECTOR)) {
/* fallback to longword write */
}
}
- LOG_DEBUG("flash write @08%" PRIX32, offset);
-
+ LOG_DEBUG("flash write @08%" PRIx32, bank->base + offset);
- /* program section command */
if (fallback == 0) {
- /*
- * Kinetis uses different terms for the granularity of
- * sector writes, e.g. "phrase" or "128 bits". We use
- * the generic term "chunk". The largest possible
- * Kinetis "chunk" is 16 bytes (128 bits).
- */
- unsigned prog_section_chunk_bytes = kinfo->sector_size >> 8;
- unsigned prog_size_bytes = kinfo->max_flash_prog_size;
- for (i = 0; i < count; i += prog_size_bytes) {
- uint8_t residual_buffer[16];
- uint8_t ftfx_fstat;
- uint32_t section_count = prog_size_bytes / prog_section_chunk_bytes;
- uint32_t residual_wc = 0;
-
- /*
- * Assume the word count covers an entire
- * sector.
- */
- wc = prog_size_bytes / 4;
-
- /*
- * If bytes to be programmed are less than the
- * full sector, then determine the number of
- * full-words to program, and put together the
- * residual buffer so that a full "section"
- * may always be programmed.
- */
- if ((count - i) < prog_size_bytes) {
- /* number of bytes to program beyond full section */
- unsigned residual_bc = (count-i) % prog_section_chunk_bytes;
-
- /* number of complete words to copy directly from buffer */
- wc = (count - i - residual_bc) / 4;
-
- /* number of total sections to write, including residual */
- section_count = DIV_ROUND_UP((count-i), prog_section_chunk_bytes);
-
- /* any residual bytes delivers a whole residual section */
- residual_wc = (residual_bc ? prog_section_chunk_bytes : 0)/4;
-
- /* clear residual buffer then populate residual bytes */
- (void) memset(residual_buffer, 0xff, prog_section_chunk_bytes);
- (void) memcpy(residual_buffer, &buffer[i+4*wc], residual_bc);
- }
-
- LOG_DEBUG("write section @ %08" PRIX32 " with length %" PRIu32 " bytes",
- offset + i, (uint32_t)wc*4);
-
- /* write data to flexram as whole-words */
- result = target_write_memory(bank->target, FLEXRAM, 4, wc,
- buffer + i);
-
- if (result != ERROR_OK) {
- LOG_ERROR("target_write_memory failed");
- return result;
- }
-
- /* write the residual words to the flexram */
- if (residual_wc) {
- result = target_write_memory(bank->target,
- FLEXRAM+4*wc,
- 4, residual_wc,
- residual_buffer);
-
- if (result != ERROR_OK) {
- LOG_ERROR("target_write_memory failed");
- return result;
- }
- }
-
- /* execute section-write command */
- result = kinetis_ftfx_command(bank->target, FTFx_CMD_SECTWRITE, kinfo->prog_base + offset + i,
- section_count>>8, section_count, 0, 0,
- 0, 0, 0, 0, &ftfx_fstat);
-
- if (result != ERROR_OK)
- return ERROR_FLASH_OPERATION_FAILED;
- }
+ /* program section command */
+ kinetis_write_sections(bank, buffer, offset, count);
}
- /* program longword command, not supported in "SF3" devices */
else if (kinfo->flash_support & FS_PROGRAM_LONGWORD) {
+ /* program longword command, not supported in FTFE */
+ uint8_t *new_buffer = NULL;
+
+ /* check word alignment */
+ if (offset & 0x3) {
+ LOG_ERROR("offset 0x%" PRIx32 " breaks the required alignment", offset);
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+ }
+
if (count & 0x3) {
uint32_t old_count = count;
count = (old_count | 3) + 1;
}
LOG_INFO("odd number of bytes to write (%" PRIu32 "), extending to %" PRIu32 " "
"and padding with 0xff", old_count, count);
- memset(new_buffer, 0xff, count);
+ memset(new_buffer + old_count, 0xff, count - old_count);
buffer = memcpy(new_buffer, buffer, old_count);
}
kinetis_disable_wdog(bank->target, kinfo->sim_sdid);
/* try using a block write */
- int retval = kinetis_write_block(bank, buffer, offset, words_remaining);
+ result = kinetis_write_block(bank, buffer, offset, words_remaining);
- if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
+ if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
/* if block write failed (no sufficient working area),
* we use normal (slow) single word accesses */
LOG_WARNING("couldn't use block writes, falling back to single "
"memory accesses");
- for (i = 0; i < count; i += 4) {
+ while (words_remaining) {
uint8_t ftfx_fstat;
- LOG_DEBUG("write longword @ %08" PRIX32, (uint32_t)(offset + i));
-
- uint8_t padding[4] = {0xff, 0xff, 0xff, 0xff};
- memcpy(padding, buffer + i, MIN(4, count-i));
+ LOG_DEBUG("write longword @ %08" PRIx32, (uint32_t)(bank->base + offset));
- result = kinetis_ftfx_command(bank->target, FTFx_CMD_LWORDPROG, kinfo->prog_base + offset + i,
- padding[3], padding[2], padding[1], padding[0],
+ result = kinetis_ftfx_command(bank->target, FTFx_CMD_LWORDPROG, kinfo->prog_base + offset,
+ buffer[3], buffer[2], buffer[1], buffer[0],
0, 0, 0, 0, &ftfx_fstat);
- if (result != ERROR_OK)
- return ERROR_FLASH_OPERATION_FAILED;
+ if (result != ERROR_OK) {
+ LOG_ERROR("Error writing longword at %08" PRIx32, bank->base + offset);
+ break;
+ }
+
+ if (ftfx_fstat & 0x01)
+ LOG_ERROR("Flash write error at %08" PRIx32, bank->base + offset);
+
+ buffer += 4;
+ offset += 4;
+ words_remaining--;
}
}
+ free(new_buffer);
} else {
LOG_ERROR("Flash write strategy not implemented");
return ERROR_FLASH_OPERATION_FAILED;
}
kinetis_invalidate_flash_cache(bank);
- return ERROR_OK;
+ return result;
+}
+
+
+static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t offset, uint32_t count)
+{
+ int result;
+ bool set_fcf = false;
+ int sect = 0;
+
+ result = kinetis_check_run_mode(bank->target);
+ if (result != ERROR_OK)
+ return result;
+
+ /* reset error flags */
+ result = kinetis_ftfx_prepare(bank->target);
+ if (result != ERROR_OK)
+ return result;
+
+ if (bank->base == 0 && !allow_fcf_writes) {
+ if (bank->sectors[1].offset <= FCF_ADDRESS)
+ sect = 1; /* 1kb sector, FCF in 2nd sector */
+
+ if (offset < bank->sectors[sect].offset + bank->sectors[sect].size
+ && offset + count > bank->sectors[sect].offset)
+ set_fcf = true; /* write to any part of sector with FCF */
+ }
+
+ if (set_fcf) {
+ uint8_t fcf_buffer[FCF_SIZE];
+ uint8_t fcf_current[FCF_SIZE];
+
+ kinetis_fill_fcf(bank, fcf_buffer);
+
+ if (offset < FCF_ADDRESS) {
+ /* write part preceding FCF */
+ result = kinetis_write_inner(bank, buffer, offset, FCF_ADDRESS - offset);
+ if (result != ERROR_OK)
+ return result;
+ }
+
+ result = target_read_memory(bank->target, FCF_ADDRESS, 4, FCF_SIZE / 4, fcf_current);
+ if (result == ERROR_OK && memcmp(fcf_current, fcf_buffer, FCF_SIZE) == 0)
+ set_fcf = false;
+
+ if (set_fcf) {
+ /* write FCF if differs from flash - eliminate multiple writes */
+ result = kinetis_write_inner(bank, fcf_buffer, FCF_ADDRESS, FCF_SIZE);
+ if (result != ERROR_OK)
+ return result;
+ }
+
+ LOG_WARNING("Flash Configuration Field written.");
+ LOG_WARNING("Reset or power off the device to make settings effective.");
+
+ if (offset + count > FCF_ADDRESS + FCF_SIZE) {
+ uint32_t delta = FCF_ADDRESS + FCF_SIZE - offset;
+ /* write part after FCF */
+ result = kinetis_write_inner(bank, buffer + delta, FCF_ADDRESS + FCF_SIZE, count - delta);
+ }
+ return result;
+
+ } else
+ /* no FCF fiddling, normal write */
+ return kinetis_write_inner(bank, buffer, offset, count);
}
+
static int kinetis_probe(struct flash_bank *bank)
{
int result, i;
- uint32_t offset = 0;
uint8_t fcfg1_nvmsize, fcfg1_pfsize, fcfg1_eesize, fcfg1_depart;
uint8_t fcfg2_maxaddr0, fcfg2_pflsh, fcfg2_maxaddr1;
uint32_t nvm_size = 0, pf_size = 0, df_size = 0, ee_size = 0;
pflash_sector_size_bytes = 1<<10;
nvm_sector_size_bytes = 1<<10;
num_blocks = 2;
- kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE_K;
break;
case KINETIS_K_SDID_K10_M72:
case KINETIS_K_SDID_K20_M72:
pflash_sector_size_bytes = 2<<10;
nvm_sector_size_bytes = 1<<10;
num_blocks = 2;
- kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE_K;
kinfo->max_flash_prog_size = 1<<10;
break;
case KINETIS_K_SDID_K10_M100:
pflash_sector_size_bytes = 2<<10;
nvm_sector_size_bytes = 2<<10;
num_blocks = 2;
- kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE_K;
break;
case KINETIS_K_SDID_K21_M120:
case KINETIS_K_SDID_K22_M120:
kinfo->max_flash_prog_size = 1<<10;
nvm_sector_size_bytes = 4<<10;
num_blocks = 2;
- kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE_K;
break;
case KINETIS_K_SDID_K10_M120:
case KINETIS_K_SDID_K20_M120:
pflash_sector_size_bytes = 4<<10;
nvm_sector_size_bytes = 4<<10;
num_blocks = 4;
- kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE_K;
break;
default:
LOG_ERROR("Unsupported K-family FAMID");
/* K02FN64, K02FN128: FTFA, 2kB sectors */
pflash_sector_size_bytes = 2<<10;
num_blocks = 1;
- kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE_K;
break;
case KINETIS_SDID_FAMILYID_K2X | KINETIS_SDID_SUBFAMID_KX2: {
/* MK24FN1M */
pflash_sector_size_bytes = 4<<10;
num_blocks = 2;
- kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE_K;
kinfo->max_flash_prog_size = 1<<10;
break;
}
/* K22 with new-style SDID - smaller pflash with FTFA, 2kB sectors */
pflash_sector_size_bytes = 2<<10;
/* autodetect 1 or 2 blocks */
- kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE_K;
break;
}
LOG_ERROR("Unsupported Kinetis K22 DIEID");
if ((kinfo->sim_sdid & (KINETIS_SDID_DIEID_MASK)) == KINETIS_SDID_DIEID_K24FN256) {
/* K24FN256 - smaller pflash with FTFA */
num_blocks = 1;
- kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE_K;
break;
}
/* K24FN1M without errata 7534 */
num_blocks = 2;
- kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE_K;
kinfo->max_flash_prog_size = 1<<10;
break;
nvm_sector_size_bytes = 4<<10;
kinfo->max_flash_prog_size = 1<<10;
num_blocks = 2;
- kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE_K;
break;
case KINETIS_SDID_FAMILYID_K2X | KINETIS_SDID_SUBFAMID_KX6:
nvm_sector_size_bytes = 4<<10;
kinfo->max_flash_prog_size = 1<<10;
num_blocks = 4;
- kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_INVALIDATE_CACHE_K;
break;
default:
LOG_ERROR("Unsupported Kinetis FAMILYID SUBFAMID");
pflash_sector_size_bytes = 1<<10;
nvm_sector_size_bytes = 1<<10;
/* autodetect 1 or 2 blocks */
- kinfo->flash_support = FS_PROGRAM_LONGWORD;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE_L;
break;
case KINETIS_SDID_SERIESID_KV:
/* KV10: FTFA, 1kB sectors */
pflash_sector_size_bytes = 1<<10;
num_blocks = 1;
- kinfo->flash_support = FS_PROGRAM_LONGWORD;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE_L;
break;
case KINETIS_SDID_FAMILYID_K1X | KINETIS_SDID_SUBFAMID_KX1:
/* KV11: FTFA, 2kB sectors */
pflash_sector_size_bytes = 2<<10;
num_blocks = 1;
- kinfo->flash_support = FS_PROGRAM_LONGWORD;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE_L;
break;
case KINETIS_SDID_FAMILYID_K3X | KINETIS_SDID_SUBFAMID_KX0:
/* KV31: FTFA, 2kB sectors, 2 blocks */
pflash_sector_size_bytes = 2<<10;
/* autodetect 1 or 2 blocks */
- kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE_K;
break;
case KINETIS_SDID_FAMILYID_K4X | KINETIS_SDID_SUBFAMID_KX2:
/* KV4x: FTFA, 4kB sectors */
pflash_sector_size_bytes = 4<<10;
num_blocks = 1;
- kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE;
+ kinfo->flash_support = FS_PROGRAM_LONGWORD | FS_INVALIDATE_CACHE_K;
break;
default:
* parts with more than 32K of PFlash. For parts with
* less the protection unit is set to 1024 bytes */
kinfo->protection_size = MAX(pf_size / 32, 1024);
- kinfo->protection_block = (32 / num_pflash_blocks) * bank->bank_number;
+ bank->num_prot_blocks = 32 / num_pflash_blocks;
+ kinfo->protection_block = bank->num_prot_blocks * bank->bank_number;
} else if ((unsigned)bank->bank_number < num_blocks) {
/* nvm, banks start at address 0x10000000 */
else
kinfo->protection_size = nvm_size / 8; /* TODO: verify on SF1, not documented in RM */
}
- kinfo->protection_block = (8 / num_nvm_blocks) * nvm_ord;
+ bank->num_prot_blocks = 8 / num_nvm_blocks;
+ kinfo->protection_block = bank->num_prot_blocks * nvm_ord;
/* EEPROM backup part of FlexNVM is not accessible, use df_size as a limit */
if (df_size > bank->size * nvm_ord)
free(bank->sectors);
bank->sectors = NULL;
}
+ if (bank->prot_blocks) {
+ free(bank->prot_blocks);
+ bank->prot_blocks = NULL;
+ }
if (kinfo->sector_size == 0) {
LOG_ERROR("Unknown sector size for bank %d", bank->bank_number);
if (bank->num_sectors > 0) {
/* FlexNVM bank can be used for EEPROM backup therefore zero sized */
- bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
-
- for (i = 0; i < bank->num_sectors; i++) {
- bank->sectors[i].offset = offset;
- bank->sectors[i].size = kinfo->sector_size;
- offset += kinfo->sector_size;
- bank->sectors[i].is_erased = -1;
- bank->sectors[i].is_protected = 1;
- }
+ bank->sectors = alloc_block_array(0, kinfo->sector_size, bank->num_sectors);
+ if (!bank->sectors)
+ return ERROR_FAIL;
+
+ bank->prot_blocks = alloc_block_array(0, kinfo->protection_size, bank->num_prot_blocks);
+ if (!bank->prot_blocks)
+ return ERROR_FAIL;
+
+ } else {
+ bank->num_prot_blocks = 0;
}
kinfo->probed = true;
if (result != ERROR_OK)
return result;
+ /* reset error flags */
+ result = kinetis_ftfx_prepare(bank->target);
+ if (result != ERROR_OK)
+ return result;
+
if (kinfo->flash_class == FC_PFLASH || kinfo->flash_class == FC_FLEX_NVM) {
bool block_dirty = false;
uint8_t ftfx_fstat;
unsigned long par, log2 = 0, ee1 = 0, ee2 = 0;
enum { SHOW_INFO, DF_SIZE, EEBKP_SIZE } sz_type = SHOW_INFO;
bool enable;
- uint8_t ftfx_fstat;
uint8_t load_flex_ram = 1;
uint8_t ee_size_code = 0x3f;
uint8_t flex_nvm_partition_code = 0;
if (result != ERROR_OK)
return result;
+ /* reset error flags */
+ result = kinetis_ftfx_prepare(target);
+ if (result != ERROR_OK)
+ return result;
+
result = kinetis_ftfx_command(target, FTFx_CMD_PGMPART, load_flex_ram,
ee_size_code, flex_nvm_partition_code, 0, 0,
- 0, 0, 0, 0, &ftfx_fstat);
+ 0, 0, 0, 0, NULL);
if (result != ERROR_OK)
return result;
return ERROR_OK;
}
+COMMAND_HANDLER(kinetis_fcf_source_handler)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1) {
+ if (strcmp(CMD_ARGV[0], "write") == 0)
+ allow_fcf_writes = true;
+ else if (strcmp(CMD_ARGV[0], "protection") == 0)
+ allow_fcf_writes = false;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (allow_fcf_writes) {
+ command_print(CMD_CTX, "Arbitrary Flash Configuration Field writes enabled.");
+ command_print(CMD_CTX, "Protection info writes to FCF disabled.");
+ LOG_WARNING("BEWARE: incorrect flash configuration may permanently lock the device.");
+ } else {
+ command_print(CMD_CTX, "Protection info writes to Flash Configuration Field enabled.");
+ command_print(CMD_CTX, "Arbitrary FCF writes disabled. Mode safe from unwanted locking of the device.");
+ }
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(kinetis_fopt_handler)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1)
+ fcf_fopt = (uint8_t)strtoul(CMD_ARGV[0], NULL, 0);
+ else
+ command_print(CMD_CTX, "FCF_FOPT 0x%02" PRIx8, fcf_fopt);
+
+ return ERROR_OK;
+}
+
static const struct command_registration kinetis_security_command_handlers[] = {
{
.usage = "('info'|'dataflash' size|'eebkp' size) [eesize1 eesize2] ['on'|'off']",
.handler = kinetis_nvm_partition,
},
+ {
+ .name = "fcf_source",
+ .mode = COMMAND_EXEC,
+ .help = "Use protection as a source for Flash Configuration Field or allow writing arbitrary values to the FCF"
+ " Mode 'protection' is safe from unwanted locking of the device.",
+ .usage = "['protection'|'write']",
+ .handler = kinetis_fcf_source_handler,
+ },
+ {
+ .name = "fopt",
+ .mode = COMMAND_EXEC,
+ .help = "FCF_FOPT value source in 'kinetis fcf_source protection' mode",
+ .usage = "[num]",
+ .handler = kinetis_fopt_handler,
+ },
COMMAND_REGISTRATION_DONE
};