From f85a137a09228a8da8c919c5e498642878be2da3 Mon Sep 17 00:00:00 2001 From: Michael Adler Date: Fri, 7 Feb 2025 13:30:10 -0500 Subject: [PATCH 1/8] userclk: separate device independent from Agilex 7 configuration (#3159) 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_reconf_c.cpp | 4 +- tests/xfpga/test_usrclk_c.cpp | 4 +- 9 files changed, 681 insertions(+), 509 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_reconf_c.cpp b/tests/xfpga/test_reconf_c.cpp index a7b821ec12ae..8ebc5471298c 100644 --- a/tests/xfpga/test_reconf_c.cpp +++ b/tests/xfpga/test_reconf_c.cpp @@ -319,10 +319,10 @@ class reconf_c_mock_p : public opae_device_p { * @test set_afu_userclock * @brief Tests: set_afu_userclock * @details When given valid parameters, set_afu_userclock - * returns FPGA_NOT_SUPPORTED on mock platforms. + * returns FPGA_NOT_FOUND on mock platforms. */ TEST_P(reconf_c_mock_p, set_afu_userclock) { - EXPECT_EQ(set_afu_userclock(device_, 312, 156), FPGA_INVALID_PARAM); + EXPECT_EQ(set_afu_userclock(device_, 312, 156), FPGA_NOT_FOUND); } /** 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); From 20b1e502be6996aca1dedcb602c91239451dd229 Mon Sep 17 00:00:00 2001 From: Michael Adler Date: Tue, 11 Feb 2025 11:01:05 -0500 Subject: [PATCH 2/8] userclk: add support for Agilex 5 configuration (#3161) Agilex 5 uses a new masked read-modify-write interface in hardware. OFS defines a new general interface (type 2) that allows software to set the address, value and mask for an IOPLL register to update. At the moment it is used only by Agilex 5. The M, N, C0 and C1 values for Agilex 5 and 7 are the same. Signed-off-by: Michael Adler --- libraries/plugins/xfpga/CMakeLists.txt | 1 + .../plugins/xfpga/usrclk/fpga_user_clk.c | 4 + .../plugins/xfpga/usrclk/fpga_user_clk_int.h | 22 ++ .../xfpga/usrclk/fpga_user_clk_type1.c | 5 - .../xfpga/usrclk/fpga_user_clk_type2.c | 311 ++++++++++++++++++ tests/xfpga/CMakeLists.txt | 1 + 6 files changed, 339 insertions(+), 5 deletions(-) create mode 100644 libraries/plugins/xfpga/usrclk/fpga_user_clk_type2.c diff --git a/libraries/plugins/xfpga/CMakeLists.txt b/libraries/plugins/xfpga/CMakeLists.txt index d1d35da2162b..851b22435304 100644 --- a/libraries/plugins/xfpga/CMakeLists.txt +++ b/libraries/plugins/xfpga/CMakeLists.txt @@ -47,6 +47,7 @@ set(SRC userclk.c usrclk/fpga_user_clk.c usrclk/fpga_user_clk_type1.c + usrclk/fpga_user_clk_type2.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 fbd3866df62c..80ff03c4660e 100644 --- a/libraries/plugins/xfpga/usrclk/fpga_user_clk.c +++ b/libraries/plugins/xfpga/usrclk/fpga_user_clk.c @@ -301,6 +301,10 @@ fpga_result set_userclock(const char *sysfs_path, // clocks. This is handled in the type1 path. result = set_userclock_type1(sysfs_path, 0, userclk_high, userclk_low); + } else if (revision == TYPE2_USRCLK_REV) { + // Type 2 is a masked read-modify-write pipeline. Agilex 5 + // uses this. + result = set_userclock_type2(sysfs_path, userclk_high, userclk_low); } else if (revision <= AGILEX_USRCLK_REV) { // Agilex user clock DFH revision 1 // S10 & A10 user clock DFH revision 0 diff --git a/libraries/plugins/xfpga/usrclk/fpga_user_clk_int.h b/libraries/plugins/xfpga/usrclk/fpga_user_clk_int.h index 514ca8cd278e..a02747bff6ce 100644 --- a/libraries/plugins/xfpga/usrclk/fpga_user_clk_int.h +++ b/libraries/plugins/xfpga/usrclk/fpga_user_clk_int.h @@ -47,6 +47,7 @@ extern "C" { #define IOPLL_ADDR GENMASK_ULL(41, 32) #define IOPLL_WRITE BIT_ULL(44) #define IOPLL_SEQ GENMASK_ULL(49, 48) +#define IOPLL_MGMT_DATA_MASK BIT_ULL(51) #define IOPLL_AVMM_RESET_N BIT_ULL(52) #define IOPLL_MGMT_RESET BIT_ULL(56) #define IOPLL_RESET BIT_ULL(57) @@ -61,6 +62,7 @@ extern "C" { #define IOPLL_FREQ_STS1 0x20 #define IOPLL_FREQUENCY GENMASK_ULL(16, 0) #define IOPLL_REF_FREQ GENMASK_ULL(50, 33) +#define IOPLL_STS1_FPGA_FAMILY GENMASK_ULL(59, 54) #define IOPLL_VERSION GENMASK_ULL(63, 60) #define IOPLL_CAL_DELAY_US 1000 @@ -70,10 +72,18 @@ extern "C" { #define IOPLL_MAX_FREQ 600 #define IOPLL_MIN_FREQ 10 +// Revision stored in the user clock DFH indicates the CSR protocol +#define TYPE2_USRCLK_REV 2 #define AGILEX_USRCLK_REV 1 + #define IOPLL_AGILEX_MAX_FREQ 800 #define IOPLL_AGILEX_MIN_FREQ 10 +#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) + struct iopll_config { unsigned int pll_freq_khz; unsigned int pll_m; @@ -126,6 +136,18 @@ fpga_result get_usrclk_uio(const char *sysfs_path, fpga_result set_userclock_type1(const char *sysfs_path, uint64_t revision, uint64_t userclk_high, uint64_t userclk_low); +/** + * @brief set fpga user clock for read-modify-write pipeline (e.g. Agilex 5) + * + * @param sysfs_path port sysfs path + * @param high user clock + * @param low user clock + * + * @return error code + */ +fpga_result set_userclock_type2(const char *sysfs_path, + uint64_t userclk_high, uint64_t userclk_low); + #ifdef __cplusplus } #endif diff --git a/libraries/plugins/xfpga/usrclk/fpga_user_clk_type1.c b/libraries/plugins/xfpga/usrclk/fpga_user_clk_type1.c index 691759166a98..1f7944359c43 100644 --- a/libraries/plugins/xfpga/usrclk/fpga_user_clk_type1.c +++ b/libraries/plugins/xfpga/usrclk/fpga_user_clk_type1.c @@ -53,11 +53,6 @@ * 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 diff --git a/libraries/plugins/xfpga/usrclk/fpga_user_clk_type2.c b/libraries/plugins/xfpga/usrclk/fpga_user_clk_type2.c new file mode 100644 index 000000000000..227a3e01d849 --- /dev/null +++ b/libraries/plugins/xfpga/usrclk/fpga_user_clk_type2.c @@ -0,0 +1,311 @@ +// 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. + +// Type 2 (from the DFH revision being set to 2) is a general masked +// read-modify-write pipeline. The FPGA family is discovered from +// the IOPLL_FREQ_STS1 register. +// +// The public entry point is set_userclock_type2(), 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" + +#define USRCLK_FAMILY_AGILEX5 1 + +#define PLL_AGX5_EN_REGS_ADDR 0x10 +#define PLL_AGX5_CAL_STATUS_ADDR 0x58 +#define PLL_AGX5_CAL_OVRD_ADDR 0x48 +#define PLL_AGX5_CAL_REQ_ADDR 0x88 +#define PLL_AGX5_MN_ADDR 0x40 +#define PLL_AGX5_C0_ADDR 0x5c +#define PLL_AGX5_C1_ADDR 0x60 +#define PLL_AGX5_CP_ADDR 0x44 +#define PLL_AGX5_RESET_ADDR 0x80 + +#define PLL_AGX5_M GENMASK_ULL(28, 20) +#define PLL_AGX5_M_BYPASS_EN BIT_ULL(31) +#define PLL_AGX5_N_HIGH GENMASK_ULL(7, 0) +#define PLL_AGX5_N_LOW GENMASK_ULL(16, 9) +#define PLL_AGX5_N_BYPASS_EN BIT_ULL(8) +#define PLL_AGX5_N_EVEN_DUTY_EN BIT_ULL(17) +#define PLL_AGX5_C_HIGH GENMASK_ULL(7, 0) +#define PLL_AGX5_C_LOW GENMASK_ULL(30, 23) +#define PLL_AGX5_C_BYPASS_EN BIT_ULL(8) +#define PLL_AGX5_C_EVEN_DUTY_EN BIT_ULL(31) +#define PLL_AGX5_CP GENMASK_ULL(15, 1) + +// Sources of data to be written to the user clock registers. +typedef enum { + PLL_PARAM_CONST, + PLL_PARAM_MN, + PLL_PARAM_C0, + PLL_PARAM_C1, + PLL_PARAM_CP +} e_pll_param; + +// Descriptor for a single stage of the user clock configuration sequence. +typedef struct { + uint32_t addr; + uint32_t mask; + e_pll_param param_type; + uint32_t const_param; + const char *msg; +} t_user_clk_type2_seq; + +// Sequence of operations to configure an Agilex 5 user clock. +const t_user_clk_type2_seq agilex5_pll_seq[] = { + {PLL_AGX5_EN_REGS_ADDR, 0x1, PLL_PARAM_CONST, 1, "Setting reconfiguration enable..."}, + {PLL_AGX5_CAL_STATUS_ADDR, 0x200080, PLL_PARAM_CONST, 0x0, "Clearing calibration status..."}, + {PLL_AGX5_MN_ADDR, 0xffffffff, PLL_PARAM_MN, 0x0, "Setting PLL M+N..."}, + {PLL_AGX5_C0_ADDR, 0xffb801ff, PLL_PARAM_C0, 0x0, "Setting PLL C0..."}, + {PLL_AGX5_C1_ADDR, 0xffb801ff, PLL_PARAM_C1, 0x0, "Setting PLL C1..."}, + {PLL_AGX5_CP_ADDR, 0xfffe, PLL_PARAM_CP, 0x0, "Setting PLL charge pump..."}, + {PLL_AGX5_RESET_ADDR, 0x4, PLL_PARAM_CONST, 0x4, "Asserting PLL reset..."}, + {PLL_AGX5_RESET_ADDR, 0x4, PLL_PARAM_CONST, 0x0, "Deasserting PLL reset..."}, + {PLL_AGX5_CAL_OVRD_ADDR, 0x4000, PLL_PARAM_CONST, 0x4000, "Permitting calibration override..."}, + {PLL_AGX5_CAL_REQ_ADDR, 0x800, PLL_PARAM_CONST, 0x0, "Clearing calibration request..."}, + {PLL_AGX5_CAL_REQ_ADDR, 0x800, PLL_PARAM_CONST, 0x800, "Requesting calibration..."}, + {0, 0, PLL_PARAM_CONST, 0, NULL} +}; + +// Table of charge pump settings for Agilex 5, indexed by M ranges. +typedef struct { + uint32_t m_low; + uint32_t m_high; + uint32_t cp; +} t_user_clk_agilex5_cp; + +#define N_AGILEX5_CP_ENTRIES 13 +const t_user_clk_agilex5_cp agilex5_cp_table[N_AGILEX5_CP_ENTRIES] = { + {2, 2, 0b000111010111111}, + {3, 5, 0b000111000111010}, + {6, 7, 0b000111000111010}, + {8, 10, 0b000101000011000}, + {11, 15, 0b000100110010010}, + {16, 20, 0b000010100101110}, + {21, 23, 0b000010100101110}, + {24, 43, 0b000000100001100}, + {44, 64, 0b000000011001001}, + {65, 85, 0b000000011000110}, + {86, 124, 0b000000010100101}, + {125, 160, 0b000000001100011}, + {161, 320, 0b000000001000010} +}; + +STATIC uint32_t user_clk_agilex5_cp_lookup(uint32_t m) +{ + if (m < agilex5_cp_table[0].m_low) { + return agilex5_cp_table[0].cp; + } + + // Simple linear search. The table is small and the search is infrequent. + for (int i = 0; i < N_AGILEX5_CP_ENTRIES; i++) { + if (m >= agilex5_cp_table[i].m_low && m <= agilex5_cp_table[i].m_high) { + return agilex5_cp_table[i].cp; + } + } + + return agilex5_cp_table[N_AGILEX5_CP_ENTRIES-1].cp; +} + +// Type 2 user clock does a masked read-modify-write for every register update. +// Set the mask for the next write by writing it to CMD0. +STATIC fpga_result user_clk_type2_set_mask(uint8_t *uio_ptr, uint32_t mask) +{ + uint64_t v = 0; + + // Mask is stored in the data field + v = FIELD_PREP(IOPLL_DATA, mask); + v |= IOPLL_MGMT_DATA_MASK; + *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_CMD0)) = v; + + return FPGA_OK; +} + +STATIC fpga_result usrclk_set_agilex5(uint8_t *uio_ptr, + uint8_t *seq, + uint64_t userclk_low) +{ + fpga_result res = FPGA_OK; + uint64_t v = 0; + uint32_t timeout = IOPLL_WRITE_POLL_TIMEOUT_US; + uint32_t cp_from_table = 0; + + unsigned int iopll_max_freq = IOPLL_AGILEX_MAX_FREQ; + unsigned int iopll_min_freq = IOPLL_AGILEX_MIN_FREQ; + + // Agilex 5 uses the same table for M, N, C0 and C1 + // as Agilex 7. + const struct iopll_config *iopll_config = + &iopll_agilex_freq_config[userclk_low]; + if ((iopll_config->pll_freq_khz > iopll_max_freq * 1000) || + (iopll_config->pll_freq_khz < iopll_min_freq * 1000)) + return FPGA_EXCEPTION; + + int i = 0; + while (agilex5_pll_seq[i].msg) { + const t_user_clk_type2_seq *cmd = &agilex5_pll_seq[i]; + + // Mask -- bits that will be updated in the next write + user_clk_type2_set_mask(uio_ptr, cmd->mask); + + // Construct the write, either with a constant value from the table or + // with the PLL parameters. + v = IOPLL_WRITE; + v |= FIELD_PREP(IOPLL_ADDR, cmd->addr); + + *seq = (*seq + 1) & 0x3; + v |= FIELD_PREP(IOPLL_SEQ, *seq); + + switch (cmd->param_type) { + case PLL_PARAM_CONST: + v |= FIELD_PREP(IOPLL_DATA, cmd->const_param); + break; + case PLL_PARAM_MN: + v |= FIELD_PREP(PLL_AGX5_M, FIELD_GET(CFG_PLL_HIGH, iopll_config->pll_m) + + FIELD_GET(CFG_PLL_LOW, iopll_config->pll_m)); + v |= FIELD_PREP(PLL_AGX5_M_BYPASS_EN, FIELD_GET(CFG_PLL_BYPASS_EN, iopll_config->pll_m)); + + v |= FIELD_PREP(PLL_AGX5_N_HIGH, FIELD_GET(CFG_PLL_HIGH, iopll_config->pll_n)); + v |= FIELD_PREP(PLL_AGX5_N_LOW, FIELD_GET(CFG_PLL_LOW, iopll_config->pll_n)); + v |= FIELD_PREP(PLL_AGX5_N_BYPASS_EN, FIELD_GET(CFG_PLL_BYPASS_EN, iopll_config->pll_n)); + v |= FIELD_PREP(PLL_AGX5_N_EVEN_DUTY_EN, FIELD_GET(CFG_PLL_EVEN_DUTY_EN, iopll_config->pll_n)); + break; + case PLL_PARAM_C0: + v |= FIELD_PREP(PLL_AGX5_C_HIGH, FIELD_GET(CFG_PLL_HIGH, iopll_config->pll_c0)); + v |= FIELD_PREP(PLL_AGX5_C_LOW, FIELD_GET(CFG_PLL_LOW, iopll_config->pll_c0)); + v |= FIELD_PREP(PLL_AGX5_C_BYPASS_EN, FIELD_GET(CFG_PLL_BYPASS_EN, iopll_config->pll_c0)); + v |= FIELD_PREP(PLL_AGX5_C_EVEN_DUTY_EN, FIELD_GET(CFG_PLL_EVEN_DUTY_EN, iopll_config->pll_c0)); + break; + case PLL_PARAM_C1: + v |= FIELD_PREP(PLL_AGX5_C_HIGH, FIELD_GET(CFG_PLL_HIGH, iopll_config->pll_c1)); + v |= FIELD_PREP(PLL_AGX5_C_LOW, FIELD_GET(CFG_PLL_LOW, iopll_config->pll_c1)); + v |= FIELD_PREP(PLL_AGX5_C_BYPASS_EN, FIELD_GET(CFG_PLL_BYPASS_EN, iopll_config->pll_c1)); + v |= FIELD_PREP(PLL_AGX5_C_EVEN_DUTY_EN, FIELD_GET(CFG_PLL_EVEN_DUTY_EN, iopll_config->pll_c1)); + break; + case PLL_PARAM_CP: + cp_from_table = user_clk_agilex5_cp_lookup( + FIELD_GET(CFG_PLL_HIGH, iopll_config->pll_m) + + FIELD_GET(CFG_PLL_LOW, iopll_config->pll_m)); + v |= FIELD_PREP(PLL_AGX5_CP, cp_from_table); + break; + } + + *((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; + } + } + + i += 1; + } + + return res; +} + + +// set fpga user clock +fpga_result set_userclock_type2(const char *sysfs_path, + uint64_t userclk_high, + uint64_t userclk_low) +{ + uint8_t seq = 1; + uint8_t *uio_ptr = NULL; + fpga_result result = FPGA_OK; + struct opae_uio uio; + unsigned int fpga_family = 0; + uint64_t v = 0; + + 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; + } + + 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; + + // Get target FPGA family + v = *((volatile uint64_t *)(uio_ptr + IOPLL_FREQ_STS1)); + fpga_family = FIELD_GET(IOPLL_STS1_FPGA_FAMILY, v); + + if (fpga_family == USRCLK_FAMILY_AGILEX5) { + result = usrclk_set_agilex5(uio_ptr, &seq, userclk_low); + } else { + OPAE_ERR("Unsupported type 2 FPGA family: %d", fpga_family); + result = FPGA_NOT_SUPPORTED; + } + if (result != FPGA_OK) { + goto uio_close; + } + + result = usrclk_reset(uio_ptr); + if (result != FPGA_OK) { + OPAE_ERR("Failed to reset user clock"); + } + +uio_close: + opae_uio_close(&uio); + return result; +} diff --git a/tests/xfpga/CMakeLists.txt b/tests/xfpga/CMakeLists.txt index 728f258adf3a..885a42c46def 100644 --- a/tests/xfpga/CMakeLists.txt +++ b/tests/xfpga/CMakeLists.txt @@ -51,6 +51,7 @@ opae_test_add_static_lib(TARGET xfpga-static ${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/usrclk/fpga_user_clk_type2.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 From baa6c1703def232336f61485c1ef546d6ca1e236 Mon Sep 17 00:00:00 2001 From: nahid hassan Date: Thu, 20 Feb 2025 14:21:05 -0800 Subject: [PATCH 3/8] Updating SECURITY.md --- SECURITY.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 4988e77be87d..dd06a2dabe1f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,5 +1,5 @@ -# Security Policy # -Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. +# Security Policy +Altera is committed to the security of its products. -## Reporting a Vulnerability ## -Please report any security vulnerabilities in this project **[utilizing the guidelines here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html)**. +## Reporting a Vulnerability +Security vulnerabilities in this project can be reported to Altera’s security incident response team utilizing the guidelines here: https://www.altera.com/security/psirt.html. From c52aef5aefdacdcc238097e6b8f260f7924f9f04 Mon Sep 17 00:00:00 2001 From: bkhodiax Date: Tue, 13 May 2025 17:51:23 +0530 Subject: [PATCH 4/8] [Fix]- Ubuntu 24.04 debian package build issue (#3165) PEP 427 [https://peps.python.org/pep-0427/] specifies that dots in the project name are normalized to underscores in the wheel filename for compatibility and to avoid issues with filesystems and tooling. --- packaging/opae/deb/opae-devel.install | 2 +- packaging/opae/deb/opae-extra-tools.install | 6 +++--- packaging/opae/deb/opae.install | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packaging/opae/deb/opae-devel.install b/packaging/opae/deb/opae-devel.install index c84c7e982dda..62950186a0ee 100644 --- a/packaging/opae/deb/opae-devel.install +++ b/packaging/opae/deb/opae-devel.install @@ -66,6 +66,6 @@ usr/lib/python3/dist-packages/hssi_ethernet* usr/lib/python3/dist-packages/pacsign* usr/lib/python3/dist-packages/packager* usr/lib/python3/dist-packages/libvfio* -usr/lib/python3/dist-packages/opae.fpga* +usr/lib/python3/dist-packages/opae_fpga* usr/lib/python3/dist-packages/opae/fpga* usr/lib/python3/dist-packages/platmgr* diff --git a/packaging/opae/deb/opae-extra-tools.install b/packaging/opae/deb/opae-extra-tools.install index c450e9489c94..ba273b34985f 100644 --- a/packaging/opae/deb/opae-extra-tools.install +++ b/packaging/opae/deb/opae-extra-tools.install @@ -26,10 +26,10 @@ usr/bin/hssi usr/bin/opae.io usr/bin/mem_tg usr/bin/ofs.uio -usr/lib/python3/dist-packages/opae.diag* +usr/lib/python3/dist-packages/opae_diag* usr/lib/python3/dist-packages/opae/diag* -usr/lib/python3/dist-packages/opae.io* +usr/lib/python3/dist-packages/opae_io* usr/lib/python3/dist-packages/opae/io* usr/lib/python3/dist-packages/pyopaeuio* -usr/lib/python3/dist-packages/ofs.uio* +usr/lib/python3/dist-packages/ofs_uio* usr/lib/python3/dist-packages/uio* diff --git a/packaging/opae/deb/opae.install b/packaging/opae/deb/opae.install index cd3992df9a14..74ceab30a0bd 100644 --- a/packaging/opae/deb/opae.install +++ b/packaging/opae/deb/opae.install @@ -34,7 +34,7 @@ usr/bin/fpgainfo usr/bin/fpgasupdate usr/bin/rsu usr/bin/pci_device -usr/lib/python3/dist-packages/opae.admin* +usr/lib/python3/dist-packages/opae_admin* usr/lib/python3/dist-packages/opae/admin* etc/sysconfig/fpgad.conf usr/lib/systemd/system/fpgad.service From 1b3d0f5d8059710d7d1d2185cb788539eea62469 Mon Sep 17 00:00:00 2001 From: Michael Adler Date: Wed, 21 May 2025 07:57:36 -0400 Subject: [PATCH 5/8] [Fix] userclk: increase the reset delay (#3166) Agilex 5 user clock reset was unreliable with the previous timeout. Signed-off-by: Michael Adler --- libraries/plugins/xfpga/usrclk/fpga_user_clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/plugins/xfpga/usrclk/fpga_user_clk.c b/libraries/plugins/xfpga/usrclk/fpga_user_clk.c index 80ff03c4660e..0da06be447eb 100644 --- a/libraries/plugins/xfpga/usrclk/fpga_user_clk.c +++ b/libraries/plugins/xfpga/usrclk/fpga_user_clk.c @@ -47,7 +47,7 @@ #define IOPLL_MEASURE_LOW 0 #define IOPLL_MEASURE_HIGH 1 #define IOPLL_MEASURE_DELAY_US 8000 -#define IOPLL_RESET_DELAY_US 1000 +#define IOPLL_RESET_DELAY_US 10000 // DFHv0 struct dfh { From c237fc2ad18932bda841c9e5c7d07f529f287419 Mon Sep 17 00:00:00 2001 From: bkhodiax Date: Wed, 21 May 2025 21:40:58 +0530 Subject: [PATCH 6/8] ubuntu 24.04: opae-unit test failing for use of deprecated function (#3167) Replaced function assertEquals with assertEqual as it deprecated. --- tests/pyopae/test_sysobject.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pyopae/test_sysobject.py b/tests/pyopae/test_sysobject.py index 9e04f1b348a8..3b6e2c2a3662 100644 --- a/tests/pyopae/test_sysobject.py +++ b/tests/pyopae/test_sysobject.py @@ -69,7 +69,7 @@ def test_token_object(self): assert u1 == u2 afuid2 = self.afu_toks[0]["afu_id"].bytes()[:-1] assert afuid == afuid2 - self.assertEquals(self.afu_toks[0].wrong, None) + self.assertEqual(self.afu_toks[0].wrong, None) with self.assertRaises(RuntimeError): print(self.afu_toks[0]['../fpga'].read64()) @@ -95,7 +95,7 @@ def test_object_read64(self): errors = self.afu_handle.errors err = errors.errors n1 = err.read64() - self.assertEquals(n1, 0) + self.assertEqual(n1, 0) with self.assertRaises(RuntimeError): print(errors.read64()) From 265e6730f1163c3a073b60d12b13bf77b491f227 Mon Sep 17 00:00:00 2001 From: bkhodiax Date: Fri, 23 May 2025 18:14:28 +0530 Subject: [PATCH 7/8] opae: bump release to 2.14.0-3 (#3168) --- CMakeLists.txt | 2 +- packaging/opae.admin/version | 2 +- packaging/opae/version | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e92e5fa5056..37b03ef9f2d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -959,7 +959,7 @@ else() set(CPACK_PACKAGE_VERSION_PATCH "${OPAE_VERSION_REVISION}") endif() set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}) -set(CPACK_PACKAGE_RELEASE 2) +set(CPACK_PACKAGE_RELEASE 3) set(CPACK_PACKAGE_CONTACT "opae@lists.linuxfoundation.org") set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") diff --git a/packaging/opae.admin/version b/packaging/opae.admin/version index 66d58304248b..05b5a09b2468 100755 --- a/packaging/opae.admin/version +++ b/packaging/opae.admin/version @@ -25,4 +25,4 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. PROJECT_VERSION='2.14.0' -PROJECT_RELEASE='2' +PROJECT_RELEASE='3' diff --git a/packaging/opae/version b/packaging/opae/version index 436909d9bf5f..827d6301677e 100755 --- a/packaging/opae/version +++ b/packaging/opae/version @@ -25,4 +25,4 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. declare -r PROJECT_VERSION='2.14.0' -declare -r PROJECT_RELEASE='2' +declare -r PROJECT_RELEASE='3' From e3a953ca34ca1c55e214584b860216c8211130ca Mon Sep 17 00:00:00 2001 From: bkhodiax Date: Fri, 23 May 2025 18:32:14 +0530 Subject: [PATCH 8/8] opae: changelog update for 2.14.0-3 (#3169) --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5d0cddd5f38..2ccc8ed2f200 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [2.14.0-3] + +### Added + +- userclk: separate device independent from Agilex 7 configuration ([#3159]) +- userclk: add support for Agilex 5 configuration ([#3161]) + +### Fixed + +- Fix Ubuntu 24.04 debian package build issue ([#3165]) +- Fix userclk: increase the reset delay ([#3166]) +- Fix ubuntu 24.04: opae-unit test failing for use of deprecated function ([#3167]) + +[2.14.0-3]: https://github.com/OFS/opae-sdk/compare/2.14.0-2...2.14.0-3 +[#3159]: https://github.com/OFS/opae-sdk/pull/3159 +[#3161]: https://github.com/OFS/opae-sdk/pull/3161 +[#3165]: https://github.com/OFS/opae-sdk/pull/3165 +[#3166]: https://github.com/OFS/opae-sdk/pull/3166 +[#3167]: https://github.com/OFS/opae-sdk/pull/3167 + + ## [2.14.0-2] ### Added