// Copyright(c) 2017, Intel Corporation
//
// Redistribution  and  use  in source  and  binary  forms,  with  or  without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of  source code  must retain the  above copyright notice,
//   this list of conditions and the following disclaimer.
// * 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.
// * Neither the name  of Intel Corporation  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 OWNER  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.
//****************************************************************************
// Arthur.Sheiman@Intel.com   Created: 03-31-16
// Revised: 10-26-16  18:28
//
// User Clock header file
//
//****************************************************************************

#ifndef USER_CLK_PGM_UCLK_H_
#define USER_CLK_PGM_UCLK_H_

#include <stdint.h>

// .h include, defines
#include "user_clk_pgm_uclock_freq_template_D.h"
// Errors, decimal code
#include "user_clk_pgm_uclock_eror_messages_D.h"
// Private member variables and objects
#include "user_clk_pgm_uclock_freq_template_A.h"
#include "user_clk_pgm_uclock_eror_messages_A.h"

#include "common_int.h"

// qph_user_clk.sv Equates
#if defined(DEF_BDX_P)
// BDX-P:
#define QUCPU_UI64_PRT_UCLK_CMD_0                                              \
	((uint64_t)0x0000000000000605LLU) // 0x03028 / 8 = 0x00605
#define QUCPU_UI64_PRT_UCLK_CMD_1                                              \
	((uint64_t)0x0000000000000606LLU) // 0x03030 / 8 = 0x00606
#define QUCPU_UI64_PRT_UCLK_STS_0                                              \
	((uint64_t)0x0000000000000607LLU) // 0x03038 / 8 = 0x00607
#define QUCPU_UI64_PRT_UCLK_STS_1                                              \
	((uint64_t)0x0000000000000608LLU) // 0x03040 / 8 = 0x00608

#elif defined(DEF_SKX_P)
// SKX-P:
#define QUCPU_UI64_PRT_UCLK_CMD_0                                              \
	((uint64_t)0x000000000000000aLLU) // 0x00050 / 8 = 0x0060a
#define QUCPU_UI64_PRT_UCLK_CMD_1                                              \
	((uint64_t)0x000000000000000bLLU) // 0x00058 / 8 = 0x0060b
#define QUCPU_UI64_PRT_UCLK_STS_0                                              \
	((uint64_t)0x000000000000000cLLU) // 0x00060 / 8 = 0x0060c
#define QUCPU_UI64_PRT_UCLK_STS_1                                              \
	((uint64_t)0x000000000000000dLLU) // 0x00068 / 8 = 0x0060d
#endif

#define QUCPU_UI64_AFU_MMIO_PRT_OFFSET_QW                                      \
	((uint64_t)0x0000000000000000LLU) // 0x00000 / 8 = 0x00000

#define QUCPU_UI64_CMD_0_SR1_b58                                               \
	((uint64_t)0x0400000000000000LLU) // fPLL Select: 0=refclk0, 1=refclk1
#define QUCPU_UI64_CMD_0_PDN_b57                                               \
	((uint64_t)0x0200000000000000LLU) // fPLL Powerdoen
#define QUCPU_UI64_CMD_0_PRS_b56                                               \
	((uint64_t)0x0100000000000000LLU) // fPLL management reset
#define QUCPU_UI64_CMD_0_MRN_b52                                               \
	((uint64_t)0x0010000000000000LLU) // mmmach machine reset_n
#define QUCPU_UI64_CMD_0_SEQ_b49t48                                            \
	((uint64_t)0x0003000000000000LLU) // mmmach sequence
#define QUCPU_UI64_CMD_0_WRT_b44                                               \
	((uint64_t)0x0000100000000000LLU) // mmmach write
#define QUCPU_UI64_CMD_0_ADR_b41t32                                            \
	((uint64_t)0x000003ff00000000LLU) // mmmach MM address
#define QUCPU_UI64_CMD_0_DAT_b31t00                                            \
	((uint64_t)0x00000000ffffffffLLU) // mmmach MM write data

#define QUCPU_UI64_CMD_0_AMM_b51t00                                            \
	((uint64_t)0x000fffffffffffffLLU) // Avmm mmmach portion

#define QUCPU_UI64_STS_0_ERR_b63                                               \
	((uint64_t)0x8000000000000000LLU) // mmmach error
#define QUCPU_UI64_STS_0_RCK_b62                                               \
	((uint64_t)0x4000000000000000LLU) // 0=refclk0, 1=refclk1
#define QUCPU_UI64_STS_0_BSY_b61                                               \
	((uint64_t)0x2000000000000000LLU) // fPLL cal busy
#define QUCPU_UI64_STS_0_LCK_b60                                               \
	((uint64_t)0x1000000000000000LLU) // fPLL locked
#define QUCPU_UI64_STS_0_SR1_b58                                               \
	((uint64_t)0x0400000000000000LLU) // fPLL Select: 0=refclk0, 1=refclk1
#define QUCPU_UI64_STS_0_PDN_b57                                               \
	((uint64_t)0x0200000000000000LLU) // fPLL Powerdoen
#define QUCPU_UI64_STS_0_PRS_b56                                               \
	((uint64_t)0x0100000000000000LLU) // fPLL management reset
#define QUCPU_UI64_STS_0_MRN_b52                                               \
	((uint64_t)0x0010000000000000LLU) // mmmach machine reset_n
#define QUCPU_UI64_STS_0_SEQ_b49t48                                            \
	((uint64_t)0x0003000000000000LLU) // mmmach sequence
#define QUCPU_UI64_STS_0_WRT_b44                                               \
	((uint64_t)0x0000100000000000LLU) // mmmach write
#define QUCPU_UI64_STS_0_ADR_b41t32                                            \
	((uint64_t)0x000003ff00000000LLU) // mmmach MM address
#define QUCPU_UI64_STS_0_DAT_b31t00                                            \
	((uint64_t)0x00000000ffffffffLLU) // mmmach MM read  data

#define QUCPU_UI64_CMD_1_MEA_b32                                               \
	((uint64_t)0x0000000100000000LLU) // 1: measure user clock; 0: measure
					  // 2nd clock, div2

#define QUCPU_UI64_STS_1_VER_b63t60                                            \
	((uint64_t)0xf000000000000000LLU) // frequency in 10 kHz units
#define QUCPU_UI64_STS_1_MEA_b32                                               \
	((uint64_t)0x0000000100000000LLU) // 1: measure user clock; 0: measure
					  // 2nd clock, div2
#define QUCPU_UI64_STS_1_FRQ_b16t00                                            \
	((uint64_t)0x000000000001ffffLLU) // frequency in 10 kHz units

#define QUCPU_UI64_STS_1_VER_version                                           \
	((uint64_t)0x03LLU) // Expected version number
#define QUCPU_UI64_STS_1_VER_version_legacy                                    \
	((uint64_t)0x01LLU) // Expected version number on legacy systems


#define QUCPU_UI64_AVMM_FPLL_IPI_200 ((uint64_t)0x200LLU) // IP identifer
#define QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RFDUAL                                \
	((uint64_t)0x05LLU) // Expected ID, RF=100 MHz & RF=322.265625 MHz
#define QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RF100M                                \
	((uint64_t)0x06LLU) // Expected ID, RF=100 MHz
#define QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RF322M                                \
	((uint64_t)0x07LLU) // Expected ID,              RF=322.265625 MHz

#define QUCPU_UI64_AVMM_FPLL_GPR_280 ((uint64_t)0x280LLU)
#define QUCPU_UI64_AVMM_FPLL_GPR_280_PDN_b00                                   \
	((uint64_t)0x0000000000000001LLU) // Powerdown when override set
#define QUCPU_UI64_AVMM_FPLL_GPR_280_ADM_b01                                   \
	((uint64_t)0x0000000000000001LLU) // 1: Override listen to ADME; 0:
					  // listen to powerdown port

// Bugs, decimal code
#define QUCPU_INT_UCLOCK_BUG_SLEEP_SHORT ((int)1) // Bug in fv_SleepShort

// Structures and Types
struct QUCPU_sInitz {
	uint64_t u64i_Version;	 // Version of clock user
	uint64_t u64i_PLL_ID;	  // PLL ID
	uint64_t u64i_NumFrq_Intg_End; // Integer/exact fPLL indices [0  ..End]
	uint64_t u64i_NumFrq_Frac_Beg; // Fractional    fPLL indices [Beg..End]
	uint64_t u64i_NumFrq_Frac_End;
	uint64_t u64i_NumFrq; // Array frequency  # of elements
	uint64_t u64i_NumReg; // Array registers  # of elements
	uint64_t u64i_NumRck; // Array ref-clocks # of elements
};

struct QUCPU_sFreqs {
	uint64_t u64i_Frq_ClkUsr; // Read user clock frequency (Hz)
	uint64_t u64i_Frq_DivBy2; // Read user clock frequency (Hz) divided-by-2
				  // output
};

typedef struct QUCPU_sInitz QUCPU_tInitz;

typedef struct QUCPU_sFreqs QUCPU_tFreqs;


struct QUCPU_Uclock {
	char sysfs_path[SYSFS_PATH_MAX];   // Port sysfs path
	int i_Bug_First;		   // First bug
	int i_Bug_Last;			   // Lasr bug
	int i_InitzState;		   // Initialization state
	QUCPU_tInitz tInitz_InitialParams; // Initialization parameters
	uint64_t u64i_cmd_reg_0;	   // Command register 0
	uint64_t u64i_cmd_reg_1;	   // Command register 1
	uint64_t u64i_AVMM_seq;		   // Sequence ID
};

int fi_GetFreqs(QUCPU_tFreqs *ptFreqs_retFreqs);

int fi_SetFreqs(uint64_t u64i_Refclk, uint64_t u64i_FrqInx);

int fi_RunInitz(const char *sysfs_path);

int sysfs_read_file(const char *sysfs_path, const char *csr_path,
		    uint64_t *value);

int sysfs_write_file(const char *sysfs_path, const char *csr_path,
		     uint64_t value);

int fi_WaitCalDone(void);

void fv_BugLog(int i_BugID);

int fi_AvmmReadModifyWrite(uint64_t u64i_AvmmAdr, uint64_t u64i_AvmmDat,
			   uint64_t u64i_AvmmMsk);

int fi_AvmmReadModifyWriteVerify(uint64_t u64i_AvmmAdr, uint64_t u64i_AvmmDat,
				 uint64_t u64i_AvmmMsk);

void fv_SleepShort(long int li_sleep_nanoseconds);

int fi_AvmmWrite(uint64_t u64i_AvmmAdr, uint64_t u64i_WriteData);

int fi_AvmmRead(uint64_t u64i_AvmmAdr, uint64_t *pu64i_ReadData);

const char *fpac_GetErrMsg(int i_ErrMsgInx);

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief Get fpga user clock
 *
 * @param sysfs_path  port sysfs path
 * @parm  pointer to  high user clock
 * @parm  pointer to  low user clock
 *
 * @return error code
 */
fpga_result get_userclock(const char *sysfs_path, uint64_t *userclk_high,
			  uint64_t *userclk_low);

/**
 * @brief set fpga user clock
 *
 * @param sysfs_path  port sysfs path
 * @parm  high user clock
 * @parm  low user clock
 *
 * @return error code
 */
fpga_result set_userclock(const char *sysfs_path, uint64_t userclk_high,
			  uint64_t userclk_low);

#ifdef __cplusplus
}
#endif

#endif // end  USER_CLK_PGM_UCLK_H_
