diff options
Diffstat (limited to 'src/vendorcode/cavium/bdk/libbdk-hal/bdk-clock.c')
-rw-r--r-- | src/vendorcode/cavium/bdk/libbdk-hal/bdk-clock.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/bdk-clock.c b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-clock.c new file mode 100644 index 0000000000..f81285dffd --- /dev/null +++ b/src/vendorcode/cavium/bdk/libbdk-hal/bdk-clock.c @@ -0,0 +1,221 @@ +/***********************license start*********************************** +* Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights +* reserved. +* +* +* 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 Cavium Inc. 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, including technical data, may be subject to U.S. export +* control laws, including the U.S. Export Administration Act and its +* associated regulations, and may be subject to export or import +* regulations in other countries. +* +* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" +* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR +* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT +* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY +* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT +* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES +* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR +* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, +* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK +* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU. +***********************license end**************************************/ +#include <bdk.h> +#include "libbdk-arch/bdk-csrs-gti.h" +#include "libbdk-arch/bdk-csrs-ocx.h" + +/** + * Called in __bdk_init to setup the global timer + */ +void bdk_clock_setup(bdk_node_t node) +{ + const bdk_node_t local_node = bdk_numa_local(); + + /* Check if the counter was already setup */ + BDK_CSR_INIT(cntcr, node, BDK_GTI_CC_CNTCR); + if (cntcr.s.en) + return; + + /* Configure GTI to tick at BDK_GTI_RATE */ + uint64_t sclk = bdk_clock_get_rate(node, BDK_CLOCK_SCLK); + uint64_t inc = (BDK_GTI_RATE << 32) / sclk; + BDK_CSR_WRITE(node, BDK_GTI_CC_CNTRATE, inc); + BDK_CSR_WRITE(node, BDK_GTI_CTL_CNTFRQ, BDK_GTI_RATE); + cntcr.s.en = 1; + if (node != local_node) + { + /* Synchronize with local node. Very simple set of counter, will be + off a little */ + BDK_CSR_WRITE(node, BDK_GTI_CC_CNTCV, bdk_clock_get_count(BDK_CLOCK_TIME)); + } + /* Enable the counter */ + BDK_CSR_WRITE(node, BDK_GTI_CC_CNTCR, cntcr.u); + BDK_CSR_READ(node, BDK_GTI_CC_CNTCR); + + if (node != local_node) + { + if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X)) + { + /* Assume the delay in each direction is the same, sync the counters */ + int64_t local1 = bdk_clock_get_count(BDK_CLOCK_TIME); + int64_t remote = BDK_CSR_READ(node, BDK_GTI_CC_CNTCV); + int64_t local2 = bdk_clock_get_count(BDK_CLOCK_TIME); + int64_t expected = (local1 + local2) / 2; + BDK_CSR_WRITE(node, BDK_GTI_CC_CNTADD, expected - remote); + BDK_TRACE(INIT, "N%d.GTI: Clock synchronization with master\n" + " expected: %ld, remote %ld\n" + " Counter correction: %ld\n", + node, expected, remote, expected - remote); + } + else + { + /* Due to errata TBD, we need to use OCX_PP_CMD to write + GTI_CC_CNTMB in order for timestamps to update. These constants + are the addresses we need for both local and remote GTI_CC_CNTMB */ + const uint64_t LOCAL_GTI_CC_CNTMB = bdk_numa_get_address(local_node, BDK_GTI_CC_CNTMB); + const uint64_t REMOTE_GTI_CC_CNTMB = bdk_numa_get_address(node, BDK_GTI_CC_CNTMB); + /* Build partial OCX_PP_CMD command used for writes. Address will + be filled later */ + BDK_CSR_DEFINE(pp_cmd, BDK_OCX_PP_CMD); + pp_cmd.u = 0; + pp_cmd.s.wr_mask = 0xff; + + const int NUM_AVERAGE = 16; /* Choose a power of two to avoid division */ + int64_t local_to_remote_sum = 0; + int64_t local_to_remote_min = 1000000; + int64_t local_to_remote_max = -1000000; + int64_t remote_to_local_sum = 0; + int64_t remote_to_local_min = 1000000; + int64_t remote_to_local_max = -1000000; + for (int loop = 0; loop < NUM_AVERAGE; loop++) + { + /* Perform a write to the remote GTI_CC_CNTMB to cause timestamp + update. We don't care about the value actually written */ + pp_cmd.s.addr = REMOTE_GTI_CC_CNTMB; + BDK_CSR_WRITE(local_node, BDK_OCX_PP_CMD, pp_cmd.u); + BDK_CSR_READ(local_node, BDK_OCX_PP_CMD); + + int64_t remote = BDK_CSR_READ(node, BDK_GTI_CC_CNTMBTS); + int64_t local = BDK_CSR_READ(local_node, BDK_GTI_CC_CNTMBTS); + int64_t delta = remote - local; + + local_to_remote_sum += delta; + if (delta < local_to_remote_min) + local_to_remote_min = delta; + if (delta > local_to_remote_max) + local_to_remote_max = delta; + + /* Perform a write to the local GTI_CC_CNTMB to cause timestamp + update. We don't care about the value actually written */ + pp_cmd.s.addr = LOCAL_GTI_CC_CNTMB; + BDK_CSR_WRITE(node, BDK_OCX_PP_CMD, pp_cmd.u); + BDK_CSR_READ(node, BDK_OCX_PP_CMD); + + remote = BDK_CSR_READ(node, BDK_GTI_CC_CNTMBTS); + local = BDK_CSR_READ(local_node, BDK_GTI_CC_CNTMBTS); + delta = local - remote; + + remote_to_local_sum += delta; + if (delta < remote_to_local_min) + remote_to_local_min = delta; + if (delta > remote_to_local_max) + remote_to_local_max = delta; + } + /* Calculate average, rounding to nearest */ + int64_t local_to_remote = (local_to_remote_sum + NUM_AVERAGE/2) / NUM_AVERAGE; + int64_t remote_to_local = (remote_to_local_sum + NUM_AVERAGE/2) / NUM_AVERAGE; + /* Calculate remote node offset */ + int64_t remote_offset = (remote_to_local - local_to_remote) / 2; + BDK_CSR_WRITE(node, BDK_GTI_CC_CNTADD, remote_offset); + BDK_TRACE(INIT, "N%d.GTI: Clock synchronization with master\n" + " local -> remote: min %ld, avg %ld, max %ld\n" + " remote -> local: min %ld, avg %ld, max %ld\n" + " Counter correction: %ld\n", + node, + local_to_remote_min, local_to_remote, local_to_remote_max, + remote_to_local_min, remote_to_local, remote_to_local_max, + remote_offset); + } + } +} + +/** + * Get cycle count based on the clock type. + * + * @param clock - Enumeration of the clock type. + * @return - Get the number of cycles executed so far. + */ +uint64_t __bdk_clock_get_count_slow(bdk_clock_t clock) +{ + bdk_node_t node = bdk_numa_local(); + BDK_CSR_INIT(rst_boot, node, BDK_RST_BOOT); + if (bdk_is_platform(BDK_PLATFORM_EMULATOR)) + { + /* Force RCLK and SCLK to be 1GHz on emulator */ + rst_boot.s.c_mul = 20; + rst_boot.s.pnr_mul = 20; + } + uint64_t ref_cntr = BDK_CSR_READ(node, BDK_RST_REF_CNTR); + switch(clock) + { + case BDK_CLOCK_TIME: + return 0; /* Handled in fast path */ + case BDK_CLOCK_MAIN_REF: + return ref_cntr; + case BDK_CLOCK_RCLK: + return ref_cntr * rst_boot.s.c_mul; + case BDK_CLOCK_SCLK: + return ref_cntr * rst_boot.s.pnr_mul; + } + return 0; +} + +/** + * Get clock rate based on the clock type. + * + * @param node Node to use in a Numa setup. Can be an exact ID or a special value. + * @param clock - Enumeration of the clock type. + * @return - return the clock rate. + */ +uint64_t __bdk_clock_get_rate_slow(bdk_node_t node, bdk_clock_t clock) +{ + /* This is currently defined to be 50Mhz */ + const uint64_t REF_CLOCK = 50000000; + + BDK_CSR_INIT(mio_rst_boot, node, BDK_RST_BOOT); + if (bdk_is_platform(BDK_PLATFORM_EMULATOR)) + { + /* Force RCLK and SCLK to be 1GHz on emulator */ + mio_rst_boot.s.c_mul = 20; + mio_rst_boot.s.pnr_mul = 20; + } + switch (clock) + { + case BDK_CLOCK_TIME: + return BDK_GTI_RATE; /* Programed as part of setup */ + case BDK_CLOCK_MAIN_REF: + return REF_CLOCK; + case BDK_CLOCK_RCLK: + return REF_CLOCK * mio_rst_boot.s.c_mul; + case BDK_CLOCK_SCLK: + return REF_CLOCK * mio_rst_boot.s.pnr_mul; + } + return 0; +} + |