/***************************************************************************
 *
 * 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.
 *
 ****************************************************************************/
#include "plat_types.h"
#include "cmsis.h"
#include "hal_analogif.h"
#include "hal_location.h"
#include "hal_spi.h"

#define ANA_REG_CHIP_ID                 0x00

#define ANA_CHIP_ID_SHIFT               (4)
#define ANA_CHIP_ID_MASK                (0xFFF << ANA_CHIP_ID_SHIFT)
#define ANA_CHIP_ID(n)                  BITFIELD_VAL(ANA_CHIP_ID, n)
#define ANA_VAL_CHIP_ID                 0x18E

// ISPI_ARBITRATOR_ENABLE should be defined when:
// 1) BT and MCU will access RF register at the same time; or
// 2) BT can access PMU/ANA, and BT will access RF register at the same time
//    when MCU is accessing PMU/ANA register

#ifdef ISPI_ARBITRATOR_ENABLE
// Min padding OSC cycles needed: BT=0 MCU=6
// When OSC=26M and SPI=6.5M, min padding SPI cycles is BT=0 MCU=2
#define PADDING_CYCLES                  2
#else
#define PADDING_CYCLES                  0
#endif

#define ANA_READ_CMD(r)                 (((1 << 24) | (((r) & 0xFF) << 16)) << PADDING_CYCLES)
#define ANA_WRITE_CMD(r, v)             (((((r) & 0xFF) << 16) | ((v) & 0xFFFF)) << PADDING_CYCLES)
#define ANA_READ_VAL(v)                 (((v) >> PADDING_CYCLES) & 0xFFFF)

#define ANA_PAGE_1                      0xA010
#define ANA_PAGE_0                      0xA000

static const BOOT_RODATA_SRAM_LOC uint8_t page_reg[3] = {
    0x00, 0x60, 0x80,
};

static const BOOT_RODATA_FLASH_LOC struct HAL_SPI_CFG_T spi_cfg = {
    .clk_delay_half = false,
    .clk_polarity = false,
    .slave = false,
    .dma_rx = false,
    .dma_tx = false,
    .rx_sep_line = false,
    .cs = 0,
    .rate = 6500000,
    .tx_bits = 25 + PADDING_CYCLES,
    .rx_bits = 25 + PADDING_CYCLES,
    .rx_frame_bits = 0,
};

static bool BOOT_BSS_LOC analogif_inited = false;

static int BOOT_TEXT_SRAM_LOC hal_analogif_rawread(unsigned short reg, unsigned short *val)
{
    int ret;
    unsigned int data;
    unsigned int cmd;

    data = 0;
    cmd = ANA_READ_CMD(reg);
    ret = hal_ispi_recv(&cmd, &data, 4);
    if (ret) {
        return ret;
    }
    *val = ANA_READ_VAL(data);
    return 0;
}

static int BOOT_TEXT_SRAM_LOC hal_analogif_rawwrite(unsigned short reg, unsigned short val)
{
    int ret;
    unsigned int cmd;

    cmd = ANA_WRITE_CMD(reg, val);
    ret = hal_ispi_send(&cmd, 4);
    if (ret) {
        return ret;
    }
    return 0;
}

int BOOT_TEXT_SRAM_LOC hal_analogif_reg_read(unsigned short reg, unsigned short *val)
{
    uint32_t lock;
    uint32_t idx;
    int ret;

#if defined(USE_CYBERON)
extern int cyb_efuse_check_status(void);

    if (cyb_efuse_check_status()) {
        if (reg == 0x5e) {
            *val = 49185;
            return 0;
        }
        if (reg == 0x00) {
            *val = 0x20e0;
            return 0;
        }
    }
#endif

    if (reg < 0x100) {
        lock = int_lock();
        ret = hal_analogif_rawread(reg, val);
        int_unlock(lock);
        return ret;
    } else if (reg >= 0x100 && reg <= 0x15F) {
        idx = 0;
    } else if (reg >= 0x160 && reg <= 0x17F) {
        idx = 1;
    } else if (reg >= 0x180 && reg <= 0x1FF) {
        idx = 2;
    } else {
        return -1;
    }

    reg &= 0xFF;

    lock = int_lock();
    hal_analogif_rawwrite(page_reg[idx], ANA_PAGE_1);
    ret = hal_analogif_rawread(reg, val);
    hal_analogif_rawwrite(page_reg[idx], ANA_PAGE_0);
    int_unlock(lock);

    return ret;
}

int BOOT_TEXT_SRAM_LOC hal_analogif_reg_write(unsigned short reg, unsigned short val)
{
    uint32_t lock;
    uint32_t idx;
    int ret;

    if (reg < 0x100) {
        lock = int_lock();
        ret = hal_analogif_rawwrite(reg, val);
        int_unlock(lock);
        return ret;
    } else if (reg >= 0x100 && reg <= 0x15F) {
        idx = 0;
    } else if (reg >= 0x160 && reg <= 0x17F) {
        idx = 1;
    } else if (reg >= 0x180 && reg <= 0x1FF) {
        idx = 2;
    } else {
        return -1;
    }

    reg &= 0xFF;

    lock = int_lock();
    hal_analogif_rawwrite(page_reg[idx], ANA_PAGE_1);
    ret = hal_analogif_rawwrite(reg, val);
    hal_analogif_rawwrite(page_reg[idx], ANA_PAGE_0);
    int_unlock(lock);

    return ret;
}

int BOOT_TEXT_FLASH_LOC hal_analogif_open(void)
{
    int ret;
    unsigned short chip_id;
    const struct HAL_SPI_CFG_T *cfg_ptr;
    struct HAL_SPI_CFG_T cfg;

    if (analogif_inited) {
        // Restore the nominal rate
        cfg_ptr = &spi_cfg;
    } else {
        analogif_inited = true;
        // Crystal freq is unknown yet. Let SPI run on half of the nominal rate
        cfg = spi_cfg;
        cfg.rate /= 2;
        cfg_ptr = &cfg;
    }

    ret = hal_ispi_open(cfg_ptr);
    if (ret) {
        return ret;
    }

    ret = hal_analogif_rawread(ANA_REG_CHIP_ID, &chip_id);
    if (ret) {
        return ret;
    }

    if (GET_BITFIELD(chip_id, ANA_CHIP_ID) != ANA_VAL_CHIP_ID) {
        return -1;
    }

    return 0;
}

