/***************************************************************************
 *
 * Copyright 2015-2019 BES.
 * All rights reserved. All unpublished rights reserved.
 *
 * No part of this work may be used or reproduced in any form or by any
 * means, or stored in a database or retrieval system, without prior written
 * permission of BES.
 *
 * Use of this work is governed by a license granted by BES.
 * This work contains confidential and proprietary information of
 * BES. which is protected by copyright, trade secret,
 * trademark and other intellectual property rights.
 *
 ****************************************************************************/
#ifdef RTOS

#include "plat_types.h"
#include "bt_drv.h"
#include "bt_drv_2300p_internal.h"
#include "hal_intersys.h"
#include "hal_i2c.h"
#include "hal_uart.h"
#include "hal_iomux.h"
#include "hal_chipid.h"
#include "string.h"
#include "bt_drv_interface.h"

struct dbg_nonsig_tester_result_tag
{
    uint16_t    pkt_counters;
    uint16_t    head_errors;
    uint16_t    payload_errors;
    int16_t    avg_estsw;
    int16_t    avg_esttpl;
    uint32_t    payload_bit_errors;
};

struct bt_drv_capval_calc_t
{
    int16_t    estsw_a;
    int16_t    estsw_b;
    uint8_t    cdac_a;
    uint8_t    cdac_b;
};

static struct dbg_nonsig_tester_result_tag nonsig_tester_result;
#ifdef RTOS
static osThreadId calib_thread_tid = NULL;
#else
static bool calib_thread_tid = false;
#endif
static bool calib_running = false;
static struct bt_drv_capval_calc_t capval_calc ={
                                                .estsw_a = 0,
                                                .estsw_b = 0,
                                                .cdac_a = 0,
                                                .cdac_b = 0,
                                                };

#define bt_drv_calib_capval_calc_reset() do{ \
                                    memset(&capval_calc, 0, sizeof(capval_calc)); \
                                 }while(0)
#ifdef RTOS
#define BT_DRV_MUTUX_WAIT(evt) do{ \
                                if (calib_thread_tid){ \
                                    osSignalClear(calib_thread_tid, 0x4); \
                                    evt = osSignalWait(0x4, 4000); \
                                }\
                            }while(0)

#define BT_DRV_MUTUX_SET() do{ \
                            if (calib_thread_tid){ \
                                osSignalSet(calib_thread_tid, 0x4); \
                            } \
                           }while(0)
#else

typedef enum  {
  osOK                    =     0,       ///< function completed; no error or event occurred.
  osEventSignal           =  0x08,       ///< function completed; signal event occurred.
  osErrorOS               =  0xFF,       ///< unspecified RTOS error: run-time error but no other error message fits.
  os_status_reserved      =  0x7FFFFFFF  ///< prevent from enum down-size compiler optimization.
} osStatus;

typedef struct  {
  osStatus                 status;     ///< status code: event or error information
} osEvent;

#define BT_DRV_MUTUX_WAIT(evt) do{ \
                                uint8_t i=16; \
                                evt.status = osErrorOS; \
                                do{ \
                                   if (calib_thread_tid){ \
                                        calib_thread_tid =  false; \
                                        evt.status = osEventSignal; \
                                        break; \
                                   }else{ \
                                      btdrv_delay(500); \
                                   } \
                                }while(i--);\
                            }while(0)

#define BT_DRV_MUTUX_SET() do{ \
                                calib_thread_tid = true; \
                           }while(0)
#endif

#define BT_DRV_CALIB_BTANA_CAPVAL_CALC_BOTTOM (50)
#define BT_DRV_CALIB_BTANA_CAPVAL_CALC_TOP (200)

#define BT_DRV_CALIB_BTANA_CAPVAL_MIN (20)
#define BT_DRV_CALIB_BTANA_CAPVAL_MAX (255-BT_DRV_CALIB_BTANA_CAPVAL_MIN)
#define BT_DRV_CALIB_BTANA_CAPVAL_STEP_HZ (200)

#define BT_DRV_CALIB_RETRY_CNT (10)
#define BT_DRV_CALIB_SKIP_RESULT_CNT (2)
#define BT_DRV_CALIB_MDM_FREQ_REFERENCE (-12)
#define BT_DRV_CALIB_MDM_FREQ_STEP_HZ (500)
#define BT_DRV_CALIB_MDM_BIT_TO_FREQ(n, step) ((n)*(step))
#define BT_DRV_CALIB_MDM_FREQ_TO_BIT(n, step) ((uint32_t)n/step)

static int16_t calib_mdm_freq_reference = BT_DRV_CALIB_MDM_FREQ_REFERENCE;

enum HAL_IOMUX_ISPI_ACCESS_T ispi_access;

static void bt_drv_rfcal_timer_handler(void const *param);
osTimerDef (BT_DRV_RFCAL_TIMER, bt_drv_rfcal_timer_handler);
static osTimerId bt_drv_rfcal_timer = NULL;

static void bt_drv_calib_stop_timer(void)
{
    if (NULL != bt_drv_rfcal_timer)
    {
        osTimerStop(bt_drv_rfcal_timer);
    }
}

static void bt_drv_rfcal_timer_handler(void const *start)
{
    static int start_nonsig_rx = 0;

    if (NULL == bt_drv_rfcal_timer)
    {
        bt_drv_rfcal_timer = osTimerCreate(osTimer(BT_DRV_RFCAL_TIMER), osTimerOnce, NULL);
    }

    osTimerStop(bt_drv_rfcal_timer);

    if (start_nonsig_rx || start) {
        start_nonsig_rx = 0;

        const uint8_t hci_cmd_hci_reset[] = {0x01, 0x03, 0x0c, 0x00};
        const uint8_t calib_hci_cmd_nonsig_rx_2dh1_pn9_t5[] = {
            0x01, 0x87, 0xfc, 0x1c, 0x01, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x01, 0x04, 0x00, 0x36, 0x00,
            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
        };

        /* start nonsig rx again */
        btdrv_SendData(hci_cmd_hci_reset, sizeof(hci_cmd_hci_reset));
        btdrv_delay(2);
        btdrv_SendData(calib_hci_cmd_nonsig_rx_2dh1_pn9_t5, sizeof(calib_hci_cmd_nonsig_rx_2dh1_pn9_t5));
        btdrv_delay(100);

        BT_DRV_TRACE(0,"bt_drv_cal start nonsig rx\n");

        BTDIGITAL_REG(0xd0210040) &= 0xff;
        BTDIGITAL_REG(0xd0210040) |= 0x55;

        osTimerStart(bt_drv_rfcal_timer, 1500);
    } else {
        start_nonsig_rx = 1;

        const uint8_t stop_nonsig_rx_cmd[] = {
            0x01, 0x87, 0xfc, 0x1c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        };

        /* stop nonsig rx and let report cal result */
        btdrv_SendData(stop_nonsig_rx_cmd, sizeof(stop_nonsig_rx_cmd));
        btdrv_delay(2);

        BT_DRV_TRACE(0,"bt_drv_cal stop nonsig rx\n");

        osTimerStart(bt_drv_rfcal_timer, 300);
    }
}


void bt_drv_start_calib(void)
{
    if (hal_get_chip_metal_id() < HAL_CHIP_METAL_ID_1)
    {
        const uint8_t calib_hci_cmd_nonsig_rx_2dh1_pn9[] = {
            0x01, 0x87, 0xfc, 0x14, 0x01, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x01, 0x04, 0x00, 0x36, 0x00
        };

        if (!calib_running){
            btdrv_SendData(calib_hci_cmd_nonsig_rx_2dh1_pn9, sizeof(calib_hci_cmd_nonsig_rx_2dh1_pn9));
            btdrv_delay(500);
            BTDIGITAL_REG(0xd0210040) &= 0xff;
            BTDIGITAL_REG(0xd0210040) |= 0x55;
            calib_running =  true;
        }
    }
    else
    {
        bt_drv_rfcal_timer_handler((void*)1);
        calib_running = true;
    }
}

void bt_drv_stop_calib(void)
{
    if (calib_running){
        btdrv_hci_reset();
        btdrv_delay(200);
        calib_running =  false;
    }

    if (hal_get_chip_metal_id() >= HAL_CHIP_METAL_ID_1)
    {
        bt_drv_calib_stop_timer();
    }
}

const uint8_t calib_hci_cmd_nonsig_tx_2dh1_pn9_t0[] = {
    0x01, 0x87, 0xfc, 0x14, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x01, 0x04, 0x04, 0x36, 0x00
};

const uint8_t calib_hci_cmd_nonsig_tx_2dh1_pn9_t1[] = {
    0x01, 0x87, 0xfc, 0x1c, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x01, 0x04, 0x04, 0x36, 0x00,
    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
};

void bt_drv_check_calib(void)
{
    const uint8_t* calib_hci_cmd_nonsig_tx_2dh1_pn9 = NULL;
    uint8_t len = 0;

    if (hal_get_chip_metal_id() < HAL_CHIP_METAL_ID_1)
    {
        calib_hci_cmd_nonsig_tx_2dh1_pn9 = calib_hci_cmd_nonsig_tx_2dh1_pn9_t0;
        len = sizeof(calib_hci_cmd_nonsig_tx_2dh1_pn9_t0);
    }
    else
    {
        calib_hci_cmd_nonsig_tx_2dh1_pn9 = calib_hci_cmd_nonsig_tx_2dh1_pn9_t1;
        len = sizeof(calib_hci_cmd_nonsig_tx_2dh1_pn9_t1);
    }

    btdrv_SendData(calib_hci_cmd_nonsig_tx_2dh1_pn9, len);
    btdrv_delay(3000);
    BTDIGITAL_REG(0xd0210040) &= 0xff;
    BTDIGITAL_REG(0xd0210040) |= 39;
}

static bool bt_drv_calib_capval_calc_run(uint16_t *capval_step_hz, int16_t estsw, uint8_t cdac)
{
    bool nRet = false;
    int16_t estsw_diff;
    int16_t cdac_diff;

    if (!capval_calc.estsw_a &&
        !capval_calc.estsw_b){
        capval_calc.estsw_a = estsw;
    }else if (capval_calc.estsw_a &&
              !capval_calc.estsw_b){
        capval_calc.estsw_b = estsw;
    }

    if (!capval_calc.cdac_a &&
        !capval_calc.cdac_b){
        capval_calc.cdac_a = cdac;
    }else if (capval_calc.cdac_a &&
              !capval_calc.cdac_b){
        capval_calc.cdac_b = cdac;
    }

    if (capval_calc.estsw_a &&
        capval_calc.estsw_b &&
        capval_calc.cdac_a &&
        capval_calc.cdac_b){

        estsw_diff = ABS(capval_calc.estsw_b - capval_calc.estsw_a);
        cdac_diff = ABS((int16_t)(capval_calc.cdac_b) - (int16_t)(capval_calc.cdac_a));
        *capval_step_hz = estsw_diff*BT_DRV_CALIB_MDM_FREQ_STEP_HZ/cdac_diff;
        BT_DRV_TRACE(7,"%d/%d %d/%d estsw_diff:%d cdac_diff:%d capval_step_hz:%d",
                                                                    capval_calc.estsw_a,
                                                                    capval_calc.estsw_b,
                                                                    capval_calc.cdac_a,
                                                                    capval_calc.cdac_b,
                                                                    estsw_diff,
                                                                    cdac_diff,
                                                                    *capval_step_hz);
        nRet = true;
    }
    return nRet;
}

void bt_drv_calib_rxonly_porc(void)
{
    osEvent evt;

    evt.status = os_status_reserved;
     /*[set tmx] */
    btdrv_write_rf_reg(0xb0,0x0f00); /* turn off tmx */

    bt_drv_start_calib();


    while(1){
        BT_DRV_MUTUX_WAIT(evt);
        if(evt.status != osEventSignal){
            break;
        }
        btdrv_delay(10);
        BT_DRV_TRACE(7,"result cnt:%d head:%d payload:%d estsw:%d esttpl:%d bit:%d est:%d", nonsig_tester_result.pkt_counters,
                                                                                nonsig_tester_result.head_errors,
                                                                                nonsig_tester_result.payload_errors,
                                                                                nonsig_tester_result.avg_estsw,
                                                                                nonsig_tester_result.avg_esttpl,
                                                                                nonsig_tester_result.payload_bit_errors,
                                                                                nonsig_tester_result.avg_estsw+nonsig_tester_result.avg_esttpl);



    };

    bt_drv_stop_calib();
}

int bt_drv_calib_result_porc(uint32_t *capval)
{
    osEvent evt;

    uint16_t read_val = 0;
    uint8_t cdac = 0;
    uint8_t cnt = 0;
    uint8_t skip_cnt = 0;
    int diff = 0;
    int est = 0;
    uint8_t next_step = 0;
    uint16_t capval_step_hz = BT_DRV_CALIB_BTANA_CAPVAL_STEP_HZ;
    int nRet = -1;
    int chk_flag = 0;
    bool need_capval_calc = true;
    evt.status = os_status_reserved;

    /*[set to capval_min] */
    btdrv_read_rf_reg(0xE9, &read_val);
    cdac = BT_DRV_CALIB_BTANA_CAPVAL_CALC_BOTTOM;
    read_val = (read_val & 0xff00)|(cdac);;
    btdrv_write_rf_reg(0xE9,read_val);

    /*[set tmx] */
    btdrv_write_rf_reg(0xb0,0x0f00); /* turn off tmx */

    bt_drv_calib_capval_calc_reset();

    bt_drv_start_calib();

check_again:
    BT_DRV_TRACE(0,"calib run !!!");
    do{
calib_again:
        BT_DRV_MUTUX_WAIT(evt);
        if(evt.status != osEventSignal){
            nRet = -1;
            BT_DRV_TRACE(1,"evt:%d", evt.status);
            goto  exit;
        }
        if (hal_get_chip_metal_id() < HAL_CHIP_METAL_ID_1)
        {
            BT_DRV_MUTUX_WAIT(evt);
            if(evt.status != osEventSignal)
            {
                nRet = -1;
                BT_DRV_TRACE(1,"evt:%d", evt.status);
                goto  exit;
            }
            BT_DRV_MUTUX_WAIT(evt);
            if(evt.status != osEventSignal)
            {
                nRet = -1;
                BT_DRV_TRACE(1,"evt:%d", evt.status);
                goto  exit;
            }
        }
        BT_DRV_TRACE(7,"result cnt:%d head:%d payload:%d estsw:%d esttpl:%d bit:%d est:%d",  nonsig_tester_result.pkt_counters,
                                                                                        nonsig_tester_result.head_errors,
                                                                                        nonsig_tester_result.payload_errors,
                                                                                        nonsig_tester_result.avg_estsw,
                                                                                        nonsig_tester_result.avg_esttpl,
                                                                                        nonsig_tester_result.payload_bit_errors,
                                                                                        nonsig_tester_result.avg_estsw+nonsig_tester_result.avg_esttpl);


        if (nonsig_tester_result.head_errors>15){
            if (++skip_cnt>BT_DRV_CALIB_SKIP_RESULT_CNT)
                break;
            else
                goto calib_again;
        }
        skip_cnt = 0;

        btdrv_read_rf_reg(0xE9, &read_val);
        cdac = read_val & 0x00ff;

        est = nonsig_tester_result.avg_estsw;
        diff = est-calib_mdm_freq_reference;

        if (need_capval_calc){
            if (bt_drv_calib_capval_calc_run(&capval_step_hz, est, cdac)){
                need_capval_calc = false;
            }
            if (cdac == BT_DRV_CALIB_BTANA_CAPVAL_CALC_BOTTOM){
                cdac = BT_DRV_CALIB_BTANA_CAPVAL_CALC_TOP;
                read_val = (read_val & 0xff00)|(cdac);
                btdrv_write_rf_reg(0xE9,read_val);
            }
            goto calib_again;
        }

        if ((BT_DRV_CALIB_MDM_BIT_TO_FREQ(ABS(diff), BT_DRV_CALIB_MDM_FREQ_STEP_HZ) < 1500) && diff>0){
            if (!chk_flag){
                chk_flag = 1;
                cnt = 0;
                next_step = 2;
                goto check_again;
            }else if(chk_flag && cnt>4){
                break;
            }
            nRet = 0;
            break;
        }else if (next_step == 2){

        }else if (BT_DRV_CALIB_MDM_BIT_TO_FREQ(ABS(diff), BT_DRV_CALIB_MDM_FREQ_STEP_HZ) < 2500){
            next_step = 2;
        }else{
            next_step = BT_DRV_CALIB_MDM_BIT_TO_FREQ(ABS(diff), BT_DRV_CALIB_MDM_FREQ_STEP_HZ)/capval_step_hz;
            if (next_step == 0){
                next_step = 2;
            }
            if (next_step>200){
                next_step = 200;
            }
        }
        BT_DRV_TRACE(4,"diff:%d read_val:%x cdac:%d next_step:%d", diff,read_val, cdac, next_step);
        if (est == calib_mdm_freq_reference){
            if (!chk_flag){
                chk_flag = 1;
                cnt = 0;
                next_step = 2;
                goto check_again;
            }else if(chk_flag && cnt>4){
                break;
            }
            nRet = 0;
            break;
        }else if (est > calib_mdm_freq_reference){
            if (cdac < (BT_DRV_CALIB_BTANA_CAPVAL_MIN+next_step)){
                if (cdac == BT_DRV_CALIB_BTANA_CAPVAL_MIN)
                    break;
                else
                    cdac = BT_DRV_CALIB_BTANA_CAPVAL_MIN;
            }else{
                cdac -= next_step;
            }
            read_val = (read_val & 0xff00)|(cdac);

            BT_DRV_TRACE(2,"-----:%x cdac:%d", read_val, cdac);
        }else if (est < calib_mdm_freq_reference){
            if (cdac>(BT_DRV_CALIB_BTANA_CAPVAL_MAX-next_step)){
                if (cdac == BT_DRV_CALIB_BTANA_CAPVAL_MAX)
                    break;
                else
                    cdac = BT_DRV_CALIB_BTANA_CAPVAL_MAX;
            }else{
                cdac += next_step;
            }
            read_val = (read_val & 0xff00)|(cdac);

            BT_DRV_TRACE(2,"+++++:%x cdac:%d", read_val, cdac);
        }

        btdrv_write_rf_reg(0xE9,read_val);
    }while(cnt++<BT_DRV_CALIB_RETRY_CNT);
    *capval = cdac;
exit:
    bt_drv_stop_calib();

    BT_DRV_TRACE(3,"capval:0x%x cnt:%d nRet:%d", *capval, cnt, nRet);
    return nRet;
}

static unsigned int bt_drv_calib_rx(const unsigned char *data, unsigned int len)
{
    const unsigned char nonsig_test_report[] = {0x04, 0x0e, 0x12};
    struct dbg_nonsig_tester_result_tag *pResult = NULL;

    hal_intersys_stop_recv(HAL_INTERSYS_ID_0);

//    BT_DRV_TRACE(2,"%s len:%d", __func__, len);
    BT_DRV_DUMP("%02x ", data, len);
    if (0 == memcmp(data, nonsig_test_report, sizeof(nonsig_test_report))){
//        BT_DRV_TRACE(0,"dbg_nonsig_tester_result !!!!!");
        pResult = (struct dbg_nonsig_tester_result_tag *)(data + 7);
        if (pResult->pkt_counters != 0)
        {
            memcpy(&nonsig_tester_result, pResult, sizeof(nonsig_tester_result));
            BT_DRV_MUTUX_SET();
        }
    }
    hal_intersys_start_recv(HAL_INTERSYS_ID_0);

    return len;
}

void btdrv_tx(const unsigned char *data, unsigned int len);
void bt_drv_calib_open(void)
{
    int ret = 0;
    BT_DRV_TRACE(1,"%s", __func__);
#ifdef RTOS
    if (calib_thread_tid == NULL){
        calib_thread_tid = osThreadGetId();
    }
#endif

    ispi_access = hal_iomux_ispi_access_enable(HAL_IOMUX_ISPI_MCU_RF);

    ret = hal_intersys_open(HAL_INTERSYS_ID_0, HAL_INTERSYS_MSG_HCI, bt_drv_calib_rx, btdrv_tx, false);

    if (ret) {
        BT_DRV_TRACE(0,"Failed to open intersys");
        return;
    }
    hal_intersys_start_recv(HAL_INTERSYS_ID_0);
}

void bt_drv_calib_close(void)
{
    btdrv_hci_reset();
    btdrv_delay(200);
    hal_intersys_close(HAL_INTERSYS_ID_0,HAL_INTERSYS_MSG_HCI);
}

#endif