/*********************************************************************
*
*       Constants (similar to defines)
*
**********************************************************************
*/

/*----------------- Pin mapping for the TRACE signals ----------------
  Copy 0
    P6_4  DS #5   cpuss.swj_swo_tdo     (SWO DATA)
    P7_0  ACT #14 cpuss.trace_clock     (ETM/SWO TRACECLK)
    P9_3  ACT #15 cpuss.trace_data[0]:0 (ETM TRACEDATA[0])
    P9_2  ACT #15 cpuss.trace_data[1]:0 (ETM TRACEDATA[1])
    P9_1  ACT #15 cpuss.trace_data[2]:0 (ETM TRACEDATA[2])
    P9_0  ACT #15 cpuss.trace_data[3]:0 (ETM TRACEDATA[3])
  Copy 1
    P10_3 ACT #15 cpuss.trace_data[0]:1 (ETM TRACEDATA[0])
    P10_2 ACT #15 cpuss.trace_data[1]:1 (ETM TRACEDATA[1])
    P10_1 ACT #15 cpuss.trace_data[2]:1 (ETM TRACEDATA[2])
    P10_0 ACT #15 cpuss.trace_data[3]:1 (ETM TRACEDATA[3])
  Copy 2
    P7_7  ACT #15 cpuss.trace_data[0]:2 (ETM TRACEDATA[0])
    P7_6  ACT #15 cpuss.trace_data[1]:2 (ETM TRACEDATA[1])
    P7_5  ACT #15 cpuss.trace_data[2]:2 (ETM TRACEDATA[2])
    P7_4  ACT #15 cpuss.trace_data[3]:2 (ETM TRACEDATA[3])
  See 'Multiple Alternate Functions' table in device datasheet.

  Examples:
  1) CY8CKIT-062-WIFI-BT Pioneer Kit, J12:
     TCLK => P7_0, TD0 => P9_3, TD1 => P7_6, TD2 => P7_5, TD3 => P7_4  
*/

/* Global variables */
U32 _IS_TRACE_CONFIGURED = 0x0;

/* Trace clock setup registers */
__constant U32 _PERI_CLOCK_CTL50_ADDR       = 0x40000CC8; // Clock control register for cpuss.clock_trace_in
__constant U32 _PERI_CLOCK_CTL_DIV_SEL_MASK = 0x0000000F; // PERI_CLOCK_CTL.DIV_SEL
__constant U32 _PERI_DIV_8_CTL0_ADDR        = 0x40001000; // Divider control (for 8.0 divider)
__constant U32 _PERI_DIV_CMD_ADDR           = 0x40000400; // Divider command
__constant U32 _PERI_DIV_CMD_ENABLE_MASK    = 0x80000000; // ENABLE field in PERI_DIV_CMD
__constant U32 _PERI_DIV_CMD_DISABLE_MASK   = 0x40000000; // DISABLE field in PERI_DIV_CMD
__constant U32 _PERI_DIV_CMD_PA_SEL_ROL     = 0x00000010; // PA_TYPE_SEL + PA_DIV_SEL fields offset in PERI_DIV_CMD
__constant U32 _PERI_DIV_PA_SEL_MASK        = 0x000003FF; // PA_TYPE_SEL + PA_DIV_SEL fields mask (size)
/* Trace pins setup registers */
__constant U32 _HSIOM_PRT7_PORT_SEL0        = 0x40300070; // Port 7 selection 0
__constant U32 _HSIOM_PRT9_PORT_SEL0        = 0x40300090; // Port 9 selection 0
__constant U32 _HSIOM_PRT10_PORT_SEL0       = 0x403000A0; // Port 10 selection 0
__constant U32 _GPIO_PRT7_CFG               = 0x403103C4; // Port 7 configuration
__constant U32 _GPIO_PRT9_CFG               = 0x403104C4; // Port 9 configuration
__constant U32 _GPIO_PRT10_CFG              = 0x40310544; // Port 10 configuration
__constant U32 _GPIO_PRT7_CFG_OUT           = 0x403103CC; // Port 7 output buffer configuration
__constant U32 _GPIO_PRT9_CFG_OUT           = 0x403104CC; // Port 9 output buffer configuration
__constant U32 _GPIO_PRT10_CFG_OUT          = 0x4031054C; // Port 10 output buffer configuration
__constant U32 _PRT_IO_SEL_MASK             = 0x1F; // Mask for IO[pin]_SEL field in HSIOM_PRT[port]_PORT_SEL[0/1] register
__constant U32 _PRT_DRIVE_MODE_MASK         = 0xF;  // Mask for IN_EN[pin] & DRIVE_MODE[pin] fields in GPIO_PRT[port]_CFG register
__constant U32 _PRT_SLOW_MASK               = 0x1;  // Mask for SLOW[pin] field in GPIO_PRT[port]_CFG_OUT register
__constant U32 _PRT_DRIVE_SEL_MASK          = 0x3;  // Mask for DRIVE_SEL[pin] field in GPIO_PRT[port]_CFG_OUT register

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

/*********************************************************************
*
* _SetupTraceClock() - Selects TPIU Clock divider for ETM Trace.
*/
int _SetupTraceClock(void) {	
  U32 ClockCtlVal;
  U32 ClockDivCtlVal;
  U32 ClockDivSel;
  U32 ClockDivVal;
  U32 ClockDivCmd;
  U32 TRACE_CLOCK_CTL_ADDR;
  U32 TRACE_CLOCK_DIV_CTL_ADDR;
  U32 TRACE_CLOCK_DIV_CMD_ADDR;
  
  TRACE_CLOCK_CTL_ADDR     = _PERI_CLOCK_CTL50_ADDR;
  TRACE_CLOCK_DIV_CTL_ADDR = _PERI_DIV_8_CTL0_ADDR;
  TRACE_CLOCK_DIV_CMD_ADDR = _PERI_DIV_CMD_ADDR;

  ClockDivSel = (7 & _PERI_CLOCK_CTL_DIV_SEL_MASK); // Peripheral clock divider index to use for trace clock
  ClockDivVal = (0 & _PERI_DIV_PA_SEL_MASK);        // Peripheral clock divider value for trace clock
                                                    // Actual divider is (1+ClockDivVal)

  ClockCtlVal = JLINK_MEM_ReadU32(TRACE_CLOCK_CTL_ADDR);
  ClockDivCtlVal = JLINK_MEM_ReadU32(TRACE_CLOCK_DIV_CTL_ADDR + (ClockDivSel*4));
  if((ClockCtlVal != ClockDivSel) || (ClockDivCtlVal != ((ClockDivVal << _PERI_DIV_CMD_PA_SEL_ROL) | 0x1))){
    JLINK_SYS_Report("JLinkScript/Trace: Setup TPIU clock");
    //
    // Select TPIU Clock divider
    //
    
    // DISABLE 8.0 DIV in PERI_DIV_CMD:
    ClockDivCmd = _PERI_DIV_CMD_DISABLE_MASK | (_PERI_DIV_PA_SEL_MASK << _PERI_DIV_CMD_PA_SEL_ROL) | ClockDivSel;
    JLINK_MEM_WriteU32(TRACE_CLOCK_DIV_CMD_ADDR, ClockDivCmd);
    // Use selected divider (8.0) for cpuss.clock_trace_in
    JLINK_MEM_WriteU32(TRACE_CLOCK_CTL_ADDR, ClockDivSel);
    // Set 8.0 DIV = ClockDivVal
    JLINK_MEM_WriteU32((TRACE_CLOCK_DIV_CTL_ADDR+(ClockDivSel*4)), (ClockDivVal << 8));
    // ENABLE 8.0 DIV 
    ClockDivCmd = _PERI_DIV_CMD_ENABLE_MASK | (_PERI_DIV_PA_SEL_MASK << _PERI_DIV_CMD_PA_SEL_ROL) | ClockDivSel;
    JLINK_MEM_WriteU32(TRACE_CLOCK_DIV_CMD_ADDR, ClockDivCmd);
  }

  return 0;  
}

/*********************************************************************
*
* _SetupTracePin() - Configures Trace Pin.
* Parameters:
*   pin:                  Pin number
*   hsiomPrtPortSel0Addr: HSIOM_PRT[port]_PORT_SEL0 register address
*   ioSelVal:             IO[pin]_SEL field value (connection) for HSIOM_PRT[port]_PORT_SEL register
*   gpioPrtCfgAddr:       GPIO_PRT[port]_CFG register address
*   gpioPrtCfgOutAddr:    GPIO_PRT[port]_CFG_OUT register address
*/
int _SetupTracePin(U32 pin,
                   U32 hsiomPrtPortSel0Addr, U32 ioSelVal,
                   U32 gpioPrtCfgAddr,
                   U32 gpioPrtCfgOutAddr) {	
  U32 reg0;
  U32 reg1;
  U32 offset;
  U32 hsiomRegAddr; // Address of HSIOM_PRT[port]_PORT_SEL0 or HSIOM_PRT[port]_PORT_SEL1
  U32 pMode;        // pin drive mode
  U32 pSlew;        // pin slew rate
  U32 pStrange;     // pin drive strange
  
  //
  // Select pin route connection in HSIOM_PRT[port]_PORT_SEL[0/1] register
  // See HSIOM_PRT0_PORT_SEL0 in registers TRM for the bit-field map of HSIOM_PRT[port]_PORT_SEL0 registers
  // See HSIOM_PRT2_PORT_SEL1 in registers TRM for the bit-field map of HSIOM_PRT[port]_PORT_SEL1 registers
  if (pin < 4) { /* Pin[0-3] selection is in HSIOM_PRT[port]_PORT_SEL0 register */
    hsiomRegAddr = hsiomPrtPortSel0Addr; // Use HSIOM_PRT[port]_PORT_SEL0
    offset = pin * 8; // Offset of the IO[pin]_SEL field for required pin number,
                      // where 8 = 5 bits for IO[pin]_SEL field + 3 not used bits
  }
  else { /* Pin[4-7] selection is in HSIOM_PRT[port]_PORT_SEL1 register */
    hsiomRegAddr = hsiomPrtPortSel0Addr + 4; // Use HSIOM_PRT[port]_PORT_SEL1
    offset = (pin - 4) * 8; // Offset of the IO[pin]_SEL field for required pin number,
                            // where 8 = 5 bits for IO[pin]_SEL field + 3 not used bits
  }
  reg0 = JLINK_MEM_ReadU32(hsiomRegAddr);
  reg1 = reg0;
  reg1 &= ~(_PRT_IO_SEL_MASK << offset); // Clear IO[pin]_SEL field
  reg1 |=  (ioSelVal         << offset); // Set field value
  if (reg0 |= reg1) {
    JLINK_MEM_WriteU32(hsiomRegAddr, reg1);
  }
  
  //
  // Disable input buffer and set drive mode in GPIO_PRT[port]_CFG register
  // See GPIO_PRT2_CFG in registers TRM for the bit-field map:
  pMode = 6;      // DRIVE_MODE[pin]:
                  //  0: HIGHZ:         Output buffer is off creating a high impedance input (default)
                  //  1: RESERVED:      This mode is reserved and should not be used
                  //  2: PULLUP:        Resistive pull up
                  //  3: PULLDOWN:      Resistive pull down
                  //  4: OD_DRIVESLOW:  Open drain, drives low
                  //  5: OD_DRIVESHIGH: Open drain, drives high
                  //  6: STRONG:        Strong D_OUTput buffer
                  //  7: PULLUP_DOWN:   Pull up or pull down
  reg0 = JLINK_MEM_ReadU32(gpioPrtCfgAddr);
  reg1 = reg0;
  offset = pin * 4; // Offset of the DRIVE_MODE[pin] field for required pin number,
                    // where 4 = 3 bits for DRIVE_MODE[pin] + 1 bit for IN_EN fields
  reg1 &= ~(_PRT_DRIVE_MODE_MASK << offset); // Clear IN_EN[pin] and DRIVE_MODE[pin] fields
  reg1 |=  (pMode                << offset); // Set DRIVE_MODE[pin] field value
  if (reg0 |= reg1) {
    JLINK_MEM_WriteU32(gpioPrtCfgAddr, reg1);
  }
  
  //
  // Set slew rate and drive strength in GPIO_PRT[port]_CFG_OUT register
  // See GPIO_PRT2_CFG_OUT in registers TRM for the bit-field map:
  pSlew = 0x0;    // SLOW[pin]:  
                  //  0 - Fast slew rate (default)
                  //  1 - Slow slew rate
  pStrange = 0x3; // DRIVE_SEL[pin]:  
                  //  0 - FULL_DRIVE:        Full drive strength: GPIO drives current at its max rated spec.
                  //  1 - ONE_HALF_DRIVE:    1/2 drive strength: GPIO drives current at 1/2 of its max rated spec (default)
                  //  2 - ONE_QUARTER_DRIVE: 1/4 drive strength: GPIO drives current at 1/4 of its max rated spec.
                  //  3 - ONE_EIGHTH_DRIVE:  1/8 drive strength: GPIO drives current at 1/8 of its max rated spec.
  reg0 = JLINK_MEM_ReadU32(gpioPrtCfgOutAddr);
  reg1 = reg0;
  offset = pin;
  reg1 &= ~(_PRT_SLOW_MASK << offset); // Clear SLOW[pin] field
  reg1 |=  (pSlew          << offset); // Set field value
  offset = 16 + pin * 2;               // Offset of the DRIVE_SEL[pin] field for required pin number,
                                       // where '16' is the offset of DRIVE_SEL[pin] for pin 0 and '2' is the size of DRIVE_SEL[pin]
  reg1 &= ~(_PRT_DRIVE_SEL_MASK << offset); // Clear DRIVE_SEL[pin] field
  reg1 |=  (pStrange            << offset); // Set field value
  if (reg0 |= reg1) {
    JLINK_MEM_WriteU32(gpioPrtCfgOutAddr, reg1);
  }
  
  return 0;
}

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

int ConfigTargetSettings(void) {
  //
  // Mark a specific memory region as memory type illegal
  // in order to make sure that the software is not allowed to access these regions
  // 
  // Note: This does not work for J-Flash tool
  //

  // Exclude SFLASH regions
  JLINK_ExecCommand("map region 0x16000000-0x160007FF XI"); // [SFLASH Start - User Data Start]
  JLINK_ExecCommand("map region 0x16001000-0x160019FF XI"); // [User Data End - NAR Start]
  JLINK_ExecCommand("map region 0x16001C00-0x160059FF XI"); // [NAR End - Public Key Start]
  JLINK_ExecCommand("map region 0x16006600-0x16007BFF XI"); // [Public Key End - TOC2 Start]

  // Exclude Cy Metadata
  JLINK_ExecCommand("map region 0x90300000-0x903FFFFF XI"); // Cy Checksum
  JLINK_ExecCommand("map region 0x90500000-0x905FFFFF XI"); // Cy Metadata
}

void InitTarget(void) {
  Report("JLinkScript/InitTarget: CORESIGHT setup");
  CORESIGHT_AddAP(0, CORESIGHT_AHB_AP);  // SYSAP
  CORESIGHT_AddAP(1, CORESIGHT_AHB_AP);  // AHB-AP used to connect to M0+ core
  CORESIGHT_AddAP(2, CORESIGHT_AHB_AP);  // AHB-AP used to connect to M4 core
  CORESIGHT_IndexAHBAPToUse = 2;
  CPU=CORTEX_M4;
}

/*********************************************************************
*
*       OnTraceStart()
*
*  Function description
*    If present, called right before trace is started.
*    Used to initialize MCU specific trace related things like configuring the trace pins for alternate function.
*
*  Return value
*    >= 0:  O.K.
*     < 0:  Error
*
*  Notes
*    (1) May use high-level API functions like JLINK_MEM_ etc.
*    (2) Should not call JLINK_TARGET_Halt(). Can rely on target being halted when entering this function
*/
int OnTraceStart(void) {
  U32 PortWidth;
  U32 IO_SEL_ACT14;
  U32 IO_SEL_ACT15;
  //U32 IO_SEL_DS5;

  if (_IS_TRACE_CONFIGURED) {
	  return 0;
  }

  // Adjust sampling point of trace pin (Optional: not needed for this cpu)
  // JLINK_ExecCommand("TraceSampleAdjust TD=2000");   
  
  // Setup peripheral clocks for tracing
  _SetupTraceClock();     
  
  // Setup pins for tracing: TCLK > P7_0, TD0 > P9_3, TD1 > P7_6, TD2 > P7_5, TD3 > P7_4
  PortWidth = JLINK_TRACE_PortWidth;      
  JLINK_SYS_Report("JLinkScript/Trace: Setup clock and data pins");
  IO_SEL_ACT14 = 0x1A; // Connection route for 'cpuss.trace_clock' signal (P7_0)
  IO_SEL_ACT15 = 0x1B; // Connection route for 'cpuss.trace_data[0-3]' signals (P7, P9 and P10)
  //IO_SEL_DS5 =   0x1D; // Connection route for 'cpuss.swj_swo_tdo' signal (P6_4)

  _SetupTracePin( /*P7_0*/ 0, _HSIOM_PRT7_PORT_SEL0, IO_SEL_ACT14, _GPIO_PRT7_CFG, _GPIO_PRT7_CFG_OUT);
  _SetupTracePin( /*P9_3*/ 3, _HSIOM_PRT9_PORT_SEL0, IO_SEL_ACT15, _GPIO_PRT9_CFG, _GPIO_PRT9_CFG_OUT);
  _SetupTracePin( /*P7_6*/ 6, _HSIOM_PRT7_PORT_SEL0, IO_SEL_ACT15, _GPIO_PRT7_CFG, _GPIO_PRT7_CFG_OUT);  
  if (PortWidth > 2) {
    _SetupTracePin( /*P7_5*/ 5, _HSIOM_PRT7_PORT_SEL0, IO_SEL_ACT15, _GPIO_PRT7_CFG, _GPIO_PRT7_CFG_OUT);  
    _SetupTracePin( /*P7_4*/ 4, _HSIOM_PRT7_PORT_SEL0, IO_SEL_ACT15, _GPIO_PRT7_CFG, _GPIO_PRT7_CFG_OUT);  
  }
  
  _IS_TRACE_CONFIGURED = 1;
  return 0;
}

