diff --git a/testjig/production/arm_kinetis_debug.cpp b/testjig/production/arm_kinetis_debug.cpp
index c3f1ae3399fa976c6cb4fdd1ea1e670c60d774c1..74a5904b5d4959b51cc95a80dc799ca48d1fdf40 100644
--- a/testjig/production/arm_kinetis_debug.cpp
+++ b/testjig/production/arm_kinetis_debug.cpp
@@ -29,13 +29,13 @@
 
 bool ARMKinetisDebug::startup()
 {
-    uint32_t idr;
-    uint32_t status;
-
-    // System resets can be slow, give them more time than the default.
-    const unsigned resetRetries = 2000;
+    return detect() && resetHalt() && peripheralInit();
+}
 
+bool ARMKinetisDebug::detect()
+{
     // Make sure we're on a compatible chip. The MDM-AP peripheral is Freescale-specific.
+    uint32_t idr;
     if (!apRead(REG_MDM_IDR, idr))
         return false;
     if (idr != 0x001C0000) {
@@ -43,51 +43,72 @@ bool ARMKinetisDebug::startup()
         return false;
     }
 
-    // Put the control register in a known state, and make sure we aren't already in the middle of a reset
-    if (!apWrite(REG_MDM_CONTROL, REG_MDM_CONTROL_CORE_HOLD_RESET))
-        return false;
-    if (!apReadPoll(REG_MDM_STATUS, status, REG_MDM_STATUS_SYS_NRESET, -1, resetRetries))
-        return false;
-
-    // System reset
-    if (!apWrite(REG_MDM_CONTROL, REG_MDM_CONTROL_SYS_RESET_REQ))
-        return false;
-    if (!apReadPoll(REG_MDM_STATUS, status, REG_MDM_STATUS_SYS_NRESET, 0))
-        return false;
-    if (!apWrite(REG_MDM_CONTROL, 0))
-        return false;
-
-    // Re-initialize the AHB-AP after reset
-    if (!initMemPort())
-        return false;
+    return true;
+}
 
-    // Wait until the flash controller is ready & system is out of reset
-    if (!apReadPoll(REG_MDM_STATUS, status, REG_MDM_STATUS_SYS_NRESET | REG_MDM_STATUS_FLASH_READY, -1, resetRetries))
-        return false;
+bool ARMKinetisDebug::resetHalt()
+{
+    uint32_t status, dhcsr;
 
-    // Enable debugging
-    if (!memStore(REG_SCB_DHCSR, 0xA05F0001))
-        return false;
+    // System resets can be slow, give them more time than the default.
+    const unsigned resetRetries = 2000;
 
-    // Halt the CPU core. Keep trying, in case we're fighting with watchdog reset :(
-    unsigned retries = 50;
-    uint32_t dhcsr;
-    do {
-        retries--;
-
-        // Request a halt, and read back status
-        if (!memStore(REG_SCB_DHCSR, 0xA05F0003))
-            return false;
-        if (!memLoad(REG_SCB_DHCSR, dhcsr))
-            return false;
-    
-        // Wait for S_HALT acknowledgment bit
-    } while (!(dhcsr & (1 << 17)) && retries);
-    if (!retries) {
-        log(LOG_ERROR, "ARMKinetisDebug: Failed to put CPU in debug halt state. (DHCSR: %08x)", dhcsr);
-        return false;
+    // Keep trying to reset/halt, in case we're fighting with watchdog timer :(
+    unsigned outerRetries = 50;
+
+    while (outerRetries--) {
+
+        // Put the control register in a known state, and make sure we aren't already in the middle of a reset
+        if (!apWrite(REG_MDM_CONTROL, REG_MDM_CONTROL_CORE_HOLD_RESET))
+            continue;
+        if (!apReadPoll(REG_MDM_STATUS, status, REG_MDM_STATUS_SYS_NRESET, -1, resetRetries))
+            continue;
+
+        // System reset
+        if (!apWrite(REG_MDM_CONTROL, REG_MDM_CONTROL_SYS_RESET_REQ))
+            continue;
+        if (!apReadPoll(REG_MDM_STATUS, status, REG_MDM_STATUS_SYS_NRESET, 0))
+            continue;
+        if (!apWrite(REG_MDM_CONTROL, 0))
+            continue;
+
+        // Wait until the flash controller is ready & system is out of reset.
+        // Also wait for security bit to be cleared. Early in reset, the chip is determining
+        // its security status. When the security bit is set, AHB-AP is disabled.
+        if (!apReadPoll(REG_MDM_STATUS, status,
+                REG_MDM_STATUS_SYS_NRESET | REG_MDM_STATUS_FLASH_READY | REG_MDM_STATUS_SYS_SECURITY,
+                REG_MDM_STATUS_SYS_NRESET | REG_MDM_STATUS_FLASH_READY,
+                resetRetries))
+            continue;
+
+        // Re-initialize the AHB-AP after reset
+        if (!initMemPort())
+            continue;
+
+        // Enable debugging
+        if (!memStore(REG_SCB_DHCSR, 0xA05F0001))
+            continue;
+
+        unsigned haltRetries = 50;
+        while (haltRetries--) {
+            // Request a halt, and read back status
+            if (!memStore(REG_SCB_DHCSR, 0xA05F0003))
+                break;
+            if (!memLoad(REG_SCB_DHCSR, dhcsr))
+                break;
+            if (dhcsr & (1 << 17)) {
+                // Halted!
+                return true;
+            }
+        }
     }
 
+    log(LOG_ERROR, "ARMKinetisDebug: Failed to put CPU in debug halt state. (DHCSR: %08x)", dhcsr);
+    return false;
+}
+
+bool ARMKinetisDebug::peripheralInit()
+{
     // Enable peripheral clocks
     if (!memStore(REG_SIM_SCGC5, 0x00043F82))
         return false;
@@ -100,7 +121,6 @@ bool ARMKinetisDebug::startup()
     if (!memStoreAndVerify(0x20000000, 0x76543210))
         return false;
 
-    // Good to go!
     return true;
 }
 
diff --git a/testjig/production/arm_kinetis_debug.h b/testjig/production/arm_kinetis_debug.h
index c7bb402f5b13bcab1003153069db1e2aa3c4eeae..bbd7fac304e8afdba40b7a502b2ffe018f5fcd0d 100644
--- a/testjig/production/arm_kinetis_debug.h
+++ b/testjig/production/arm_kinetis_debug.h
@@ -28,9 +28,14 @@
 class ARMKinetisDebug : public ARMDebug
 {
 public:
-    // Hold the processor core in reset, and initialize peripherals
+    // First-time initialization, resetting into system halt state.
     bool startup();
 
+    // Individual parts of startup():
+    bool detect();              // Detect supported Kinetis hardware
+    bool resetHalt();           // Reset into system halt state
+    bool peripheralInit();      // Initialize peripherals into default state
+
     // Flash mass-erase operation. Works even on protected devices.
     bool flashMassErase();
 };