/*
 * Copyright (c) 2013-2017 ARM Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ----------------------------------------------------------------------
 *
 * $Date:        1. December 2017
 * $Revision:    V2.0.0
 *
 * Project:      CMSIS-DAP Source
 * Title:        SW_DP.c CMSIS-DAP SW DP I/O
 *
 *---------------------------------------------------------------------------*/

#include <stdio.h>

#include "DAP_config.h"
#include "cmsis-dap/include/DAP.h"
#include "cmsis-dap/include/spi_op.h"
#include "cmsis-dap/include/spi_switch.h"
#include "cmsis-dap/include/dap_utility.h"


// Debug
#define PRINT_SWD_PROTOCOL 0

// SW Macros

#define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay)

#define PIN_SWCLK_SET PIN_SWCLK_TCK_SET
#define PIN_SWCLK_CLR PIN_SWCLK_TCK_CLR

// Space for time in the original version,
// and time for space in our implementation

#define SW_CLOCK_CYCLE()                \
  PIN_SWCLK_CLR();                      \
  if (need_delay) { PIN_DELAY(); }      \
  PIN_SWCLK_SET();                      \
  if (need_delay) { PIN_DELAY(); }

#define SW_WRITE_BIT(bit)               \
  PIN_SWDIO_OUT(bit);                   \
  PIN_SWCLK_CLR();                      \
  if (need_delay) { PIN_DELAY(); }      \
  PIN_SWCLK_SET();                      \
  if (need_delay) { PIN_DELAY(); }

#define SW_READ_BIT(bit)                \
  PIN_SWCLK_CLR();                      \
  if (need_delay) { PIN_DELAY(); }      \
  bit = PIN_SWDIO_IN();                 \
  PIN_SWCLK_SET();                      \
  if (need_delay) { PIN_DELAY(); }



uint8_t SWD_TransferSpeed = kTransfer_GPIO_normal;


void SWJ_Sequence_GPIO (uint32_t count, const uint8_t *data, uint8_t need_delay);
void SWJ_Sequence_SPI (uint32_t count, const uint8_t *data);

void SWD_Sequence_GPIO (uint32_t info, const uint8_t *swdo, uint8_t *swdi);
void SWD_Sequence_SPI (uint32_t info, const uint8_t *swdo, uint8_t *swdi);


// Generate SWJ Sequence
//   count:  sequence bit count
//   data:   pointer to sequence bit data
//   return: none
#if ((DAP_SWD != 0) || (DAP_JTAG != 0))
void SWJ_Sequence (uint32_t count, const uint8_t *data) {
  // if (count != 8 && count != 16 && count!= 51)
  // {
  //   os_printf("[ERROR] wrong SWJ Swquence length:%d\r\n", (int)count);
  //   return;
  // }

  if(SWD_TransferSpeed == kTransfer_SPI) {
    SWJ_Sequence_SPI(count, data);
  } else {
    SWJ_Sequence_GPIO(count, data, 1);
  }

}


void SWJ_Sequence_GPIO (uint32_t count, const uint8_t *data, uint8_t need_delay) {
    uint32_t val;
    uint32_t n;

    val = 0U;
    n = 0U;
    while (count--) {
      if (n == 0U) {
        val = *data++;
        n = 8U;
      }
      if (val & 1U) {
        PIN_SWDIO_TMS_SET();
      } else {
        PIN_SWDIO_TMS_CLR();
      }
      SW_CLOCK_CYCLE();
      val >>= 1;
      n--;
    }
}

void SWJ_Sequence_SPI (uint32_t count, const uint8_t *data) {
  DAP_SPI_Enable();
  DAP_SPI_WriteBits(count, data);
}
#endif


// Generate SWD Sequence
//   info:   sequence information
//   swdo:   pointer to SWDIO generated data
//   swdi:   pointer to SWDIO captured data
//   return: none
#if (DAP_SWD != 0)
void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
  if (SWD_TransferSpeed == kTransfer_SPI) {
    SWD_Sequence_SPI(info, swdo, swdi);
  } else {
    SWD_Sequence_GPIO(info, swdo, swdi);
  }
}

void SWD_Sequence_GPIO (uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
  const uint8_t need_delay = 1;

  uint32_t val;
  uint32_t bit;
  uint32_t n, k;

  n = info & SWD_SEQUENCE_CLK;
  if (n == 0U) {
    n = 64U;
  }
  // n = 1 ~ 64

  // LSB
  if (info & SWD_SEQUENCE_DIN) {
    while (n) {
      val = 0U;
      for (k = 8U; k && n; k--, n--) {
        SW_READ_BIT(bit);
        val >>= 1;
        val  |= bit << 7;
      }
      val >>= k;
      *swdi++ = (uint8_t)val;
    }
  } else {
    while (n) {
      val = *swdo++;
      for (k = 8U; k && n; k--, n--) {
        SW_WRITE_BIT(val);
        val >>= 1;
      }
    }
  }
}

void SWD_Sequence_SPI (uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
  uint32_t n;
  n = info & SWD_SEQUENCE_CLK;
  if (n == 0U) {
    n = 64U;
  }
  // n = 1 ~ 64

  if (info & SWD_SEQUENCE_DIN) {
    DAP_SPI_ReadBits(n, swdi);
  } else {
    DAP_SPI_WriteBits(n, swdo);
  }
}

#endif


#if (DAP_SWD != 0)


// SWD Transfer I/O
//   request: A[3:2] RnW APnDP
//   data:    DATA[31:0]
//   return:  ACK[2:0]
static uint8_t SWD_Transfer_SPI (uint32_t request, uint32_t *data) {
  //// FIXME: overrun detection
  // SPI transfer mode does not require operations such as PIN_DELAY
  uint8_t ack;
  // uint32_t bit;
  uint32_t val;
  uint8_t parity;
  uint8_t computedParity;

  uint32_t n;

  const uint8_t constantBits = 0b10000001U; /* Start Bit  & Stop Bit & Park Bit is fixed. */
  uint8_t requestByte;  /* LSB */


  DAP_SPI_Enable();

  requestByte = constantBits | (((uint8_t)(request & 0xFU)) << 1U) | (ParityEvenUint8(request & 0xFU) << 5U);
  /* For 4bit, Parity can be equivalent to 8bit with all 0 high bits */

#if (PRINT_SWD_PROTOCOL == 1)
  switch (requestByte)
    {
    case 0xA5U:
      os_printf("IDCODE\r\n");
      break;
    case 0xA9U:
      os_printf("W CTRL/STAT\r\n");
      break;
    case 0xBDU:
      os_printf("RDBUFF\r\n");
      break;
    case 0x8DU:
      os_printf("R CTRL/STAT\r\n");
      break;
    case 0x81U:
      os_printf("W ABORT\r\n");
      break;
    case 0xB1U:
      os_printf("W SELECT\r\n");
      break;
    case 0xBBU:
      os_printf("W APc\r\n");
      break;
    case 0x9FU:
      os_printf("R APc\r\n");
      break;
    case 0x8BU:
      os_printf("W AP4\r\n");
      break;
    case 0xA3U:
      os_printf("W AP0\r\n");
      break;
    case 0X87U:
      os_printf("R AP0\r\n");
      break;
    case 0xB7U:
      os_printf("R AP8\r\n");
      break;
    default:
    //W AP8
      os_printf("Unknown:%08x\r\n", requestByte);
      break;
    }
#endif

  if (request & DAP_TRANSFER_RnW) {
    /* Read data */

    DAP_SPI_Send_Header(requestByte, &ack, 0); // 0 Trn After ACK
    if (ack == DAP_TRANSFER_OK) {
      DAP_SPI_Read_Data(&val, &parity);
      computedParity = ParityEvenUint32(val);

      if ((computedParity ^ parity) & 1U) {
        ack = DAP_TRANSFER_ERROR;
      }
      if (data) { *data = val; }

      /* Capture Timestamp */
      if (request & DAP_TRANSFER_TIMESTAMP) {
        DAP_Data.timestamp = TIMESTAMP_GET();
      }

    }
    else if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) {
#if defined CONFIG_IDF_TARGET_ESP8266 || defined CONFIG_IDF_TARGET_ESP32
      DAP_SPI_Generate_Cycle(1);
#elif defined CONFIG_IDF_TARGET_ESP32C3
      DAP_SPI_Fast_Cycle();
#endif

#if (PRINT_SWD_PROTOCOL == 1)
      os_printf("WAIT\r\n");
#endif

      // return DAP_TRANSFER_WAIT;
    }
    else {
      /* Protocol error */
      DAP_SPI_Disable();
      PIN_SWDIO_TMS_SET();

      DAP_SPI_Enable();
      DAP_SPI_Protocol_Error_Read();

      DAP_SPI_Disable();
      PIN_SWDIO_TMS_SET();
#if (PRINT_SWD_PROTOCOL == 1)
      os_printf("Protocol Error: Read\r\n");
#endif
    }

    return ((uint8_t)ack);
  }
  else {
    /* Write data */
    parity = ParityEvenUint32(*data);
    DAP_SPI_Send_Header(requestByte, &ack, 1); // 1 Trn After ACK
    if (ack == DAP_TRANSFER_OK) {
      DAP_SPI_Write_Data(*data, parity);
      /* Capture Timestamp */
      if (request & DAP_TRANSFER_TIMESTAMP) {
        DAP_Data.timestamp = TIMESTAMP_GET();
      }
      /* Idle cycles */
      n = DAP_Data.transfer.idle_cycles;
      if (n) { DAP_SPI_Generate_Cycle(n); }

      DAP_SPI_Disable();
      PIN_SWDIO_TMS_SET();

      return ((uint8_t)ack);
    }
    else if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) {
      /* already turnaround. */

      /* TODO: overrun transfer -> for read */
#if (PRINT_SWD_PROTOCOL == 1)
      os_printf("WAIT\r\n");
#endif

    }
    else {
      /* Protocol error */
      DAP_SPI_Disable();
      PIN_SWDIO_TMS_SET();

      DAP_SPI_Enable();
      DAP_SPI_Protocol_Error_Write();

      DAP_SPI_Disable();
      PIN_SWDIO_TMS_SET();

#if (PRINT_SWD_PROTOCOL == 1)
      os_printf("Protocol Error: Write\r\n");
#endif
    }

    return ((uint8_t)ack);

  }

  return DAP_TRANSFER_ERROR;
}



static uint8_t SWD_Transfer_GPIO (uint32_t request, uint32_t *data, uint8_t need_delay) {
  uint32_t ack;
  uint32_t bit;
  uint32_t val;
  uint32_t parity;

  uint32_t n;

  /* Packet Request */
  parity = 0U;
  SW_WRITE_BIT(1U);                     /* Start Bit */
  bit = request >> 0;
  SW_WRITE_BIT(bit);                    /* APnDP Bit */
  parity += bit;
  bit = request >> 1;
  SW_WRITE_BIT(bit);                    /* RnW Bit */
  parity += bit;
  bit = request >> 2;
  SW_WRITE_BIT(bit);                    /* A2 Bit */
  parity += bit;
  bit = request >> 3;
  SW_WRITE_BIT(bit);                    /* A3 Bit */
  parity += bit;
  SW_WRITE_BIT(parity);                 /* Parity Bit */
  SW_WRITE_BIT(0U);                     /* Stop Bit */
  SW_WRITE_BIT(1U);                     /* Park Bit */

  /* Turnaround */
  PIN_SWDIO_OUT_DISABLE();
  for (n = DAP_Data.swd_conf.turnaround; n; n--) {
    SW_CLOCK_CYCLE();
  }

  /* Acknowledge response */
  SW_READ_BIT(bit);
  ack  = bit << 0;
  SW_READ_BIT(bit);
  ack |= bit << 1;
  SW_READ_BIT(bit);
  ack |= bit << 2;

  if (ack == DAP_TRANSFER_OK) {         /* OK response */
    /* Data transfer */
    if (request & DAP_TRANSFER_RnW) {
      /* Read data */
      val = 0U;
      parity = 0U;
      for (n = 32U; n; n--) {
        SW_READ_BIT(bit);               /* Read RDATA[0:31] */
        parity += bit;
        val >>= 1;
        val  |= bit << 31;
      }
      SW_READ_BIT(bit);                 /* Read Parity */
      if ((parity ^ bit) & 1U) {
        ack = DAP_TRANSFER_ERROR;
      }
      if (data) { *data = val; }
      /* Turnaround */
      for (n = DAP_Data.swd_conf.turnaround; n; n--) {
        SW_CLOCK_CYCLE();
      }
      PIN_SWDIO_OUT_ENABLE();
    } else {
      /* Turnaround */
      for (n = DAP_Data.swd_conf.turnaround; n; n--) {
        SW_CLOCK_CYCLE();
      }
      PIN_SWDIO_OUT_ENABLE();
      /* Write data */
      val = *data;
      parity = 0U;
      for (n = 32U; n; n--) {
        SW_WRITE_BIT(val);              /* Write WDATA[0:31] */
        parity += val;
        val >>= 1;
      }
      SW_WRITE_BIT(parity);             /* Write Parity Bit */
    }
    /* Capture Timestamp */
    if (request & DAP_TRANSFER_TIMESTAMP) {
      DAP_Data.timestamp = TIMESTAMP_GET();
    }
    /* Idle cycles */
    n = DAP_Data.transfer.idle_cycles;
    if (n) {
      PIN_SWDIO_OUT(0U);
      for (; n; n--) {
        SW_CLOCK_CYCLE();
      }
    }
    PIN_SWDIO_OUT(1U);
    return ((uint8_t)ack);
  }

  if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) {
    /* WAIT or FAULT response */
    if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) {
      for (n = 32U+1U; n; n--) {
        SW_CLOCK_CYCLE();               /* Dummy Read RDATA[0:31] + Parity */
      }
    }
    /* Turnaround */
    for (n = DAP_Data.swd_conf.turnaround; n; n--) {
      SW_CLOCK_CYCLE();
    }
    PIN_SWDIO_OUT_ENABLE();
    if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) {
      PIN_SWDIO_OUT(0U);
      for (n = 32U+1U; n; n--) {
        SW_CLOCK_CYCLE();               /* Dummy Write WDATA[0:31] + Parity */
      }
    }
    PIN_SWDIO_OUT(1U);
    return ((uint8_t)ack);
  }

  /* Protocol error */
  for (n = DAP_Data.swd_conf.turnaround + 32U + 1U; n; n--) {
    SW_CLOCK_CYCLE();                   /* Back off data phase */
  }
  PIN_SWDIO_OUT_ENABLE();
  PIN_SWDIO_OUT(1U);
  return ((uint8_t)ack);
}


// SWD Transfer I/O
//   request: A[3:2] RnW APnDP
//   data:    DATA[31:0]
//   return:  ACK[2:0]
uint8_t  SWD_Transfer(uint32_t request, uint32_t *data) {
  switch (SWD_TransferSpeed) {
    case kTransfer_SPI:
      return SWD_Transfer_SPI(request, data);
    case kTransfer_GPIO_fast:
      return SWD_Transfer_GPIO(request, data, 0);
    case kTransfer_GPIO_normal:
      return SWD_Transfer_GPIO(request, data, 1);
    default:
      return SWD_Transfer_GPIO(request, data, 1);
  }
}


#endif  /* (DAP_SWD != 0) */