/***************************************************************************
 *
 * 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.
 *
 ****************************************************************************/
#if defined(CHIP_HAS_PSRAM) && (CHIP_PSRAM_CTRL_VER >= 2)

#include "plat_types.h"
#include "plat_addr_map.h"
#include "hal_location.h"
#include "hal_psram.h"
#include "hal_timer.h"
#include "hal_trace.h"
#include "hal_cache.h"
#include "pmu.h"
#include "reg_psram_mc_v2.h"
#include "reg_psram_phy_v2.h"
#include "string.h"

#define PSRAM_RESET
//#define PSRAM_DUAL_8BIT
//#define PSRAM_WRAP_ENABLE

//#define PSRAM_DEBUG
#ifdef PSRAM_DEBUG
#define PSRAM_TRACE TRACE_IMM
#else
#define PSRAM_TRACE(...)
#endif

#ifdef PSRAM_DEBUG
#define PSRAM_TRACENOCRLF_NOTS REL_TRACE_NOCRLF_NOTS
#else
#define PSRAM_TRACENOCRLF_NOTS(...)
#endif

#define TX_FIFO_DEPTH                       8
#define RX_FIFO_DEPTH                       8

// MR0
#define MR0_DRIVE_STR_SHIFT                 0
#define MR0_DRIVE_STR_MASK                  (0x3 << MR0_DRIVE_STR_SHIFT)
#define MR0_DRIVE_STR(n)                    BITFIELD_VAL(MR0_DRIVE_STR, n)
#define MR0_READ_LATENCY_SHIFT              2
#define MR0_READ_LATENCY_MASK               (0x7 << MR0_READ_LATENCY_SHIFT)
#define MR0_READ_LATENCY(n)                 BITFIELD_VAL(MR0_READ_LATENCY, n)
#define MR0_LT                              (1 << 5)
#define MR0_FIXED_00_SHIFT                  6
#define MR0_FIXED_00_MASK                   (0x3 << MR0_FIXED_00_SHIFT)
#define MR0_FIXED_00(n)                     BITFIELD_VAL(MR0_FIXED_00, n)

// MR1
#define MR1_VENDOR_ID_SHIFT                 0
#define MR1_VENDOR_ID_MASK                  (0x1F << MR1_VENDOR_ID_SHIFT)
#define MR1_VENDOR_ID(n)                    BITFIELD_VAL(MR1_VENDOR_ID, n)
#define MR1_DENSITY_SHIFT                   5
#define MR1_DENSITY_MASK                    (0x3 << MR1_DENSITY_SHIFT)
#define MR1_DENSITY(n)                      BITFIELD_VAL(MR1_DENSITY, n)
#define MR1_ULP                             (1 << 7)

// MR2
#define MR2_VENDOR_ID_SHIFT                 0
#define MR2_VENDOR_ID_MASK                  (0x7 << MR2_VENDOR_ID_SHIFT)
#define MR2_VENDOR_ID(n)                    BITFIELD_VAL(MR2_VENDOR_ID, n)
#define MR2_DEV_ID_SHIFT                    3
#define MR2_DEV_ID_MASK                     (0x3 << MR2_DEV_ID_SHIFT)
#define MR2_DEV_ID(n)                       BITFIELD_VAL(MR2_DEV_ID, n)
#define MR2_RSVD                            (1 << 5)
#define MR2_FIXED_1                         (1 << 6)
#define MR2_GB                              (1 << 7)

// MR4
#define MR4_PASR_SHIFT                      0
#define MR4_PASR_MASK                       (0x7 << MR4_PASR_SHIFT)
#define MR4_PASR(n)                         BITFIELD_VAL(MR4_PASR, n)
#define MR4_RF                              (1 << 3)
#define MR4_FIXED_0                         (1 << 4)
#define MR4_WRITE_LATENCY_SHIFT             5
#define MR4_WRITE_LATENCY_MASK              (0x7 << MR4_WRITE_LATENCY_SHIFT)
#define MR4_WRITE_LATENCY(n)                BITFIELD_VAL(MR4_WRITE_LATENCY, n)

// MR6
#define MR6_RSVD_SHIFT                      0
#define MR6_RSVD_MASK                       (0xF << MR6_RSVD_SHIFT)
#define MR6_RSVD(n)                         BITFIELD_VAL(MR6_RSVD, n)
#define MR6_HALF_SLEEP_SHIFT                4
#define MR6_HALF_SLEEP_MASK                 (0xF << MR6_HALF_SLEEP_SHIFT)
#define MR6_HALF_SLEEP(n)                   BITFIELD_VAL(MR6_HALF_SLEEP, n)

// MR8
#define MR8_BL_SHIFT                        0
#define MR8_BL_MASK                         (0x3 << MR8_BL_SHIFT)
#define MR8_BL(n)                           BITFIELD_VAL(MR8_BL, n)
#define MR8_BT                              (1 << 2)
#define MR8_FIXED_0                         (1 << 3)
#define MR8_RSVD_SHIFT                      4
#define MR8_RSVD_MASK                       (0x7 << MR8_RSVD_SHIFT)
#define MR8_RSVD(n)                         BITFIELD_VAL(MR8_RSVD, n)
#define MR8_FIXED_00                        (1 << 7)

enum PSRAM_CMD_T {
    PSRAM_CMD_SYNC_READ     = 0x00,
    PSRAM_CMD_SYNC_WRITE    = 0x80,
    PSRAM_CMD_4BYTE_READ    = 0x3F,
    PSRAM_CMD_4BYTE_WRITE   = 0xBF,
    PSRAM_CMD_REG_READ      = 0x40,
    PSRAM_CMD_REG_WRITE     = 0xC0,
    PSRAM_CMD_GLOBAL_RESET  = 0xFF,
};

enum CP_FSM_STATE_T {
    CP_FSM_STATE_SELF_REFRESH   = 1,
    CP_FSM_STATE_PD             = 2,
    CP_FSM_STATE_READY          = 4,
};

enum MEMIF_CMD_T {
    MEMIF_NO_CMD        = 0x00,
    MEMIF_WRITE         = 0x01,
    MEMIF_READ          = 0x02,
    MEMIF_MRS           = 0x05,
    MEMIF_MRR           = 0x06,
    MEMIF_REF           = 0x08,
    MEMIF_SREF          = 0x09,
    MEMIF_PD            = 0x10,
    MEMIF_NOP           = 0x20,
    MEMIF_RST           = 0xFF,
    MEMIF_ZQCL          = 0x85,
    MEMIF_ZQCS          = 0x45,
    MEMIF_ZQCRST        = 0x25,
    MEMIF_START_CLOCK   = 0x40,
    MEMIF_STOP_CLOCK    = 0x80,
    MEMIF_NEW_CMD       = 0x7F,
};

static struct PSRAM_MC_T * const psram_mc = (struct PSRAM_MC_T *)PSRAM_CTRL_BASE;
static struct PSRAM_PHY_T * const psram_phy = (struct PSRAM_PHY_T *)(PSRAM_CTRL_BASE + 0x8000);

static const uint32_t psram_cfg_clk = 48*1000*1000;

#if (PSRAM_SPEED != 0)
static const uint32_t psram_run_clk = PSRAM_SPEED*1000*1000;
#else
#error "invalid PSRAMUHS_SPEED"
#endif

static void psram_chip_timing_config(uint32_t clk, bool psram_first);

int hal_psramip_mc_busy(void)
{
    return !!(psram_mc->REG_404 & PSRAM_ULP_MC_BUSY);
}

static int hal_psramip_wb_busy(void)
{
    return !!(psram_mc->REG_404 & PSRAM_ULP_MC_WB_FILL_LEVEL_MASK);
}

int hal_psramip_mc_in_sleep(void)
{
    return GET_BITFIELD(psram_mc->REG_404, PSRAM_ULP_MC_CP_FSM_STATE) == CP_FSM_STATE_PD;
}

int hal_psramip_rx_fifo_empty(void)
{
    return !!(psram_mc->REG_404 & PSRAM_ULP_MC_MGR_RXFIFO_R_EMPTY);
}

int hal_psramip_tx_fifo_full(void)
{
    return !!(psram_mc->REG_404 & PSRAM_ULP_MC_MGR_TXFIFO_W_FULL);
}

uint32_t hal_psramip_get_rx_fifo_len(void)
{
    return GET_BITFIELD(psram_mc->REG_404, PSRAM_ULP_MC_MGR_RXFIFO_FULL_CNT);
}

uint32_t hal_psramip_get_tx_fifo_free_len(void)
{
    return GET_BITFIELD(psram_mc->REG_404, PSRAM_ULP_MC_MGR_TXFIFO_EMPTY_CNT);
}

void hal_psramip_mc_busy_wait(void)
{
    while (hal_psramip_mc_busy());
}

void hal_psramip_wb_busy_wait(void)
{
    while (hal_psramip_wb_busy());
}

void hal_psramip_flush_tx_fifo(void)
{
    hal_psramip_mc_busy_wait();
    psram_mc->REG_01C = PSRAM_ULP_MC_MGR_TX_FIFO_CLR;
    hal_psramip_mc_busy_wait();
}

void hal_psramip_flush_rx_fifo(void)
{
    hal_psramip_mc_busy_wait();
    psram_mc->REG_01C = PSRAM_ULP_MC_MGR_RX_FIFO_CLR;
    hal_psramip_mc_busy_wait();
}

void hal_psramip_flush_all_fifo(void)
{
    hal_psramip_mc_busy_wait();
    psram_mc->REG_01C = PSRAM_ULP_MC_MGR_TX_FIFO_CLR | PSRAM_ULP_MC_MGR_RX_FIFO_CLR;
    hal_psramip_mc_busy_wait();
}

void hal_psramip_xfer_addr_len(uint32_t addr, uint32_t len)
{
    psram_mc->REG_008 = addr;
    psram_mc->REG_00C = len;
}

void hal_psramip_write_fifo(uint32_t *data, uint32_t len)
{
    for (int i = 0; i < len; i++) {
        psram_mc->REG_014 = *data++;
    }
}

void hal_psramip_read_fifo(uint32_t *data, uint32_t len)
{
    for (int i = 0; i < len; i++) {
        *data++ = psram_mc->REG_018;
    }
}

void hal_psramip_set_reg_data_mask(void)
{
#ifdef PSRAM_DUAL_8BIT
    psram_mc->REG_010 = 0xFC;
#else
    psram_mc->REG_010 = 0xFE;
#endif
}

void hal_psramip_set_mem_data_mask(void)
{
    psram_mc->REG_010 = 0;
}

void hal_psramip_set_cmd(enum MEMIF_CMD_T cmd)
{
    psram_mc->REG_004 = cmd;
}

POSSIBLY_UNUSED void psram_read_reg(uint32_t reg, uint32_t *val)
{
    hal_psramip_flush_all_fifo();
    hal_psramip_xfer_addr_len(reg, 1);
    hal_psramip_set_cmd(MEMIF_MRR);
    while (hal_psramip_rx_fifo_empty());
    hal_psramip_read_fifo(val, 1);
}

static void psram_send_cmd_reg(enum MEMIF_CMD_T cmd, uint32_t reg, uint32_t val)
{
#ifdef PSRAM_DUAL_8BIT
    val &= 0xFF;
    val |= (val << 8);
#endif
    hal_psramip_flush_all_fifo();
    //hal_psramip_set_reg_data_mask();
    hal_psramip_write_fifo(&val, 1);
    hal_psramip_xfer_addr_len(reg, 1);
    hal_psramip_set_cmd(cmd);
    while (hal_psramip_get_tx_fifo_free_len() != TX_FIFO_DEPTH);
    hal_psramip_mc_busy_wait();
    //hal_psramip_set_mem_data_mask();
}

static void psram_write_reg(uint32_t reg, uint32_t val)
{
    psram_send_cmd_reg(MEMIF_MRS, reg, val);
}

static void psram_single_cmd(enum MEMIF_CMD_T cmd)
{
    hal_psramip_flush_all_fifo();
    hal_psramip_set_cmd(cmd);
    hal_psramip_mc_busy_wait();
}

static POSSIBLY_UNUSED void psram_reset(void)
{
    psram_single_cmd(MEMIF_RST);
}

static void psram_set_timing(uint32_t clk)
{
    uint32_t reg;
    uint32_t val;

#if PSRAMSIZE == 0x800000
    reg = 8;
#ifdef PSRAM_WRAP_ENABLE
    // Wrap 32
    val = MR8_BL(1);
#else
    // Wrap 1k
    val = MR8_BL(0x3);
#endif
    psram_write_reg(reg, val);
#endif
    reg = 0;
    if (clk <= 66000000) {
        val = 2;
    } else if (clk <= 109000000) {
        val = 3;
    } else if (clk <= 133000000) {
        val = 4;
    } else if (clk <= 166000000) {
        val = 5;
    } else {
        val = 6;
    }
    // Latency type: Variable
    val = MR0_DRIVE_STR(3) | MR0_READ_LATENCY(val);
    psram_write_reg(reg, val);

    reg = 4;
    if (clk <= 166000000) {
        val = 0;
    } else {
        val = 4;
    }
    //Fast Refresh,
    val = MR4_PASR(0) | MR4_WRITE_LATENCY(val);
    psram_write_reg(reg, val);
}

static void hal_psram_phy_dll_config(uint32_t clk)
{
    uint32_t phy_clk;
    uint32_t range;
    uint32_t val;

    val = psram_phy->REG_050;
    val &= ~PSRAM_ULP_PHY_REG_DLL_RESETB | PSRAM_ULP_PHY_REG_DLL_CK_RDY;
    psram_phy->REG_050 = val;
    phy_clk = clk;
    if (phy_clk <= 100000000 / 2) {
        range = 3;
    } else if (phy_clk <= 150000000 / 2) {
        range = 2;
    } else if (phy_clk <= 300000000 / 2) {
        range = 1;
    } else {
        range = 0;
    }
    val = SET_BITFIELD(val, PSRAM_ULP_PHY_REG_DLL_RANGE, range);
    psram_phy->REG_050 = val;
    val |= PSRAM_ULP_PHY_REG_DLL_RESETB | PSRAM_ULP_PHY_REG_DLL_CK_RDY;
    psram_phy->REG_050 = val;
}

static void hal_psram_phy_init(uint32_t clk)
{
    uint32_t val;
    val = psram_phy->REG_048;
    val |= PSRAM_ULP_PHY_REG_LDO_PU | PSRAM_ULP_PHY_REG_LDO_PRECHARGE;
    psram_phy->REG_048 = val;
    hal_sys_timer_delay_us(10);

    val &= ~PSRAM_ULP_PHY_REG_LDO_PRECHARGE;
    val = SET_BITFIELD(val, PSRAM_ULP_PHY_REG_LDO_IEN1, 0xc);
    val = SET_BITFIELD(val, PSRAM_ULP_PHY_REG_LDO_IEN2, 0x5);
    val = SET_BITFIELD(val, PSRAM_ULP_PHY_REG_LDO_VTUNE, 0x0);
    psram_phy->REG_048 = val;

    val = psram_phy->REG_04C;
    val |= PSRAM_ULP_PHY_REG_PSRAM_PU;
    val = SET_BITFIELD(val, PSRAM_ULP_PHY_REG_PSRAM_SWRC, 0x3);
    val = SET_BITFIELD(val, PSRAM_ULP_PHY_REG_PSRAM_TXDRV, 0x3);
    psram_phy->REG_04C = val;

    val = psram_phy->REG_050;
    val |= PSRAM_ULP_PHY_REG_DLL_PU;
    //val = SET_BITFIELD(val, PSRAM_ULP_PHY_REG_DLL_SWRC, 0x3);
    psram_phy->REG_050 = val;
    hal_sys_timer_delay_us(2);

    val |= PSRAM_ULP_PHY_REG_DLL_RESETB;
    psram_phy->REG_050 = val;
    hal_sys_timer_delay_us(20);

    hal_psram_phy_dll_config(clk);
}

static void hal_psram_mc_set_timing(uint32_t clk)
{
    uint32_t val;

    if (clk <= 166000000) {
        val = PSRAM_ULP_MC_WRITE_LATENCY(0);
    } else {
        val = PSRAM_ULP_MC_WRITE_LATENCY(2);
    }
    psram_mc->REG_028 = val;
#if (CHIP_PSRAM_CTRL_VER == 2)
    if (clk <= 66000000) {
        val = PSRAM_ULP_MC_READ_LATENCY(2);
    } else if (clk <= 109000000) {
        val = PSRAM_ULP_MC_READ_LATENCY(3);
    } else if (clk <= 133000000) {
        val = PSRAM_ULP_MC_READ_LATENCY(4);
    } else if (clk <= 166000000) {
        val = PSRAM_ULP_MC_READ_LATENCY(5);
    } else {
        val = PSRAM_ULP_MC_READ_LATENCY(6);
    }
    psram_mc->REG_02C = val;
#else
    // Min latency: 2 cycles
    psram_mc->REG_02C = PSRAM_ULP_MC_READ_LATENCY(2);
#endif
    // tRC >= 55 ns
    val = (clk / 1000000 * 55 + (1000 - 1)) / 1000;
    psram_mc->REG_050 = PSRAM_ULP_MC_T_RC(val);
    val = 2;
    psram_mc->REG_058 = PSRAM_ULP_MC_T_CPHR(val);
    psram_mc->REG_068 = PSRAM_ULP_MC_T_MRR(val);
    val = 6;
    psram_mc->REG_060 = PSRAM_ULP_MC_T_CPHW(val);
#ifdef CHIP_BEST2001
    val += 1;
#endif
    psram_mc->REG_06C = PSRAM_ULP_MC_T_MRS(val);
    // tCEM <= 2.5 us
    val = clk / 1000000 * 25 / 10;
    psram_mc->REG_070 = PSRAM_ULP_MC_T_CEM(val);
    // tRST >= 2 us
    val = clk / 1000000 * 2 + 1;
    psram_mc->REG_074 = PSRAM_ULP_MC_T_RST(val);
    // tHS >= 4 us
    val = clk / 1000000 * 4 + 1;
    psram_mc->REG_080 = PSRAM_ULP_MC_T_HS(val);
    // tXPHS in [60 ns, 4 us]
    val = (clk / 1000000 * 60 + (1000 - 1)) / 1000;
    psram_mc->REG_084 = PSRAM_ULP_MC_T_XPHS(val);
    // tXHS >= 70 us
    val = clk / 1000000 * 70 + 1;
    psram_mc->REG_088 = PSRAM_ULP_MC_T_XHS(val);
    psram_mc->REG_09C = PSRAM_ULP_MC_WR_DMY_CYC(1);
    // NOP dummy cycles, same as tXPHS in [60 ns, 4 us]
    val = (clk / 1000000 * 60 + (1000 - 1)) / 1000;
    psram_mc->REG_0A0 = PSRAM_ULP_MC_STOP_CLK_IN_NOP | PSRAM_ULP_MC_NOP_DMY_CYC(val);
    psram_mc->REG_0A4 = PSRAM_ULP_MC_QUEUE_IDLE_CYCLE(5000);
}

static void hal_psram_init_calib(void)
{
    uint32_t delay;

    while ((psram_phy->REG_058 & PSRAM_ULP_PHY_DLL_LOCK) == 0);

    delay = GET_BITFIELD(psram_phy->REG_058, PSRAM_ULP_PHY_DLL_DLY_IN);
    //ASSERT(delay < (PSRAM_ULP_PHY_DLL_DLY_IN_MASK >> PSRAM_ULP_PHY_DLL_DLY_IN_SHIFT),
    //    "%s: Bad DLL_DLY_IN=0x%X reg=0x%08X", __func__, delay, psram_phy->REG_058);

    delay /= 2;
    psram_phy->REG_054 = PSRAM_ULP_PHY_REG_PSRAM_TX_CEB_DLY(delay) | PSRAM_ULP_PHY_REG_PSRAM_TX_CLK_DLY(delay) |
        PSRAM_ULP_PHY_REG_PSRAM_TX_DQS_DLY(delay) | PSRAM_ULP_PHY_REG_PSRAM_RX_DQS_DLY(delay);
}

static void hal_psram_mc_init(uint32_t clk)
{
#ifdef PSRAM_DUAL_8BIT
    psram_mc->REG_000 = PSRAM_ULP_MC_CHIP_BIT;
#else
    psram_mc->REG_000 = 0;
#endif
    psram_mc->REG_020 = 0;
    psram_mc->REG_024 =
#ifndef CHIP_BEST2001
        PSRAM_ULP_MC_ENTRY_SLEEP_IDLE |
#endif
        PSRAM_ULP_MC_AUTOWAKEUP_EN |
        PSRAM_ULP_MC_PD_MR(6) | PSRAM_ULP_MC_PD_CMD(0xF0);
#ifdef PSRAM_WRAP_ENABLE
    // Burst len: 32 bytes, page: 1K
    psram_mc->REG_034 = PSRAM_ULP_MC_BURST_LENGTH(1) | PSRAM_ULP_MC_PAGE_BOUNDARY(0);
#else
    // 8MB psram
    // Burst len: 1K, page: 1K
    psram_mc->REG_034 = PSRAM_ULP_MC_BURST_LENGTH(4) | PSRAM_ULP_MC_PAGE_BOUNDARY(0);
#endif
    // AHB bus width: 32 bits
    psram_mc->REG_038 = 0;
    // Write buffer level with high priority: 0~7
    psram_mc->REG_03C = PSRAM_ULP_MC_HIGH_PRI_LEVEL(4);
#ifdef PSRAM_WRAP_ENABLE
    psram_mc->REG_040 = PSRAM_ULP_MC_CP_WRAP_EN;
#else
    psram_mc->REG_040 = PSRAM_ULP_MC_WRAP_CRT_RET_EN;
#endif
    psram_mc->REG_044 = 0;
    psram_mc->REG_048 = 0;

    hal_psramip_set_reg_data_mask();

    hal_psram_mc_set_timing(clk);

    psram_mc->REG_400 = PSRAM_ULP_MC_INIT_COMPLETE;

    hal_psram_init_calib();
}

void hal_psram_sleep(void)
{
    hal_psramip_mc_busy_wait();
    if (!hal_psramip_mc_in_sleep()) {
#ifndef CHIP_BEST2001
        psram_mc->REG_024 &= ~PSRAM_ULP_MC_ENTRY_SLEEP_IDLE;
#endif
        hal_psramip_mc_busy_wait();
        hal_psramip_set_cmd(MEMIF_PD);
        hal_psramip_mc_busy_wait();
    }
}

void hal_psram_wakeup(void)
{
    hal_psramip_mc_busy_wait();
#ifndef CHIP_BEST2001
    psram_mc->REG_024 |= PSRAM_ULP_MC_ENTRY_SLEEP_IDLE;
#endif
}

static void psram_chip_timing_config(uint32_t clk, bool update_psram_first)
{
    enum HAL_CMU_FREQ_T freq;

    if (clk <= 52000000) {
        freq = HAL_CMU_FREQ_104M;
    } else if (clk <= 104000000) {
        freq = HAL_CMU_FREQ_208M;
    } else {
#ifdef HAL_CMU_FREQ_T
        freq = HAL_CMU_FREQ_390M;
#else
        freq = HAL_CMU_FREQ_208M;
#endif
    }

    if (update_psram_first) {
        psram_set_timing(clk);
    }

    hal_cmu_mem_set_freq(freq);
    hal_sys_timer_delay_us(3);
    hal_psram_phy_dll_config(clk);
    hal_psram_init_calib();
    hal_psram_mc_set_timing(clk);
    if (!update_psram_first) {
        psram_set_timing(clk);
    }
}

static bool psramphy_check_write_valid()
{
    int i;
    volatile uint32_t *psram_base = (volatile uint32_t *)PSRAM_NC_BASE;
    for (i=0; i<0x8; ++i) {
        *(psram_base+i) = 0xffffffff;
    }
    for (i=0; i<0x8; ++i) {
        *(psram_base+i) = ((i << 0) | (i << 8) | (i << 16) | (i << 24));
    }
    hal_psramip_wb_busy_wait();
    hal_psramip_mc_busy_wait();
    for (i=0; i<0x8; ++i) {
        uint32_t check_val = *(psram_base+i);
        if (check_val != ((i << 0) | (i << 8) | (i << 16) | (i << 24))) {
            //PSRAM_TRACE(2,"write fail, %p = 0x%x", (uint32_t)(psram_base+i), check_val);
            return false;
        }
    }
    return true;
}

static void hal_psram_calib_range(uint32_t range)
{
    uint32_t val;
    uint32_t delay;
    uint8_t tx_dqs, rx_dqs;
    uint8_t inc_delay, volume;
    uint8_t cali_valid[0x20][0x20];
    uint8_t cali_value[0x20][0x20];

    ASSERT(range <= (PSRAM_ULP_PHY_DLL_DLY_IN_MASK >> PSRAM_ULP_PHY_DLL_DLY_IN_SHIFT), "ERROR, bad ana phy range:%d", range);

    val = psram_phy->REG_050;
    val &= ~(PSRAM_ULP_PHY_REG_DLL_RESETB | PSRAM_ULP_PHY_REG_DLL_CK_RDY);
    psram_phy->REG_050 = val;
    val = SET_BITFIELD(val, PSRAM_ULP_PHY_REG_DLL_RANGE, range);
    psram_phy->REG_050 = val;
    val |= (PSRAM_ULP_PHY_REG_DLL_RESETB | PSRAM_ULP_PHY_REG_DLL_CK_RDY);
    psram_phy->REG_050 = val;

    hal_sys_timer_delay_us(100);
    while ((psram_phy->REG_058 & PSRAM_ULP_PHY_DLL_LOCK) == 0);

    val = psram_phy->REG_058;
    if ((val & PSRAM_ULP_PHY_DLL_ALL_ONE)) {
        PSRAM_TRACE(2,"%s: all one, increase range=%d", __func__, range+1);
        return hal_psram_calib_range(range+1);
    }

    delay = GET_BITFIELD(val, PSRAM_ULP_PHY_DLL_DLY_IN);
    PSRAM_TRACE(4,"%s, range:%d, T/4 = 0x%x(psram_phy->REG_058:0x%x)", __func__, range, delay/2, val);
    if (delay > (PSRAM_ULP_PHY_REG_PSRAM_TX_DQS_DLY_MASK>>PSRAM_ULP_PHY_REG_PSRAM_TX_DQS_DLY_SHIFT) && range<3) {
        PSRAM_TRACE("%s: bad delay (T/2 > 0x1f). increase range=%d", __func__, range+1);
        return hal_psram_calib_range(range+1);
    }

    inc_delay = delay/8;
    if (inc_delay == 0)
        inc_delay = 1;

    //volume = (PSRAM_ULP_PHY_REG_PSRAM_TX_DQS_DLY_MASK>>PSRAM_ULP_PHY_REG_PSRAM_TX_DQS_DLY_SHIFT) / inc_delay;
    volume = MIN(delay, (PSRAM_ULP_PHY_REG_PSRAM_TX_DQS_DLY_MASK>>PSRAM_ULP_PHY_REG_PSRAM_TX_DQS_DLY_SHIFT)) / inc_delay;

    PSRAM_TRACE(2,"volume:%d, inc_delay:%d", volume, inc_delay);

    uint8_t all_valid = 1;

    memset(cali_valid, 0, sizeof(cali_valid));
    for (tx_dqs=0; tx_dqs<=volume; tx_dqs++) {
        for (rx_dqs=0; rx_dqs<=volume; rx_dqs++) {
            psram_phy->REG_054 = PSRAM_ULP_PHY_REG_PSRAM_TX_CEB_DLY(delay/2) | PSRAM_ULP_PHY_REG_PSRAM_TX_CLK_DLY(delay/2) |
                PSRAM_ULP_PHY_REG_PSRAM_TX_DQS_DLY(tx_dqs*inc_delay) | PSRAM_ULP_PHY_REG_PSRAM_RX_DQS_DLY(rx_dqs*inc_delay);
            cali_valid[tx_dqs][rx_dqs] = psramphy_check_write_valid();
            if (cali_valid[tx_dqs][rx_dqs] == 0)
                all_valid = 0;
        }
    }

    if (all_valid && range < (PSRAM_ULP_PHY_DLL_DLY_IN_MASK >> PSRAM_ULP_PHY_DLL_DLY_IN_SHIFT)) {
        PSRAM_TRACE(2,"%s: all valid increase range=%d", __func__, range+1);
        //return hal_psram_calib_range(range+1);
    }

    memset(cali_value, 0, sizeof(cali_value));
    PSRAM_TRACENOCRLF_NOTS("\r\n\r\n ---------------------------------------------------------------------- \r\n");
    PSRAM_TRACENOCRLF_NOTS("    rx_dqs");
    for (tx_dqs=0; tx_dqs<=volume; tx_dqs++) {
        PSRAM_TRACENOCRLF_NOTS(" %2d ", tx_dqs*inc_delay);
    }
    PSRAM_TRACENOCRLF_NOTS("\r\n");
    for (tx_dqs=0; tx_dqs<=volume; tx_dqs++) {
        PSRAM_TRACENOCRLF_NOTS("tx_dqs:%2d ", tx_dqs*inc_delay);
        for (rx_dqs=0; rx_dqs<=volume; rx_dqs++) {
            PSRAM_TRACENOCRLF_NOTS("  %d ", cali_valid[tx_dqs][rx_dqs]);
            if (cali_valid[tx_dqs][rx_dqs]) {
                uint8_t len_from_zero;
                int8_t p;
                p = tx_dqs;
                while (p>=0) {
                    if (cali_valid[p][rx_dqs] == 0)
                        break;
                    p--;
                }
                len_from_zero = tx_dqs - p;
                cali_value[tx_dqs][rx_dqs] = len_from_zero;

                p = tx_dqs;
                while (p<=volume) {
                    if (cali_valid[p][rx_dqs] == 0)
                        break;
                    p++;
                }
                len_from_zero = p - tx_dqs;
                cali_value[tx_dqs][rx_dqs] = MIN(cali_value[tx_dqs][rx_dqs], len_from_zero);

                p = rx_dqs;
                while (p>=0) {
                    if (cali_valid[tx_dqs][p] == 0)
                        break;
                    p--;
                }
                len_from_zero = rx_dqs - p;
                cali_value[tx_dqs][rx_dqs] = MIN(cali_value[tx_dqs][rx_dqs], len_from_zero);

                p = rx_dqs;
                while (p<=volume) {
                    if (cali_valid[tx_dqs][p] == 0)
                        break;
                    p++;
                }
                len_from_zero = p - rx_dqs;
                cali_value[tx_dqs][rx_dqs] = MIN(cali_value[tx_dqs][rx_dqs], len_from_zero);
            }
        }
        PSRAM_TRACENOCRLF_NOTS("\r\n");
    }
    PSRAM_TRACENOCRLF_NOTS(" -------------------------------------------------------------------------- \r\n");

#if 0
    PSRAM_TRACENOCRLF_NOTS("\r\n\r\n ---------------------------------------------------------------------- \r\n");
    PSRAM_TRACENOCRLF_NOTS("    rx_dqs");
    for (tx_dqs=0; tx_dqs<=volume; tx_dqs++) {
        PSRAM_TRACENOCRLF_NOTS(" %2d ", tx_dqs*inc_delay);
    }
    PSRAM_TRACENOCRLF_NOTS("\r\n");
    for (tx_dqs=0; tx_dqs<=volume; tx_dqs++) {
        PSRAM_TRACENOCRLF_NOTS("tx_dqs:%2d ", tx_dqs*inc_delay);
        for (rx_dqs=0; rx_dqs<=volume; rx_dqs++) {
            PSRAM_TRACENOCRLF_NOTS("  %d ", cali_value[tx_dqs][rx_dqs]);
        }
        PSRAM_TRACENOCRLF_NOTS("\r\n");
    }
    PSRAM_TRACENOCRLF_NOTS(" -------------------------------------------------------------------------- \r\n");
#endif

    uint32_t position = 0;
    uint8_t max_value = 0;
    for (tx_dqs=0; tx_dqs<=volume; tx_dqs++) {
        for (rx_dqs=0; rx_dqs<=volume; rx_dqs++) {
            if (cali_value[tx_dqs][rx_dqs] > max_value) {
                max_value = cali_value[tx_dqs][rx_dqs];
                position = tx_dqs*(volume+1) + rx_dqs;
            }
        }
    }
    PSRAM_TRACENOCRLF_NOTS("position:%d\r\n", position);
    tx_dqs = position/(volume+1)*inc_delay;
    rx_dqs = (position%(volume+1))*inc_delay;
    PSRAM_TRACENOCRLF_NOTS("most optimal position. tx_dqs:%d, rx_dqs:%d\r\n", tx_dqs, rx_dqs);

    psram_phy->REG_054 = PSRAM_ULP_PHY_REG_PSRAM_TX_CEB_DLY(delay/2) | PSRAM_ULP_PHY_REG_PSRAM_TX_CLK_DLY(delay/2) |
        PSRAM_ULP_PHY_REG_PSRAM_TX_DQS_DLY(tx_dqs) | PSRAM_ULP_PHY_REG_PSRAM_RX_DQS_DLY(rx_dqs);

}
static void hal_psram_calib(uint32_t clk)
{
    uint32_t phy_clk;
    uint32_t range;
    PSRAM_TRACE("%s, speed:%d", __func__, clk);
    phy_clk = clk;
    if (phy_clk <= 100000000 / 2) {
        range = 3;
    } else if (phy_clk <= 150000000 / 2) {
        range = 2;
    } else if (phy_clk <= 300000000 / 2) {
        range = 1;
    } else {
        range = 0;
    }
    hal_psram_calib_range(range);
}
void hal_psram_snoop_enable()
{
    psram_mc->REG_044 &= ~PSRAM_ULP_MC_SNP_DISABLE;
}
void hal_psram_snoop_disable()
{
    psram_mc->REG_044 |= PSRAM_ULP_MC_SNP_DISABLE;
}

void hal_psram_init(void)
{
    hal_cache_wrap_enable(HAL_CACHE_ID_I_CACHE);
    hal_cache_wrap_enable(HAL_CACHE_ID_D_CACHE);

    hal_cmu_mem_set_freq(HAL_CMU_FREQ_104M);
    hal_cmu_clock_enable(HAL_CMU_MOD_O_PSRAM);
    hal_cmu_clock_enable(HAL_CMU_MOD_H_PSRAM);
    hal_cmu_reset_clear(HAL_CMU_MOD_O_PSRAM);
    hal_cmu_reset_clear(HAL_CMU_MOD_H_PSRAM);

    hal_psram_phy_init(psram_cfg_clk);
    hal_sys_timer_delay_us(30);
    hal_psram_mc_init(psram_cfg_clk);

#ifdef PSRAM_RESET
    psram_reset();
    psram_chip_timing_config(psram_run_clk, true);
#else
    uint32_t reg;
    uint32_t val;

    reg = 4;
    psram_read_reg(reg, &val);
    if (val & MR4_WL) {
        psram_chip_timing_config(psram_run_clk, false);
    } else {
        psram_chip_timing_config(psram_run_clk, true);
    }
#endif

    hal_psram_snoop_disable();
    hal_psram_calib(psram_run_clk);
    hal_psram_snoop_enable();
}

#endif

