text: /*
   * Copyright (c) 2020 Bouffalolab.
   *
   * This file is part of
   *     *** Bouffalolab Software Dev Kit ***
   *      (see www.bouffalolab.com).
   *
   * Redistribution and use in source and binary forms, with or without modification,
   * are permitted provided that the following conditions are met:
   *   1. Redistributions of source code must retain the above copyright notice,
   *      this list of conditions and the following disclaimer.
   *   2. Redistributions in binary form must reproduce the above copyright notice,
   *      this list of conditions and the following disclaimer in the documentation
   *      and/or other materials provided with the distribution.
   *   3. Neither the name of Bouffalo Lab nor the names of its contributors
   *      may be used to endorse or promote products derived from this software
   *      without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  #include <stdio.h>
  #include <stdint.h>
  #include <string.h>
  
  #include <bl602_aon.h>
  #include <bl602_hbn.h>
  #include <bl602_common.h>
  #include <bl602_glb.h>
  #include <bl602_hbn.h>
  #include <bl602_dma.h>
  #include <bl602_adc.h>
  #include <bl602.h>
  #include <bl_irq.h>
  #include <blog.h>
  
  
  #include <blog.h>
  #include <bl_dma.h>
  #include <bl_adc.h>
  #include <bl_timer.h>
  
  //#define TEMP_OFFSET_X   22791
  #define TEMP_OFFSET_X     2318
  #define ADC_CLOCK_FREQ    96000000
  
  ADC_CFG_Type adcCfg = {
      .v18Sel=ADC_V18_SEL_1P82V,                /*!< ADC 1.8V select */
      .v11Sel=ADC_V11_SEL_1P1V,                 /*!< ADC 1.1V select */
      .clkDiv=ADC_CLK_DIV_16,                   /*!< Clock divider */
      .gain1=ADC_PGA_GAIN_NONE,                 /*!< PGA gain 1 */
      .gain2=ADC_PGA_GAIN_NONE,                 /*!< PGA gain 2 */
      .chopMode=ADC_CHOP_MOD_AZ_PGA_ON,           /*!< ADC chop mode select */
      .biasSel=ADC_BIAS_SEL_MAIN_BANDGAP,       /*!< ADC current form main bandgap or aon bandgap */
      .vcm=ADC_PGA_VCM_1V,                      /*!< ADC VCM value */
      .vref=ADC_VREF_2V,                      /*!< ADC voltage reference */
      .inputMode=ADC_INPUT_SINGLE_END,          /*!< ADC input signal type */
      .resWidth=ADC_DATA_WIDTH_16_WITH_256_AVERAGE,              /*!< ADC resolution and oversample rate */
      .offsetCalibEn=0,                         /*!< Offset calibration enable */
      .offsetCalibVal=0,                        /*!< Offset calibration value */
  };
  
  
  ADC_FIFO_Cfg_Type adcFifoCfg = {
      .fifoThreshold = ADC_FIFO_THRESHOLD_1,
      .dmaEn = DISABLE,
  };
  
  void ADC_Clock_Init(uint8_t div)
  {
      GLB_Set_ADC_CLK(ENABLE,GLB_ADC_CLK_96M,div);
      blog_info("GLB_Set_ADC_CLK_Div(%d) == clock 96M/(%d+1)\r\n",div,div);
  }
  
  void TSEN_Calibration(void)
  {
      ADC_SET_TSVBE_LOW();
      ADC_Start();
      ARCH_Delay_MS(100);
      while(ADC_Get_FIFO_Count() == 0);
      ADC_Read_FIFO();
  
      ADC_SET_TSVBE_HIGH();
      ADC_Start();
      ARCH_Delay_MS(100);
      while(ADC_Get_FIFO_Count() == 0);
      ADC_Read_FIFO();
  
      ADC_SET_TSVBE_LOW();
  }
  
  static void ADC_tsen_case(void)
  {
      ADC_Result_Type result;
      uint32_t regVal=0;
      uint8_t i=0;
      uint32_t v0=0,v1=0;
      float v_error=0;
  
      ADC_Reset();
  
      ADC_Disable();
      ADC_Enable();
  
      ADC_Init(&adcCfg);
      ADC_Channel_Config(ADC_CHAN_TSEN_P,ADC_CHAN_GND,0);
      ADC_Tsen_Init(ADC_TSEN_MOD_INTERNAL_DIODE);
  
      ADC_FIFO_Cfg(&adcFifoCfg);
      TSEN_Calibration();
  
      for(i=0;i<40;i++){
          ADC_Start();
  
          while(ADC_Get_FIFO_Count() == 0);
  
          do{
              regVal = ADC_Read_FIFO();
              ADC_Parse_Result(&regVal,1,&result);
  
              if(i%2 ==0){
                  v0 = result.value;
              }else{
                  v1 = result.value;
              }
          }while(ADC_Get_FIFO_Count() != 0);
  
          if(i%2 !=0){
                v_error = (float)v0 - (float)v1;
                v_error = v_error - TEMP_OFFSET_X;
                v_error = v_error /7.753;
              blog_info(" v0=%ld  v1 = %ld \n",v0,v1);
              //MSG(" ((v0-v1)-X)/7.753= %d \n",(uint32_t)(v_error * 1000));
              blog_info(" chip Tempture = %ld degree centigrade\n",(uint32_t)(v_error));
          }
          if( i%2 ==0 )
              ADC_SET_TSVBE_HIGH();
          else
              ADC_SET_TSVBE_LOW();
  
          ARCH_Delay_MS(500);
      }
  }
  
  
  int test_adc_init(void)
  {
      ADC_Clock_Init(2);
  
      ADC_Reset();
  
      ADC_Disable();
      ADC_Enable();
  
      ADC_Init(&adcCfg);
      ADC_Channel_Config(ADC_CHAN_TSEN_P, ADC_CHAN_GND, 0);
      ADC_Tsen_Init(ADC_TSEN_MOD_INTERNAL_DIODE);
  
      ADC_FIFO_Cfg(&adcFifoCfg);
      TSEN_Calibration();
  
      return 0;
  }
  
  int test_adc_get(int16_t *tmp)
  {
      ADC_Result_Type result;
      uint32_t regVal=0;
      uint8_t i=0;
      uint32_t v0=0,v1=0;
      float v_error=0;
  
      for (i = 0; i < 2; i++) {
          ADC_Start();
  
          while(ADC_Get_FIFO_Count() == 0) {
              extern void aos_msleep(int ms);
              aos_msleep(1);
          }
  
          do{
              regVal = ADC_Read_FIFO();
              ADC_Parse_Result(&regVal,1,&result);
  
              if(i%2 == 0) {
                  v0 = result.value;
              } else {
                  v1 = result.value;
              }
          } while(ADC_Get_FIFO_Count() != 0);
  
          if (i%2 != 0) {
              v_error = (float)v0 - (float)v1;
              //blog_info("v_error = %d\r\n", v_error);
              v_error = v_error - TEMP_OFFSET_X;
              v_error = v_error / 7.753;
              //blog_info("v0 = %ld, v1 = %ld\r\n", v0, v1);
              //blog_info(" chip Tempture = %ld degree centigrade\r\n", (uint32_t)(v_error));
              *tmp = (int16_t)v_error;
          }
          if (i%2 == 0) {
              ADC_SET_TSVBE_HIGH();
          } else {
              ADC_SET_TSVBE_LOW();
          }
      }
      return 0;
  }
  
  int test_adc_test(void)
  {
      ADC_tsen_case();
      return 0;
  }
  
  int bl_tsen_adc_get(int16_t *temp, uint8_t log_flag)
  {
      static uint16_t tsen_offset = 0xFFFF;
      float val = 0.0;
  
      if (0xFFFF == tsen_offset) {
          tsen_offset = 0;
          ADC_CFG_Type adcCfg = {
              .v18Sel=ADC_V18_SEL_1P82V,                /*!< ADC 1.8V select */
              .v11Sel=ADC_V11_SEL_1P1V,                 /*!< ADC 1.1V select */
              .clkDiv=ADC_CLK_DIV_32,                   /*!< Clock divider */
              .gain1=ADC_PGA_GAIN_1,                 /*!< PGA gain 1 */
              .gain2=ADC_PGA_GAIN_1,                 /*!< PGA gain 2 */
              .chopMode=ADC_CHOP_MOD_AZ_PGA_ON,           /*!< ADC chop mode select */
              .biasSel=ADC_BIAS_SEL_MAIN_BANDGAP,       /*!< ADC current form main bandgap or aon bandgap */
              .vcm=ADC_PGA_VCM_1V,                      /*!< ADC VCM value */
              .vref=ADC_VREF_2V,                      /*!< ADC voltage reference */
              .inputMode=ADC_INPUT_SINGLE_END,          /*!< ADC input signal type */
              .resWidth=ADC_DATA_WIDTH_16_WITH_256_AVERAGE,              /*!< ADC resolution and oversample rate */
              .offsetCalibEn=0,                         /*!< Offset calibration enable */
              .offsetCalibVal=0,                        /*!< Offset calibration value */
          };
  
  
          ADC_FIFO_Cfg_Type adcFifoCfg = {
              .fifoThreshold = ADC_FIFO_THRESHOLD_1,
              .dmaEn = DISABLE,
          };
  
          GLB_Set_ADC_CLK(ENABLE,GLB_ADC_CLK_96M, 7);
  
          ADC_Disable();
          ADC_Enable();
  
          ADC_Reset();
  
          ADC_Init(&adcCfg);
          ADC_Channel_Config(ADC_CHAN_TSEN_P, ADC_CHAN_GND, 0);
          ADC_Tsen_Init(ADC_TSEN_MOD_INTERNAL_DIODE);
  
          ADC_FIFO_Cfg(&adcFifoCfg);
  
          if (ADC_Trim_TSEN(&tsen_offset) == ERROR) {
              log_error("read efuse data failed\r\n");
          } else {
              log_info("offset = %d\r\n", tsen_offset);
          }
      }
      val = TSEN_Get_Temp(tsen_offset);
      if (log_flag) {
          printf("temperature = %f Celsius\r\n", val);
      }
  
      if (temp) {
          *temp = (int16_t)(val);
      }
  
      return 0;
  }
  
  /* adc driver */
  static void adc_data_update(void)
  {
      adc_ctx_t *pstctx;
  
      pstctx =  bl_dma_find_ctx_by_channel(ADC_DMA_CHANNEL);
      if (pstctx == NULL) {
          blog_error("get ctx error \r\n");
          return;
      }
  
      if (pstctx->lli_flag == 0) {
          pstctx->channel_data = (uint32_t *)((DMA_LLI_Ctrl_Type *)(pstctx->adc_lli))[0].destDmaAddr;
          pstctx->lli_flag = 1;
      } else {
          pstctx->channel_data = (uint32_t *)((DMA_LLI_Ctrl_Type *)(pstctx->adc_lli))[1].destDmaAddr;
          pstctx->lli_flag = 0;
      }
  
      if (pstctx->cb != NULL) {
          (pstctx->cb)(pstctx->mode, pstctx->channel_data, pstctx->data_size);
      }
  
      return;
  }
  
  //mode = 0; for nomal adc, freq 40HZ ~ 1300HZ
  //mode = 1; for mic use. freq 500HZ ~ 16000HZ
  int bl_adc_freq_init(int mode, uint32_t freq)
  {
      uint32_t div;
      uint32_t source_freq;
      uint32_t mode_freq;
  
      if (mode == 0) {
          mode_freq = 12;
      } else {
          mode_freq = 1;
      }
  
      source_freq = ADC_CLOCK_FREQ / (128 * 24 * mode_freq);
      div = source_freq / freq;
      if (((div + 1) * freq - source_freq) < (source_freq - freq * div)) {
          div = div + 1;
      }
  
      if (div > 64) {
          div = 64;
      }
  
      GLB_Set_ADC_CLK(ENABLE, GLB_ADC_CLK_96M, div - 1);
  
      return 0;
  }
  
  int bl_adc_init(int mode, int gpio_num)
  {
      int i;
      int chan;
      int channel_num;
      ADC_CFG_Type adccfg;
      ADC_Chan_Type pos_chlist_single[ADC_CHANNEL_MAX];
      ADC_Chan_Type neg_chlist_single[ADC_CHANNEL_MAX];
      ADC_FIFO_Cfg_Type adc_fifo_cfg;
  
      adccfg.v18Sel=ADC_V18_SEL_1P82V;
      adccfg.v11Sel=ADC_V11_SEL_1P1V;
      adccfg.clkDiv=ADC_CLK_DIV_24;
      adccfg.gain1=ADC_PGA_GAIN_NONE;
      adccfg.gain2=ADC_PGA_GAIN_NONE;
      adccfg.chopMode=ADC_CHOP_MOD_ALL_OFF;
      adccfg.biasSel=ADC_BIAS_SEL_MAIN_BANDGAP;
      adccfg.vcm=ADC_PGA_VCM_1V;
      adccfg.vref=ADC_VREF_3P2V;
      adccfg.inputMode=ADC_INPUT_SINGLE_END;
      adccfg.resWidth=ADC_DATA_WIDTH_16_WITH_128_AVERAGE;
      adccfg.offsetCalibEn=0;
      adccfg.offsetCalibVal=0;
  
      ADC_Disable();
      ADC_Enable();
      ADC_Reset();
  
      ADC_Init(&adccfg);
  
      if (mode == 0) {
          for (i = 0; i < ADC_CHANNEL_MAX; i++) {
              pos_chlist_single[i] = i;
              neg_chlist_single[i] = ADC_CHAN_GND;
          }
  
          channel_num = ADC_CHANNEL_MAX;
      } else {
          chan = bl_adc_get_channel_by_gpio(gpio_num);
          pos_chlist_single[0] = chan;
          neg_chlist_single[0] = ADC_CHAN_GND;
          channel_num = 1;
      }
      ADC_Scan_Channel_Config(pos_chlist_single, neg_chlist_single, channel_num, ENABLE);
  
      adc_fifo_cfg.fifoThreshold = ADC_FIFO_THRESHOLD_1;
      adc_fifo_cfg.dmaEn = ENABLE;
      ADC_FIFO_Cfg(&adc_fifo_cfg);
  
      return 0;
  }
  
  static void adc_dma_lli_init(DMA_LLI_Ctrl_Type *pstlli, uint32_t *buf, uint32_t data_num)
  {
      struct DMA_Control_Reg dma_ctrl_reg;
  
      dma_ctrl_reg.TransferSize = data_num;
      dma_ctrl_reg.SBSize=DMA_BURST_SIZE_1;
      dma_ctrl_reg.DBSize=DMA_BURST_SIZE_1;
      dma_ctrl_reg.SWidth=DMA_TRNS_WIDTH_32BITS;
      dma_ctrl_reg.DWidth=DMA_TRNS_WIDTH_32BITS;
      dma_ctrl_reg.SI=DMA_MINC_DISABLE;
      dma_ctrl_reg.DI=DMA_MINC_ENABLE;
      dma_ctrl_reg.I = 1;
      dma_ctrl_reg.SLargerD = 0;
      dma_ctrl_reg.Prot = 0;
  
      pstlli[0].srcDmaAddr = 0x40002000+0x4;
      pstlli[0].destDmaAddr = (uint32_t)&buf[0];
      pstlli[0].nextLLI = (uint32_t)&pstlli[1];
      pstlli[0].dmaCtrl= dma_ctrl_reg;
  
      pstlli[1].srcDmaAddr = 0x40002000+0x4;
      pstlli[1].destDmaAddr = (uint32_t)&buf[ADC_CHANNEL_MAX];
      pstlli[1].nextLLI = (uint32_t)&pstlli[0];
      pstlli[1].dmaCtrl= dma_ctrl_reg;
  
      return;
  }
  
  int bl_adc_dma_init(int mode, uint32_t data_num)
  {
      DMA_LLI_Ctrl_Type *pstlli;
      uint32_t *llibuf;
      DMA_LLI_Cfg_Type llicfg;
      adc_ctx_t *pstctx;
  
      if (data_num < 1) {
          blog_error("illegal para. \r\n");
  
          return -1;
      }
  
      pstctx = pvPortMalloc(sizeof(adc_ctx_t));
      if (pstctx == NULL) {
          blog_error("malloc adc_ctx failed \r\n");
  
          return -1;
      }
  
      pstlli = pvPortMalloc(sizeof(DMA_LLI_Ctrl_Type) * 2);
      if (pstlli == NULL) {
          blog_error("malloc lli failed. \r\n");
  
          return -1;
      }
  
      llibuf = pvPortMalloc(sizeof(uint32_t) * data_num * 2);
      if (llibuf == NULL) {
          blog_error("malloc lli buf failed. \r\n");
  
          return -1;
      }
  
      llicfg.dir = DMA_TRNS_P2M;
      llicfg.srcPeriph = DMA_REQ_GPADC0;
      llicfg.dstPeriph = DMA_REQ_NONE;
      DMA_Channel_Disable(ADC_DMA_CHANNEL);
  
      adc_dma_lli_init(pstlli, llibuf, data_num);
      DMA_LLI_Init(ADC_DMA_CHANNEL, &llicfg);
      DMA_LLI_Update(ADC_DMA_CHANNEL, (uint32_t)&(pstlli[0]));
  
      pstctx->mode = mode;
      pstctx->adc_lli = pstlli;
      pstctx->lli_flag = 0;
      pstctx->chan_init_table = 0;
      pstctx->channel_data = NULL;
      pstctx->data_size = data_num;
      pstctx->cb = NULL;
      bl_dma_irq_register(ADC_DMA_CHANNEL, adc_data_update, NULL, pstctx);
  
      return 0;
  }
  
  int bl_adc_start(void)
  {
      DMA_IntMask(ADC_DMA_CHANNEL, DMA_INT_ALL, MASK);
      DMA_IntMask(ADC_DMA_CHANNEL, DMA_INT_TCOMPLETED, UNMASK);
      DMA_IntMask(ADC_DMA_CHANNEL, DMA_INT_ERR, UNMASK);
  
      ADC_Start();
      DMA_Enable();
      DMA_Channel_Enable(ADC_DMA_CHANNEL);
  
      return 0;
  }
  
  int bl_adc_gpio_init(int gpio_num)
  {
      ////  TODO: For Mynewt, adc_pin should have type GLB_GPIO_Type
      uint8_t adc_pin = gpio_num;
  
      GLB_GPIO_Func_Init(GPIO_FUN_ANALOG, &adc_pin, 1);
  
      return 0;
  }
  
  static uint32_t adc_calc_data(uint32_t data)
  {
      uint32_t val;
  
      val = data & 0xffff;
      val = (val  * 3200) >> 16;
  
      return val;
  }
  
  int32_t bl_adc_parse_data(uint32_t *parr, int data_size, int channel, int raw_flag)
  {
      int i;
      int32_t data;
  
      for (i = 0; i < data_size; i++) {
          if (parr[i] >> 21 == channel) {
              if (raw_flag == 0) {
                  data = parr[i] & 0xffff;
                  return data;
              } else {
                  data = adc_calc_data(parr[i]);
  
                  return data;
              }
          }
      }
  
      return -1;
  }
  
  int bl_adc_get_channel_by_gpio(int gpio_num)
  {
      int channel;
  
      if (gpio_num == 4) {
          channel = 1;
      } else if (gpio_num == 5) {
          channel = 4;
      } else if (gpio_num == 6) {
          channel = 5;
      } else if (gpio_num == 9) {
          channel = 7;
      } else if (gpio_num == 10) {
          channel = 9;
      } else if (gpio_num == 11) {
          channel = 10;
      } else if (gpio_num == 12) {
          channel = 0;
      } else if (gpio_num == 13) {
          channel = 3;
      } else if (gpio_num == 14) {
          channel = 2;
      } else if (gpio_num == 15) {
          channel = 11;
      } else {
          return -1;
      }
  
      return channel;
  }
  
  