+
+ enum {
+ FS_PROGRAM_SECTOR = 1,
+ FS_PROGRAM_LONGWORD = 2,
+ FS_PROGRAM_PHRASE = 4, /* Unsupported */
+ FS_INVALIDATE_CACHE = 8,
+ } flash_support;
+};
+
+#define MDM_AP 1
+
+#define MDM_REG_STAT 0x00
+#define MDM_REG_CTRL 0x04
+#define MDM_REG_ID 0xfc
+
+#define MDM_STAT_FMEACK (1<<0)
+#define MDM_STAT_FREADY (1<<1)
+#define MDM_STAT_SYSSEC (1<<2)
+#define MDM_STAT_SYSRES (1<<3)
+#define MDM_STAT_FMEEN (1<<5)
+#define MDM_STAT_BACKDOOREN (1<<6)
+#define MDM_STAT_LPEN (1<<7)
+#define MDM_STAT_VLPEN (1<<8)
+#define MDM_STAT_LLSMODEXIT (1<<9)
+#define MDM_STAT_VLLSXMODEXIT (1<<10)
+#define MDM_STAT_CORE_HALTED (1<<16)
+#define MDM_STAT_CORE_SLEEPDEEP (1<<17)
+#define MDM_STAT_CORESLEEPING (1<<18)
+
+#define MDM_CTRL_FMEIP (1<<0)
+#define MDM_CTRL_DBG_DIS (1<<1)
+#define MDM_CTRL_DBG_REQ (1<<2)
+#define MDM_CTRL_SYS_RES_REQ (1<<3)
+#define MDM_CTRL_CORE_HOLD_RES (1<<4)
+#define MDM_CTRL_VLLSX_DBG_REQ (1<<5)
+#define MDM_CTRL_VLLSX_DBG_ACK (1<<6)
+#define MDM_CTRL_VLLSX_STAT_ACK (1<<7)
+
+#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, MDM_AP), reg, value);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("MDM: failed to queue a write request");
+ return retval;
+ }
+
+ retval = dap_run(dap);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("MDM: dap_run failed");
+ return retval;
+ }
+
+
+ return ERROR_OK;
+}
+
+static int kinetis_mdm_read_register(struct adiv5_dap *dap, unsigned reg, uint32_t *result)
+{
+ int retval;
+
+ 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;
+ }
+
+ retval = dap_run(dap);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("MDM: dap_run failed");
+ return retval;
+ }
+
+ LOG_DEBUG("MDM_REG[0x%02x]: %08" PRIX32, reg, *result);
+ return ERROR_OK;
+}
+
+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;
+ int64_t ms_timeout = timeval_ms() + timeout_ms;
+
+ do {
+ retval = kinetis_mdm_read_register(dap, reg, &val);
+ if (retval != ERROR_OK || (val & mask) == value)
+ return retval;
+
+ alive_sleep(1);
+ } while (timeval_ms() < ms_timeout);
+
+ LOG_DEBUG("MDM: polling timed out");
+ return ERROR_FAIL;
+}
+
+/*
+ * This command can be used to break a watchdog reset loop when
+ * connecting to an unsecured target. Unlike other commands, halt will
+ * automatically retry as it does not know how far into the boot process
+ * it is when the command is called.
+ */
+COMMAND_HANDLER(kinetis_mdm_halt)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct cortex_m_common *cortex_m = target_to_cm(target);
+ struct adiv5_dap *dap = cortex_m->armv7m.arm.dap;
+ 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");
+ return ERROR_FAIL;
+ }
+
+ while (true) {
+ tries++;
+
+ kinetis_mdm_write_register(dap, MDM_REG_CTRL, MDM_CTRL_CORE_HOLD_RES);
+
+ alive_sleep(1);
+
+ retval = kinetis_mdm_read_register(dap, MDM_REG_STAT, &stat);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("MDM: failed to read MDM_REG_STAT");
+ continue;
+ }
+
+ /* Repeat setting MDM_CTRL_CORE_HOLD_RES until system is out of
+ * reset with flash ready and without security
+ */
+ if ((stat & (MDM_STAT_FREADY | MDM_STAT_SYSSEC | MDM_STAT_SYSRES))
+ == (MDM_STAT_FREADY | MDM_STAT_SYSRES))
+ break;
+
+ if (timeval_ms() >= ms_timeout) {
+ LOG_ERROR("MDM: halt timed out");
+ return ERROR_FAIL;
+ }
+ }
+
+ LOG_DEBUG("MDM: halt succeded after %d attempts.", tries);
+
+ target_poll(target);
+ /* enable polling in case kinetis_check_flash_security_status disabled it */
+ jtag_poll_set_enabled(true);
+
+ alive_sleep(100);
+
+ target->reset_halt = true;
+ target->type->assert_reset(target);
+
+ retval = kinetis_mdm_write_register(dap, MDM_REG_CTRL, 0);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("MDM: failed to clear MDM_REG_CTRL");
+ return retval;
+ }
+
+ target->type->deassert_reset(target);
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(kinetis_mdm_reset)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct cortex_m_common *cortex_m = target_to_cm(target);
+ struct adiv5_dap *dap = cortex_m->armv7m.arm.dap;
+ int retval;
+
+ if (!dap) {
+ LOG_ERROR("Cannot perform reset with a high-level adapter");
+ return ERROR_FAIL;
+ }
+
+ retval = kinetis_mdm_write_register(dap, MDM_REG_CTRL, MDM_CTRL_SYS_RES_REQ);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("MDM: failed to write MDM_REG_CTRL");
+ return retval;
+ }
+
+ 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;
+ }
+
+ retval = kinetis_mdm_write_register(dap, MDM_REG_CTRL, 0);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("MDM: failed to clear MDM_REG_CTRL");
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+/*
+ * This function implements the procedure to mass erase the flash via
+ * SWD/JTAG on Kinetis K and L series of devices as it is described in
+ * AN4835 "Production Flash Programming Best Practices for Kinetis K-
+ * and L-series MCUs" Section 4.2.1. To prevent a watchdog reset loop,
+ * the core remains halted after this function completes as suggested
+ * by the application note.
+ */
+COMMAND_HANDLER(kinetis_mdm_mass_erase)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct cortex_m_common *cortex_m = target_to_cm(target);
+ struct adiv5_dap *dap = cortex_m->armv7m.arm.dap;
+
+ if (!dap) {
+ LOG_ERROR("Cannot perform mass erase with a high-level adapter");
+ return ERROR_FAIL;
+ }
+
+ int retval;
+
+ /*
+ * ... Power on the processor, or if power has already been
+ * applied, assert the RESET pin to reset the processor. For
+ * devices that do not have a RESET pin, write the System
+ * Reset Request bit in the MDM-AP control register after
+ * establishing communication...
+ */
+
+ /* assert SRST if configured */
+ bool has_srst = jtag_get_reset_config() & RESET_HAS_SRST;
+ if (has_srst)
+ adapter_assert_reset();
+
+ retval = kinetis_mdm_write_register(dap, MDM_REG_CTRL, MDM_CTRL_SYS_RES_REQ);
+ if (retval != ERROR_OK && !has_srst) {
+ LOG_ERROR("MDM: failed to assert reset");
+ goto deassert_reset_and_exit;
+ }
+
+ /*
+ * ... 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.
+ */
+ int cnt_mass_erase_disabled = 0;
+ int cnt_ready = 0;
+ int64_t ms_start = timeval_ms();
+ bool man_reset_requested = false;
+
+ do {
+ uint32_t stat = 0;
+ int64_t ms_elapsed = timeval_ms() - ms_start;
+
+ if (!man_reset_requested && ms_elapsed > 100) {
+ LOG_INFO("MDM: Press RESET button now if possible.");
+ man_reset_requested = true;
+ }
+
+ 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;
+ }
+
+ 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
+ * Erase in Progress bit. This will start the mass erase
+ * process...
+ */
+ retval = kinetis_mdm_write_register(dap, MDM_REG_CTRL, MDM_CTRL_SYS_RES_REQ | MDM_CTRL_FMEIP);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("MDM: failed to start mass erase");
+ goto deassert_reset_and_exit;
+ }
+
+ /*
+ * ... 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, 16000);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("MDM: mass erase timeout");
+ goto deassert_reset_and_exit;
+ }
+
+ target_poll(target);
+ /* enable polling in case kinetis_check_flash_security_status disabled it */
+ jtag_poll_set_enabled(true);
+
+ alive_sleep(100);
+
+ target->reset_halt = true;
+ target->type->assert_reset(target);
+
+ /*
+ * ... Negate the RESET signal or clear the System Reset Request
+ * bit in the MDM-AP control register.
+ */
+ retval = kinetis_mdm_write_register(dap, MDM_REG_CTRL, 0);
+ if (retval != ERROR_OK)
+ LOG_ERROR("MDM: failed to clear MDM_REG_CTRL");
+
+ target->type->deassert_reset(target);
+
+ return retval;
+
+deassert_reset_and_exit:
+ kinetis_mdm_write_register(dap, MDM_REG_CTRL, 0);
+ if (has_srst)
+ adapter_deassert_reset();
+ return retval;
+}
+
+static const uint32_t kinetis_known_mdm_ids[] = {
+ 0x001C0000, /* Kinetis-K Series */
+ 0x001C0020, /* Kinetis-L/M/V/E Series */