/*********************************************************************
*            (c) 1995 - 2026 SEGGER Microcontroller GmbH             *
*                        The Embedded Experts                        *
*                           www.segger.com                           *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------

File    : NXP_iMXRT1024_eFuse.JLinkScript
Purpose : Burns one OCOTP eFuse word of the i.MX RT1024 after
          flash programming (HandleAfterFlashProg()).
Literature:
  [1] i.MX RT1024 Processor Reference Manual, Rev. 1, 02/2021
      Chapter 23 (OCOTP_CTRL), Chapter 22 (Fusemap),
      Chapter 14 (CCM), Table 9-7 (ROM clock setting)
  [2] NXP MCUXpresso SDK, drivers/ocotp/fsl_ocotp.c and
      devices/MIMXRT1024/drivers/fsl_clock.c

Notes:
  (1) Sequence per [1] 23.3.1.3 / 23.3.1.5:
        Timing for ipg_clk -> BUSY clear -> clear ERROR ->
        CTRL = WR_UNLOCK | ADDR -> DATA = value -> wait BUSY clear ->
        2 us postamble -> check ERROR -> RELOAD_SHADOWS -> verify
  (2) ipg_clk is calculated from the CCM / CCM_ANALOG registers
      (same logic as CLOCK_GetIpgFreq() in [2]). Unsupported clock
      configurations abort without burning anything.
  (3) Burn time: [1] uses RELAX_PROG (TIMING2) in the tPGM formula,
      [2] uses RELAX (TIMING) and never writes TIMING2. STROBE_PROG is
      chosen so that tPGM is inside 9..11 us for both formulas:
        STROBE_PROG + 1 = tPGM + (RELAX_PROG + 1) + (RELAX + 1)
      giving ~9.15 us (RM formula) / ~10.85 us (SDK formula).
      Once NXP confirms the RM formula, use
        STROBE_PROG + 1 = tPGM + 2 * (RELAX_PROG + 1)
  (4) A value of 0 in _FUSE_VAL means nothing to burn: the function
      returns immediately without touching the target.
  (5) Fuse bits that are already blown are detected via the shadow
      register and the burn is skipped. Re-running is harmless.
  (6) Conservative language subset: helper functions are not declared
      static; no compound assignments (|=, ...), for, switch, break,
      ternary operator, pointers or casts are used. The division
      operator is not supported, _Div() is used instead.
*/

/*********************************************************************
*
*       Configuration
*
**********************************************************************
*/
__constant U32 _FUSE_ADDR             = 0x26;        // OCOTP word index (0x26 = GP1). Shadow address = 0x401F4400 + _FUSE_ADDR * 0x10
__constant U32 _FUSE_VAL              = 0x00000000;  // Bits to blow (1 = blow). 0 = nothing to do

/*********************************************************************
*
*       Constants (similar to defines)
*
**********************************************************************
*/
//
// CCM / CCM_ANALOG
//
__constant U32 _CCM_CCSR              = 0x400FC00C;
__constant U32 _CCM_CBCDR             = 0x400FC014;
__constant U32 _CCM_CBCMR             = 0x400FC018;
__constant U32 _CCM_CGPR              = 0x400FC064;
__constant U32 _CCM_CCGR2             = 0x400FC070;
__constant U32 _CCM_ANALOG_PLL_USB1   = 0x400D8010;  // PLL3
__constant U32 _CCM_ANALOG_PLL_SYS    = 0x400D8030;  // PLL2
__constant U32 _CCM_ANALOG_PLL_SYS_NUM   = 0x400D8050;
__constant U32 _CCM_ANALOG_PLL_SYS_DENOM = 0x400D8060;
__constant U32 _CCM_ANALOG_PFD_480    = 0x400D80F0;
__constant U32 _CCM_ANALOG_PFD_528    = 0x400D8100;
__constant U32 _CCM_CCGR2_CG6         = 0x00003000;  // OCOTP_CTRL clock
__constant U32 _CCM_CGPR_EFUSE_SUPPLY = 0x00000010;  // EFUSE_PROG_SUPPLY_GATE: 1 = allow fuse programming
__constant U32 _OSC_KHZ               = 24000;
__constant U32 _PLL6_DIV_KHZ          = 500000;      // "divided PLL6", fixed 500 MHz as in [2]
__constant U32 _IPG_MAX_KHZ           = 150000;
//
// OCOTP
//
__constant U32 _OCOTP_CTRL            = 0x401F4000;
__constant U32 _OCOTP_CTRL_SET        = 0x401F4004;
__constant U32 _OCOTP_CTRL_CLR        = 0x401F4008;
__constant U32 _OCOTP_TIMING          = 0x401F4010;
__constant U32 _OCOTP_DATA            = 0x401F4020;
__constant U32 _OCOTP_TIMING2         = 0x401F4100;
__constant U32 _OCOTP_SHADOW_BASE     = 0x401F4400;
__constant U32 _OCOTP_CTRL_BUSY       = 0x00000100;
__constant U32 _OCOTP_CTRL_ERROR      = 0x00000200;
__constant U32 _OCOTP_CTRL_RELOAD     = 0x00000400;
__constant U32 _OCOTP_WR_UNLOCK       = 0x3E770000;
//
// OTP timing requirements [1] 23.3.3, in ns
//
__constant U32 _T_WAIT_NS             = 150;
__constant U32 _T_RELAX_NS            = 100;
__constant U32 _T_RELAX_READ_NS       = 25;     // RM minimum is 10 ns. 25 ns keeps the reset value RELAX_READ = 3 at 125..150 MHz (left untouched by [2])
__constant U32 _T_RD_NS               = 45;
__constant U32 _T_RELAX_PROG_NS       = 950;
__constant U32 _T_RELAX1_NS           = 1000;
__constant U32 _T_PGM_NS              = 10000;
__constant U32 _T_PGM_MIN_NS          = 9000;
__constant U32 _T_PGM_MAX_NS          = 11000;
//
// Timeouts in ms
//
__constant int _TIMEOUT_IDLE_MS       = 20;
__constant int _TIMEOUT_BURN_MS       = 100;    // Max. 32 bits * 10 us, generous margin for J-Link round trips

/*********************************************************************
*
*       Local functions
*
**********************************************************************
*/

/*********************************************************************
*
*       _Div()
*
*  Function description
*    Unsigned integer division (the script language has no division
*    operator). Binary long division using shift and subtract.
*
*  Parameters
*    Num  Dividend. Must be < 0x80000000.
*    Den  Divisor.  Must be < 0x80000000.
*
*  Return value
*    Num / Den, rounded down. 0 if Den == 0.
*/
U32 _Div(U32 Num, U32 Den) {
  U32 q;
  U32 Bit;

  q   = 0;
  Bit = 1;
  if (Den != 0) {
    //
    // Align divisor with the dividend, stay below bit 31
    //
    while ((Den < Num) && ((Den & 0x40000000) == 0)) {
      Den = Den << 1;
      Bit = Bit << 1;
    }
    //
    // Subtract and shift back
    //
    while (Bit != 0) {
      if (Num >= Den) {
        Num = Num - Den;
        q   = q | Bit;
      }
      Den = Den >> 1;
      Bit = Bit >> 1;
    }
  }
  return q;
}

/*********************************************************************
*
*       _NsToCycles()
*
*  Function description
*    Converts a time to the number of clock cycles, rounded up.
*
*  Parameters
*    ns   Time in ns. Must be <= 11000.
*    kHz  Clock frequency in kHz. Must be <= _IPG_MAX_KHZ.
*
*  Return value
*    Number of cycles (>= time).
*/
U32 _NsToCycles(U32 ns, U32 kHz) {
  U32 r;

  r = _Div(ns * kHz + 999999, 1000000);
  return r;
}

/*********************************************************************
*
*       _NsToCyclesFloor()
*
*  Function description
*    Converts a time to the number of clock cycles, rounded down.
*/
U32 _NsToCyclesFloor(U32 ns, U32 kHz) {
  U32 r;

  r = _Div(ns * kHz, 1000000);
  return r;
}

/*********************************************************************
*
*       _GetPll2kHz()
*
*  Function description
*    Returns the PLL2 (System PLL) output frequency.
*
*  Return value
*    == 0  Not running or unsupported configuration
*    >  0  Frequency in kHz
*/
U32 _GetPll2kHz(void) {
  U32 v;
  U32 Num;
  U32 Denom;
  U32 kHz;

  kHz = 0;
  v   = JLINK_MEM_ReadU32(_CCM_ANALOG_PLL_SYS);
  if ((v & 0x00002000) == 0) {                   // ENABLE == 0: output off
    kHz = 0;
  } else if ((v & 0x0000C000) != 0) {            // BYPASS_CLK_SRC is not the 24 MHz OSC: Not supported
    kHz = 0;
  } else if ((v & 0x00010000) != 0) {            // BYPASS
    kHz = _OSC_KHZ;
  } else if ((v & 0x00001000) != 0) {            // POWERDOWN
    kHz = 0;
  } else {
    if ((v & 0x00000001) != 0) {                 // DIV_SELECT
      kHz = _OSC_KHZ * 22;
    } else {
      kHz = _OSC_KHZ * 20;
    }
    Num   = JLINK_MEM_ReadU32(_CCM_ANALOG_PLL_SYS_NUM)   & 0x3FFFFFFF;
    Denom = JLINK_MEM_ReadU32(_CCM_ANALOG_PLL_SYS_DENOM) & 0x3FFFFFFF;
    if (Num != 0) {                              // Fractional part present
      if (Num >= Denom) {                        // NUM must be < DENOM (also covers DENOM == 0)
        kHz = 0;
      } else {
        while (Denom > 0xFFFF) {                 // Scale down to avoid 32-bit overflow in _OSC_KHZ * Num
          Num   = Num   >> 1;
          Denom = Denom >> 1;
        }
        kHz = kHz + _Div(_OSC_KHZ * Num, Denom);
      }
    }
  }
  return kHz;
}

/*********************************************************************
*
*       _GetPll3kHz()
*
*  Function description
*    Returns the PLL3 (USB1 PLL) main output frequency.
*
*  Return value
*    == 0  Not running or unsupported configuration
*    >  0  Frequency in kHz
*/
U32 _GetPll3kHz(void) {
  U32 v;
  U32 kHz;

  kHz = 0;
  v   = JLINK_MEM_ReadU32(_CCM_ANALOG_PLL_USB1);
  if ((v & 0x00002000) == 0) {                   // ENABLE == 0: output off
    kHz = 0;
  } else if ((v & 0x0000C000) != 0) {            // BYPASS_CLK_SRC is not the 24 MHz OSC: Not supported
    kHz = 0;
  } else if ((v & 0x00010000) != 0) {            // BYPASS
    kHz = _OSC_KHZ;
  } else if ((v & 0x00001000) == 0) {            // POWER == 0
    kHz = 0;
  } else if ((v & 0x00000002) != 0) {            // DIV_SELECT
    kHz = _OSC_KHZ * 22;
  } else {
    kHz = _OSC_KHZ * 20;
  }
  return kHz;
}

/*********************************************************************
*
*       _GetPfd3kHz()
*
*  Function description
*    Returns the PFD3 output frequency of a PLL.
*
*  Parameters
*    PllkHz  PLL frequency in kHz.
*    PfdReg  Address of CCM_ANALOG_PFD_480 or CCM_ANALOG_PFD_528.
*
*  Return value
*    == 0  Gated or invalid
*    >  0  Frequency in kHz
*/
U32 _GetPfd3kHz(U32 PllkHz, U32 PfdReg) {
  U32 v;
  U32 Frac;
  U32 kHz;

  kHz  = 0;
  v    = JLINK_MEM_ReadU32(PfdReg);
  Frac = (v >> 24) & 0x3F;
  if (((v >> 31) & 1) != 0) {                    // PFD3_CLKGATE
    kHz = 0;
  } else if ((Frac < 12) || (Frac > 35)) {       // Valid range of PFDn_FRAC
    kHz = 0;
  } else {
    kHz = _Div(PllkHz * 18, Frac);
  }
  return kHz;
}

/*********************************************************************
*
*       _GetIpgkHz()
*
*  Function description
*    Returns the ipg_clk (IPG_CLK_ROOT) frequency, see [1] Figure 14-2
*    and CLOCK_GetIpgFreq() in [2].
*
*  Return value
*    == 0  Unsupported clock configuration
*    >  0  Frequency in kHz
*/
U32 _GetIpgkHz(void) {
  U32 Cbcdr;
  U32 Cbcmr;
  U32 Sel;
  U32 PllkHz;
  U32 kHz;

  kHz   = 0;
  Cbcdr = JLINK_MEM_ReadU32(_CCM_CBCDR);
  Cbcmr = JLINK_MEM_ReadU32(_CCM_CBCMR);
  if (((Cbcdr >> 25) & 1) != 0) {                // PERIPH_CLK_SEL = 1: periph_clk2_clk_divided
    Sel = (Cbcmr >> 12) & 3;                     // PERIPH_CLK2_SEL
    if (Sel == 0) {                              // pll3_sw_clk
      if ((JLINK_MEM_ReadU32(_CCM_CCSR) & 1) != 0) {
        kHz = _OSC_KHZ;                          // PLL3_SW_CLK_SEL = 1: PLL3 bypass clock
      } else {
        kHz = _GetPll3kHz();
      }
    } else if (Sel == 1) {                       // osc_clk
      kHz = _OSC_KHZ;
    } else if (Sel == 2) {                       // pll2_bypass_clk
      if ((JLINK_MEM_ReadU32(_CCM_ANALOG_PLL_SYS) & 0x0000C000) == 0) {
        kHz = _OSC_KHZ;
      }
    }
    kHz = _Div(kHz, ((Cbcdr >> 27) & 7) + 1);    // PERIPH_CLK2_PODF
  } else {                                       // PERIPH_CLK_SEL = 0: pre_periph_clk
    Sel = (Cbcmr >> 18) & 3;                     // PRE_PERIPH_CLK_SEL
    if (Sel == 0) {                              // PLL2
      kHz = _GetPll2kHz();
    } else if (Sel == 1) {                       // PLL3 PFD3
      PllkHz = _GetPll3kHz();
      kHz    = _GetPfd3kHz(PllkHz, _CCM_ANALOG_PFD_480);
    } else if (Sel == 2) {                       // PLL2 PFD3 (ROM default)
      PllkHz = _GetPll2kHz();
      kHz    = _GetPfd3kHz(PllkHz, _CCM_ANALOG_PFD_528);
    } else {                                     // Divided PLL6
      kHz = _PLL6_DIV_KHZ;
    }
  }
  kHz = _Div(kHz, ((Cbcdr >> 10) & 7) + 1);      // AHB_PODF
  kHz = _Div(kHz, ((Cbcdr >>  8) & 3) + 1);      // IPG_PODF
  return kHz;
}

/*********************************************************************
*
*       _WaitCtrlClear()
*
*  Function description
*    Waits until the given OCOTP_CTRL bits are cleared.
*
*  Parameters
*    Mask       Bits to wait for.
*    TimeoutMs  Timeout in ms.
*
*  Return value
*    >= 0  O.K.
*    <  0  Timeout
*/
int _WaitCtrlClear(U32 Mask, int TimeoutMs) {
  int r;
  int t0;
  int t;
  U32 v;

  r  = -1;
  t  = 0;
  t0 = JLINK_GetTime();
  do {
    v = JLINK_MEM_ReadU32(_OCOTP_CTRL);
    if ((v & Mask) == 0) {
      r = 0;
    }
    t = JLINK_GetTime() - t0;
  } while ((r < 0) && (t < TimeoutMs));
  return r;
}

/*********************************************************************
*
*       _SetTiming()
*
*  Function description
*    Calculates and writes OCOTP_TIMING / OCOTP_TIMING2 for the given
*    ipg_clk frequency, see [1] 23.3.3 and Note (3).
*
*  Parameters
*    kHz  ipg_clk frequency in kHz.
*
*  Return value
*    >= 0  O.K.
*    <  0  Error, frequency not supported
*/
int _SetTiming(U32 kHz) {
  int r;
  U32 Wait;
  U32 Relax;
  U32 RelaxRead;
  U32 RelaxProg;
  U32 Relax1;
  U32 StrobeRead;
  U32 StrobeProg;
  U32 TpgmMin;
  U32 TpgmMax;
  U32 Timing;
  U32 Timing2;

  r = 0;
  if ((kHz == 0) || (kHz > _IPG_MAX_KHZ)) {
    JLINK_SYS_Report1("eFuse: ipg_clk not supported [kHz]: ", kHz);
    r = -1;
  }
  if (r >= 0) {
    //
    // All values below are field value + 1, i.e. number of cycles
    //
    Wait       = _NsToCycles(_T_WAIT_NS,       kHz);
    Relax      = _NsToCycles(_T_RELAX_NS,      kHz);
    RelaxRead  = _NsToCycles(_T_RELAX_READ_NS, kHz);
    RelaxProg  = _NsToCycles(_T_RELAX_PROG_NS, kHz);
    Relax1     = _NsToCycles(_T_RELAX1_NS,     kHz);
    if (RelaxRead > Relax) {
      StrobeRead = 2 * RelaxRead + _NsToCycles(_T_RD_NS, kHz);
    } else {
      StrobeRead = 2 * Relax     + _NsToCycles(_T_RD_NS, kHz);
    }
    StrobeProg = _NsToCycles(_T_PGM_NS, kHz) + RelaxProg + Relax;
    //
    // Check field widths
    //
    if ((Wait > 64) || (Relax > 16) || (StrobeRead > 64) || (StrobeProg > 4096) ||
        (RelaxRead > 64) || (RelaxProg > 4096) || (Relax1 > 256)) {
      JLINK_SYS_Report("eFuse: OCOTP timing out of range.");
      r = -1;
    }
  }
  if (r >= 0) {
    //
    // Check that tPGM is within 9..11 us for both formulas (RM and SDK)
    //
    TpgmMin = _NsToCycles(_T_PGM_MIN_NS, kHz);
    TpgmMax = _NsToCyclesFloor(_T_PGM_MAX_NS, kHz);
    if (((StrobeProg - 2 * RelaxProg) < TpgmMin) || ((StrobeProg - 2 * RelaxProg) > TpgmMax) ||
        ((StrobeProg - 2 * Relax)     < TpgmMin) || ((StrobeProg - 2 * Relax)     > TpgmMax)) {
      JLINK_SYS_Report("eFuse: tPGM out of 9..11 us window.");
      r = -1;
    }
  }
  if (r >= 0) {
    Timing  = ((Wait - 1) << 22) | ((StrobeRead - 1) << 16) | ((Relax - 1) << 12) | (StrobeProg - 1);
    Timing2 = ((Relax1 - 1) << 22) | ((RelaxRead - 1) << 16) | (RelaxProg - 1);
    JLINK_MEM_WriteU32(_OCOTP_TIMING,  Timing);
    JLINK_MEM_WriteU32(_OCOTP_TIMING2, Timing2);
    JLINK_SYS_Report1("eFuse: OCOTP_TIMING  = ", Timing);
    JLINK_SYS_Report1("eFuse: OCOTP_TIMING2 = ", Timing2);
  }
  return r;
}

/*********************************************************************
*
*       _ProgramWord()
*
*  Function description
*    Burns one OCOTP word, see [1] 23.3.1.3. Skips the burn if all
*    requested bits are already blown. Timing must be set up.
*
*  Parameters
*    Addr  OCOTP word index (0x00..0x3F).
*    Val   Bits to blow.
*
*  Return value
*    >= 0  O.K.
*    <  0  Error
*/
int _ProgramWord(U32 Addr, U32 Val) {
  int r;
  U32 v;

  r = 0;
  v = JLINK_MEM_ReadU32(_OCOTP_SHADOW_BASE + Addr * 0x10);
  if ((v & Val) == Val) {
    JLINK_SYS_Report1("eFuse: Bits already blown, skipping word ", Addr);
  } else {
    r = _WaitCtrlClear(_OCOTP_CTRL_BUSY | _OCOTP_CTRL_RELOAD, _TIMEOUT_IDLE_MS);
    if (r < 0) {
      JLINK_SYS_Report("eFuse: OCOTP busy.");
    }
    if (r >= 0) {
      JLINK_MEM_WriteU32(_OCOTP_CTRL_CLR, _OCOTP_CTRL_ERROR);
      JLINK_MEM_WriteU32(_OCOTP_CTRL, _OCOTP_WR_UNLOCK | (Addr & 0x3F));   // Unlock must be repeated for every word
      JLINK_MEM_WriteU32(_OCOTP_DATA, Val);                                // Starts the burn
      r = _WaitCtrlClear(_OCOTP_CTRL_BUSY, _TIMEOUT_BURN_MS);
      JLINK_SYS_Sleep(1);                                                  // Postamble: >= 2 us after BUSY clears, [1] 23.3.1.5
      if (r < 0) {
        JLINK_SYS_Report("eFuse: Timeout while burning.");
      }
    }
    if (r >= 0) {
      v = JLINK_MEM_ReadU32(_OCOTP_CTRL);
      if ((v & _OCOTP_CTRL_ERROR) != 0) {                                  // Word locked, nothing burned
        JLINK_SYS_Report1("eFuse: Burn rejected (word locked?), word ", Addr);
        JLINK_MEM_WriteU32(_OCOTP_CTRL_CLR, _OCOTP_CTRL_ERROR);
        r = -1;
      }
    }
    if (r >= 0) {
      JLINK_SYS_Report1("eFuse: Burned word ", Addr);
    }
  }
  return r;
}

/*********************************************************************
*
*       _ReloadShadows()
*
*  Function description
*    Reloads all shadow registers from the fuse array, see [1] 23.3.1.1.
*    Timing must be set up.
*
*  Return value
*    >= 0  O.K.
*    <  0  Error
*/
int _ReloadShadows(void) {
  int r;
  U32 v;

  r = _WaitCtrlClear(_OCOTP_CTRL_BUSY | _OCOTP_CTRL_RELOAD, _TIMEOUT_IDLE_MS);
  if (r >= 0) {
    JLINK_MEM_WriteU32(_OCOTP_CTRL_SET, _OCOTP_CTRL_RELOAD);
    r = _WaitCtrlClear(_OCOTP_CTRL_BUSY | _OCOTP_CTRL_RELOAD, _TIMEOUT_IDLE_MS);
  }
  if (r >= 0) {
    v = JLINK_MEM_ReadU32(_OCOTP_CTRL);
    if ((v & _OCOTP_CTRL_ERROR) != 0) {
      r = -1;
    }
  }
  if (r < 0) {
    JLINK_SYS_Report("eFuse: Shadow reload failed.");
  }
  return r;
}

/*********************************************************************
*
*       _ProgramFuses()
*
*  Function description
*    Complete eFuse sequence: ipg_clk, clocks, supply gate, timing,
*    burn, reload, verify, restore supply gate.
*
*  Return value
*    >= 0  O.K.
*    <  0  Error
*/
int _ProgramFuses(void) {
  int r;
  U32 kHz;
  U32 Cgpr;
  U32 v;

  kHz = _GetIpgkHz();
  JLINK_SYS_Report1("eFuse: ipg_clk [kHz] = ", kHz);
  //
  // OCOTP clock on, wait for controller idle (shadow load after reset), set timing
  //
  v = JLINK_MEM_ReadU32(_CCM_CCGR2);
  JLINK_MEM_WriteU32(_CCM_CCGR2, v | _CCM_CCGR2_CG6);
  r = _WaitCtrlClear(_OCOTP_CTRL_BUSY | _OCOTP_CTRL_RELOAD, _TIMEOUT_IDLE_MS);
  if (r < 0) {
    JLINK_SYS_Report("eFuse: OCOTP busy.");
  }
  if (r >= 0) {
    r = _SetTiming(kHz);
  }
  if (r >= 0) {
    JLINK_MEM_WriteU32(_OCOTP_CTRL_CLR, _OCOTP_CTRL_ERROR);
    Cgpr = JLINK_MEM_ReadU32(_CCM_CGPR);
    JLINK_MEM_WriteU32(_CCM_CGPR, Cgpr | _CCM_CGPR_EFUSE_SUPPLY);        // Program supply on
    //
    // Burn. For more words, call _ProgramWord() once per word here.
    //
    r = _ProgramWord(_FUSE_ADDR, _FUSE_VAL);
    if (r >= 0) {
      r = _ReloadShadows();
    }
    if (r >= 0) {
      v = JLINK_MEM_ReadU32(_OCOTP_SHADOW_BASE + _FUSE_ADDR * 0x10);
      if ((v & _FUSE_VAL) != _FUSE_VAL) {
        JLINK_SYS_Report1("eFuse: Verify failed, shadow = ", v);
        r = -1;
      }
    }
    JLINK_MEM_WriteU32(_CCM_CGPR, Cgpr);                                   // Program supply back to previous state
  }
  return r;
}

/*********************************************************************
*
*       Global functions
*
**********************************************************************
*/

/*********************************************************************
*
*       HandleAfterFlashProg()
*
*  Function description
*    Called by the J-Link DLL right after flash programming.
*    Burns the configured eFuse word, see Notes in the file header.
*
*  Return value
*    >= 0  O.K.
*    <  0  Error
*/
int HandleAfterFlashProg(void) {
  int r;

  r = 0;
  if (_FUSE_VAL != 0) {
    r = _ProgramFuses();
    if (r < 0) {
      JLINK_SYS_Report("eFuse: FAILED.");
    } else {
      JLINK_SYS_Report("eFuse: O.K.");
    }
  }
  return r;
}

/*************************** End of file ****************************/
