text: /*
   * Copyright (C) 2017 C-SKY Microsystems Co., Ltd. All rights reserved.
   *
   * 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
   *
   *   http://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.
   */
  
  /******************************************************************************
   * @file     ck_trng.c
   * @brief    CSI Source File for TRNG Driver
   * @version  V1.0
   * @date     02. June 2017
   ******************************************************************************/
  #include <stdbool.h>
  #include <stdio.h>
  #include <string.h>
  #include "drv_trng.h"
  #include "ck_trng.h"
  
  
  #define ERR_TRNG(errno) (CSI_DRV_ERRNO_TRNG_BASE | errno)
  #define TRNG_NULL_PARAM_CHK(para)                         \
          do {                                        \
              if (para == NULL) {                     \
                  return ERR_TRNG(EDRV_PARAMETER);   \
              }                                       \
          } while (0)
  
  typedef struct {
      uint32_t base;
      trng_event_cb_t cb;
      trng_status_t status;
  } ck_trng_priv_t;
  
  static ck_trng_priv_t trng_handle[CONFIG_TRNG_NUM];
  
  /* Driver Capabilities */
  static const trng_capabilities_t driver_capabilities = {
      .lowper_mode = 1 /* low power mode */
  };
  
  //
  // Functions
  //
  
  ck_trng_reg_t *trng_reg = NULL;
  
  static int32_t trng_enable(void)
  {
      trng_reg->TCR |= TRNG_EN;
      return 0;
  }
  
  static int32_t trng_get_data(void)
  {
      int data = trng_reg->TDR;
      return data;
  }
  
  static int32_t trng_data_is_ready(void)
  {
      int flag = (trng_reg->TCR & TRNG_DATA_READY);
      return flag;
  }
  
  
  int32_t __attribute__((weak)) target_get_trng_count(void)
  {
      return 0;
  }
  
  int32_t __attribute__((weak)) target_get_trng(int32_t idx, uint32_t *base)
  {
      return NULL;
  }
  /**
    \brief       get trng handle count.
    \return      trng handle count
  */
  int32_t csi_trng_get_instance_count(void)
  {
      return target_get_trng_count();
  }
  
  /**
    \brief       Initialize TRNG Interface. 1. Initializes the resources needed for the TRNG interface 2.registers event callback function
    \param[in]   idx  must not exceed return value of csi_trng_get_instance_count()
    \param[in]   cb_event  Pointer to \ref trng_event_cb_t
    \return      pointer to trng handle
  */
  trng_handle_t csi_trng_initialize(int32_t idx, trng_event_cb_t cb_event)
  {
  
      if (idx < 0 || idx >= CONFIG_TRNG_NUM) {
          return NULL;
      }
  
      /* obtain the trng information */
      uint32_t base = 0u;
      int32_t real_idx = target_get_trng(idx, &base);
  
      if (real_idx != idx) {
          return NULL;
      }
  
      ck_trng_priv_t *trng_priv = &trng_handle[idx];
      trng_priv->base = base;
  
      /* initialize the trng context */
      trng_reg = (ck_trng_reg_t *)(trng_priv->base);
      trng_priv->cb = cb_event;
      trng_priv->status.busy = 0;
      trng_priv->status.data_valid = 0;
  
      return (trng_handle_t)trng_priv;
  }
  
  /**
    \brief       De-initialize TRNG Interface. stops operation and releases the software resources used by the interface
    \param[in]   handle  trng handle to operate.
    \return      error code
  */
  int32_t csi_trng_uninitialize(trng_handle_t handle)
  {
      TRNG_NULL_PARAM_CHK(handle);
  
      ck_trng_priv_t *trng_priv = handle;
      trng_priv->cb = NULL;
  
      return 0;
  }
  
  /**
    \brief       Get driver capabilities.
    \param[in]   trng handle to operate.
    \return      \ref trng_capabilities_t
  */
  trng_capabilities_t csi_trng_get_capabilities(trng_handle_t handle)
  {
      return driver_capabilities;
  }
  
  /**
    \brief       Get data from the TRNG.
    \param[in]   handle  trng handle to operate.
    \param[out]  data  Pointer to buffer with data get from TRNG
    \param[in]   num   Number of data items to obtain
    \return      error code
  */
  int32_t csi_trng_get_data(trng_handle_t handle, void *data, uint32_t num)
  {
      TRNG_NULL_PARAM_CHK(handle);
      TRNG_NULL_PARAM_CHK(data);
      TRNG_NULL_PARAM_CHK(num);
  
      ck_trng_priv_t *trng_priv = handle;
  
      trng_priv->status.busy = 1U;
      trng_priv->status.data_valid = 0U;
  
      uint8_t left_len = (uint32_t)data & 0x3;
      uint32_t result = 0;
  
      /* if the data addr is not aligned by word */
      if (left_len) {
          trng_enable();
          while (!trng_data_is_ready());
          result = trng_get_data();
          /* wait the data is ready */
          while (trng_data_is_ready());
  
          if (num > (4 - left_len)) {
              memcpy(data, &result, 4 - left_len);
          } else {
              memcpy(data, &result, num);
              trng_priv->status.busy = 0U;
              trng_priv->status.data_valid = 1U;
  
              if (trng_priv->cb) {
                  trng_priv->cb(TRNG_EVENT_DATA_GENERATE_COMPLETE);
              }
              return 0;
          }
          num -= (4 - left_len);
      }
  
      uint32_t word_len = num >> 2;
      left_len = num & 0x3;
  
      /* obtain the data by word */
      while (word_len--) {
          trng_enable();
          while (!trng_data_is_ready());
          result = trng_get_data();
          while (trng_data_is_ready());
          *(uint32_t *)data = result;
          data += 4;
      }
  
      /* if the num is not aligned by word */
      if (left_len) {
          trng_enable();
          while (!trng_data_is_ready());
          result = trng_get_data();
          while (trng_data_is_ready());
          memcpy(data, &result, left_len);
      }
  
      trng_priv->status.busy = 0U;
      trng_priv->status.data_valid = 1U;
  
      if (trng_priv->cb) {
          trng_priv->cb(TRNG_EVENT_DATA_GENERATE_COMPLETE);
      }
  
      return 0;
  }
  
  /**
    \brief       Get TRNG status.
    \param[in]   handle  trng handle to operate.
    \return      TRNG status \ref trng_status_t
  */
  trng_status_t csi_trng_get_status(trng_handle_t handle)
  {
      ck_trng_priv_t *trng_priv = handle;
      return trng_priv->status;
  }
  