From 14bc09c7f8f7d63c5a60a5721ae4dc59aaf1cfd3 Mon Sep 17 00:00:00 2001 From: Michael Adler Date: Thu, 6 Feb 2025 14:05:06 -0500 Subject: [PATCH] userclk: separate device independent from Agilex 7 configuration The user clock frequency configuration protocol varies across devices. Move the Agilex 7 and Stratix 10 configuration sequences out of fpga_user_clk.c and into a new protocol-specific file. Signed-off-by: Michael Adler --- libraries/plugins/xfpga/CMakeLists.txt | 1 + .../plugins/xfpga/usrclk/fpga_user_clk.c | 504 +---------------- .../plugins/xfpga/usrclk/fpga_user_clk_freq.h | 19 +- .../plugins/xfpga/usrclk/fpga_user_clk_int.h | 133 +++++ .../xfpga/usrclk/fpga_user_clk_type1.c | 518 ++++++++++++++++++ tests/userclk/test_userclk_c.cpp | 6 +- tests/xfpga/CMakeLists.txt | 1 + tests/xfpga/test_usrclk_c.cpp | 4 +- 8 files changed, 679 insertions(+), 507 deletions(-) create mode 100644 libraries/plugins/xfpga/usrclk/fpga_user_clk_int.h create mode 100644 libraries/plugins/xfpga/usrclk/fpga_user_clk_type1.c diff --git a/libraries/plugins/xfpga/CMakeLists.txt b/libraries/plugins/xfpga/CMakeLists.txt index 9c82bf097a50..d1d35da2162b 100644 --- a/libraries/plugins/xfpga/CMakeLists.txt +++ b/libraries/plugins/xfpga/CMakeLists.txt @@ -46,6 +46,7 @@ set(SRC version.c userclk.c usrclk/fpga_user_clk.c + usrclk/fpga_user_clk_type1.c plugin.c sysobject.c manage.c diff --git a/libraries/plugins/xfpga/usrclk/fpga_user_clk.c b/libraries/plugins/xfpga/usrclk/fpga_user_clk.c index 4761a7b6e73d..fbd3866df62c 100644 --- a/libraries/plugins/xfpga/usrclk/fpga_user_clk.c +++ b/libraries/plugins/xfpga/usrclk/fpga_user_clk.c @@ -36,105 +36,18 @@ #include #include "fpga_user_clk.h" +#include "fpga_user_clk_int.h" #include "fpga_user_clk_freq.h" #include "mock/opae_std.h" // user clock sysfs #define IOPLL_CLOCK_FREQ "dfl*/userclk/frequency" #define IOPLL_REVISION "dfl*/feature_rev" -#define MAX_FPGA_FREQ 1200 -#define MIN_FPGA_FREQ 25 -#define AGILEX_USRCLK_REV 1 - -/* - * USER CLK CSR register definitions - */ - /* Field definitions for both USERCLK_FREQ_CMD0 and USERCLK_FREQ_STS0 */ -#define IOPLL_FREQ_CMD0 0x8 -#define IOPLL_DATA GENMASK_ULL(31, 0) -#define IOPLL_ADDR GENMASK_ULL(41, 32) -#define IOPLL_WRITE BIT_ULL(44) -#define IOPLL_SEQ GENMASK_ULL(49, 48) -#define IOPLL_AVMM_RESET_N BIT_ULL(52) -#define IOPLL_MGMT_RESET BIT_ULL(56) -#define IOPLL_RESET BIT_ULL(57) - -#define IOPLL_FREQ_CMD1 0x10 -/* Field definitions for both USERCLKL_FREQ_CMD1 and USERCLK_FREQ_STS1 */ -#define IOPLL_CLK_MEASURE BIT_ULL(32) -#define IOPLL_FREQ_STS0 0x18 -#define IOPLL_LOCKED BIT_ULL(60) -#define IOPLL_AVMM_ERROR BIT_ULL(63) - -#define IOPLL_FREQ_STS1 0x20 -#define IOPLL_FREQUENCY GENMASK_ULL(16, 0) -#define IOPLL_REF_FREQ GENMASK_ULL(50, 33) -#define IOPLL_VERSION GENMASK_ULL(63, 60) - -/* - * Control and status registers for the IOPLL - * https://www.altera.com/en_US/pdfs/literature/hb/stratix-10/ug-s10-clkpll.pdf - * Section 7.2 - */ - -#define CFG_PLL_LOW GENMASK_ULL(7, 0) -#define CFG_PLL_HIGH GENMASK_ULL(15, 8) -#define CFG_PLL_BYPASS_EN BIT_ULL(16) -#define CFG_PLL_EVEN_DUTY_EN BIT_ULL(17) - -#define PLL_EVEN_DUTY_EN_SHIFT 7 - -#define PLL_N_HIGH_ADDR 0x100 -#define PLL_N_BYPASS_EN_ADDR 0x101 -#define PLL_N_EVEN_DUTY_EN_ADDR 0x101 -#define PLL_N_LOW_ADDR 0x102 - -#define PLL_M_HIGH_ADDR 0x104 -#define PLL_M_BYPASS_EN_ADDR 0x105 -#define PLL_M_EVEN_DUTY_EN_ADDR 0x106 -#define PLL_M_LOW_ADDR 0x107 - -#define PLL_C0_HIGH_ADDR 0x11b -#define PLL_C0_BYPASS_EN_ADDR 0x11c -#define PLL_C0_EVEN_DUTY_EN_ADDR 0x11d -#define PLL_C0_LOW_ADDR 0x11e - -#define PLL_C1_HIGH_ADDR 0x11f -#define PLL_C1_BYPASS_EN_ADDR 0x120 -#define PLL_C1_EVEN_DUTY_EN_ADDR 0x121 -#define PLL_C1_LOW_ADDR 0x122 - -#define CFG_PLL_CP1 GENMASK_ULL(2, 0) -#define PLL_CP1_ADDR 0x101 -#define PLL_CP1_SHIFT 4 - -#define CFG_PLL_LF GENMASK_ULL(13, 6) -#define PLL_LF_ADDR 0x10a -#define PLL_LF_SHIFT 3 - -#define CFG_PLL_CP2 GENMASK_ULL(5, 3) -#define PLL_CP2_ADDR 0x10d -#define PLL_CP2_SHIFT 5 - -#define CFG_PLL_RC GENMASK_ULL(1, 0) -#define PLL_RC_SHIFT 1 - -#define PLL_REQUEST_CAL_ADDR 0x149 -#define PLL_REQUEST_CALIBRATION BIT(6) - -#define PLL_ENABLE_CAL_ADDR 0x14a -#define PLL_ENABLE_CALIBRATION 0x03 #define IOPLL_MEASURE_LOW 0 #define IOPLL_MEASURE_HIGH 1 #define IOPLL_MEASURE_DELAY_US 8000 #define IOPLL_RESET_DELAY_US 1000 -#define IOPLL_CAL_DELAY_US 1000 - -#define IOPLL_WRITE_POLL_INVL_US 10 /* Write poll interval */ -#define IOPLL_WRITE_POLL_TIMEOUT_US 1000000 /* Write poll timeout */ - -#define USRCLK_FEATURE_ID 0x14 // DFHv0 struct dfh { @@ -153,8 +66,6 @@ struct dfh { }; }; -static int using_iopll(char *sysfs_usrpath, const char *sysfs_path); - fpga_result usrclk_reset(uint8_t *uio_ptr) { uint64_t v = 0; @@ -192,7 +103,7 @@ fpga_result usrclk_reset(uint8_t *uio_ptr) return res; } -fpga_result usrclk_read_freq(uint8_t *uio_ptr, +STATIC fpga_result usrclk_read_freq(uint8_t *uio_ptr, uint8_t clock_sel, uint32_t *freq) { uint64_t v = 0; @@ -220,285 +131,6 @@ fpga_result usrclk_read_freq(uint8_t *uio_ptr, return FPGA_OK; } -fpga_result usrclk_write(uint8_t *uio_ptr, uint16_t address, - uint32_t data, uint8_t seq) -{ - fpga_result res = FPGA_OK; - uint64_t v = 0; - uint32_t timeout = IOPLL_WRITE_POLL_TIMEOUT_US; - - if (uio_ptr == NULL) { - OPAE_ERR("Invalid input parameters"); - return FPGA_INVALID_PARAM; - } - seq &= 0x3; - - v = FIELD_PREP(IOPLL_DATA, data); - v |= FIELD_PREP(IOPLL_ADDR, address); - v |= IOPLL_WRITE; - v |= FIELD_PREP(IOPLL_SEQ, seq); - v |= IOPLL_AVMM_RESET_N; - *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_CMD0)) = v; - - v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS0)); - - while (!(FIELD_GET(IOPLL_SEQ, v) == seq)) { - v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS0)); - usleep(IOPLL_WRITE_POLL_INVL_US); - if (--timeout == 0) { - OPAE_ERR("Timeout on IOPLL write"); - res = FPGA_EXCEPTION; - break; - } - } - - return res; -} - -fpga_result usrclk_read(uint8_t *uio_ptr, uint16_t address, - uint32_t *data, uint8_t seq) -{ - uint64_t v = 0; - uint32_t timeout = IOPLL_WRITE_POLL_TIMEOUT_US; - - if (uio_ptr == NULL) { - OPAE_ERR("Invalid input parameters"); - return FPGA_INVALID_PARAM; - } - seq &= 0x3; - - v = FIELD_PREP(IOPLL_ADDR, address); - v |= FIELD_PREP(IOPLL_SEQ, seq); - v |= IOPLL_AVMM_RESET_N; - *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_CMD0)) = v; - - v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS0)); - - while (!(FIELD_GET(IOPLL_SEQ, v) == seq)) { - v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS0)); - usleep(IOPLL_WRITE_POLL_INVL_US); - if (--timeout == 0) { - OPAE_ERR("Timeout on IOPLL write"); - return FPGA_EXCEPTION; - } - } - - *data = FIELD_GET(IOPLL_DATA, v); - return FPGA_OK; -} - -fpga_result usrclk_update_bits(uint8_t *uio_ptr, uint16_t address, - uint32_t mask, uint32_t bits, uint8_t *seq) -{ - uint32_t data = 0; - fpga_result res = FPGA_OK; - - if (uio_ptr == NULL) { - OPAE_ERR("Invalid input parameters"); - return FPGA_INVALID_PARAM; - } - - res = usrclk_read(uio_ptr, address, &data, (*seq)++); - if (res) - return res; - - data &= ~mask; - data |= (bits & mask); - - return usrclk_write(uio_ptr, PLL_REQUEST_CAL_ADDR, - data | PLL_REQUEST_CALIBRATION, (*seq)++); - - return res; -} - -fpga_result usrclk_m_write(uint8_t *uio_ptr, - uint32_t cfg_pll_m, uint8_t *seq) -{ - uint32_t high, low, bypass_en, even_duty_en = 0; - fpga_result res = FPGA_OK; - - if (uio_ptr == NULL) { - OPAE_ERR("Invalid input parameters"); - return FPGA_INVALID_PARAM; - } - - high = FIELD_GET(CFG_PLL_HIGH, cfg_pll_m); - res = usrclk_write(uio_ptr, PLL_M_HIGH_ADDR, high, (*seq)++); - if (res) - return res; - - low = FIELD_GET(CFG_PLL_LOW, cfg_pll_m); - res = usrclk_write(uio_ptr, PLL_M_LOW_ADDR, low, (*seq)++); - if (res) - return res; - - bypass_en = FIELD_GET(CFG_PLL_BYPASS_EN, cfg_pll_m); - res = usrclk_write(uio_ptr, PLL_M_BYPASS_EN_ADDR, bypass_en, (*seq)++); - if (res) - return res; - - even_duty_en = FIELD_GET(CFG_PLL_EVEN_DUTY_EN, cfg_pll_m) << - PLL_EVEN_DUTY_EN_SHIFT; - return usrclk_write(uio_ptr, PLL_M_EVEN_DUTY_EN_ADDR, - even_duty_en, (*seq)++); -} - -fpga_result usrclk_n_write(uint8_t *uio_ptr, uint32_t cfg_pll_n, - uint32_t cfg_pll_cp, uint8_t *seq) -{ - uint32_t high, low, bypass_en, even_duty_en, cp1 = 0; - fpga_result res = FPGA_OK; - - if (uio_ptr == NULL) { - OPAE_ERR("Invalid input parameters"); - return FPGA_INVALID_PARAM; - } - - high = FIELD_GET(CFG_PLL_HIGH, cfg_pll_n); - res = usrclk_write(uio_ptr, PLL_N_HIGH_ADDR, high, (*seq)++); - if (res) - return res; - - low = FIELD_GET(CFG_PLL_LOW, cfg_pll_n); - res = usrclk_write(uio_ptr, PLL_N_LOW_ADDR, low, (*seq)++); - if (res) - return res; - - even_duty_en = FIELD_GET(CFG_PLL_EVEN_DUTY_EN, cfg_pll_n) << - PLL_EVEN_DUTY_EN_SHIFT; - cp1 = FIELD_GET(CFG_PLL_CP1, cfg_pll_cp) << PLL_CP1_SHIFT; - bypass_en = FIELD_GET(CFG_PLL_BYPASS_EN, cfg_pll_n); - return usrclk_write(uio_ptr, PLL_N_BYPASS_EN_ADDR, - even_duty_en | cp1 | bypass_en, (*seq)++); -} - -fpga_result usrclk_c0_write(uint8_t *uio_ptr, - uint32_t cfg_pll_c0, uint8_t *seq) -{ - uint32_t high, low, bypass_en, even_duty_en = 0; - fpga_result res = FPGA_OK; - - if (uio_ptr == NULL) { - OPAE_ERR("Invalid input parameters"); - return FPGA_INVALID_PARAM; - } - - high = FIELD_GET(CFG_PLL_HIGH, cfg_pll_c0); - res = usrclk_write(uio_ptr, PLL_C0_HIGH_ADDR, high, (*seq)++); - if (res) - return res; - - low = FIELD_GET(CFG_PLL_LOW, cfg_pll_c0); - res = usrclk_write(uio_ptr, PLL_C0_LOW_ADDR, low, (*seq)++); - if (res) - return res; - - bypass_en = FIELD_GET(CFG_PLL_BYPASS_EN, cfg_pll_c0); - res = usrclk_write(uio_ptr, PLL_C0_BYPASS_EN_ADDR, bypass_en, (*seq)++); - if (res) - return res; - - even_duty_en = FIELD_GET(CFG_PLL_EVEN_DUTY_EN, cfg_pll_c0) << - PLL_EVEN_DUTY_EN_SHIFT; - return usrclk_write(uio_ptr, PLL_C0_EVEN_DUTY_EN_ADDR, - even_duty_en, (*seq)++); -} - -fpga_result usrclk_c1_write(uint8_t *uio_ptr, - uint32_t cfg_pll_c1, uint8_t *seq) -{ - uint32_t high, low, bypass_en, even_duty_en = 0; - fpga_result res = FPGA_OK; - - if ((uio_ptr == NULL) || - (seq == NULL)) { - OPAE_ERR("Invalid input parameters"); - return FPGA_INVALID_PARAM; - } - - high = FIELD_GET(CFG_PLL_HIGH, cfg_pll_c1); - res = usrclk_write(uio_ptr, PLL_C1_HIGH_ADDR, high, (*seq)++); - if (res) - return res; - - low = FIELD_GET(CFG_PLL_LOW, cfg_pll_c1); - res = usrclk_write(uio_ptr, PLL_C1_LOW_ADDR, low, (*seq)++); - if (res) - return res; - - bypass_en = FIELD_GET(CFG_PLL_BYPASS_EN, cfg_pll_c1); - res = usrclk_write(uio_ptr, PLL_C1_BYPASS_EN_ADDR, bypass_en, (*seq)++); - if (res) - return res; - - even_duty_en = FIELD_GET(CFG_PLL_EVEN_DUTY_EN, cfg_pll_c1) << - PLL_EVEN_DUTY_EN_SHIFT; - return usrclk_write(uio_ptr, PLL_C1_EVEN_DUTY_EN_ADDR, - even_duty_en, (*seq)++); -} - -fpga_result usrclk_set_freq(uint8_t *uio_ptr, - struct pll_config *c, uint8_t *seq) -{ - uint32_t cp2, lf, rc = 0; - fpga_result res = FPGA_OK; - - if ((uio_ptr == NULL) || - (seq == NULL)) { - OPAE_ERR("Invalid input parameters"); - return FPGA_INVALID_PARAM; - } - - res = usrclk_m_write(uio_ptr, c->pll_m, seq); - if (res) - return res; - - res = usrclk_n_write(uio_ptr, c->pll_n, c->pll_cp, seq); - if (res) - return res; - - res = usrclk_c0_write(uio_ptr, c->pll_c0, seq); - if (res) - return res; - - res = usrclk_c1_write(uio_ptr, c->pll_c1, seq); - if (res) - return res; - - cp2 = FIELD_GET(CFG_PLL_CP2, c->pll_cp) << PLL_CP2_SHIFT; - res = usrclk_write(uio_ptr, PLL_CP2_ADDR, cp2, (*seq)++); - if (res) - return res; - - lf = FIELD_GET(CFG_PLL_LF, c->pll_lf) << PLL_LF_SHIFT; - rc = FIELD_GET(CFG_PLL_RC, c->pll_rc) << PLL_RC_SHIFT; - return usrclk_write(uio_ptr, PLL_LF_ADDR, lf | rc, (*seq)++); -} - -fpga_result usrclk_calibrate(uint8_t *uio_ptr, uint8_t *seq) -{ - fpga_result res = FPGA_OK; - - if ((uio_ptr == NULL) || - (seq == NULL)) { - OPAE_ERR("Invalid input parameters"); - return FPGA_INVALID_PARAM; - } - - /* Request IOPLL Calibration */ - res = usrclk_update_bits(uio_ptr, PLL_REQUEST_CAL_ADDR, - PLL_REQUEST_CALIBRATION, - PLL_REQUEST_CALIBRATION, seq); - if (res) - return res; - - /* Enable calibration interface */ - res = usrclk_write(uio_ptr, PLL_ENABLE_CAL_ADDR, PLL_ENABLE_CALIBRATION, - (*seq)++); - usleep(IOPLL_CAL_DELAY_US); - return res; -} - fpga_result get_usrclk_uio(const char *sysfs_path, uint32_t feature_id, struct opae_uio *uio, @@ -529,7 +161,7 @@ fpga_result get_usrclk_uio(const char *sysfs_path, OPAE_ERR("Failed pattern match %s: %s", feature_path, strerror(errno)); opae_globfree(&pglob); - return FPGA_INVALID_PARAM; + return FPGA_NOT_FOUND; } for (i = 0; i < pglob.gl_pathc; i++) { @@ -601,7 +233,7 @@ fpga_result get_userclock(const char *sysfs_path, // Test for the existence of the userclk_frequency file // which indicates an S10 driver - ret = using_iopll(sysfs_usrpath, sysfs_path); + ret = usrclk_using_iopll(sysfs_usrpath, sysfs_path); if (ret == FPGA_OK) { result = sysfs_read_u32_pair(sysfs_usrpath, &low, &high, ' '); if (FPGA_OK != result) @@ -649,23 +281,8 @@ fpga_result set_userclock(const char *sysfs_path, uint64_t userclk_high, uint64_t userclk_low) { - char sysfs_usrpath[SYSFS_PATH_MAX] = { 0 }; - int fd, ret = 0; - char *bufp = NULL; - ssize_t cnt = 0; - uint64_t revision = 0; - uint8_t seq = 1; - uint8_t *uio_ptr = NULL; - fpga_result result = FPGA_OK; - ssize_t bytes_written = 0; - struct opae_uio uio; - uint64_t v = 0; - - unsigned int iopll_max_freq = IOPLL_MAX_FREQ; - unsigned int iopll_min_freq = IOPLL_MIN_FREQ; - unsigned int slow_freq = MIN_FPGA_FREQ; - - memset(&uio, 0, sizeof(uio)); + uint64_t revision = 0; + fpga_result result = FPGA_OK; if (sysfs_path == NULL) { OPAE_ERR("Invalid Input parameters"); @@ -677,110 +294,29 @@ fpga_result set_userclock(const char *sysfs_path, return FPGA_INVALID_PARAM; } - // Agilex user clock DFH revision 1 - // S10 & A10 user clock DFH revision 0 + // Revision determines the algorithm used for updating clocks result = get_userclk_revision(sysfs_path, &revision); - if (result == FPGA_OK && revision == AGILEX_USRCLK_REV) { - iopll_max_freq = IOPLL_AGILEX_MAX_FREQ; - iopll_min_freq = IOPLL_AGILEX_MIN_FREQ; - - // 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; - } - - bufp = (char *)&iopll_agilex_freq_config[userclk_low]; - } else { - // S10 & A10 user clock - // 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; - } - bufp = (char *)&iopll_freq_config[userclk_low]; - } - - // Transitions from a currently configured very high frequency - // or very low frequency to another extreme frequency sometimes - // fails to stabilize. Start by forcing the fast clock to half - // speed. - slow_freq = iopll_max_freq / 4; - if (userclk_low != slow_freq) { - result = set_userclock(sysfs_path, slow_freq * 2, slow_freq); - } - - ret = using_iopll(sysfs_usrpath, sysfs_path); - if (ret == FPGA_OK) { - - fd = opae_open(sysfs_usrpath, O_WRONLY); - if (fd < 0) { - OPAE_MSG("open(%s) failed: %s", - sysfs_usrpath, strerror(errno)); - return FPGA_NOT_FOUND; - } - cnt = sizeof(struct iopll_config); - - bytes_written = eintr_write(fd, bufp, cnt); - if (bytes_written != cnt) { - OPAE_ERR("Failed to write: %s", strerror(errno)); - opae_close(fd); - return FPGA_EXCEPTION; - } - opae_close(fd); - - return FPGA_OK; - } else if (ret == FPGA_NO_ACCESS) { - return FPGA_NO_ACCESS; - } - - struct pll_config *iopll_config = (struct pll_config *)bufp; - if ((iopll_config->pll_freq_khz > iopll_max_freq * 1000) || - (iopll_config->pll_freq_khz < iopll_min_freq * 1000)) - return FPGA_EXCEPTION; - - result = get_usrclk_uio(sysfs_path, - USRCLK_FEATURE_ID, - &uio, - &uio_ptr); if (result != FPGA_OK) { - OPAE_ERR("Failed to get user clock uio"); - return result; - } - - // Initialize seq from the current sequence number in STS0. The - // next command must start there. - v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS0)); - seq = FIELD_GET(IOPLL_SEQ, v) + 1; - - result = usrclk_set_freq(uio_ptr, iopll_config, &seq); - if (result != FPGA_OK) { - OPAE_ERR("Failed to set user clock"); - goto uio_close; - } - - result = usrclk_reset(uio_ptr); - if (result != FPGA_OK) { - OPAE_ERR("Failed to reset user clock"); - goto uio_close; - } - - result = usrclk_calibrate(uio_ptr, &seq); - if (result != FPGA_OK) { - OPAE_ERR("Failed to calibrate user clock"); - goto uio_close; + // Very old systems may use a sysfs path to directly update + // clocks. This is handled in the type1 path. + result = set_userclock_type1(sysfs_path, 0, + userclk_high, userclk_low); + } else if (revision <= AGILEX_USRCLK_REV) { + // Agilex user clock DFH revision 1 + // S10 & A10 user clock DFH revision 0 + result = set_userclock_type1(sysfs_path, revision, + userclk_high, userclk_low); + } else { + OPAE_ERR("Unsupported FPGA device revision: %d", revision); + return FPGA_NOT_SUPPORTED; } -uio_close: - opae_uio_close(&uio); return result; } // 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) +int usrclk_using_iopll(char *sysfs_usrpath, const char *sysfs_path) { glob_t iopll_glob; size_t len = 0; diff --git a/libraries/plugins/xfpga/usrclk/fpga_user_clk_freq.h b/libraries/plugins/xfpga/usrclk/fpga_user_clk_freq.h index 6bb33aaa2440..c581221eb455 100644 --- a/libraries/plugins/xfpga/usrclk/fpga_user_clk_freq.h +++ b/libraries/plugins/xfpga/usrclk/fpga_user_clk_freq.h @@ -44,23 +44,6 @@ #ifndef FPGA_USER_CLK_FREQ_H_ #define FPGA_USER_CLK_FREQ_H_ -#define IOPLL_MAX_FREQ 600 -#define IOPLL_MIN_FREQ 10 - -#define IOPLL_AGILEX_MAX_FREQ 800 -#define IOPLL_AGILEX_MIN_FREQ 10 - -struct iopll_config { - unsigned int pll_freq_khz; - unsigned int pll_m; - unsigned int pll_n; - unsigned int pll_c1; - unsigned int pll_c0; - unsigned int pll_lf; - unsigned int pll_cp; - unsigned int pll_rc; -}; - // Reference frequency: 100MHz const struct iopll_config iopll_freq_config[] = { { 0, 0, 0, 0, 0, 0, 0, 0 }, // Freq 0 not configured @@ -1470,4 +1453,4 @@ const struct iopll_config iopll_agilex_freq_config[] = { { 790000, 0x22827, 0x505, 0x10000, 0x10000, 0x180, 0x4, 0x2}, { 800000, 0x404, 0x10000, 0x10000, 0x10000, 0xc0, 0x4, 0x0} }; -#endif // end FPGA_USER_CLK_FREQ_H_ \ No newline at end of file +#endif // end FPGA_USER_CLK_FREQ_H_ diff --git a/libraries/plugins/xfpga/usrclk/fpga_user_clk_int.h b/libraries/plugins/xfpga/usrclk/fpga_user_clk_int.h new file mode 100644 index 000000000000..514ca8cd278e --- /dev/null +++ b/libraries/plugins/xfpga/usrclk/fpga_user_clk_int.h @@ -0,0 +1,133 @@ +// Copyright(c) 2017-2022, 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. + +#ifndef FPGA_USER_CLK_INT_H_ +#define FPGA_USER_CLK_INT_H_ + +#include "fpga_user_clk.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define USRCLK_FEATURE_ID 0x14 + +#define MAX_FPGA_FREQ 1200 +#define MIN_FPGA_FREQ 25 + +/* + * USER CLK CSR register definitions + */ + /* Field definitions for both USERCLK_FREQ_CMD0 and USERCLK_FREQ_STS0 */ +#define IOPLL_FREQ_CMD0 0x8 +#define IOPLL_DATA GENMASK_ULL(31, 0) +#define IOPLL_ADDR GENMASK_ULL(41, 32) +#define IOPLL_WRITE BIT_ULL(44) +#define IOPLL_SEQ GENMASK_ULL(49, 48) +#define IOPLL_AVMM_RESET_N BIT_ULL(52) +#define IOPLL_MGMT_RESET BIT_ULL(56) +#define IOPLL_RESET BIT_ULL(57) + +#define IOPLL_FREQ_CMD1 0x10 +/* Field definitions for both USERCLKL_FREQ_CMD1 and USERCLK_FREQ_STS1 */ +#define IOPLL_CLK_MEASURE BIT_ULL(32) +#define IOPLL_FREQ_STS0 0x18 +#define IOPLL_LOCKED BIT_ULL(60) +#define IOPLL_AVMM_ERROR BIT_ULL(63) + +#define IOPLL_FREQ_STS1 0x20 +#define IOPLL_FREQUENCY GENMASK_ULL(16, 0) +#define IOPLL_REF_FREQ GENMASK_ULL(50, 33) +#define IOPLL_VERSION GENMASK_ULL(63, 60) + +#define IOPLL_CAL_DELAY_US 1000 +#define IOPLL_WRITE_POLL_INVL_US 10 /* Write poll interval */ +#define IOPLL_WRITE_POLL_TIMEOUT_US 1000000 /* Write poll timeout */ + +#define IOPLL_MAX_FREQ 600 +#define IOPLL_MIN_FREQ 10 + +#define AGILEX_USRCLK_REV 1 +#define IOPLL_AGILEX_MAX_FREQ 800 +#define IOPLL_AGILEX_MIN_FREQ 10 + +struct iopll_config { + unsigned int pll_freq_khz; + unsigned int pll_m; + unsigned int pll_n; + unsigned int pll_c1; + unsigned int pll_c0; + unsigned int pll_lf; + unsigned int pll_cp; + unsigned int pll_rc; +}; + + +// Configuration tables are initialized in fpga_user_clk_freq.h, included +// only by fpga_user_clk.c. + +// S10 reference frequency: 100MHz +extern const struct iopll_config iopll_freq_config[]; +// Agilex reference frequency: 100MHz +extern const struct iopll_config iopll_agilex_freq_config[]; + + +fpga_result usrclk_reset(uint8_t *uio_ptr); +int usrclk_using_iopll(char *sysfs_usrpath, const char *sysfs_path); + +/** + * @brief open UIO handle to fpga user clock manager + * + * @param sysfs_path port sysfs path + * @param feature_id + * @param uio returned OPAE uio struct + * @param uio_ptr returned handle to open UIO device + * + * @return error code + */ +fpga_result get_usrclk_uio(const char *sysfs_path, + uint32_t feature_id, + struct opae_uio *uio, + uint8_t **uio_ptr); + +/** + * @brief set fpga user clock for Agilex 7 and Stratix 10 + * + * @param sysfs_path port sysfs path + * @param revision DFH revision number + * @param high user clock + * @param low user clock + * + * @return error code + */ +fpga_result set_userclock_type1(const char *sysfs_path, uint64_t revision, + uint64_t userclk_high, uint64_t userclk_low); + +#ifdef __cplusplus +} +#endif + +#endif // end FPGA_USER_CLK_INT_H_ diff --git a/libraries/plugins/xfpga/usrclk/fpga_user_clk_type1.c b/libraries/plugins/xfpga/usrclk/fpga_user_clk_type1.c new file mode 100644 index 000000000000..691759166a98 --- /dev/null +++ b/libraries/plugins/xfpga/usrclk/fpga_user_clk_type1.c @@ -0,0 +1,518 @@ +// Copyright(c) 2025, 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. + +// Agilex 7 and Stratix 10 user clock frequency management. Named type1 +// because the user clock feature header revision is 1 for Agilex 7. +// Legacy support for Stratix 10 (revision 0) is maintained here as +// well. +// +// The public entry point is set_userclock_type1(), which should +// be called only by set_userclock(). + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include +#include +#include + +#include "fpga_user_clk.h" +#include "fpga_user_clk_int.h" +#include "mock/opae_std.h" + +/* + * Control and status registers for the IOPLL + * https://www.altera.com/en_US/pdfs/literature/hb/stratix-10/ug-s10-clkpll.pdf + * Section 7.2 + */ + +#define CFG_PLL_LOW GENMASK_ULL(7, 0) +#define CFG_PLL_HIGH GENMASK_ULL(15, 8) +#define CFG_PLL_BYPASS_EN BIT_ULL(16) +#define CFG_PLL_EVEN_DUTY_EN BIT_ULL(17) + +#define PLL_EVEN_DUTY_EN_SHIFT 7 + +#define PLL_N_HIGH_ADDR 0x100 +#define PLL_N_BYPASS_EN_ADDR 0x101 +#define PLL_N_EVEN_DUTY_EN_ADDR 0x101 +#define PLL_N_LOW_ADDR 0x102 + +#define PLL_M_HIGH_ADDR 0x104 +#define PLL_M_BYPASS_EN_ADDR 0x105 +#define PLL_M_EVEN_DUTY_EN_ADDR 0x106 +#define PLL_M_LOW_ADDR 0x107 + +#define PLL_C0_HIGH_ADDR 0x11b +#define PLL_C0_BYPASS_EN_ADDR 0x11c +#define PLL_C0_EVEN_DUTY_EN_ADDR 0x11d +#define PLL_C0_LOW_ADDR 0x11e + +#define PLL_C1_HIGH_ADDR 0x11f +#define PLL_C1_BYPASS_EN_ADDR 0x120 +#define PLL_C1_EVEN_DUTY_EN_ADDR 0x121 +#define PLL_C1_LOW_ADDR 0x122 + +#define CFG_PLL_CP1 GENMASK_ULL(2, 0) +#define PLL_CP1_ADDR 0x101 +#define PLL_CP1_SHIFT 4 + +#define CFG_PLL_LF GENMASK_ULL(13, 6) +#define PLL_LF_ADDR 0x10a +#define PLL_LF_SHIFT 3 + +#define CFG_PLL_CP2 GENMASK_ULL(5, 3) +#define PLL_CP2_ADDR 0x10d +#define PLL_CP2_SHIFT 5 + +#define CFG_PLL_RC GENMASK_ULL(1, 0) +#define PLL_RC_SHIFT 1 + +#define PLL_REQUEST_CAL_ADDR 0x149 +#define PLL_REQUEST_CALIBRATION BIT(6) + +#define PLL_ENABLE_CAL_ADDR 0x14a +#define PLL_ENABLE_CALIBRATION 0x03 + + +STATIC fpga_result usrclk_write(uint8_t *uio_ptr, uint16_t address, + uint32_t data, uint8_t seq) +{ + fpga_result res = FPGA_OK; + uint64_t v = 0; + uint32_t timeout = IOPLL_WRITE_POLL_TIMEOUT_US; + + if (uio_ptr == NULL) { + OPAE_ERR("Invalid input parameters"); + return FPGA_INVALID_PARAM; + } + seq &= 0x3; + + v = FIELD_PREP(IOPLL_DATA, data); + v |= FIELD_PREP(IOPLL_ADDR, address); + v |= IOPLL_WRITE; + v |= FIELD_PREP(IOPLL_SEQ, seq); + v |= IOPLL_AVMM_RESET_N; + *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_CMD0)) = v; + + v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS0)); + + while (!(FIELD_GET(IOPLL_SEQ, v) == seq)) { + v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS0)); + usleep(IOPLL_WRITE_POLL_INVL_US); + if (--timeout == 0) { + OPAE_ERR("Timeout on IOPLL write"); + res = FPGA_EXCEPTION; + break; + } + } + + return res; +} + +STATIC fpga_result usrclk_read(uint8_t *uio_ptr, uint16_t address, + uint32_t *data, uint8_t seq) +{ + uint64_t v = 0; + uint32_t timeout = IOPLL_WRITE_POLL_TIMEOUT_US; + + if (uio_ptr == NULL) { + OPAE_ERR("Invalid input parameters"); + return FPGA_INVALID_PARAM; + } + seq &= 0x3; + + v = FIELD_PREP(IOPLL_ADDR, address); + v |= FIELD_PREP(IOPLL_SEQ, seq); + v |= IOPLL_AVMM_RESET_N; + *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_CMD0)) = v; + + v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS0)); + + while (!(FIELD_GET(IOPLL_SEQ, v) == seq)) { + v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS0)); + usleep(IOPLL_WRITE_POLL_INVL_US); + if (--timeout == 0) { + OPAE_ERR("Timeout on IOPLL write"); + return FPGA_EXCEPTION; + } + } + + *data = FIELD_GET(IOPLL_DATA, v); + return FPGA_OK; +} + +STATIC fpga_result usrclk_update_bits(uint8_t *uio_ptr, uint16_t address, + uint32_t mask, uint32_t bits, uint8_t *seq) +{ + uint32_t data = 0; + fpga_result res = FPGA_OK; + + if (uio_ptr == NULL) { + OPAE_ERR("Invalid input parameters"); + return FPGA_INVALID_PARAM; + } + + res = usrclk_read(uio_ptr, address, &data, (*seq)++); + if (res) + return res; + + data &= ~mask; + data |= (bits & mask); + + return usrclk_write(uio_ptr, PLL_REQUEST_CAL_ADDR, + data | PLL_REQUEST_CALIBRATION, (*seq)++); + + return res; +} + +STATIC fpga_result usrclk_m_write(uint8_t *uio_ptr, + uint32_t cfg_pll_m, uint8_t *seq) +{ + uint32_t high, low, bypass_en, even_duty_en = 0; + fpga_result res = FPGA_OK; + + if (uio_ptr == NULL) { + OPAE_ERR("Invalid input parameters"); + return FPGA_INVALID_PARAM; + } + + high = FIELD_GET(CFG_PLL_HIGH, cfg_pll_m); + res = usrclk_write(uio_ptr, PLL_M_HIGH_ADDR, high, (*seq)++); + if (res) + return res; + + low = FIELD_GET(CFG_PLL_LOW, cfg_pll_m); + res = usrclk_write(uio_ptr, PLL_M_LOW_ADDR, low, (*seq)++); + if (res) + return res; + + bypass_en = FIELD_GET(CFG_PLL_BYPASS_EN, cfg_pll_m); + res = usrclk_write(uio_ptr, PLL_M_BYPASS_EN_ADDR, bypass_en, (*seq)++); + if (res) + return res; + + even_duty_en = FIELD_GET(CFG_PLL_EVEN_DUTY_EN, cfg_pll_m) << + PLL_EVEN_DUTY_EN_SHIFT; + return usrclk_write(uio_ptr, PLL_M_EVEN_DUTY_EN_ADDR, + even_duty_en, (*seq)++); +} + +STATIC fpga_result usrclk_n_write(uint8_t *uio_ptr, uint32_t cfg_pll_n, + uint32_t cfg_pll_cp, uint8_t *seq) +{ + uint32_t high, low, bypass_en, even_duty_en, cp1 = 0; + fpga_result res = FPGA_OK; + + if (uio_ptr == NULL) { + OPAE_ERR("Invalid input parameters"); + return FPGA_INVALID_PARAM; + } + + high = FIELD_GET(CFG_PLL_HIGH, cfg_pll_n); + res = usrclk_write(uio_ptr, PLL_N_HIGH_ADDR, high, (*seq)++); + if (res) + return res; + + low = FIELD_GET(CFG_PLL_LOW, cfg_pll_n); + res = usrclk_write(uio_ptr, PLL_N_LOW_ADDR, low, (*seq)++); + if (res) + return res; + + even_duty_en = FIELD_GET(CFG_PLL_EVEN_DUTY_EN, cfg_pll_n) << + PLL_EVEN_DUTY_EN_SHIFT; + cp1 = FIELD_GET(CFG_PLL_CP1, cfg_pll_cp) << PLL_CP1_SHIFT; + bypass_en = FIELD_GET(CFG_PLL_BYPASS_EN, cfg_pll_n); + return usrclk_write(uio_ptr, PLL_N_BYPASS_EN_ADDR, + even_duty_en | cp1 | bypass_en, (*seq)++); +} + +STATIC fpga_result usrclk_c0_write(uint8_t *uio_ptr, + uint32_t cfg_pll_c0, uint8_t *seq) +{ + uint32_t high, low, bypass_en, even_duty_en = 0; + fpga_result res = FPGA_OK; + + if (uio_ptr == NULL) { + OPAE_ERR("Invalid input parameters"); + return FPGA_INVALID_PARAM; + } + + high = FIELD_GET(CFG_PLL_HIGH, cfg_pll_c0); + res = usrclk_write(uio_ptr, PLL_C0_HIGH_ADDR, high, (*seq)++); + if (res) + return res; + + low = FIELD_GET(CFG_PLL_LOW, cfg_pll_c0); + res = usrclk_write(uio_ptr, PLL_C0_LOW_ADDR, low, (*seq)++); + if (res) + return res; + + bypass_en = FIELD_GET(CFG_PLL_BYPASS_EN, cfg_pll_c0); + res = usrclk_write(uio_ptr, PLL_C0_BYPASS_EN_ADDR, bypass_en, (*seq)++); + if (res) + return res; + + even_duty_en = FIELD_GET(CFG_PLL_EVEN_DUTY_EN, cfg_pll_c0) << + PLL_EVEN_DUTY_EN_SHIFT; + return usrclk_write(uio_ptr, PLL_C0_EVEN_DUTY_EN_ADDR, + even_duty_en, (*seq)++); +} + +STATIC fpga_result usrclk_c1_write(uint8_t *uio_ptr, + uint32_t cfg_pll_c1, uint8_t *seq) +{ + uint32_t high, low, bypass_en, even_duty_en = 0; + fpga_result res = FPGA_OK; + + if ((uio_ptr == NULL) || + (seq == NULL)) { + OPAE_ERR("Invalid input parameters"); + return FPGA_INVALID_PARAM; + } + + high = FIELD_GET(CFG_PLL_HIGH, cfg_pll_c1); + res = usrclk_write(uio_ptr, PLL_C1_HIGH_ADDR, high, (*seq)++); + if (res) + return res; + + low = FIELD_GET(CFG_PLL_LOW, cfg_pll_c1); + res = usrclk_write(uio_ptr, PLL_C1_LOW_ADDR, low, (*seq)++); + if (res) + return res; + + bypass_en = FIELD_GET(CFG_PLL_BYPASS_EN, cfg_pll_c1); + res = usrclk_write(uio_ptr, PLL_C1_BYPASS_EN_ADDR, bypass_en, (*seq)++); + if (res) + return res; + + even_duty_en = FIELD_GET(CFG_PLL_EVEN_DUTY_EN, cfg_pll_c1) << + PLL_EVEN_DUTY_EN_SHIFT; + return usrclk_write(uio_ptr, PLL_C1_EVEN_DUTY_EN_ADDR, + even_duty_en, (*seq)++); +} + +STATIC fpga_result usrclk_set_freq(uint8_t *uio_ptr, + struct pll_config *c, uint8_t *seq) +{ + uint32_t cp2, lf, rc = 0; + fpga_result res = FPGA_OK; + + if ((uio_ptr == NULL) || + (seq == NULL)) { + OPAE_ERR("Invalid input parameters"); + return FPGA_INVALID_PARAM; + } + + res = usrclk_m_write(uio_ptr, c->pll_m, seq); + if (res) + return res; + + res = usrclk_n_write(uio_ptr, c->pll_n, c->pll_cp, seq); + if (res) + return res; + + res = usrclk_c0_write(uio_ptr, c->pll_c0, seq); + if (res) + return res; + + res = usrclk_c1_write(uio_ptr, c->pll_c1, seq); + if (res) + return res; + + cp2 = FIELD_GET(CFG_PLL_CP2, c->pll_cp) << PLL_CP2_SHIFT; + res = usrclk_write(uio_ptr, PLL_CP2_ADDR, cp2, (*seq)++); + if (res) + return res; + + lf = FIELD_GET(CFG_PLL_LF, c->pll_lf) << PLL_LF_SHIFT; + rc = FIELD_GET(CFG_PLL_RC, c->pll_rc) << PLL_RC_SHIFT; + return usrclk_write(uio_ptr, PLL_LF_ADDR, lf | rc, (*seq)++); +} + +STATIC fpga_result usrclk_calibrate(uint8_t *uio_ptr, uint8_t *seq) +{ + fpga_result res = FPGA_OK; + + if ((uio_ptr == NULL) || + (seq == NULL)) { + OPAE_ERR("Invalid input parameters"); + return FPGA_INVALID_PARAM; + } + + /* Request IOPLL Calibration */ + res = usrclk_update_bits(uio_ptr, PLL_REQUEST_CAL_ADDR, + PLL_REQUEST_CALIBRATION, + PLL_REQUEST_CALIBRATION, seq); + if (res) + return res; + + /* Enable calibration interface */ + res = usrclk_write(uio_ptr, PLL_ENABLE_CAL_ADDR, PLL_ENABLE_CALIBRATION, + (*seq)++); + usleep(IOPLL_CAL_DELAY_US); + return res; +} + +// set fpga user clock +fpga_result set_userclock_type1(const char *sysfs_path, + uint64_t revision, + uint64_t userclk_high, + uint64_t userclk_low) +{ + char sysfs_usrpath[SYSFS_PATH_MAX] = { 0 }; + int fd, ret = 0; + char *bufp = NULL; + ssize_t cnt = 0; + uint8_t seq = 1; + uint8_t *uio_ptr = NULL; + fpga_result result = FPGA_OK; + ssize_t bytes_written = 0; + struct opae_uio uio; + uint64_t v = 0; + + unsigned int iopll_max_freq = IOPLL_MAX_FREQ; + unsigned int iopll_min_freq = IOPLL_MIN_FREQ; + unsigned int slow_freq = MIN_FPGA_FREQ; + + memset(&uio, 0, sizeof(uio)); + + if (sysfs_path == NULL) { + OPAE_ERR("Invalid Input parameters"); + return FPGA_INVALID_PARAM; + } + + if (userclk_high < MIN_FPGA_FREQ) { + OPAE_ERR("Invalid Input frequency"); + return FPGA_INVALID_PARAM; + } + + // Agilex user clock DFH revision 1 + // S10 & A10 user clock DFH revision 0 + if (revision == AGILEX_USRCLK_REV) { + iopll_max_freq = IOPLL_AGILEX_MAX_FREQ; + iopll_min_freq = IOPLL_AGILEX_MIN_FREQ; + + // 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; + } + + bufp = (char *)&iopll_agilex_freq_config[userclk_low]; + } else if (revision == 0) { + // S10 & A10 user clock + // 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; + } + bufp = (char *)&iopll_freq_config[userclk_low]; + } else { + OPAE_ERR("Unsupported FPGA device revision: %d", revision); + return FPGA_NOT_SUPPORTED; + } + + // Transitions from a currently configured very high frequency + // or very low frequency to another extreme frequency sometimes + // fails to stabilize. Start by forcing the fast clock to half + // speed. + slow_freq = iopll_max_freq / 4; + if (userclk_low != slow_freq) { + result = set_userclock_type1(sysfs_path, revision, slow_freq * 2, slow_freq); + } + + ret = usrclk_using_iopll(sysfs_usrpath, sysfs_path); + if (ret == FPGA_OK) { + + fd = opae_open(sysfs_usrpath, O_WRONLY); + if (fd < 0) { + OPAE_MSG("open(%s) failed: %s", + sysfs_usrpath, strerror(errno)); + return FPGA_NOT_FOUND; + } + cnt = sizeof(struct iopll_config); + + bytes_written = eintr_write(fd, bufp, cnt); + if (bytes_written != cnt) { + OPAE_ERR("Failed to write: %s", strerror(errno)); + opae_close(fd); + return FPGA_EXCEPTION; + } + opae_close(fd); + + return FPGA_OK; + } else if (ret == FPGA_NO_ACCESS) { + return FPGA_NO_ACCESS; + } + + struct pll_config *iopll_config = (struct pll_config *)bufp; + if ((iopll_config->pll_freq_khz > iopll_max_freq * 1000) || + (iopll_config->pll_freq_khz < iopll_min_freq * 1000)) + return FPGA_EXCEPTION; + + result = get_usrclk_uio(sysfs_path, + USRCLK_FEATURE_ID, + &uio, + &uio_ptr); + if (result != FPGA_OK) { + OPAE_ERR("Failed to get user clock uio"); + return result; + } + + // Initialize seq from the current sequence number in STS0. The + // next command must start there. + v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS0)); + seq = FIELD_GET(IOPLL_SEQ, v) + 1; + + result = usrclk_set_freq(uio_ptr, iopll_config, &seq); + if (result != FPGA_OK) { + OPAE_ERR("Failed to set user clock"); + goto uio_close; + } + + result = usrclk_reset(uio_ptr); + if (result != FPGA_OK) { + OPAE_ERR("Failed to reset user clock"); + goto uio_close; + } + + result = usrclk_calibrate(uio_ptr, &seq); + if (result != FPGA_OK) { + OPAE_ERR("Failed to calibrate user clock"); + goto uio_close; + } + +uio_close: + opae_uio_close(&uio); + return result; +} diff --git a/tests/userclk/test_userclk_c.cpp b/tests/userclk/test_userclk_c.cpp index bf9e1c2a71c3..9b3af4e36015 100644 --- a/tests/userclk/test_userclk_c.cpp +++ b/tests/userclk/test_userclk_c.cpp @@ -559,7 +559,7 @@ TEST_P(userclk_c_mock_p, main3) { ** FPGA_NOT_SUPPORTED. EXPECT_EQ(userclk_main(11, argv), 0); */ - EXPECT_EQ(userclk_main(11, argv), FPGA_INVALID_PARAM); + EXPECT_EQ(userclk_main(11, argv), FPGA_NOT_FOUND); } /** @@ -607,7 +607,7 @@ TEST_P(userclk_c_mock_p, main4) { ** FPGA_NOT_SUPPORTED. EXPECT_EQ(userclk_main(11, argv), 0); */ - EXPECT_EQ(userclk_main(11, argv), FPGA_INVALID_PARAM); + EXPECT_EQ(userclk_main(11, argv), FPGA_NOT_FOUND); } /** @@ -686,7 +686,7 @@ TEST_P(userclk_c_mock_p, main6) { char *argv[] = { zero, one, two, three, four, five, six, seven, eight, NULL }; - EXPECT_EQ(userclk_main(9, argv), FPGA_INVALID_PARAM); + EXPECT_EQ(userclk_main(9, argv), FPGA_NOT_FOUND); } GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(userclk_c_mock_p); diff --git a/tests/xfpga/CMakeLists.txt b/tests/xfpga/CMakeLists.txt index f71c6ee1ee15..728f258adf3a 100644 --- a/tests/xfpga/CMakeLists.txt +++ b/tests/xfpga/CMakeLists.txt @@ -50,6 +50,7 @@ opae_test_add_static_lib(TARGET xfpga-static ${OPAE_LIB_SOURCE}/plugins/xfpga/version.c ${OPAE_LIB_SOURCE}/plugins/xfpga/wsid_list.c ${OPAE_LIB_SOURCE}/plugins/xfpga/usrclk/fpga_user_clk.c + ${OPAE_LIB_SOURCE}/plugins/xfpga/usrclk/fpga_user_clk_type1.c ${OPAE_LIB_SOURCE}/plugins/xfpga/metrics/metrics_max10.c ${OPAE_LIB_SOURCE}/plugins/xfpga/metrics/metrics_utils.c ${OPAE_LIB_SOURCE}/plugins/xfpga/metrics/afu_metrics.c diff --git a/tests/xfpga/test_usrclk_c.cpp b/tests/xfpga/test_usrclk_c.cpp index 664a9c48289d..7f087497fbab 100644 --- a/tests/xfpga/test_usrclk_c.cpp +++ b/tests/xfpga/test_usrclk_c.cpp @@ -205,14 +205,14 @@ class usrclk_mock_c : public usrclk_c {}; * @test set_user_clock * @brief Tests: xfpga_fpgaSetUserClock() * @details When the parameters are valid, fpgaGetUserClock returns - * FPGA_NOT_SUPPORTED on mock platforms. + * FPGA_NOT_FOUND on mock platforms. */ TEST_P(usrclk_mock_c, set_user_clock) { uint64_t high = 312; uint64_t low = 156; int flags = 0; EXPECT_EQ(xfpga_fpgaSetUserClock(accel_, high, low, flags), - FPGA_INVALID_PARAM); + FPGA_NOT_FOUND); } GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(usrclk_mock_c);