// Copyright(c) 2017-2020, 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: 09-08-16
// Revision: 10-18-16 18:06
#include <errno.h>
#include <malloc.h> /* malloc */
#include <stdlib.h> /* exit */
#include <stdio.h> /* printf */
#include <string.h> /* memcpy */
#include <unistd.h> /* getpid */
#include <stdint.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <time.h>
#include <glob.h>
#include "user_clk_pgm_uclock.h"
#include "user_clk_pgm_uclock_freq_template.h"
#include "user_clk_pgm_uclock_freq_template_322.h"
#include "user_clk_pgm_uclock_eror_messages.h"
#include "user_clk_iopll_freq.h"
// user clock sysfs
#define USER_CLOCK_CMD0 "userclk_freqcmd"
#define USER_CLOCK_CMD1 "userclk_freqcntrcmd"
#define USER_CLOCK_STS0 "userclk_freqsts"
#define USER_CLOCK_STS1 "userclk_freqcntrsts"
#define IOPLL_CLOCK_FREQ "intel-pac-iopll.*.auto/userclk/frequency"
#define MAX_FPGA_FREQ 1200
#define MIN_FPGA_FREQ 25
// User clock sleep
#define USRCLK_SLEEEP_1MS 1000000
#define USRCLK_SLEEEP_10MS 10000000
struct QUCPU_Uclock gQUCPU_Uclock;
static int using_iopll(char* sysfs_usrpath, const char* sysfs_path);
//Get fpga user clock
fpga_result get_userclock(const char* sysfs_path,
uint64_t* userclk_high,
uint64_t* userclk_low)
{
char sysfs_usrpath[SYSFS_PATH_MAX];
QUCPU_tFreqs userClock;
fpga_result result;
uint32_t high, low;
int ret;
if ((sysfs_path == NULL) ||
(userclk_high == NULL) ||
(userclk_low == NULL)) {
OPAE_ERR("Invalid input parameters");
return FPGA_INVALID_PARAM;
}
// Test for the existence of the userclk_frequency file
// which indicates an S10 driver
ret = using_iopll(sysfs_usrpath, sysfs_path);
if (ret == FPGA_OK) {
result = sysfs_read_u32_pair(sysfs_usrpath, &low, &high, ' ');
if (FPGA_OK != result)
return result;
*userclk_high = high * 1000; // Adjust to Hz
*userclk_low = low * 1000;
return FPGA_OK;
} else if (ret == FPGA_NO_ACCESS) {
return ret;
}
// Initialize
if (fi_RunInitz(sysfs_path) != 0) {
OPAE_ERR("Failed to initialize user clock ");
return FPGA_NOT_SUPPORTED;
}
// get user clock
if (fi_GetFreqs(&userClock) != 0) {
OPAE_ERR("Failed to get user clock Frequency ");
return FPGA_NOT_SUPPORTED;
}
*userclk_high = userClock.u64i_Frq_ClkUsr;
*userclk_low = userClock.u64i_Frq_DivBy2;
return FPGA_OK;
}
// set fpga user clock
fpga_result set_userclock(const char* sysfs_path,
uint64_t userclk_high,
uint64_t userclk_low)
{
char sysfs_usrpath[SYSFS_PATH_MAX] = { 0, };
uint64_t freq = userclk_high;
uint64_t refClk = 0;
int fd, res;
char *bufp;
size_t cnt;
int ret;
if (sysfs_path == NULL) {
OPAE_ERR("Invalid Input parameters");
return FPGA_INVALID_PARAM;
}
ret = using_iopll(sysfs_usrpath, sysfs_path);
if (ret == FPGA_OK) {
// Enforce 1x clock within valid range
if ((userclk_low > IOPLL_MAX_FREQ) ||
(userclk_low < IOPLL_MIN_FREQ)) {
OPAE_ERR("Invalid Input frequency");
return FPGA_INVALID_PARAM;
}
fd = open(sysfs_usrpath, O_WRONLY);
if (fd < 0) {
OPAE_MSG("open(%s) failed: %s",
sysfs_usrpath, strerror(errno));
return FPGA_NOT_FOUND;
}
bufp = (char *)&iopll_freq_config[userclk_low];
cnt = sizeof(struct iopll_config);
do {
res = write(fd, bufp, cnt);
if (res < 0) {
OPAE_ERR("Failed to write");
break;
}
bufp += res;
cnt -= res;
} while (cnt > 0);
close(fd);
return FPGA_OK;
} else if (ret == FPGA_NO_ACCESS) {
return ret;
}
// verify user clock freq range (100hz to 1200hz)
if ((userclk_high > MAX_FPGA_FREQ) ||
(userclk_low > MAX_FPGA_FREQ)) {
OPAE_ERR("Invalid Input frequency");
return FPGA_INVALID_PARAM;
}
if (userclk_low != 0 && userclk_high != 0
&& userclk_low > userclk_high) {
OPAE_ERR("Invalid Input low frequency");
return FPGA_INVALID_PARAM;
}
if (freq < MIN_FPGA_FREQ){
OPAE_ERR("Invalid Input frequency");
return FPGA_INVALID_PARAM;
}
// Initialize
if (fi_RunInitz(sysfs_path) != 0) {
OPAE_ERR("Failed to initialize user clock ");
return FPGA_NOT_SUPPORTED;
}
if ((gQUCPU_Uclock.tInitz_InitialParams.u64i_Version == QUCPU_UI64_STS_1_VER_version_legacy) &&
(gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID == QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RF322M))
{ // Use the 322.265625 MHz REFCLK
refClk = 1;
}
OPAE_DBG("User clock: %ld \n", freq);
// set user clock
if (fi_SetFreqs(refClk, freq) != 0) {
OPAE_ERR("Failed to set user clock frequency ");
return FPGA_NOT_SUPPORTED;
}
return FPGA_OK;
}
//fi_RunInitz
int fi_RunInitz(const char* sysfs_path)
{
// fi_RunInitz
// Initialize
// Reinitialization okay too, since will issue machine reset
uint64_t u64i_PrtData = 0;
uint64_t u64i_AvmmAdr, u64i_AvmmDat;
int i_ReturnErr;
char sysfs_usrpath[SYSFS_PATH_MAX];
size_t len;
gQUCPU_Uclock.i_InitzState = 0;
gQUCPU_Uclock.tInitz_InitialParams.u64i_Version = (uint64_t) 0;
gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID = (uint64_t) 0;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq_Intg_End = (uint64_t) 0;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq_Frac_Beg = (uint64_t) 0;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq_Frac_End = (uint64_t) 0;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq = (uint64_t) 0;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumReg = (uint64_t) 0;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumRck = (uint64_t) 0;
gQUCPU_Uclock.u64i_cmd_reg_0 = (uint64_t) 0x0LLU;
gQUCPU_Uclock.u64i_cmd_reg_1 = (uint64_t) 0x0LLU;
gQUCPU_Uclock.u64i_AVMM_seq = (uint64_t) 0x0LLU;
gQUCPU_Uclock.i_Bug_First = 0;
gQUCPU_Uclock.i_Bug_Last = 0;
if (sysfs_path == NULL) {
printf(" Invalid input sysfs path \n");
return -1;
}
len = strnlen(sysfs_path, SYSFS_PATH_MAX - 1);
memcpy(gQUCPU_Uclock.sysfs_path, sysfs_path, len);
gQUCPU_Uclock.sysfs_path[len] = '\0';
// Assume return error okay, for now
i_ReturnErr = 0;
// Initialize default values (for error abort)
gQUCPU_Uclock.tInitz_InitialParams.u64i_Version = 0;
gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID = 0;
// Initialize command shadow registers
gQUCPU_Uclock.u64i_cmd_reg_0 = ((uint64_t) 0x0LLU);
gQUCPU_Uclock.u64i_cmd_reg_1 = ((uint64_t) 0x0LLU);
// Initialize sequence IO
gQUCPU_Uclock.u64i_AVMM_seq = ((uint64_t) 0x0LLU);
// Static values
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq_Intg_End = (uint64_t) QUCPU_INT_NUMFRQ_INTG_END;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq_Frac_Beg = (uint64_t) QUCPU_INT_NUMFRQ_FRAC_BEG;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq_Frac_End = (uint64_t) QUCPU_INT_NUMFRQ_FRAC_END;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq = (uint64_t) QUCPU_INT_NUMFRQ;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumReg = (uint64_t) QUCPU_INT_NUMREG;
gQUCPU_Uclock.tInitz_InitialParams.u64i_NumRck = (uint64_t) QUCPU_INT_NUMRCK;
// Read version number
if (i_ReturnErr == 0) // This always true; added for future safety
{
// Verifying User Clock version number
u64i_PrtData = 0;
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_STS1) < 0) {
OPAE_ERR("snprintf buffer overflow");
} else {
sysfs_read_u64(sysfs_usrpath, &u64i_PrtData);
//printf(" fi_RunInitz u64i_PrtData %llx \n", u64i_PrtData);
}
gQUCPU_Uclock.tInitz_InitialParams.u64i_Version = (u64i_PrtData & QUCPU_UI64_STS_1_VER_b63t60) >> 60;
if ((gQUCPU_Uclock.tInitz_InitialParams.u64i_Version != QUCPU_UI64_STS_1_VER_version) &&
(gQUCPU_Uclock.tInitz_InitialParams.u64i_Version != QUCPU_UI64_STS_1_VER_version_legacy))
{ // User Clock wrong version number
i_ReturnErr = QUCPU_INT_UCLOCK_RUNINITZ_ERR_VER;
} // User Clock wrong version number
} // Verifying User Clock version number
OPAE_DBG("User clock version = %lx \n", gQUCPU_Uclock.tInitz_InitialParams.u64i_Version);
// Read PLL ID
if (i_ReturnErr == 0)
{ // Waiting for fcr PLL calibration not to be busy
i_ReturnErr = fi_WaitCalDone();
} // Waiting for fcr PLL calibration not to be busy
if (i_ReturnErr == 0)
{
// Cycle reset and wait for any calibration to finish
// Activating management & machine reset
gQUCPU_Uclock.u64i_cmd_reg_0 |= (QUCPU_UI64_CMD_0_PRS_b56);
gQUCPU_Uclock.u64i_cmd_reg_0 &= ~(QUCPU_UI64_CMD_0_MRN_b52);
u64i_PrtData = gQUCPU_Uclock.u64i_cmd_reg_0;
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_CMD0) < 0) {
OPAE_ERR("snprintf buffer overflow");
} else {
sysfs_write_u64(sysfs_usrpath, u64i_PrtData);
// Deasserting management & machine reset
gQUCPU_Uclock.u64i_cmd_reg_0 |= (QUCPU_UI64_CMD_0_MRN_b52);
gQUCPU_Uclock.u64i_cmd_reg_0 &= ~(QUCPU_UI64_CMD_0_PRS_b56);
u64i_PrtData = gQUCPU_Uclock.u64i_cmd_reg_0;
sysfs_write_u64(sysfs_usrpath, u64i_PrtData);
//printf(" fi_RunInitz u64i_PrtData %llx \n", u64i_PrtData);
// Waiting for fcr PLL calibration not to be busy
i_ReturnErr = fi_WaitCalDone();
}
} // Cycle reset and wait for any calibration to finish
if (i_ReturnErr == 0)
{ // Checking fPLL ID
u64i_AvmmAdr = QUCPU_UI64_AVMM_FPLL_IPI_200;
i_ReturnErr = fi_AvmmRead(u64i_AvmmAdr, &u64i_AvmmDat);
if (i_ReturnErr == 0)
{ // Check identifier
gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID = u64i_AvmmDat & 0xffLLU;
if (!(gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID == QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RFDUAL
|| gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID == QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RF100M
|| gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID == QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RF322M))
{ // ERROR: Wrong fPLL ID Identifer
printf(" ERROR \n");
i_ReturnErr = QUCPU_INT_UCLOCK_RUNINITZ_ERR_FPLL_ID_ILLEGAL;
} // ERROR: Wrong fPLL ID Identifer
} // Check identifier
} // Checking fPLL ID
// Copy structure, initialize, and return based on error status
//*ptInitz_retInitz = gQUCPU_Uclock.tInitz_InitialParams;
gQUCPU_Uclock.i_InitzState = !i_ReturnErr; // Set InitzState to 0 or 1
return (i_ReturnErr);
} // fi_RunInitz
//fu64i_GetAVMM_seq
uint64_t fu64i_GetAVMM_seq()
{
// fu64i_GetAVMM_seq
// Increment seq
gQUCPU_Uclock.u64i_AVMM_seq++;
gQUCPU_Uclock.u64i_AVMM_seq &= 0x03LLU;
return(gQUCPU_Uclock.u64i_AVMM_seq);
} // fu64i_GetAVMM_seq
//fi_AvmmRWcom
int fi_AvmmRWcom(int i_CmdWrite,
uint64_t u64i_AvmmAdr,
uint64_t u64i_WriteData,
uint64_t *pu64i_ReadData)
{
// fi_AvmmRWcom
uint64_t u64i_SeqCmdAddrData, u64i_SeqCmdAddrData_seq_2, u64i_SeqCmdAddrData_wrt_1;
uint64_t u64i_SeqCmdAddrData_adr_10, u64i_SeqCmdAddrData_dat_32;
uint64_t u64i_PrtData;
uint64_t u64i_DataX;
uint64_t u64i_FastPoll, u64i_SlowPoll;
long int li_sleep_nanoseconds;
int i_ReturnErr;
char sysfs_usrpath[SYSFS_PATH_MAX] = { 0, };
// Assume return error okay, for now
i_ReturnErr = 0;
// Common portion
u64i_SeqCmdAddrData_seq_2 = fu64i_GetAVMM_seq();
u64i_SeqCmdAddrData_adr_10 = u64i_AvmmAdr;
if (i_CmdWrite == 1)
{
// Write data
u64i_SeqCmdAddrData_wrt_1 = 0x1LLU;
u64i_SeqCmdAddrData_dat_32 = u64i_WriteData;
} // Write data
else
{ // Read data
u64i_SeqCmdAddrData_wrt_1 = 0x0LLU;
u64i_SeqCmdAddrData_dat_32 = 0x0LLU;
} // Read data
u64i_SeqCmdAddrData = (u64i_SeqCmdAddrData_seq_2 & 0x00000003LLU) << 48 // [49:48]
| (u64i_SeqCmdAddrData_wrt_1 & 0x00000001LLU) << 44 // [ 44]
| (u64i_SeqCmdAddrData_adr_10 & 0x000003ffLLU) << 32 // [41:32]
| (u64i_SeqCmdAddrData_dat_32 & 0xffffffffLLU) << 0; // [31:00]
gQUCPU_Uclock.u64i_cmd_reg_0 &= ~QUCPU_UI64_CMD_0_AMM_b51t00;
gQUCPU_Uclock.u64i_cmd_reg_0 |= u64i_SeqCmdAddrData;
// Write register 0 to kick it off
u64i_PrtData = gQUCPU_Uclock.u64i_cmd_reg_0;
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_CMD0) < 0) {
OPAE_ERR("snprintf buffer overflow");
} else {
sysfs_write_u64(sysfs_usrpath, u64i_PrtData);
}
li_sleep_nanoseconds = USRCLK_SLEEEP_1MS;
fv_SleepShort(li_sleep_nanoseconds);
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_STS0) < 0) {
OPAE_ERR("snprintf buffer overflow");
}
// Poll register 0 for completion.
// CCI is synchronous and needs only 1 read with matching sequence.
for (u64i_SlowPoll = 0; u64i_SlowPoll<100; ++u64i_SlowPoll) // 100 ms
{ // Poll 0, slow outer loop with 1 ms sleep
for (u64i_FastPoll = 0; u64i_FastPoll<100; ++u64i_FastPoll)
{
// Poll 0, fast inner loop with no sleep
sysfs_read_u64(sysfs_usrpath, &u64i_DataX);
if ((u64i_DataX & QUCPU_UI64_STS_0_SEQ_b49t48) == (u64i_SeqCmdAddrData & QUCPU_UI64_STS_0_SEQ_b49t48))
{ // Have result
goto GOTO_LABEL_HAVE_RESULT;
} // Have result
} // Poll 0, fast inner loop with no sleep
// Sleep 1 ms
li_sleep_nanoseconds = USRCLK_SLEEEP_1MS;
fv_SleepShort(li_sleep_nanoseconds);
} // Poll 0, slow outer loop with 1 ms sleep
i_ReturnErr = QUCPU_INT_UCLOCK_AVMMRWCOM_ERR_TIMEOUT; // Error
GOTO_LABEL_HAVE_RESULT: // No error
if (i_CmdWrite == 0) *pu64i_ReadData = u64i_DataX;
return(i_ReturnErr);
} // fi_AvmmRWcom
//fi_AvmmRead
int fi_AvmmRead(uint64_t u64i_AvmmAdr, uint64_t *pu64i_ReadData)
{
// fi_AvmmRead
int i_CmdWrite = 0;
uint64_t u64i_WriteData = 0;
int res = 0;
// Perform read with common code
i_CmdWrite = 0;
u64i_WriteData = 0; // Not used for read
res = fi_AvmmRWcom(i_CmdWrite, u64i_AvmmAdr, u64i_WriteData, pu64i_ReadData);
// Return error status
return(res);
} // fi_AvmmRead
//fi_AvmmWrite
int fi_AvmmWrite(uint64_t u64i_AvmmAdr, uint64_t u64i_WriteData)
{
// fi_AvmmWrite
int i_CmdWrite = 0;
uint64_t u64i_ReadData = 0; // Read data is not used
int res = 0;
// Perform write with common code
i_CmdWrite = 1;
res = fi_AvmmRWcom(i_CmdWrite, u64i_AvmmAdr, u64i_WriteData, &u64i_ReadData);
// Return error status
return(res);
} // fi_AvmmWrite
//Sleep for nanoseconds
void fv_SleepShort(long int li_sleep_nanoseconds)
{
// fv_SleepShort
// Sleep for nanoseconds
struct timespec timespecRemaining = {0};
struct timespec timespecWait = {0};
int res = 0;
timespecRemaining.tv_nsec = li_sleep_nanoseconds; timespecRemaining.tv_sec = 0;
do
{ // Wait, and retry if wait ended early
timespecWait = timespecRemaining;
res = (int) nanosleep(×pecWait, ×pecRemaining);
if (res != 0 && res != -1)
{ // BUG: unexpected nanosleep return value
fv_BugLog((int) QUCPU_INT_UCLOCK_BUG_SLEEP_SHORT);
} // BUG: unexpected nanosleep return value
} // Wait, and retry if wait ended early
while (res != 0);
return;
} // fv_SleepShort
// get user clock
// Read the frequency for the User clock and div2 clock
int fi_GetFreqs(QUCPU_tFreqs *ptFreqs_retFreqs)
{
// fi_GetFreqs
// Read the frequency for the User clock and div2 clock
uint64_t u64i_PrtData = 0;
long int li_sleep_nanoseconds = 0;
int res = 0;
char sysfs_usrpath[SYSFS_PATH_MAX] = { 0, };
// Assume return error okay, for now
res = 0;
if (!gQUCPU_Uclock.i_InitzState) res = QUCPU_INT_UCLOCK_GETFREQS_ERR_INITZSTATE;
if (res == 0)
{ // Read div2 and 1x user clock frequency
// Low frequency
gQUCPU_Uclock.u64i_cmd_reg_1 &= ~QUCPU_UI64_CMD_1_MEA_b32;
u64i_PrtData = gQUCPU_Uclock.u64i_cmd_reg_1;
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_CMD1) < 0) {
OPAE_ERR("snprintf buffer overflow");
}
sysfs_write_u64(sysfs_usrpath, u64i_PrtData);
li_sleep_nanoseconds = USRCLK_SLEEEP_10MS; // 10 ms for frequency counter
fv_SleepShort(li_sleep_nanoseconds);
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_STS1) < 0) {
OPAE_ERR("snprintf buffer overflow");
} else {
sysfs_read_u64(sysfs_usrpath, &u64i_PrtData);
}
ptFreqs_retFreqs->u64i_Frq_DivBy2 = (u64i_PrtData & QUCPU_UI64_STS_1_FRQ_b16t00) * 10000; // Hz
//printf(" ptFreqs_retFreqs->u64i_Frq_ClkUsr %llx \n", ptFreqs_retFreqs->u64i_Frq_DivBy2);
li_sleep_nanoseconds = USRCLK_SLEEEP_10MS;
fv_SleepShort(li_sleep_nanoseconds);
// High frequency
gQUCPU_Uclock.u64i_cmd_reg_1 |= QUCPU_UI64_CMD_1_MEA_b32;
u64i_PrtData = gQUCPU_Uclock.u64i_cmd_reg_1;
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_CMD1) < 0) {
OPAE_ERR("snprintf buffer overflow");
} else {
sysfs_write_u64(sysfs_usrpath, u64i_PrtData);
}
li_sleep_nanoseconds = USRCLK_SLEEEP_10MS; // 10 ms for frequency counter
fv_SleepShort(li_sleep_nanoseconds);
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_STS1) < 0) {
OPAE_ERR("snprintf buffer overflow");
} else {
sysfs_read_u64(sysfs_usrpath, &u64i_PrtData);
ptFreqs_retFreqs->u64i_Frq_ClkUsr = (u64i_PrtData & QUCPU_UI64_STS_1_FRQ_b16t00) * 10000; // Hz
}
//printf(" ptFreqs_retFreqs->u64i_Frq_ClkUsr %llx \n", ptFreqs_retFreqs->u64i_Frq_ClkUsr);
fv_SleepShort(li_sleep_nanoseconds);
} // Read div2 and 1x user clock frequency
OPAE_DBG("\nApproximate frequency:\n"
"High clock = %5.1f MHz\n"
"Low clock = %5.1f MHz\n \n",
ptFreqs_retFreqs->u64i_Frq_ClkUsr / 1.0e6, (ptFreqs_retFreqs->u64i_Frq_DivBy2) / 1.0e6);
return (res);
} // fi_GetFreqs
// set user clock
int fi_SetFreqs(uint64_t u64i_Refclk,
uint64_t u64i_FrqInx)
{
// fi_SetFreqs
// Set the user clock frequency
uint64_t u64i_I, u64i_MifReg, u64i_PrtData;
uint64_t u64i_AvmmAdr, u64i_AvmmDat, u64i_AvmmMsk;
long int li_sleep_nanoseconds;
int i_ReturnErr;
char sysfs_usrpath[SYSFS_PATH_MAX] = { 0, };
// Assume return error okay, for now
i_ReturnErr = 0;
if (!gQUCPU_Uclock.i_InitzState) i_ReturnErr = QUCPU_INT_UCLOCK_SETFREQS_ERR_INITZSTATE;
if (i_ReturnErr == 0)
{ // Check REFCLK
if (u64i_Refclk == 0)
{ // 100 MHz REFCLK requested
if (!(gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID == QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RFDUAL
|| gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID == QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RF100M))
i_ReturnErr = QUCPU_INT_UCLOCK_SETFREQS_ERR_REFCLK_100M_MISSING;
} // 100 MHz REFCLK requested
else if (u64i_Refclk == 1)
{ // 322.265625 MHz REFCLK requested
if (!(gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID == QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RFDUAL
|| gQUCPU_Uclock.tInitz_InitialParams.u64i_PLL_ID == QUCPU_UI64_AVMM_FPLL_IPI_200_IDI_RF322M))
i_ReturnErr = QUCPU_INT_UCLOCK_SETFREQS_ERR_REFCLK_322M_MISSING;
} // 322.265625 MHz REFCLK requested
else i_ReturnErr = QUCPU_INT_UCLOCK_SETFREQS_ERR_REFCLK_ILLEGAL;
} // Check REFCLK
if (i_ReturnErr == 0)
{ // Check frequency index
if (u64i_FrqInx > gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq_Frac_End)
i_ReturnErr = QUCPU_INT_UCLOCK_SETFREQS_ERR_FINDEX_OVERRANGE;
else if (u64i_FrqInx < gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq_Frac_Beg
&& u64i_FrqInx > gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq_Intg_End)
i_ReturnErr = QUCPU_INT_UCLOCK_SETFREQS_ERR_FINDEX_INTG_RANGE_BAD;
else if (u64i_FrqInx < gQUCPU_Uclock.tInitz_InitialParams.u64i_NumFrq_Frac_Beg
&& u64i_Refclk != 1) // Integer-PLL mode, exact requires 322.265625 MHz
i_ReturnErr = QUCPU_INT_UCLOCK_SETFREQS_ERR_FINDEX_INTG_NEEDS_322M;
} // Check frequency index
if (i_ReturnErr == 0)
{ // Power down PLL
// Altera bug. Power down pin doesn't work SR #11229652.
// WORKAROUND: Use power down port
u64i_AvmmAdr = 0x2e0LLU;
u64i_AvmmDat = 0x03LLU;
u64i_AvmmMsk = 0x03LLU;
i_ReturnErr = fi_AvmmReadModifyWriteVerify(u64i_AvmmAdr, u64i_AvmmDat, u64i_AvmmMsk);
// Sleep 1 ms
li_sleep_nanoseconds = USRCLK_SLEEEP_1MS;
fv_SleepShort(li_sleep_nanoseconds);
} // Power down PLL
if (i_ReturnErr == 0)
{ // Verifying fcr PLL not locking
u64i_PrtData = 0;
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_STS0) < 0) {
OPAE_ERR("snprintf buffer overflow");
} else {
sysfs_read_u64(sysfs_usrpath, &u64i_PrtData);
//sysfs_read_uint64(gQUCPU_Uclock.sys_path, USER_CLOCK_STS0, &u64i_PrtData);
}
if ((u64i_PrtData & QUCPU_UI64_STS_0_LCK_b60) != 0)
{ // fcr PLL is locked but should be unlocked
i_ReturnErr = QUCPU_INT_UCLOCK_SETFREQS_ERR_PLL_NO_UNLOCK;
} // fcr PLL is locked but should be unlocked
} // Verifying fcr PLL not locking
if (i_ReturnErr == 0)
{ // Select reference and push table
// Selecting desired reference clock
gQUCPU_Uclock.u64i_cmd_reg_0 &= ~QUCPU_UI64_CMD_0_SR1_b58;
if (u64i_Refclk) gQUCPU_Uclock.u64i_cmd_reg_0 |= QUCPU_UI64_CMD_0_SR1_b58;
u64i_PrtData = gQUCPU_Uclock.u64i_cmd_reg_0;
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_CMD0) < 0) {
OPAE_ERR("snprintf buffer overflow");
} else {
sysfs_write_u64(sysfs_usrpath, u64i_PrtData);
}
// Sleep 1 ms
li_sleep_nanoseconds = USRCLK_SLEEEP_1MS;
fv_SleepShort(li_sleep_nanoseconds);
// Pushing the table
for (u64i_MifReg = 0; u64i_MifReg<gQUCPU_Uclock.tInitz_InitialParams.u64i_NumReg; u64i_MifReg++)
{ // Write each register in the diff mif
uint32_t tbl_entry;
if (u64i_Refclk == 0)
{ // 100 MHz table
tbl_entry = scu32ia3d_DiffMifTbl[(int) u64i_FrqInx][(int) u64i_MifReg][(int) u64i_Refclk];
}
else
{ // 322.265625 MHz table
tbl_entry = scu32ia3d_DiffMifTbl_322[(int) u64i_FrqInx][(int) u64i_MifReg][(int) u64i_Refclk];
}
u64i_AvmmAdr = (uint64_t) (tbl_entry) >> 16;
u64i_AvmmDat = (uint64_t) (tbl_entry & 0x000000ff);
u64i_AvmmMsk = (uint64_t) (tbl_entry & 0x0000ff00) >> 8;
i_ReturnErr = fi_AvmmReadModifyWriteVerify(u64i_AvmmAdr, u64i_AvmmDat, u64i_AvmmMsk);
if (i_ReturnErr) break;
} // Write each register in the diff mif
} // Select reference and push table
if (i_ReturnErr == 0)
{ // Waiting for fcr PLL calibration not to be busy
i_ReturnErr = fi_WaitCalDone();
} // Waiting for fcr PLL calibration not to be busy
if (i_ReturnErr == 0)
{ // Recalibrating
// "Request user access to the internal configuration bus"
// and "Wait for reconfig_waitrequest to be deasserted."
// Note that the Verify operation performs the post "wait."
u64i_AvmmAdr = 0x000LLU;
u64i_AvmmDat = 0x02LLU;
u64i_AvmmMsk = 0xffLLU;
i_ReturnErr = fi_AvmmReadModifyWriteVerify(u64i_AvmmAdr, u64i_AvmmDat, u64i_AvmmMsk);
if (i_ReturnErr == 0)
{ // "To calibrate the fPLL, Read-Modify-Write:" set B1 of 0x100 high
u64i_AvmmAdr = 0x100LLU;
u64i_AvmmDat = 0x02LLU;
u64i_AvmmMsk = 0x02LLU;
i_ReturnErr = fi_AvmmReadModifyWrite(u64i_AvmmAdr, u64i_AvmmDat, u64i_AvmmMsk);
} // "To calibrate the fPLL, Read-Modify-Write:" set B1 of 0x100 high
if (i_ReturnErr == 0)
{ // "Release the internal configuraiton bus to PreSICE to perform recalibration"
u64i_AvmmAdr = 0x000LLU;
u64i_AvmmDat = 0x01LLU;
i_ReturnErr = fi_AvmmWrite(u64i_AvmmAdr, u64i_AvmmDat);
// Sleep 1 ms
li_sleep_nanoseconds = USRCLK_SLEEEP_1MS;
fv_SleepShort(li_sleep_nanoseconds);
} // "Release the internal configuraiton bus to PreSICE to perform recalibration"
} // Recalibrating
if (i_ReturnErr == 0)
{ // Waiting for fcr PLL calibration not to be busy
i_ReturnErr = fi_WaitCalDone();
} // Waiting for fcr PLL calibration not to be busy
if (i_ReturnErr == 0)
{ // Power up PLL
// Altera bug. Power down pin doesn't work SR #11229652.
// WORKAROUND: Use power down port
u64i_AvmmAdr = 0x2e0LLU;
u64i_AvmmDat = 0x02LLU;
u64i_AvmmMsk = 0x03LLU;
i_ReturnErr = fi_AvmmReadModifyWriteVerify(u64i_AvmmAdr, u64i_AvmmDat, u64i_AvmmMsk);
} // Power up PLL
if (i_ReturnErr == 0)
{ // Wait for PLL to lock
for (u64i_I = 0; u64i_I<100; u64i_I++)
{ // Poll with 100 ms timeout
u64i_PrtData = 0;
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_STS0) < 0) {
OPAE_ERR("snprintf buffer overflow");
} else {
sysfs_read_u64(sysfs_usrpath, &u64i_PrtData);
}
if ((u64i_PrtData & QUCPU_UI64_STS_0_LCK_b60) != 0) break;
// Sleep 1 ms
li_sleep_nanoseconds = USRCLK_SLEEEP_1MS;
fv_SleepShort(li_sleep_nanoseconds);
} // Poll with 100 ms timeout
if ((u64i_PrtData & QUCPU_UI64_STS_0_LCK_b60) == 0)
{ // fcr PLL lock error
i_ReturnErr = QUCPU_INT_UCLOCK_SETFREQS_ERR_PLL_LOCK_TO;
} // fcr PLL lock error
} // Verifying fcr PLL is locking
return (i_ReturnErr);
} // fi_SetFreqs
// get error message
//Read the frequency for the User clock and div2 clock
const char * fpac_GetErrMsg(int i_ErrMsgInx)
{
// fpac_GetErrMsg
// Read the frequency for the User clock and div2 clock
const char * pac_ErrMsgStr = NULL;
// Extra "+1" message has index range error message
pac_ErrMsgStr = pac_UclockErrorMsg[QUCPU_INT_UCLOCK_NUM_ERROR_MESSAGES + 1 - 1];
// Check index range
if (i_ErrMsgInx >= 0
&& i_ErrMsgInx < QUCPU_INT_UCLOCK_NUM_ERROR_MESSAGES) {
// All okay, set the message string
pac_ErrMsgStr = pac_UclockErrorMsg[i_ErrMsgInx];
} // All okay, set the message string
return (pac_ErrMsgStr);
} // fpac_GetErrMsg
// fi_AvmmReadModifyWriteVerify
int fi_AvmmReadModifyWriteVerify(uint64_t u64i_AvmmAdr,
uint64_t u64i_AvmmDat,
uint64_t u64i_AvmmMsk)
{
// fi_AvmmReadModifyWriteVerify
int res = 0;
uint64_t u64i_VerifyData = 0;
res = fi_AvmmReadModifyWrite(u64i_AvmmAdr, u64i_AvmmDat, u64i_AvmmMsk);
if (res == 0)
{ // Read back the data and verify mask-enabled bits
res = fi_AvmmRead(u64i_AvmmAdr, &u64i_VerifyData);
if (res == 0)
{ // Perform verify
if ((u64i_VerifyData & u64i_AvmmMsk) != (u64i_AvmmDat & u64i_AvmmMsk))
{ // Verify failure
res = QUCPU_INT_UCLOCK_AVMMRMWV_ERR_VERIFY;
} // Verify failure
} // Perform verify
} // Read back the data and verify mask-enabled bits
return(res);
} // fi_AvmmReadModifyWriteVerify
// fi_AvmmReadModifyWrite
int fi_AvmmReadModifyWrite(uint64_t u64i_AvmmAdr,
uint64_t u64i_AvmmDat,
uint64_t u64i_AvmmMsk)
{
uint64_t u64i_ReadData = 0;
uint64_t u64i_WriteData = 0;
int res = 0;
// Read data
res = fi_AvmmRead(u64i_AvmmAdr, &u64i_ReadData);
if (res == 0)
{ // Modify the read data and write it
u64i_WriteData = (u64i_ReadData & ~u64i_AvmmMsk) | (u64i_AvmmDat & u64i_AvmmMsk);
res = fi_AvmmWrite(u64i_AvmmAdr, u64i_WriteData);
} // Modify the read data and write it
return(res);
} // fi_AvmmReadModifyWrite
// fv_BugLog
// Logs first and last bugs
void fv_BugLog(int i_BugID)
{
if (gQUCPU_Uclock.i_Bug_First)
{ // This is not the first bug
gQUCPU_Uclock.i_Bug_Last = i_BugID;
} // This is not the first bug
else
{ // This is the first bug
gQUCPU_Uclock.i_Bug_First = i_BugID;
} // This is the first bug
return;
} // fv_BugLog
// wait caldone
// Wait for calibration to be done
int fi_WaitCalDone(void)
{
// fi_WaitCalDone
// Wait for calibration to be done
uint64_t u64i_PrtData = 0;
uint64_t u64i_I = 0;
long int li_sleep_nanoseconds = 0;
int res = 0;
char sysfs_usrpath[SYSFS_PATH_MAX] = { 0, };
// Waiting for fcr PLL calibration not to be busy
for (u64i_I = 0; u64i_I<1000; u64i_I++)
{ // Poll with 1000 ms timeout
u64i_PrtData = 0;
if (snprintf(sysfs_usrpath, sizeof(sysfs_usrpath),
"%s/%s", gQUCPU_Uclock.sysfs_path, USER_CLOCK_STS0) < 0) {
OPAE_ERR("snprintf buffer overflow");
} else {
sysfs_read_u64(sysfs_usrpath, &u64i_PrtData);
}
if ((u64i_PrtData & QUCPU_UI64_STS_0_BSY_b61) == 0) break;
// Sleep 1 ms
li_sleep_nanoseconds = USRCLK_SLEEEP_1MS;
fv_SleepShort(li_sleep_nanoseconds);
} // Poll with 1000 ms timeout
if ((u64i_PrtData & QUCPU_UI64_STS_0_BSY_b61) != 0)
{ // ERROR: calibration busy too long
res = QUCPU_INT_UCLOCK_WAITCALDONE_ERR_BSY_TO;
} // ERROR: calibration busy too long
return(res);
} // fi_WaitCalDone
// Determine whether or not the IOPLL is serving as the source of
// the user clock.
static int using_iopll(char* sysfs_usrpath, const char* sysfs_path)
{
glob_t iopll_glob;
size_t len;
int res;
// Test for the existence of the userclk_frequency file
// which indicates an S10 driver
if (snprintf(sysfs_usrpath, SYSFS_PATH_MAX,
"%s/%s", sysfs_path, IOPLL_CLOCK_FREQ) < 0) {
OPAE_ERR("snprintf buffer overflow");
return FPGA_EXCEPTION;
}
res = glob(sysfs_usrpath, 0, NULL, &iopll_glob);
if (res) {
if (iopll_glob.gl_pathv)
globfree(&iopll_glob);
return FPGA_NOT_FOUND;
}
if (iopll_glob.gl_pathc > 1)
OPAE_MSG("WARNING: Port has multiple sysfs frequency files");
len = strnlen(iopll_glob.gl_pathv[0], SYSFS_PATH_MAX - 1);
memcpy(sysfs_usrpath, iopll_glob.gl_pathv[0], len);
sysfs_usrpath[len] = '\0';
globfree(&iopll_glob);
if (access(sysfs_usrpath, F_OK | R_OK | W_OK) != 0) {
OPAE_ERR("Unable to access sysfs frequency file");
return FPGA_NO_ACCESS;
}
return FPGA_OK;
}