/*
 * Copyright (c) 2021-2022 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#ifndef _HPM_FEMC_DRV_H
#define _HPM_FEMC_DRV_H
#include "hpm_femc_regs.h"

/**
 * @brief femc driver APIs
 * @defgroup dram_interface femc driver APIs
 * @ingroup io_interfaces
 * @{
 *
 */

#define FEMC_SDRAM_MAX_BURST_LENGTH_IN_BYTE (8UL)
/* @brief femc sdram column address bit width */
#define FEMC_SDRAM_COLUMN_ADDR_12_BITS (0U)
#define FEMC_SDRAM_COLUMN_ADDR_11_BITS (1U)
#define FEMC_SDRAM_COLUMN_ADDR_10_BITS (2U)
#define FEMC_SDRAM_COLUMN_ADDR_9_BITS (3U)
#define FEMC_SDRAM_COLUMN_ADDR_8_BITS (4U)
/* @brief cas latency */
#define FEMC_SDRAM_CAS_LATENCY_1 (1U)
#define FEMC_SDRAM_CAS_LATENCY_2 (2U)
#define FEMC_SDRAM_CAS_LATENCY_3 (3U)
/* @brief sdram bank number */
#define FEMC_SDRAM_BANK_NUM_4 (0U)
#define FEMC_SDRAM_BANK_NUM_2 (1U)
/* @brief chip select */
#define FEMC_SDRAM_CS0 (0U)
#define FEMC_SDRAM_CS1 (1U)
/* @brief sdram port size */
#define FEMC_SDRAM_PORT_SIZE_8_BITS (0U)
#define FEMC_SDRAM_PORT_SIZE_16_BITS (1U)
#define FEMC_SDRAM_PORT_SIZE_32_BITS (2U)

#define FEMC_AXI_Q_COUNT (2U)
#define FEMC_AXI_Q_A (0U)
#define FEMC_AXI_Q_B (1U)
/* @brief DQS option */
#define FEMC_DQS_INTERNAL (0U)
#define FEMC_DQS_FROM_PAD (1U)

#define FEMC_CMD_KEY                   FEMC_IPCMD_KEY_SET(0xA55A)
#define FEMC_CMD_WRITE_FLAG            (1UL << 31)
#define FEMC_CMD_SDRAM_READ            (0x8U)
#define FEMC_CMD_SDRAM_WRITE           (FEMC_CMD_WRITE_FLAG | 0x9U)
#define FEMC_CMD_SDRAM_MODE_SET        (FEMC_CMD_WRITE_FLAG | 0xAU)
#define FEMC_CMD_SDRAM_ACTIVE          (0xBU)
#define FEMC_CMD_SDRAM_AUTO_REFRESH    (0xCU)
#define FEMC_CMD_SDRAM_SELF_REFRESH    (0xDU)
#define FEMC_CMD_SDRAM_PRECHARGE       (0xEU)
#define FEMC_CMD_SDRAM_PRECHARGE_ALL   (0xFU)

/* @brief sram address mode */
#define FEMC_SRAM_AD_MUX_MODE (0U)
#define FEMC_SRAM_AD_NONMUX_MODE (3U)

/* @brief sram adv hold state */
#define FEMC_SRAM_ADV_HOLD_HIGH (0U)
#define FEMC_SRAM_ADV_HOLD_LOW (1U)

/* @brief sram adv polarity */
#define FEMC_SRAM_ADV_ACTIVE_LOW (0U)
#define FEMC_SRAM_ADV_ACTIVE_HIGH (1U)

/* @brief sram port size */
#define FEMC_SRAM_PORT_SIZE_8_BITS (0U)
#define FEMC_SRAM_PORT_SIZE_16_BITS (1U)

/* @brief IO_CSX selection */
#define FEMC_IO_CSX_SDRAM_CS1 (1U)
#define FEMC_IO_CSX_SRAM_CE (6U)

/**
 * @brief Structure for specifying the configuration of AXI queue weight
 */
typedef struct {
    bool enable;                /**< Enable AXI weight setting flag */
    uint8_t qos;
    uint8_t age;
    uint8_t slave_hit_wo_rw;
    uint8_t slave_hit;          /**< only available for queue A */
    uint8_t page_hit;           /**< only available for queue B */
    uint8_t bank_rotation;      /**< only available for queue B */
} femc_axi_q_weight_t;

/**
 * @brief Structure for specifying the configuration of SDRAM
 */
typedef struct {
    uint32_t base_address;              /**< external SDRAM base address */
    uint32_t size_in_byte;              /**< external SDRAM size in byte */
    uint32_t refresh_count;             /**< referesh count */
    uint8_t col_addr_bits;              /**< column address bit count */
    uint8_t cas_latency;                /**< CAS latency */
    uint8_t cs;                         /**< chip select */
    uint8_t bank_num;                   /**< bank number */
    uint8_t prescaler;                  /**< presecaler */
    uint8_t port_size;                  /**< SDRAM port size */
    uint8_t burst_len_in_byte;          /**< 1/2/4/8 bytes */
    uint8_t cke_off_in_ns;
    uint8_t act_to_precharge_in_ns;     /**< Tras */
    uint8_t precharge_to_act_in_ns;     /**< Trp  */
    uint8_t act_to_rw_in_ns;            /**< Trcd */
    uint8_t act_to_act_in_ns;           /**< Trrd */
    uint8_t refresh_to_refresh_in_ns;   /**< Trc */
    uint8_t write_recover_in_ns;        /**< Twr */
    uint8_t self_refresh_recover_in_ns; /**< Txsr */
    uint8_t refresh_recover_in_ns;      /**< Trc */
    uint8_t refresh_in_ms;              /**< Tref */
    uint8_t idle_timeout_in_ns;
    uint8_t cmd_data_width;
    uint8_t auto_refresh_count_in_one_burst;
    bool delay_cell_disable;            /**< Delay cell disable */
    uint8_t delay_cell_value;           /**< Delay cell value */
} femc_sdram_config_t;

/**
 * @brief Structure for specifying the configuration of SRAM
 */
typedef struct {
    uint32_t base_address;              /**< external SRAM base address */
    uint32_t size_in_byte;              /**< external SRAM size in byte */
    uint8_t cs_index;                   /**< chip select */
    uint8_t address_mode;               /**< address mode */
    uint8_t port_size;                  /**< port size */
    uint8_t adv_hold_state;             /**< adv hold level */
    uint8_t adv_polarity;               /**< adv polarity */
    uint8_t oeh_in_ns;                  /**< OE high time */
    uint8_t oel_in_ns;                  /**< OE low time */
    uint8_t weh_in_ns;                  /**< WE high time */
    uint8_t wel_in_ns;                  /**< WE low time */
    uint8_t ah_in_ns;                   /**< address hold time */
    uint8_t as_in_ns;                   /**< address setup time */
    uint8_t ceh_in_ns;                  /**< chip enable hold time */
    uint8_t ces_in_ns;                  /**< chip enable setup time */
} femc_sram_config_t;

/**
 * @brief Structure for specifying the configuration of FEMC
 */
typedef struct {
    uint8_t dqs;                        /**< DQS setting */
    uint8_t cmd_timeout;                /**< command timeout */
    uint8_t bus_timeout;                /**< bus timeout */
    femc_axi_q_weight_t axi_q_weight[FEMC_AXI_Q_COUNT];
} femc_config_t;

/**
 * @brief Structure for FEMC command
 */
typedef struct {
    uint32_t opcode;
    uint32_t data;
} femc_cmd_t;

/*
 * @brief FEMC specific status
 */
enum {
    status_femc_cmd_err = MAKE_STATUS(status_group_femc, 1),
};

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief femc enable
 *
 * Enable FEMC
 *
 * @param[in] ptr FEMC base address
 */
static inline void femc_enable(FEMC_Type *ptr)
{
    ptr->CTRL &= ~FEMC_CTRL_DIS_MASK;
}

/**
 * @brief femc disable
 *
 * Disable FEMC
 *
 * @param[in] ptr FEMC base address
 */
static inline void femc_disable(FEMC_Type *ptr)
{
    while ((ptr->STAT0 & (uint32_t) FEMC_STAT0_IDLE_MASK) == 0) {
    }
    ptr->CTRL |= FEMC_CTRL_DIS_MASK;
}

/**
 * @brief femc software reset
 *
 * Perform software reset
 *
 * @param[in] ptr FEMC base address
 */
static inline void femc_sw_reset(FEMC_Type *ptr)
{
    ptr->CTRL = FEMC_CTRL_RST_MASK;
    while ((ptr->CTRL & (uint32_t) FEMC_CTRL_RST_MASK) != 0) {
    }
}

/**
 * @brief femc get default config
 *
 * Get FEMC default parameters
 *
 * @param[in] ptr FEMC base address
 * @param[out] config femc_config_t address
 */
void femc_default_config(FEMC_Type *ptr, femc_config_t *config);

/**
 * @brief femc init controller
 *
 * Initialize FEMC with give femc_config_t
 *
 * @param[in] ptr FEMC base address
 * @param[in] config femc_config_t to initialize femc
 */
void femc_init(FEMC_Type *ptr, femc_config_t *config);

/**
 * @brief femc get typical sdram config
 *
 * Fill out the structure of femc_sdram_config_t with typical SDRAM parameters which should work
 * with most SDRAMs.
 *
 * @param[in] ptr FEMC base address
 * @param[out] config femc_sdram_config_t sdram configuration struction to config femc
 */
void femc_get_typical_sdram_config(FEMC_Type *ptr, femc_sdram_config_t *config);

/**
 * @brief femc config sdram
 *
 * Configure FEMC controlling external SDRAM using parameters specified in femc_sdram_config_t
 *
 * @param[in] ptr FEMC base address
 * @param[in] clk_in_hz femc source clock frequency in Hz
 * @param[in] config femc_sdram_config_t sdram configuration struction to config femc
 */
hpm_stat_t femc_config_sdram(FEMC_Type *ptr, uint32_t clk_in_hz, femc_sdram_config_t *config);

/**
 * @brief femc get typical sram config
 *
 * Fill out the structure of femc_sram_config_t with typical SRAM parameters which should work
 * with most SRAMs.
 *
 * @param[in] ptr FEMC base address
 * @param[out] config femc_sdram_config_t sdram configuration struction to config femc
 */
void femc_get_typical_sram_config(FEMC_Type *ptr, femc_sram_config_t *config);

/**
 * @brief femc config sram
 *
 * Configure FEMC controlling external SRAM using parameters specified in femc_sram_config_t
 *
 * @param[in] ptr FEMC base address
 * @param[in] clk_in_hz femc source clock frequency in Hz
 * @param[in] config femc_sram_config_t sram configuration struction to config femc
 */
hpm_stat_t femc_config_sram(FEMC_Type *ptr, uint32_t clk_in_hz, femc_sram_config_t *config);

#ifdef __cplusplus
}
#endif
/**
 * @}
 */
#endif    /* _HPM_FEMC_DRV_H */

