sdk/userspace/fpga_libs/fpga_clkgen/fpga_clkgen_mmcm.c (224 lines of code) (raw):
/*
* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may
* not use this file except in compliance with the License. A copy of the
* License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#include "fpga_clkgen_mmcm.h"
#include <utils/lcd.h>
#include <utils/log.h>
#include <hal/fpga_common.h>
static struct fpga_clkgen_mmcm_private {
struct fpga_clkgen_mmcm group_a;
struct fpga_clkgen_mmcm group_b;
struct fpga_clkgen_mmcm group_c;
struct fpga_clkgen_mmcm group_hbm;
} private;
static struct fpga_clkgen_mmcm* get_mmcm(enum fpga_clkgen_mmcm_group group) {
struct fpga_clkgen_mmcm* mmcm_ptr = NULL;
switch (group) {
case clkgen_group_a:
mmcm_ptr = &private.group_a;
break;
case clkgen_group_b:
mmcm_ptr = &private.group_b;
break;
case clkgen_group_c:
mmcm_ptr = &private.group_c;
break;
case clkgen_group_hbm:
mmcm_ptr = &private.group_hbm;
break;
}
return mmcm_ptr;
}
// Ensure clkgen ip is part of the image before calling mmcm_init.
int mmcm_init(enum fpga_clkgen_mmcm_group group, pci_bar_handle_t handle) {
int rc = 0;
struct fpga_clkgen_mmcm* mmcm = get_mmcm(group);
fail_on_with_code(mmcm == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "mmcm invalid pointer");
mmcm->handle = handle;
switch (group) {
case clkgen_group_a: {
mmcm->mmcm_base_offset = AWS_CLKGEN_BASE_A;
mmcm->avail_bit_start = 1;
mmcm->num_clocks = 3;
mmcm->lock_bit = 0;
break;
}
case clkgen_group_b: {
mmcm->mmcm_base_offset = AWS_CLKGEN_BASE_B;
mmcm->avail_bit_start = 4;
mmcm->num_clocks = 2;
mmcm->lock_bit = 4;
break;
}
case clkgen_group_c: {
mmcm->mmcm_base_offset = AWS_CLKGEN_BASE_C;
mmcm->avail_bit_start = 6;
mmcm->num_clocks = 2;
mmcm->lock_bit = 6;
break;
}
case clkgen_group_hbm: {
mmcm->mmcm_base_offset = AWS_CLKGEN_BASE_HBM;
mmcm->avail_bit_start = 8;
mmcm->num_clocks = 1;
mmcm->lock_bit = 8;
break;
}
default:
fail_on_with_code(mmcm == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "unexpected mmcm group");
}
uint64_t clks_avail_offset = AWS_CLKGEN_CLKS_AVAIL;
uint32_t clks_avail_value = 0;
rc = fpga_pci_peek(handle, clks_avail_offset, &clks_avail_value);
fail_on(rc, out, "Unable to read AWS_CLKGEN_CLKS_AVAIL");
uint64_t clk_cfg_regs[] = {MMCM_CLK0_CFG_REG, MMCM_CLK1_CFG_REG, MMCM_CLK2_CFG_REG};
bool clk_div_has_frac[] = {true, false, false};
uint32_t clks_avail_shifted = clks_avail_value >> mmcm->avail_bit_start;
for (uint32_t i = 0; i < mmcm->num_clocks; ++i) {
uint32_t clock_avail_mask = 1 << i;
mmcm->clk_avails[i] = clks_avail_shifted & clock_avail_mask;
mmcm->clk_cfg_regs[i] = clk_cfg_regs[i];
mmcm->clk_div_has_frac[i] = clk_div_has_frac[i];
if (mmcm->clk_avails[i]) {
mmcm->any_clocks_available = 1;
}
}
out:
return rc;
}
int mmcm_clk_avail_any(enum fpga_clkgen_mmcm_group group, bool* is_avail) {
struct fpga_clkgen_mmcm* mmcm = get_mmcm(group);
fail_on_quiet(mmcm == NULL, out, "invalid mmcm pointer");
fail_on_quiet(is_avail == NULL, out, "invalid is_avail pointer");
*is_avail = mmcm->any_clocks_available;
return 0;
out:
*is_avail = false;
return 0;
}
int mmcm_clk_avail(enum fpga_clkgen_mmcm_group group, uint32_t clk_index, bool* is_avail) {
struct fpga_clkgen_mmcm* mmcm = get_mmcm(group);
fail_on_quiet(mmcm == NULL, out, "invalid mmcm pointer");
fail_on_quiet(clk_index >= mmcm->num_clocks, out, "invalid clk_index");
fail_on_quiet(is_avail == NULL, out, "invalid is_avail pointer");
*is_avail = mmcm->clk_avails[clk_index];
return 0;
out:
*is_avail = false;
return 0;
}
int mmcm_is_locked(enum fpga_clkgen_mmcm_group group, bool* is_locked) {
int rc = 0;
struct fpga_clkgen_mmcm* mmcm = get_mmcm(group);
fail_on_with_code(mmcm == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid mmcm pointer");
fail_on_with_code(is_locked == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid is_locked pointer");
uint64_t status_reg_offset = mmcm->mmcm_base_offset + MMCM_STATUS_REG;
uint32_t status_reg_value = 0;
rc = fpga_pci_peek(mmcm->handle, status_reg_offset, &status_reg_value);
fail_on(rc, out, "Failed to read from register @0x%08lx", status_reg_offset);
*is_locked = ((status_reg_value & MMCM_STATUS_LOCKED) != 0);
out:
return rc;
}
int mmcm_wait_for_locked(enum fpga_clkgen_mmcm_group group) {
int rc = 0;
struct fpga_clkgen_mmcm* mmcm = get_mmcm(group);
fail_on_with_code(mmcm == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid mmcm pointer");
int loop_count = 0;
uint64_t status_reg_offset = mmcm->mmcm_base_offset + MMCM_STATUS_REG;
uint32_t status_reg_value = 0;
while (loop_count < MAX_CLKGEN_LOOP_RETRIES) {
rc = fpga_pci_peek(mmcm->handle, status_reg_offset, &status_reg_value);
fail_on(rc, out, "Failed to read from register @0x%08lx", status_reg_offset);
if (status_reg_value == MMCM_STATUS_LOCKED) {
break;
}
msleep(AWS_CLKGEN_LOOP_DELAY_MS);
++loop_count;
}
fail_on_with_code(loop_count >= MAX_CLKGEN_LOOP_RETRIES, out, rc, FPGA_ERR_HARDWARE_BUSY, "Timeout: MMCM not locked after %d iterations. MMCM address = 0x%08lx\n", loop_count, status_reg_offset);
out:
return rc;
}
int mmcm_set_shared_clock(enum fpga_clkgen_mmcm_group group, uint32_t mult, uint32_t mult_frac, uint32_t div) {
int rc = 0;
struct fpga_clkgen_mmcm* mmcm = get_mmcm(group);
fail_on_with_code(mmcm == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid mmcm pointer");
uint64_t main_cfg_reg_offset = mmcm->mmcm_base_offset + MMCM_MAIN_CFG_REG;
uint32_t tmp_mult = (mult == 0) ? 1 : mult;
uint32_t tmp_div = (div == 0) ? 1 : div;
uint32_t main_cfg_reg_value = ((tmp_div << 0) & 0xFF) | ((tmp_mult << 8) & 0xFF00) | ((mult_frac << 16) & 0x0FFF0000);
rc = fpga_pci_poke(mmcm->handle, main_cfg_reg_offset, main_cfg_reg_value);
fail_on(rc, out, "Failed to write to register @ 0x%08lx, value = 0x%08x", main_cfg_reg_offset, main_cfg_reg_value);
out:
return rc;
}
int mmcm_get_shared_clock(enum fpga_clkgen_mmcm_group group, uint32_t* mult, uint32_t* mult_frac, uint32_t* div) {
int rc = 0;
struct fpga_clkgen_mmcm* mmcm = get_mmcm(group);
fail_on_with_code(mmcm == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid mmcm pointer");
fail_on_with_code(mult == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid mult pointer");
fail_on_with_code(mult_frac == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid mult_frac pointer");
fail_on_with_code(div == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid div pointer");
uint64_t main_cfg_reg_offset = mmcm->mmcm_base_offset + MMCM_MAIN_CFG_REG;
uint32_t main_cfg_reg_value = 0;
rc = fpga_pci_peek(mmcm->handle, main_cfg_reg_offset, &main_cfg_reg_value);
fail_on(rc, out, "Failed to read from register @0x%08lx", main_cfg_reg_offset);
*mult = (main_cfg_reg_value >> 8) & 0xFF;
*mult_frac = (main_cfg_reg_value >> 16) & 0x3FF;
*div = (main_cfg_reg_value >> 0) & 0xFF;
out:
return rc;
}
int mmcm_set_clk_div(enum fpga_clkgen_mmcm_group group, uint32_t clk_index, uint32_t div, uint32_t div_frac) {
int rc = 0;
struct fpga_clkgen_mmcm* mmcm = get_mmcm(group);
fail_on_with_code(mmcm == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid mmcm pointer");
fail_on_with_code(clk_index >= mmcm->num_clocks, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid clk_index");
uint64_t clk_cfg_reg_offset = mmcm->mmcm_base_offset + mmcm->clk_cfg_regs[clk_index];
uint32_t tmp_div = (div == 0) ? 1 : div;
uint32_t clk_cfg_reg_value = mmcm->clk_div_has_frac[clk_index] ? ((tmp_div << 0) & 0xFF) | ((div_frac << 8) & 0x000FFF00) : ((tmp_div << 0) & 0xFF);
rc = fpga_pci_poke(mmcm->handle, clk_cfg_reg_offset, clk_cfg_reg_value);
fail_on(rc, out, "Failed to write to register @ 0x%08lx, value = 0x%08x", clk_cfg_reg_offset, clk_cfg_reg_value);
out:
return rc;
}
int mmcm_get_clk_div(enum fpga_clkgen_mmcm_group group, uint32_t clk_index, uint32_t* div, uint32_t* div_frac) {
int rc = 0;
struct fpga_clkgen_mmcm* mmcm = get_mmcm(group);
fail_on_with_code(mmcm == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid mmcm pointer");
fail_on_with_code(clk_index >= mmcm->num_clocks, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid clk_index");
fail_on_with_code(div == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid div pointer");
fail_on_with_code(div_frac == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid div_frac pointer");
uint64_t clk_cfg_reg_offset = mmcm->mmcm_base_offset + mmcm->clk_cfg_regs[clk_index];
uint32_t clk_cfg_reg_value = 0;
rc = fpga_pci_peek(mmcm->handle, clk_cfg_reg_offset, &clk_cfg_reg_value);
fail_on(rc, out, "Failed to read from register @0x%08lx", clk_cfg_reg_offset);
*div = (clk_cfg_reg_value >> 0) & 0xFF;
if (mmcm->clk_div_has_frac[clk_index]) {
*div_frac = (clk_cfg_reg_value >> 8) & 0x3FF;
} else {
*div_frac = 0;
}
out:
return rc;
}
int mmcm_load_cfg(enum fpga_clkgen_mmcm_group group, uint32_t reset) {
int rc = 0;
struct fpga_clkgen_mmcm* mmcm = get_mmcm(group);
fail_on_with_code(mmcm == NULL, out, rc, FPGA_ERR_SOFTWARE_PROBLEM, "invalid mmcm pointer");
uint64_t load_cfg_reg_offset = mmcm->mmcm_base_offset + MMCM_LOAD_CFG_REG;
uint32_t load_cfg_reg_value = reset ? 0x1 : 0x3;
rc = fpga_pci_poke(mmcm->handle, load_cfg_reg_offset, load_cfg_reg_value);
fail_on(rc, out, "Failed to write to register @ 0x%08lx, value = 0x%08x", load_cfg_reg_offset, load_cfg_reg_value);
out:
return rc;
}
int mmcm_get_expected_lock_mask(uint32_t* lock_mask) {
int rc = 0;
*lock_mask = 0;
enum fpga_clkgen_mmcm_group groups[] = {clkgen_group_a, clkgen_group_b, clkgen_group_c, clkgen_group_hbm};
for (uint32_t i = 0; i < sizeof(groups) / sizeof(groups[0]); ++i) {
struct fpga_clkgen_mmcm* mmcm = get_mmcm(groups[i]);
fail_on(mmcm == NULL, out, "invalid mmcm pointer");
*lock_mask |= mmcm->any_clocks_available ? (1 << mmcm->lock_bit) : 0;
}
out:
return rc;
}