From b87cabb27df7f34bc883181f3642d45c203dcec9 Mon Sep 17 00:00:00 2001 From: Ken Chen Date: Mon, 18 Nov 2024 14:25:17 -0800 Subject: [PATCH] common: Add original Aries_C_SDK_v2.16.2 Summary: # Description 1. Add original Aries_C_SDK_v2.16.2 2. Modify the include path for a successful build ex. -#include "../include/aries_margin.h" +#include "aries_margin.h" 3. Mofify bitbake & meson # Motivation Due to Meta request the function of retimer health/link status/margin test. First, we plan to push the original Aries_C_SDK_v2.16.2 to Meta github Then, we modify the SDK to meet the project platform architecture. X-link: https://github.com/facebookexternal/openbmc.quanta/pull/4481 Test Plan: build libretimer 2.16.2 pass Reviewed By: wangx6f Differential Revision: D65525604 fbshipit-source-id: 1bc732a7897cc041cd00c2b18ca70c9b3a45c789 --- .../recipes-lib/retimer-v2.16.2/files/LICENSE | 201 + .../files/aries_a0_reg_defines.h | 604 ++ .../retimer-v2.16.2/files/aries_api.c | 2904 ++++++++ .../retimer-v2.16.2/files/aries_api.h | 177 + .../retimer-v2.16.2/files/aries_api_types.h | 573 ++ .../retimer-v2.16.2/files/aries_error.h | 98 + .../retimer-v2.16.2/files/aries_globals.h | 192 + .../retimer-v2.16.2/files/aries_i2c.c | 2400 +++++++ .../retimer-v2.16.2/files/aries_i2c.h | 210 + .../retimer-v2.16.2/files/aries_link.c | 2407 +++++++ .../retimer-v2.16.2/files/aries_link.h | 97 + .../retimer-v2.16.2/files/aries_margin.c | 1886 ++++++ .../retimer-v2.16.2/files/aries_margin.h | 174 + .../retimer-v2.16.2/files/aries_misc.c | 5956 +++++++++++++++++ .../retimer-v2.16.2/files/aries_misc.h | 399 ++ .../files/aries_mpw_reg_defines.h | 319 + .../retimer-v2.16.2/files/astera_log.c | 177 + .../retimer-v2.16.2/files/astera_log.h | 75 + .../retimer-v2.16.2/files/meson.build | 60 + .../retimer-v2.16.2/files/plat/meson.build | 2 + .../retimer-v2.16.2/files/platform.c | 157 + .../retimer-v2.16.2/files/platform.h | 44 + .../retimer-v2.16.2/libretimer_2.16.2.bb | 43 + 23 files changed, 19155 insertions(+) create mode 100755 common/recipes-lib/retimer-v2.16.2/files/LICENSE create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_a0_reg_defines.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_api.c create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_api.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_api_types.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_error.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_globals.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_i2c.c create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_i2c.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_link.c create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_link.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_margin.c create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_margin.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_misc.c create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_misc.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/aries_mpw_reg_defines.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/astera_log.c create mode 100755 common/recipes-lib/retimer-v2.16.2/files/astera_log.h create mode 100755 common/recipes-lib/retimer-v2.16.2/files/meson.build create mode 100644 common/recipes-lib/retimer-v2.16.2/files/plat/meson.build create mode 100755 common/recipes-lib/retimer-v2.16.2/files/platform.c create mode 100755 common/recipes-lib/retimer-v2.16.2/files/platform.h create mode 100755 common/recipes-lib/retimer-v2.16.2/libretimer_2.16.2.bb diff --git a/common/recipes-lib/retimer-v2.16.2/files/LICENSE b/common/recipes-lib/retimer-v2.16.2/files/LICENSE new file mode 100755 index 000000000000..dfb0eca43c1b --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2022 Astera Labs, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License 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. diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_a0_reg_defines.h b/common/recipes-lib/retimer-v2.16.2/files/aries_a0_reg_defines.h new file mode 100755 index 000000000000..15264c697600 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_a0_reg_defines.h @@ -0,0 +1,604 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_a0_reg_defines.h + * @brief Definition of register offsets used in Aries A0 + */ +#pragma once +#ifndef ASTERA_ARIES_A0_REG_DEFINES_H +#define ASTERA_ARIES_A0_REG_DEFINES_H + +/////////////////////////////////////////////////////////////// +///////////////////////// Global Regs ///////////////////////// +/////////////////////////////////////////////////////////////// + +/** Aries global param reg0 register */ +#define ARIES_GLB_PARAM_REG0_ADDR 0x0 +/** Aries global param reg0 register */ +#define ARIES_GLB_PARAM_REG1_ADDR 0x4 +/** Main Micro reg offset for EEPROM assist (data)*/ +#define ARIES_MM_EEPROM_ASSIST_DATA_ADDR 0x410 +/** Main Micro reg offset for EEPROM assist (cmd)*/ +#define ARIES_MM_EEPROM_ASSIST_CMD_ADDR 0x920 + +/** EEPROM ic cmd reg offset*/ +#define ARIES_I2C_MST_IC_CMD_ADDR 0xd04 +/** EEPROM data0 reg offset*/ +#define ARIES_I2C_MST_DATA0_ADDR 0xd05 +/** EEPROM data1 reg offset*/ +#define ARIES_I2C_MST_DATA1_ADDR 0xd06 +/** EEPROM data2 reg offset*/ +#define ARIES_I2C_MST_DATA2_ADDR 0xd07 +/** EEPROM data3 reg offset*/ +#define ARIES_I2C_MST_DATA3_ADDR 0xd08 +/** EEPROM cmd reg offset*/ +#define ARIES_I2C_MST_CMD_ADDR 0xd09 + +/** HW reset reg */ +#define ARIES_HW_RST_ADDR 0x600 +/** SW reset reg */ +#define ARIES_SW_RST_ADDR 0x602 + +/** Efuse control register */ +#define ARIES_EFUSE_CNTL 0x8ec +/** SMS Efuse control register */ +#define ARIES_SMS_EFUSE_CNTL 0x8f6 +/** SMS Efuse status register */ +#define ARIES_SMS_EFUSE_STS 0x8f7 + +/** Main Micro Heartbeat reg */ +#define ARIES_MM_HEARTBEAT_ADDR 0x923 + +/** Generic FW status/clear registers */ +#define ARIES_GENERIC_STATUS_ADDR 0x922 +#define ARIES_GENERIC_STATUS_CLR_ADDR 0x921 +#define ARIES_GENERIC_STATUS_MASK_I2C_CLEAR_EVENT 1 + +/** Enable/Disable thermal shutdown */ +#define ARIES_EN_THERMAL_SHUTDOWN 0x420 + +/** I2C_MST_BB_OUTPUT reg address */ +#define ARIES_I2C_MST_BB_OUTPUT_ADDRESS 0xd0b + +/** I2C_MST_INIT_CTRL reg address */ +#define ARIES_I2C_MST_INIT_CTRL_ADDRESS 0xd0a + +/////////////////////////////////////////////////////////////// +////////////////////////// Main SRAM ////////////////////////// +/////////////////////////////////////////////////////////////// + +/** AL Main SRAM DMEM offset (A0) */ +#define AL_MAIN_SRAM_DMEM_OFFSET (64 * 1024) + +/** AL Path SRAM DMEM offset (A0) */ +#define AL_PATH_SRAM_DMEM_OFFSET (45 * 1024) + +/** SRAM read command */ +#define AL_TG_RD_LOC_IND_SRAM 0x16 + +/** SRAM write command */ +#define AL_TG_WR_LOC_IND_SRAM 0x17 + +/////////////////////////////////////////////////////////// +////////////////////////// Micros ///////////////////////// +/////////////////////////////////////////////////////////// + +/** Offset for main micro FW info */ +#define ARIES_MAIN_MICRO_FW_INFO (96 * 1024 - 128) + +/** Offset for path micro FW info */ +#define ARIES_PATH_MICRO_FW_INFO_ADDRESS (48 * 1024 - 256) + +/** Link struct offset in Main Micro */ +#define ARIES_LINK_0_MM_BASE_ADDR 0x10669 + +/** Link path struct size address*/ +#define ARIES_LINK_PATH_STRUCT_SIZE_ADDR 0x17faa + +/** Size of each link element in aries_link_path_struct */ +#define ARIES_LINK_ADDR_EL_SIZE 2 + +/** Path micro link struct size */ +#define ARIES_LINK_PATH_STRUCT_SIZE 38 + +/////////////////////////////////////////////////////////// +/////////////////// Path Micro Members //////////////////// +/////////////////////////////////////////////////////////// + +/** FW Info (Major) offset location in struct */ +#define ARIES_MM_FW_VERSION_MAJOR 0 + +/** FW Info (Minor) offset location in struct */ +#define ARIES_MM_FW_VERSION_MINOR 1 + +/** FW Info (Build no.) offset location in struct */ +#define ARIES_MM_FW_VERSION_BUILD 2 + +/** AL print info struct address (Main Micro) */ +#define ARIES_MM_AL_PRINT_INFO_STRUCT_ADDR 4 + +/** Aries Link Struct Addr offset */ +#define ARIES_MM_LINK_STRUCT_ADDR_OFFSET 10 + +/** Aries FW build option FW_ATE_CUSTOMER_BOARD offset */ +#define ARIES_MM_FW_ATE_CUSTOMER_BOARD 50 + +/** Aries FW build option FW_SELF_TEST offset*/ +#define ARIES_MM_FW_SELF_TEST 53 + +/**AL print info struct address (Path Micro) */ +#define ARIES_PM_AL_PRINT_INFO_STRUCT_ADDR 4 + +/** GP Ctrl status struct address (Main Micro) */ +#define ARIES_MM_GP_CTRL_STS_STRUCT_ADDR 6 + +/** GP Ctrl status struct address (Path Micro) */ +#define ARIES_PM_GP_CTRL_STS_STRUCT_ADDR 6 + +/** Pma temp code offset location in struct */ +#define ARIES_MM_PMA_TJ_ADC_CODE_OFFSET 26 + +/** Offset to enable LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_PRINT_EN_OFFSET 0 + +/** Offset to enable one batch mode inside LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_ONE_BATCH_MODE_EN_OFFSET 2 + +/** Offset to enable one batch write inside LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_ONE_BATCH_WR_EN_OFFSET 3 + +/** Offset to get write_offset inside LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_WR_PTR_OFFSET 5 + +/** Offset to get write_offset inside LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_LNK_RECOV_ENTRIES_PTR_OFFSET 7 + +/** Offset to get print_class_en inside LTSSM logger for Path Micro */ +#define ARIES_PM_PRINT_INFO_STRUCT_PRINT_CLASS_EN_OFFSET 7 + +/** Offset to get print_class_en inside LTSSM logger for Main Micro */ +#define ARIES_MM_PRINT_INFO_STRUCT_PRINT_CLASS_EN_OFFSET 15 + +/** Offset to get print buffer inside LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_PRINT_BUFFER_OFFSET 23 + +/** Offset to get FW state inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_FW_STATE 0 + +/** Offset to get last speed inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_PCIE_GEN 2 + +/** Offset to get number of last preset reqs for lane 0 inside + * GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_NUM_PRESET_REQS_LN0 3 + +/** Offset to get number of last preset reqs for lane 1 inside + * GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_NUM_PRESET_REQS_LN1 4 + +/** Offset to get last preset reqs for lane 0 inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_PRESET_REQS_LN0 5 + +/** Offset to get last preset reqs for lane 1 inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_PRESET_REQS_LN1 15 + +/** Offset to get last FOMs for lane 0 inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FOMS_LN0 25 + +/** Offset to get last FOMs for lane 1 inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FOMS_LN1 35 + +/** Offset to get preset val for lane 0 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRESET_LN0 45 + +/** Offset to get preset val for lane 1 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRESET_LN1 46 + +/** Offset to get pre cursor val for lane 0 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRE_LN0 47 + +/** Offset to get pre cursor val for lane 1 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRE_LN1 48 + +/** Offset to get cursor val for lane 0 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_CUR_LN0 49 + +/** Offset to get cursor val for lane 1 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_CUR_LN1 50 + +/** Offset to get post cursor val for lane 0 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PST_LN0 51 + +/** Offset to get post cursor val for lane 1 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PST_LN1 52 + +/** Offset to link width member in link struct*/ +#define ARIES_LINK_STRUCT_WIDTH_OFFSET 1 + +/** Offset to detected link width member in link struct*/ +#define ARIES_LINK_STRUCT_DETECTED_WIDTH_OFFSET 43 + +/** Offset to link rate member in link struct*/ +#define ARIES_LINK_STRUCT_RATE_OFFSET 6 + +/** Offset to link state member in link struct*/ +#define ARIES_LINK_STRUCT_STATE_OFFSET 10 + +/** Offset seperating 2 links in Aries Link struct */ +#define ARIES_LINK_STRUCT_LINK_SEP_OFFSET 126 + +/////////////////////////////////////////////////////////// +////////////////////// PMA registers ////////////////////// +/////////////////////////////////////////////////////////// + +/** PMA Slice 0 Cmd register address */ +#define ARIES_PMA_QS0_CMD_ADDRESS 0x4400 + +/** PMA Slice 0 Address_1 register address */ +#define ARIES_PMA_QS0_ADDR_1_ADDRESS 0x4401 + +/** PMA Slice 0 Address_0 register address */ +#define ARIES_PMA_QS0_ADDR_0_ADDRESS 0x4402 + +/** PMA Slice 0 Data_0 register address */ +#define ARIES_PMA_QS0_DATA_0_ADDRESS 0x4403 + +/** PMA Slice 0 Data_1 register address */ +#define ARIES_PMA_QS0_DATA_1_ADDRESS 0x4404 + +/** PMA offset for SUP_DIG_MPLLB_OVRD_IN_0 */ +#define ARIES_PMA_SUP_DIG_MPLLB_OVRD_IN_0 0x13 + +/** PMA offset for ARIES_PMA_SUP_DIG_RTUNE_DEBUG */ +#define ARIES_PMA_SUP_DIG_RTUNE_DEBUG 0x60 + +/** PMA offset for ARIES_PMA_SUP_DIG_RTUNE_STAT */ +#define ARIES_PMA_SUP_DIG_RTUNE_STAT 0x62 + +/** PMA offset for ARIES_PMA_SUP_ANA_RTUNE_CTRL */ +#define ARIES_PMA_SUP_ANA_RTUNE_CTRL 0xea + +/** PMA offset for SUP_ANA_SWITCH_MISC_MEAS */ +#define ARIES_PMA_SUP_ANA_SWITCH_MISC_MEAS 0xec + +/** PMA offset for ARIES_PMA_SUP_ANA_BG */ +#define ARIES_PMA_SUP_ANA_BG 0xed + +/** Reg offset for PMA register LANE_DIG_ASIC_TX_OVRD_IN_0 */ +#define ARIES_PMA_LANE_DIG_ASIC_TX_OVRD_IN_0 0x1001 + +/** Reg offset for PMA register LANE_DIG_ASIC_TX_ASIC_IN_0 */ +#define ARIES_PMA_LANE_DIG_ASIC_TX_ASIC_IN_0 0x100d + +/** Reg offset for PMA register LANE_DIG_ASIC_RX_OVRD_IN_0 */ +#define ARIES_PMA_LANE_DIG_ASIC_RX_OVRD_IN_0 0x1017 + +/** Reg offset for PMA register LANE_DIG_ASIC_RX_OVRD_IN_3 */ +#define ARIES_PMA_LANE_DIG_ASIC_RX_OVRD_IN_3 0x101a + +/** Reg offset for PMA register LANE_DIG_ASIC_RX_ASIC_IN_0 */ +#define ARIES_PMA_LANE_DIG_ASIC_RX_ASIC_IN_0 0x1028 + +/** Reg offset for PMA register LANE_DIG_ASIC_RX_ASIC_IN_1 */ +#define ARIES_PMA_LANE_DIG_ASIC_RX_ASIC_IN_1 0x1029 + +/** Reg offset for PMA register LANE_DIG_ASIC_RX_ASIC_OUT_0 */ +#define ARIES_PMA_LANE_DIG_ASIC_RX_ASIC_OUT_0 0x1033 + +/** Reg offset for PMA register LANE_DIG_TX_LBERT_CTL */ +#define ARIES_PMA_LANE_DIG_TX_LBERT_CTL 0x1072 + +/** Reg offset for PMA register LANE_DIG_RX_LBERT_CTL */ +#define ARIES_PMA_LANE_DIG_RX_LBERT_CTL 0x108c + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_ATT_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_ATT_STATUS 0x10ab + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_VGA_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_VGA_STATUS 0x10ac + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_CTLE_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_CTLE_STATUS 0x10ad + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_DFE_TAP1_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP1_STATUS 0x10ae + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_DFE_TAP2_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP2_STATUS 0x10af + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_DFE_TAP3_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP3_STATUS 0x10b0 + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_DFE_TAP4_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP4_STATUS 0x10b1 + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_DFE_TAP5_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP5_STATUS 0x10b2 + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_DFE_TAP6_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP6_STATUS 0x10ce + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_DFE_TAP7_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP7_STATUS 0x10cf + +/** Reg offset for PMA register LANE_DIG_RX_ADPTCTL_DFE_TAP8_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP8_STATUS 0x10d0 + +/** Reg offset for PMA register LANE_DIG_ANA_ROPLL_ANA_OUT_2 */ +#define ARIES_PMA_LANE_DIG_ANA_ROPLL_ANA_OUT_2 0x1132 + +/** Reg offset for PMA register LANE_ANA_RX_VREG_CTRL2 */ +#define ARIES_PMA_LANE_ANA_RX_VREG_CTRL2 0x11da + +/** Reg offset for PMA register RAWLANE_DIG_PCS_XF_RX_OVRD_IN_1 */ +#define ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_1 0x2006 + +/** Reg offset for PMA register RAWLANE_DIG_PCS_XF_RX_OVRD_IN_7 */ +#define ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_7 0x200c + +/** Reg offset for PMA register RAWLANE_DIG_PCS_XF_RX_PCS_OUT */ +#define ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_PCS_OUT 0x2019 + +/** Reg offset for PMA register RAWLANE_DIG_PCS_XF_RX_ADAPT_FOM */ +#define ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_ADAPT_FOM 0x201b + +/** Reg offset for PMA register RAWLANE_DIG_PCS_XF_ATE_OVRD_IN */ +#define ARIES_PMA_RAWLANE_DIG_PCS_XF_ATE_OVRD_IN 0x2020 + +/** Reg offset for PMA register RAWLANE_DIG_PCS_XF_RX_RECAL_BANK_OVRD */ +#define ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_RECAL_BANK_OVRD 0x2030 + +/** Reg offset for PMA register RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9 */ +#define ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9 0x203a + +/** Reg offset for PMA register ARIES_PMA_RAWLANE_DIG_FSM_FSM_OVRD_CTL */ +#define ARIES_PMA_RAWLANE_DIG_FSM_FSM_OVRD_CTL 0x2060 + +/** Reg offset for PMA register RAWLANE_DIG_RX_CTL_RX_ADAPT_MM_FOM */ +#define ARIES_PMA_RAWLANE_DIG_RX_CTL_RX_ADAPT_MM_FOM 0x2109 + +/** Reg offset for PMA register RAWLANE_DIG_RX_CTL_RX_MARGIN_ERROR */ +#define ARIES_PMA_RAWLANE_DIG_RX_CTL_RX_MARGIN_ERROR 0x212d + +/** Reg offset for PMA register RAWLANEAON_DIG_RX_ADPT_IQ */ +#define ARIES_PMA_RAWLANEAON_DIG_RX_ADPT_IQ 0x301d + +/** Reg offset for PMA register RAWLANEAON_DIG_RX_ADAPT_DONE */ +#define ARIES_PMA_RAWLANEAON_DIG_RX_ADAPT_DONE 0x301f + +/** Reg offset for PMA register RAWLANEAON_DIG_RX_ADPT_IQ_B1 */ +#define ARIES_PMA_RAWLANEAON_DIG_RX_ADPT_IQ_B1 0x3028 + +/** Reg offset for PMA register RAWLANEAON_DIG_RX_ADAPT_DONE_B1 */ +#define ARIES_PMA_RAWLANEAON_DIG_RX_ADAPT_DONE_B1 0x302a + +/** Reg offset for PMA register RAWLANEAON_DIG_RX_ADPT_IQ_B2 */ +#define ARIES_PMA_RAWLANEAON_DIG_RX_ADPT_IQ_B2 0x30c2 + +/** Reg offset for PMA register RAWLANEAON_DIG_RX_ADAPT_DONE_B2 */ +#define ARIES_PMA_RAWLANEAON_DIG_RX_ADAPT_DONE_B2 0x30c4 + +/////////////////////////////////////////////////////// +////////// Offsets for MM assisted accesses /////////// +/////////////////////////////////////////////////////// + +// Legacy Address and Data registers (using wide registers) +/** Reg offset to specify Address for MM assisted accesses */ +#define ARIES_MM_ASSIST_REG_ADDR_OFFSET 0xd99 + +/** Reg offset to specify Path ID for MM assisted accesses */ +#define ARIES_MM_ASSIST_PATH_ID_OFFSET 0xd9b + +/** Reg offset to specify Data for MM assisted accesses */ +#define ARIES_MM_ASSIST_DATA_OFFSET 0xd9c + +/** Reg offset to specify Command for MM assisted accesses */ +#define ARIES_MM_ASSIST_CMD_OFFSET 0xd9d + +/** Reg offset to specify Status for MM assisted accesses */ +#define ARIES_MM_ASSIST_STS_OFFSET 0xd9e + +// New Address and Data registers (not using wide registers) +/** Reg offset to MM SPARE 0 used specify Address[7:0] for MM assisted accesses + */ +#define ARIES_MM_ASSIST_SPARE_0_OFFSET 0xd9f + +/** Reg offset to MM SPARE 1 used specify Address[15:8] for MM assisted accesses + */ +#define ARIES_MM_ASSIST_SPARE_1_OFFSET 0xda0 + +/** Reg offset to MM SPARE 2 used specify Address[16] for MM assisted accesses + */ +#define ARIES_MM_ASSIST_SPARE_2_OFFSET 0xda1 + +/** Reg offset to MM SPARE 3 used specify Data Byte 0 for MM assisted accesses + */ +#define ARIES_MM_ASSIST_SPARE_3_OFFSET 0xda2 + +/** Reg offset to MM SPARE 4 used specify Data Byte 1 for MM assisted accesses + */ +#define ARIES_MM_ASSIST_SPARE_4_OFFSET 0xda3 + +/** Reg offset to MM SPARE 5 used specify Data Byte 2 for MM assisted accesses + */ +#define ARIES_MM_ASSIST_SPARE_5_OFFSET 0xda4 + +/** Reg offset to MM SPARE 6 used specify Data Byte 3 for MM assisted accesses + */ +#define ARIES_MM_ASSIST_SPARE_6_OFFSET 0xda5 + +/** Reg offset to MM SPARE 7 used specify Data Byte 4 for MM assisted accesses + */ +#define ARIES_MM_ASSIST_SPARE_7_OFFSET 0xda6 + +/////////////////////////////////////////////////////////// +////////////////////// Common offsets ///////////////////// +/////////////////////////////////////////////////////////// + +/** Address offset between between lanes */ +#define ARIES_PMA_LANE_STRIDE 0x200 + +/** Address offset between quad slices */ +#define ARIES_QS_STRIDE 0x4000 + +/** Address offset between between path wrappers */ +#define ARIES_PATH_WRP_STRIDE 0x1000 + +/** Address offset between between path lanes */ +#define ARIES_PATH_LANE_STRIDE 0x400 + +/////////////////////////////////////////////////////////// +//////////////////// Misc block offsets /////////////////// +/////////////////////////////////////////////////////////// + +/** PCIe Perst low register*/ +#define ARIES_PERST_N 0x604 + +/** Device Load check register */ +#define ARIES_CODE_LOAD_REG 0x605 + +/////////////////////////////////////////////////////////// +/////////////////// Hierarchical offsets ////////////////// +/////////////////////////////////////////////////////////// + +////////////////////// AL Top CSRs /////////////////////// + +/** AL Misc CSR Offset */ +#define ARIES_MISC_CSR_OFFSET 0x0 + +/** AL Pseudo Port 0 CSR Offset */ +#define ARIES_MISC_AL_PSEUDO_PORT_0_CSR_OFFSET 0x608 + +/** AL Pseudo Port 1 CSR Offset */ +#define ARIES_MISC_AL_PSEUDO_PORT_1_CSR_OFFSET 0x778 + +/** AL Main Micro CSR Offset */ +#define ARIES_MAIN_MICRO_CSR_OFFSET 0xc00 + +/** AL Quad Slice 0 Offset */ +#define ARIES_QS_0_CSR_OFFSET 0x4000 + +/** AL Quad Slice 1 Offset */ +#define ARIES_QS_1_CSR_OFFSET 0x8000 + +/** AL Quad Slice 2 Offset */ +#define ARIES_QS_2_CSR_OFFSET 0xc000 + +/** AL Quad Slice 3 Offset */ +#define ARIES_QS_3_CSR_OFFSET 0x10000 + +/////////////////// AL Quad Slice CSRs //////////////////// + +/** AL Path Wrapper 0 CSR Offset */ +#define ARIES_PATH_WRAPPER_0_CSR_OFFSET 0x0 + +/** AL Path Wrapper 1 CSR Offset */ +#define ARIES_PATH_WRAPPER_1_CSR_OFFSET 0x1000 + +/** AL Path Wrapper 2 CSR Offset */ +#define ARIES_PATH_WRAPPER_2_CSR_OFFSET 0x2000 + +/** AL Path Wrapper 3 CSR Offset */ +#define ARIES_PATH_WRAPPER_3_CSR_OFFSET 0x3000 + +/////////////////// AL Path Wrapper CSRs //////////////////// + +/** Path Micro CSR Offset */ +#define ARIES_PATH_MICRO_CSR_OFFSET 0x0 + +/** Pac CSR Offset */ +#define ARIES_PAC_CSR_OFFSET 0x400 + +/** Path Global CSR Offset */ +#define ARIES_PATH_GLOBAL_CSR_OFFSET 0x600 + +/** AL Path Lane 0 CSR offset */ +#define ARIES_PATH_LANE_0_CSR_OFFSET 0x800 + +/** AL Path Lane 1 CSR offset */ +#define ARIES_PATH_LANE_1_CSR_OFFSET 0xc00 + +/////////////////// AL Path Global CSRs //////////////////// + +/** Reg offset for MAC-to-PHY rate and pclk_rate overrides */ +#define ARIES_GBL_CSR_MAC_PHY_RATE_AND_PCLK_RATE 0xe + +/////////////////// AL Path Lane X CSRs //////////////////// + +/** Captured Lane number reg offset */ +#define ARIES_LN_CAPT_NUM 0x6 + +/** MAC-to-PHY Tx equalization setting override */ +#define ARIES_MAC_PHY_TXDEEMPH 0x11c + +/** MAC-to-PHY Rx polarity override */ +#define ARIES_MAC_RX_POLARITY 0x121 + +/** Deskew delta in pclk cycles */ +#define ARIES_DSK_CC_DELTA 0x160 + +/** Deskew status */ +#define ARIES_DESKEW_STATUS 0x161 + +/** MAC-to-PHY Tx deemph idle observe */ +#define ARIES_MAC_PHY_TXDEEMPH_OB 0x17c + +/** RET_PTH Next State reg offset */ +#define ARIES_RET_PTH_NEXT_STATE_OFFSET 0x61d + +/** Reg offset for PIPE RxStandby */ +#define ARIES_RET_PTH_LN_MAC_PHY_RXSTANDBY_ADDR 0x4922 + +/** Reg offset for PIPE Powerdown */ +#define ARIES_RET_PTH_GBL_MAC_PHY_POWERDOWN_ADDR 0x4609 + +/** Reg offset for PIPE BlockAlignControl */ +#define ARIES_RET_PTH_GBL_MAC_PHY_BLOCKALIGNCONTROL_ADDR 0x460c + +/** Reg offset for PIPE Rate and PCLK Rate */ +#define ARIES_RET_PTH_GBL_MAC_PHY_RATE_AND_PCLK_RATE_ADDR 0x460e + +/** Reg offset for PIPE TxDataValid */ +#define ARIES_RET_PTH_GBL_MAC_PHY_TXDATAVALID_ADDR 0x4610 + +/** Reg offset for PIPE TxElecIdle */ +#define ARIES_RET_PTH_GBL_MAC_PHY_TXELECIDLE_ADDR 0x4611 + +/** Reg offset for PIPE Rx termination */ +#define ARIES_RET_PTH_LN_PCS_RX_TERMINATION_ADDR 0x48d8 + +/** Reg offset for PIPE Rx Eq in progress */ +#define ARIES_RET_PTH_LN_MAC_PHY_RXEQINPROGRESS 0x491a + +/** Reg offset for PIPE Tx deemphasis */ +#define ARIES_RET_PTH_LN_MAC_PHY_TXDEEMPH_ADDR 0x491c + +/** Reg offset for PIPE Tx deemphasis observe */ +#define ARIES_RET_PTH_LN_MAC_PHY_TXDEEMPH_OB_ADDR 0x497c + +/** Reg offset for PIPE Rx polarity */ +#define ARIES_RET_PTH_LN_MAC_PHY_RXPOLARITY_ADDR 0x4921 + +/** Reg offset for PIPE RxEqEval */ +#define ARIES_RET_PTH_LN_MAC_PHY_RXEQEVAL_ADDR 0x4927 + +/** Reg offset for PIPE PhyStatus */ +#define ARIES_RET_PTH_LN_PHY_MAC_PHYSTATUS_ADDR 0x4981 + +/** Reg offset for PIPE FomFeedback */ +#define ARIES_RET_PTH_LN_PHY_MAC_FOMFEEDBACK_ADDR 0x498c + +#endif diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_api.c b/common/recipes-lib/retimer-v2.16.2/files/aries_api.c new file mode 100755 index 000000000000..7d59e09fa533 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_api.c @@ -0,0 +1,2904 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_api.c + * @brief Implementation of public functions for the SDK. + */ + +#include "aries_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Return the SDK version + * + * @return const char* - SDK version number + */ +const char* ariesGetSDKVersion(void) +{ + return ARIES_SDK_VERSION; +} + +/** + * @brief Initialize Aries device data structure + * + * Capture the FW version, device id, vendor id and revision id and store + * the values in the device struct + * + * BMC should wait until the Retimer has had the opportunity to exit reset and + * load firmware before initializing the ariesDevice object with the + * ariesInitDevice() API. This can be accomplished by any of the following: + * - Wait until the device’s GPIO0 pin is high, indicating FW load is complete + * - Wait >1.8 seconds after the device’s RESET_N pin is de-asserted low and + * REFCLK is present + * - Check with the ariesFWStatusCheck(AriesDeviceType* device) API + * + * If the BMC cannot implement these checks, then BMC can poll for device ready + * status purely in software: + * - Run the ariesInitDevice() API + * - If FW has not yet loaded successfully, then wait for 2 seconds before + * calling ariesInitDevice() again. FW load successful is indicated by: + * + device->mmHeartbeatOkay, and + * + (device->fwVersion.major != 0) || (device->fwVersion.minor != 0) || + * (device->fwVersion.build != 0) + * - Do not call ariesInitDevice() after it has been established that FW is + * loaded, as this would be a redundant + * - ariesInitDevice() need not (and should not) be called again after it is + * determined FW has loaded. Device monitoring can proceed once it is + * established that FW has loaded successfully. + * + * @param[in,out] device Aries device struct + * @param[in] recoveryAddr Desired I2C (7-bit) address in case ARP needs + * to be run + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesInitDevice(AriesDeviceType* device, uint8_t recoveryAddr) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t dataWord[2] = {0}; + uint8_t dataBytes[4] = {0}; + + rc = ariesCheckConnectionHealth(device, recoveryAddr); + CHECK_SUCCESS(rc); + + device->i2cDriver->mmWideRegisterValid = 0; + // Set lock = 0 (if it hasnt been set before) + if (device->i2cDriver->lockInit == 0) + { + device->i2cDriver->lock = 0; + device->i2cDriver->lockInit = 1; + } + + // Initialize eeprom struct by setting all values to default values + device->eeprom.pageSize = ARIES_EEPROM_PAGE_SIZE; + device->eeprom.pageCount = ARIES_EEPROM_PAGE_COUNT; + device->eeprom.maxBurstSize = ARIES_MAX_BURST_SIZE; + + // Initialize default FW update parameters + device->eeprom.blockBaseAddr = ARIES_EEPROM_BLOCK_BASE_ADDR_WIDE; + device->eeprom.blockCmdModifier = ARIES_EEPROM_BLOCK_CMD_MODIFIER_WIDE; + device->eeprom.blockWriteSize = ARIES_EEPROM_BLOCK_WRITE_SIZE_WIDE; + + rc = ariesFWStatusCheck(device); + CHECK_SUCCESS(rc); + + // FW update optimizations made starting with FW 1.24.0 and later + if (ariesFirmwareIsAtLeast(device, 1, 24, 0)) + { + // Use optimized FW update parameters + device->eeprom.blockBaseAddr = ARIES_EEPROM_BLOCK_BASE_ADDR_NOWIDE; + device->eeprom.blockCmdModifier = + ARIES_EEPROM_BLOCK_CMD_MODIFIER_NOWIDE; + device->eeprom.blockWriteSize = ARIES_EEPROM_BLOCK_WRITE_SIZE_NOWIDE; + } + + // MM Wide Register access improvements starting with FW 2.2.0 and later + if (ariesFirmwareIsAtLeast(device, 2, 2, 0)) + { + device->i2cDriver->mmWideRegisterValid = 1; + } + + // Determine the device revision from registers + rc = ariesDeviceRevisionGet(device); + CHECK_SUCCESS(rc); + + // Get link_path_struct size + // Prior to FW 1.1.52, this size is 38 + device->linkPathStructSize = ARIES_LINK_PATH_STRUCT_SIZE; + if (ariesFirmwareIsAtLeast(device, 1, 1, 52)) + { + rc = ariesReadBlockDataMainMicroIndirect( + device->i2cDriver, ARIES_LINK_PATH_STRUCT_SIZE_ADDR, 1, dataByte); + CHECK_SUCCESS(rc); + device->linkPathStructSize = dataByte[0]; + } + + if (device->mmHeartbeatOkay) + { + // Capture vendor id and device id + rc = ariesReadWideRegister(device->i2cDriver, ARIES_GLB_PARAM_REG1_ADDR, + 4, dataBytes); + CHECK_SUCCESS(rc); + device->vendorId = ((dataBytes[3] << 8) + dataBytes[2]); + device->deviceId = dataBytes[1]; + + // Get the al print info struct offset for Main Micro + rc = ariesReadBlockDataMainMicroIndirect( + device->i2cDriver, + (ARIES_MAIN_MICRO_FW_INFO + ARIES_MM_AL_PRINT_INFO_STRUCT_ADDR), 2, + dataWord); + CHECK_SUCCESS(rc); + device->mm_print_info_struct_addr = AL_MAIN_SRAM_DMEM_OFFSET + + (dataWord[1] << 8) + dataWord[0]; + + // Get the gp ctrl status struct offset for Main Micro + rc = ariesReadBlockDataMainMicroIndirect( + device->i2cDriver, + (ARIES_MAIN_MICRO_FW_INFO + ARIES_MM_GP_CTRL_STS_STRUCT_ADDR), 2, + dataWord); + CHECK_SUCCESS(rc); + device->mm_gp_ctrl_sts_struct_addr = AL_MAIN_SRAM_DMEM_OFFSET + + (dataWord[1] << 8) + dataWord[0]; + + // Get AL print info struct address for path micros + // All Path Micros will have same address, so get for PM 4 (present on + // both x16 and x8 devices) + rc = ariesReadBlockDataPathMicroIndirect( + device->i2cDriver, 4, + (ARIES_PATH_MICRO_FW_INFO_ADDRESS + + ARIES_PM_AL_PRINT_INFO_STRUCT_ADDR), + 2, dataWord); + CHECK_SUCCESS(rc); + device->pm_print_info_struct_addr = AL_PATH_SRAM_DMEM_OFFSET + + (dataWord[1] << 8) + dataWord[0]; + + // Get GP ctrl status struct address for path micros + // All Path Micros will have same address, so get for PM 4 (present on + // both x16 and x8 devices) + rc = ariesReadBlockDataPathMicroIndirect( + device->i2cDriver, 4, + (ARIES_PATH_MICRO_FW_INFO_ADDRESS + + ARIES_PM_GP_CTRL_STS_STRUCT_ADDR), + 2, dataWord); + CHECK_SUCCESS(rc); + device->pm_gp_ctrl_sts_struct_addr = AL_PATH_SRAM_DMEM_OFFSET + + (dataWord[1] << 8) + dataWord[0]; + + rc = ariesGetTempCalibrationCodes(device); + CHECK_SUCCESS(rc); + } + + rc = ariesGetPinMap(device); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Initializes array of link structs + * + * Takes an array of AriesLinkType structs and populates the first N indices + * for each link the device has been bifurcated into via FW (max 8 links per + * device). Also returns the number of links for the device's bifurcation. + * + * @param[in] device Aries device struct + * @param[out] links Array of link structs to be populated + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesCreateLinkStructs(AriesDeviceType* device, + AriesLinkType* links) +{ + AriesErrorType rc; + AriesBifurcationType bifMode; + AriesBifurcationParamsType bif; + int i; + + rc = ariesGetBifurcationMode(device, &bifMode); + CHECK_SUCCESS(rc); + + bif = bifurcationModes[bifMode]; + device->numLinks = bif.numLinks; + + for (i = 0; i < bif.numLinks; i++) + { + links[i].device = device; + links[i].config.partNumber = device->partNumber; + links[i].config.maxWidth = bif.links[i].linkWidth; + links[i].config.startLane = bif.links[i].startLane; + links[i].config.linkId = bif.links[i].linkId; + } + return ARIES_SUCCESS; +} + +/** + * @brief Check status of FW loaded + * + * Check the code load register and the Main Micro heartbeat. If all + * is good, then read the firmware version + * + * @param[in,out] device Aries device struct + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesFWStatusCheck(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t dataWord[2] = {0}; + uint8_t dataBytes[4] = {0}; + + // Read Code Load reg + rc = ariesReadBlockData(device->i2cDriver, ARIES_CODE_LOAD_REG, 1, + dataBytes); + CHECK_SUCCESS(rc); + + if (dataBytes[0] < 0xe) + { + ASTERA_WARN("Code Load reg unexpected. Not all modules are loaded"); + device->codeLoadOkay = false; + } + else + { + device->codeLoadOkay = true; + } + + // Check Main Micro heartbeat + // If heartbeat value does not change for 100 tries, no MM heartbeat + // Else heartbeat present even if one value changes + uint8_t numTries = 100; + uint8_t tryIndex = 0; + uint8_t heartbeatVal; + device->mmHeartbeatOkay = false; + rc = ariesReadByteData(device->i2cDriver, ARIES_MM_HEARTBEAT_ADDR, + dataByte); + CHECK_SUCCESS(rc); + heartbeatVal = dataByte[0]; + while (tryIndex < numTries) + { + rc = ariesReadByteData(device->i2cDriver, ARIES_MM_HEARTBEAT_ADDR, + dataByte); + CHECK_SUCCESS(rc); + if (dataByte[0] != heartbeatVal) + { + device->mmHeartbeatOkay = true; + break; + } + tryIndex++; + } + + // Read FW version + // If heartbeat not there, set default FW values to 0.0.0 + // and return ARIES_SUCCESS + device->fwVersion.major = 0; + device->fwVersion.minor = 0; + device->fwVersion.build = 0; + if (device->mmHeartbeatOkay) + { + // Get FW version (major) + rc = ariesReadBlockDataMainMicroIndirect( + device->i2cDriver, + (ARIES_MAIN_MICRO_FW_INFO + ARIES_MM_FW_VERSION_MAJOR), 1, + dataByte); + CHECK_SUCCESS(rc); + device->fwVersion.major = dataByte[0]; + + // Get FW version (minor) + rc = ariesReadBlockDataMainMicroIndirect( + device->i2cDriver, + (ARIES_MAIN_MICRO_FW_INFO + ARIES_MM_FW_VERSION_MINOR), 1, + dataByte); + CHECK_SUCCESS(rc); + device->fwVersion.minor = dataByte[0]; + + // Get FW version (build) + rc = ariesReadBlockDataMainMicroIndirect( + device->i2cDriver, + (ARIES_MAIN_MICRO_FW_INFO + ARIES_MM_FW_VERSION_BUILD), 2, + dataWord); + CHECK_SUCCESS(rc); + device->fwVersion.build = (dataWord[1] << 8) + dataWord[0]; + + rc = ariesComplianceFWGet(device); + CHECK_SUCCESS(rc); + } + else + { + ASTERA_WARN("No Main Micro Heartbeat"); + } + + return ARIES_SUCCESS; +} + +/** + * @brief Get the retimer device revsion + * + * Updates the AriesDevice struct revNumber variable + * 0 = MPW|"A-Step", 1 = A0|"B-Step", 2 = B0|"C-Step" + * + * @param[in,out] device Aries device struct + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesDeviceRevisionGet(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + dataByte[0] = 0; + rc = ariesReadByteData(device->i2cDriver, 0x949, dataByte); + CHECK_SUCCESS(rc); + + if (dataByte[0] == 0x64) + { + rc = ariesReadByteData(device->i2cDriver, 0x924, dataByte); + CHECK_SUCCESS(rc); + if ((dataByte[0] & 1) == 1) + { + device->revNumber = 2; + } + else + { + device->revNumber = 1; + } + } + else + { + device->revNumber = 0; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Checks if Aries device is running compliance FW + * + * Checks the ate_customer_board and self_test compile options to determine + * if compliance FW is running on the device, rather than mission-mode FW. + * + * @param[in,out] device Aries device struct + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesComplianceFWGet(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t fw_ate_customer_board = 0; + uint8_t fw_self_test = 0; + + rc = ariesReadBlockDataMainMicroIndirect( + device->i2cDriver, + (ARIES_MAIN_MICRO_FW_INFO + ARIES_MM_FW_ATE_CUSTOMER_BOARD), 1, + &fw_ate_customer_board); + CHECK_SUCCESS(rc); + rc = ariesReadBlockDataMainMicroIndirect( + device->i2cDriver, (ARIES_MAIN_MICRO_FW_INFO + ARIES_MM_FW_SELF_TEST), + 1, &fw_self_test); + CHECK_SUCCESS(rc); + + device->fwVersion.isComplianceFW = (fw_ate_customer_board || fw_self_test); + return ARIES_SUCCESS; +} + +/** + * @brief Update the EEPROM page variables + * + * This is only necessariy if a 512 kbit EEPROM is being used + * + * @param[in, out] device Aries device struct + * @param[in] pageSize Page size of EEPROMs in bytes + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesSetEEPROMPageSize(AriesDeviceType* device, int pageSize) +{ + // pageSize and maxBurstSize are the same + device->eeprom.pageSize = pageSize; + device->eeprom.maxBurstSize = pageSize; + device->eeprom.pageCount = ARIES_EEPROM_MAX_NUM_BYTES / pageSize; + + return ARIES_SUCCESS; +} + +/** + * @brief Set the bifurcation mode + * + * Bifurcation mode is written to global parameter register 0. Returns a + * negative error code, else zero on success. + * + * @param[in] device Struct containing device information + * @param[in] bifur Bifurcation mode + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesSetBifurcationMode(AriesDeviceType* device, + AriesBifurcationType bifur) +{ + AriesErrorType rc; + uint8_t dataBytes[4] = {0}; + + rc = ariesReadWideRegister(device->i2cDriver, ARIES_GLB_PARAM_REG0_ADDR, 4, + dataBytes); + CHECK_SUCCESS(rc); + + // Bifurcation setting is in bits 12:7 + dataBytes[0] = ((bifur & 0x01) << 7) | (dataBytes[0] & 0x7f); + dataBytes[1] = ((bifur & 0x3e) >> 1) | (dataBytes[1] & 0xe0); + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_GLB_PARAM_REG0_ADDR, 4, + dataBytes); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Get the bifurcation mode + * + * Bifurcation mode is read from the global parameter register 0. Returns a + * negative error code, else zero on success. + * + * @param[in] device Struct containing device information + * @param[in] bifur Pointer to bifurcation mode variable + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetBifurcationMode(AriesDeviceType* device, + AriesBifurcationType* bifur) +{ + AriesErrorType rc; + uint8_t dataBytes[4] = {0}; + + rc = ariesReadWideRegister(device->i2cDriver, ARIES_GLB_PARAM_REG0_ADDR, 4, + dataBytes); + CHECK_SUCCESS(rc); + *bifur = (AriesBifurcationType) (((dataBytes[1] & 0x1f) << 1) + ((dataBytes[0] & 0x80) >> 7)); + + return ARIES_SUCCESS; +} + +/** + * @brief Set the Retimer HW Reset. + * + * This function sets the register to assert reset (1) or de-assert reset (0). + * Asserting reset would set the retimer in reset, and de-asserting it will + * bring the retimer out of reset and cause a firmware reload. User must wait + * 1ms between assertion and de-assertion + * + * @param[in] device Struct containing device information + * @param[in] reset Reset assert (1) or de-assert (0) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesSetHwReset(AriesDeviceType* device, uint8_t reset) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + + if (reset == 1) // Put retimer into reset + { + // Disable MM assisted wide register accesses since MM is in reset + device->i2cDriver->mmWideRegisterValid = 0; + // Assert full device reset + dataWord[0] = 0xff; + dataWord[1] = 0x06; + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_HW_RST_ADDR, 2, + dataWord); + CHECK_SUCCESS(rc); + } + else if (reset == 0) // Take retimer out of reset (FW will reload) + { + // De-assert full device reset + dataWord[0] = 0x0; + dataWord[1] = 0x0; + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_HW_RST_ADDR, 2, + dataWord); + CHECK_SUCCESS(rc); + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Set the Main Micro and I2C Master Reset. + * + * This function sets the register to assert reset (1) or de-assert reset (0). + * Asserting the MM reset will halt firmware execution and de-asserting will + * cause firmware to reload + * Asserting the I2C master reset can be used to keep the Retimer from sending + * transactions on the I2C bus connected to the EEPROM + * + * @param[in] device Struct containing device information + * @param[in] reset Reset assert (1) or de-assert (0) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesSetMMI2CMasterReset(AriesDeviceType* device, uint8_t reset) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + + if (reset == 1) // Put MM and I2C Master into reset + { + // Disable MM assisted wide register accesses since MM is in reset + device->i2cDriver->mmWideRegisterValid = 0; + // Assert MM reset + dataWord[0] = 0x0; + dataWord[1] = 0x4; + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_SW_RST_ADDR, 2, + dataWord); + CHECK_SUCCESS(rc); + // Assert MM reset and I2C reset + dataWord[0] = 0x0; + dataWord[1] = 0x6; + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_SW_RST_ADDR, 2, + dataWord); + CHECK_SUCCESS(rc); + // Assert MM reset (de-assert I2C reset) + dataWord[0] = 0x0; + dataWord[1] = 0x4; + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_SW_RST_ADDR, 2, + dataWord); + CHECK_SUCCESS(rc); + } + else if (reset == 0) // Take MM and I2C Master out of reset + { + // De-assert MM reset + dataWord[0] = 0x0; + dataWord[1] = 0x0; + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_SW_RST_ADDR, 2, + dataWord); + CHECK_SUCCESS(rc); + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Set the I2C Master Reset. + * + * This function sets the register to assert reset (1) or de-assert reset (0). + * Asserting the I2C master reset can be used to keep the Retimer from sending + * tranactions on the I2C bus connected to the EEPROM + * + * @param[in] device Struct containing device information + * @param[in] reset Reset assert (1) or de-assert (0) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesSetI2CMasterReset(AriesDeviceType* device, uint8_t reset) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + + if (reset == 1) // Put I2C Master into reset + { + // Assert I2C reset + dataWord[0] = 0x0; + dataWord[1] = 0x2; + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_SW_RST_ADDR, 2, + dataWord); + CHECK_SUCCESS(rc); + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_HW_RST_ADDR, 2, + dataWord); + CHECK_SUCCESS(rc); + } + else if (reset == 0) // Take I2C Master out of reset + { + // De-assert I2C reset + dataWord[0] = 0x0; + dataWord[1] = 0x0; + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_HW_RST_ADDR, 2, + dataWord); + CHECK_SUCCESS(rc); + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_SW_RST_ADDR, 2, + dataWord); + CHECK_SUCCESS(rc); + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Update the FW image in the EEPROM connected to the Retimer. + * + * The firmware image specififed by filename will be written to the EEPROM + * attached to the Retimer then verified using the optimal method. + * + * @param[in] device Struct containing device information + * @param[in] filename Filename of the file containing the firmware + * @param[in] fileType Enum specifying firmware image file type (IHX or BIN) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesUpdateFirmware(AriesDeviceType* device, + const char* filename, + AriesFWImageFormatType fileType) +{ + AriesErrorType rc; + //uint8_t image[ARIES_EEPROM_MAX_NUM_BYTES] = {0}; + uint8_t* image = calloc(ARIES_EEPROM_MAX_NUM_BYTES, sizeof(uint8_t)); + if (image == NULL) + { + ASTERA_ERROR("Failed to allocate memmory"); + return ARIES_FAILURE; + } + + if (fileType == ARIES_FW_IMAGE_FORMAT_IHX) + { + // Load ihx file + rc = ariesLoadIhxFile(filename, image); + if (rc != ARIES_SUCCESS) + { + ASTERA_ERROR("Failed to load the .ihx file. RC = %d", rc); + free(image); + return ARIES_FAILURE; + } + } + else if (fileType == ARIES_FW_IMAGE_FORMAT_BIN) + { + // Load bin file + rc = ariesLoadBinFile(filename, image); + if (rc != ARIES_SUCCESS) + { + ASTERA_ERROR("Failed to load the .bin file. RC = %d", rc); + free(image); + return ARIES_FAILURE; + } + } + else + { + ASTERA_ERROR("Invalid Aries FW image format type"); + free(image); + return ARIES_INVALID_ARGUMENT; + } + + rc = ariesUpdateFirmwareViaBuffer(device, image); + free(image); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Update the FW image in the EEPROM connected to the Retimer via buffer + * + * The firmware image preloaded into a buffer will be written to the EEPROM + * attached to the Retimer then verified using the optimal method. + * + * @param[in] device Struct containing device information + * @param[in] image Pointer to buffer of bytes containing image + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesUpdateFirmwareViaBuffer(AriesDeviceType* device, + uint8_t* image) +{ + AriesErrorType rc; + bool legacyMode = false; + bool checksumVerifyFailed = false; + + // Enable legacy mode if ARP is enabled or not running valid FW + if (device->arpEnable || !device->mmHeartbeatOkay || + device->fwVersion.isComplianceFW) + { + legacyMode = true; + } + + // Program EEPROM image + rc = ariesWriteEEPROMImage(device, image, legacyMode); + if (rc != ARIES_SUCCESS) + { + ASTERA_ERROR("Failed to program the EEPROM. RC = %d", rc); + } + + if (!legacyMode) + { + // Verify EEPROM programming by reading EEPROM and computing a checksum + rc = ariesVerifyEEPROMImageViaChecksum(device, image); + if (rc != ARIES_SUCCESS) + { + ASTERA_ERROR("Failed to verify the EEPROM using checksum. RC = %d", + rc); + checksumVerifyFailed = true; + } + } + + // If the EEPROM verify via checksum failed, attempt the byte by byte verify + // Optionally, it can be manually enabled by sending a 1 as the 4th argument + if (legacyMode || checksumVerifyFailed) + { + if (legacyMode) + { + // To massively speed up verify process, reset the device and check + // for a heartbeat. If the device is alive we can do a main micro + // assisted byte-by-byte verify, otherwise we need to do a legacy + // byte-by-byte verify which takes a long time. + ASTERA_INFO("Attempting device reset"); + rc = ariesSetHwReset(device, 1); + CHECK_SUCCESS(rc); + // Wait 10 ms before de-asserting + usleep(10000); + // De-assert HW reset + rc = ariesSetHwReset(device, 0); + CHECK_SUCCESS(rc); + usleep(10000); + // Wait for 5s before checking heartbeat + usleep(5000000); + // Check for heartbeat + rc = ariesFWStatusCheck(device); + CHECK_SUCCESS(rc); + // If we have a valid heartbeat disable legacy mode + if (device->mmHeartbeatOkay) + { + ASTERA_INFO( + "Heartbeat detected, using main micro assisted verify"); + legacyMode = false; + } + else + { + ASTERA_INFO("Heartbeat NOT detected, using legacy verify"); + } + } + // Verify EEPROM programming by reading EEPROM and comparing data with + // expected image. In case there is a failure, the API will attempt a + // rewrite once + rc = ariesVerifyEEPROMImage(device, image, legacyMode); + if (rc != ARIES_SUCCESS) + { + ASTERA_ERROR("Failed to read and verify the EEPROM. RC = %d", rc); + return rc; + } + } + + device->fwUpdateProg = ARIES_FW_UPDATE_PROGRESS_COMPLETE; + + return ARIES_SUCCESS; +} + +/** + * @brief Get the progress of the FW update in percent complete. + * + * @param[in] device Struct containing device information + * @param[in] percentComplete Pointer to percent complete variable + * + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesFirmwareUpdateProgress(AriesDeviceType* device, + uint8_t* percentComplete) +{ + *percentComplete = 100 * device->fwUpdateProg / + (ARIES_FW_UPDATE_PROGRESS_COMPLETE - + ARIES_FW_UPDATE_PROGRESS_START + 1); + + return ARIES_SUCCESS; +} + +/** + * @brief Load a FW image into the EEPROM connected to the Retimer. + * + * numBytes from the values[] buffer will be written to the EEPROM attached + * to the Retimer. + * + * @param[in] device Struct containing device information + * @param[in] values Pointer to byte array containing the data to be + * written to the EEPROM + * @param[in] legacyMode If true, write EEPROM in slower legacy mode + * (set this flag in error scenarios, when you wish to + * write EEPROM without using faster Main Micro + * assisted writes). Please set to false otherwise. + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteEEPROMImage(AriesDeviceType* device, uint8_t* values, + bool legacyMode) +{ + int currentPage = 0; + AriesErrorType rc; + + // Deassert HW/SW resets + rc = ariesSetHwReset(device, 0); + CHECK_SUCCESS(rc); + + // Update device FW update progress state + device->fwUpdateProg = ARIES_FW_UPDATE_PROGRESS_WRITE_0; + + // If operating in legacy mode, put MM in reset + if (legacyMode) + { + rc = ariesSetMMI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + } + else + { + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + rc = ariesSetI2CMasterReset(device, 0); + CHECK_SUCCESS(rc); + } + + rc = ariesI2cMasterSoftReset(device->i2cDriver); + CHECK_SUCCESS(rc); + usleep(2000); + + int addr = 0; + int burst; + int eepromWriteDelta = 0; + int addrFlag = -1; + int addrDiff = 0; + int addrDiffDelta = 0; + + time_t start_t, end_t; + + uint8_t data[device->eeprom.maxBurstSize]; + int addrMSB = 0; + int addrI2C = 0; + + int eepromEnd; + int eepromEndLoc = ariesGetEEPROMImageEnd(values); + eepromEnd = eepromEndLoc; + + // Update EEPROM end index to match end of valid portion of EEPROM + if (eepromEndLoc == -1) + { + eepromEnd = ARIES_EEPROM_MAX_NUM_BYTES; + } + else + { + eepromEnd += 8; + eepromWriteDelta = eepromEnd % device->eeprom.blockWriteSize; + if (eepromWriteDelta) + { + eepromEnd += device->eeprom.blockWriteSize - eepromWriteDelta; + } + // Calculate the location of the last 256 bytes + addrDiff = (eepromEnd % device->eeprom.pageSize); + addrFlag = eepromEnd - addrDiff; + // The last write needs to be 16 bytes, so set location accordingly + addrDiffDelta = addrDiff % device->eeprom.blockWriteSize; + if (addrDiffDelta) + { + addrDiff += device->eeprom.blockWriteSize - addrDiffDelta; + } + } + + // Start timer + time(&start_t); + + // Init I2C Master + rc = ariesI2CMasterInit(device->i2cDriver); + CHECK_SUCCESS(rc); + + // Set Page address + rc = ariesI2CMasterSetPage(device->i2cDriver, currentPage); + CHECK_SUCCESS(rc); + + bool mainMicroWriteAssist = false; + if (!legacyMode) + { + if (ariesFirmwareIsAtLeast(device, 1, 0, 48)) + { + mainMicroWriteAssist = true; + } + } + + if ((!legacyMode) && mainMicroWriteAssist) + { + ASTERA_INFO("Starting Main Micro assisted EEPROM write"); + + while (addr < eepromEnd) + { + // Set MSB and local addresses for this page + addrMSB = addr / 65536; + addrI2C = addr % 65536; + + if (addrMSB != currentPage) + { + // Increment page num when you increment MSB + rc = ariesI2CMasterSetPage(device->i2cDriver, addrMSB); + CHECK_SUCCESS(rc); + currentPage = addrMSB; + } + + if (!(addrI2C % 8192)) + { + ASTERA_INFO("Slv: 0x%02x, Reg: 0x%04x", (0x50 + addrMSB), + addrI2C); + } + + // Send blocks of data (defined by burst size) to speed up process + burst = 0; + while (burst < device->eeprom.pageSize) + { + int addrBurst = addrI2C + burst; + int indx; + // In last iteration, no need to write all 256 bytes + if (addr == addrFlag) + { + for (indx = 0; indx < addrDiff; indx++) + { + data[indx] = values[(addr + burst + indx)]; + } + // Send a block of bytes to the EEPROM starting at address + // addrBurst + rc = ariesI2CMasterMultiBlockWrite(device, addrBurst, + addrDiff, data); + CHECK_SUCCESS(rc); + } + else + { + for (indx = 0; indx < device->eeprom.maxBurstSize; indx++) + { + data[indx] = values[(addr + burst + indx)]; + } + // Send a block of bytes to the EEPROM starting at address + // addrBurst + rc = ariesI2CMasterMultiBlockWrite( + device, addrBurst, device->eeprom.maxBurstSize, data); + CHECK_SUCCESS(rc); + } + usleep(ARIES_DATA_BLOCK_PROGRAM_TIME_USEC); + burst += device->eeprom.maxBurstSize; + } + + // Update address + addr += device->eeprom.pageSize; + + // Calcualte and update progress + uint8_t prog = 10 * addr / eepromEnd; + device->fwUpdateProg = (AriesFWUpdateProgressType) (ARIES_FW_UPDATE_PROGRESS_WRITE_0 + prog); + } + } + else // Block writes not supported here. Must write one byte a a time + { + ASTERA_INFO("Starting legacy mode EEPROM write"); + + while (addr < eepromEnd) + { + // Set MSB and local addresses for this page + addrMSB = addr / 65536; + addrI2C = addr % 65536; + + if (addrMSB != currentPage) + { + // Increment page num when you increment MSB + rc = ariesI2CMasterSetPage(device->i2cDriver, addrMSB); + CHECK_SUCCESS(rc); + currentPage = addrMSB; + } + + if (!(addrI2C % 8192)) + { + ASTERA_INFO("Slv: 0x%02x, Reg: 0x%04x", (0x50 + addrMSB), + addrI2C); + } + + // Send blocks of data (defined by burst size) to speed up process + burst = 0; + while (burst < device->eeprom.pageSize) + { + int addrBurst = addrI2C + burst; + int indx; + // In last iteration, no need to write all 256 bytes + if (addr == addrFlag) + { + for (indx = 0; indx < addrDiff; indx++) + { + data[indx] = values[(addr + burst + indx)]; + } + // Send a block of bytes to the EEPROM starting at address + // addrBurst + rc = ariesI2CMasterSendByteBlockData( + device->i2cDriver, addrBurst, addrDiff, data); + CHECK_SUCCESS(rc); + } + else + { + for (indx = 0; indx < device->eeprom.maxBurstSize; indx++) + { + data[indx] = values[(addr + burst + indx)]; + } + // Send bytes to the EEPROM starting at address + // addrBust + rc = ariesI2CMasterSendByteBlockData( + device->i2cDriver, addrBurst, + device->eeprom.maxBurstSize, data); + CHECK_SUCCESS(rc); + } + usleep(ARIES_DATA_BLOCK_PROGRAM_TIME_USEC); + burst += device->eeprom.maxBurstSize; + } + + // Update address + addr += device->eeprom.pageSize; + + // Calcualte and update progress + uint8_t prog = 10 * addr / eepromEnd; + device->fwUpdateProg = (AriesFWUpdateProgressType) (ARIES_FW_UPDATE_PROGRESS_WRITE_0 + prog); + } + } + ASTERA_INFO("Ending write"); + + // Stop timer + time(&end_t); + ASTERA_INFO("EEPROM load time: %.2f seconds", difftime(end_t, start_t)); + + // Update device FW update progress state + device->fwUpdateProg = ARIES_FW_UPDATE_PROGRESS_WRITE_DONE; + + // Assert HW/SW resets for I2C master interface + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + + usleep(2000); + + return ARIES_SUCCESS; +} + +/** + * @brief Verify the FW image in the EEPROM connected to the Retimer. + * + * numBytes from the values[] buffer will be compared against the contents + * of the EEPROM attached to the Retimer. Returns a negative error code, else + * zero on success. + * + * @param[in] device Struct containing device information + * @param[in] values Pointer to byte array containing the expected + * firmware image. The actual contents of the EEPROM + * will be compared against this. + * @param[in] legacyMode If true, write EEPROM in slower legacy mode + * (set this flag in error scenarios, when you wish to + * read EEPROM without using faster Main Micro + * assisted read). Please set to false otherwise. + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesVerifyEEPROMImage(AriesDeviceType* device, uint8_t* values, + bool legacyMode) +{ + int currentPage; + bool firstByte; + int addr; + uint8_t dataByte[1] = {0}; + uint8_t reWriteByte[1] = {0}; + uint8_t expectedByte; + AriesErrorType rc; + AriesErrorType matchError; + + // Set current page to 0 + currentPage = 0; + matchError = ARIES_SUCCESS; + firstByte = true; + + // Deassert HW/SW resets + rc = ariesSetHwReset(device, 0); + CHECK_SUCCESS(rc); + + // Update device FW update progress state + device->fwUpdateProg = ARIES_FW_UPDATE_PROGRESS_VERIFY_0; + + // If operating in legacy mode, put MM in reset + if (legacyMode) + { + rc = ariesSetMMI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + } + else + { + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + rc = ariesSetI2CMasterReset(device, 0); + CHECK_SUCCESS(rc); + } + + rc = ariesI2cMasterSoftReset(device->i2cDriver); + CHECK_SUCCESS(rc); + usleep(2000); + + // Set page address + rc = ariesI2CMasterSetPage(device->i2cDriver, 0); + CHECK_SUCCESS(rc); + + // Send EEPROM address 0 + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 2); + CHECK_SUCCESS(rc); + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 1); + CHECK_SUCCESS(rc); + + time_t start_t, end_t; + + uint8_t dataBytes[ARIES_EEPROM_BLOCK_WRITE_SIZE_MAX] = {0}; + int addrMSB = 0; + int addrI2C = 0; + int mismatchCount = 0; + + int eepromEnd; + int eepromWriteDelta; + int eepromEndLoc = ariesGetEEPROMImageEnd(values); + eepromEnd = eepromEndLoc; + + // Update EEPROM end index to match end of valid portion of EEPROM + if (eepromEndLoc == -1) + { + eepromEnd = ARIES_EEPROM_MAX_NUM_BYTES; + } + else + { + eepromEnd += 8; + eepromWriteDelta = eepromEnd % device->eeprom.blockWriteSize; + if (eepromWriteDelta) + { + eepromEnd += device->eeprom.blockWriteSize - eepromWriteDelta; + } + } + + // Start timer + time(&start_t); + + bool mainMicroAssist = false; + if (!legacyMode) + { + if (ariesFirmwareIsAtLeast(device, 1, 0, 50)) + { + mainMicroAssist = true; + } + } + + if ((!legacyMode) && mainMicroAssist) + { + bool rewriteFlag; + ASTERA_INFO("Starting Main Micro assisted EEPROM verify"); + for (addr = 0; addr < eepromEnd; addr += device->eeprom.blockWriteSize) + { + addrMSB = addr / 65536; + addrI2C = addr % 65536; + + if (addrMSB != currentPage) + { + // Set updated page address + rc = ariesI2CMasterSetPage(device->i2cDriver, addrMSB); + CHECK_SUCCESS(rc); + currentPage = addrMSB; + // Send EEPROM address 0 after page update + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 2); + CHECK_SUCCESS(rc); + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 1); + CHECK_SUCCESS(rc); + } + + if (!(addrI2C % 8192)) + { + ASTERA_INFO("Slv: 0x%02x, Reg: 0x%04x, Mismatch count: %d", + (0x50 + addrMSB), addrI2C, mismatchCount); + } + + // Receive byte(s) + // Address decided starting at what was set after setting + // page address + // Read entire page as a continuous set of 16 block bytes. + rc = ariesI2CMasterReceiveByteBlock(device, dataBytes); + CHECK_SUCCESS(rc); + + int byteIdx; + rewriteFlag = false; + for (byteIdx = 0; byteIdx < device->eeprom.blockWriteSize; + byteIdx++) + { + expectedByte = values[(addr + byteIdx)]; + if (expectedByte != dataBytes[byteIdx]) + { + mismatchCount += 1; + reWriteByte[0] = expectedByte; + ASTERA_ERROR("Data mismatch"); + ASTERA_ERROR( + " (Addr: %d) Expected: 0x%02x, Received: 0x%02x", + (addr + byteIdx), expectedByte, dataBytes[byteIdx]); + ASTERA_INFO(" Re-trying ..."); + rc = ariesI2CMasterRewriteAndVerifyByte( + device->i2cDriver, (addr + byteIdx), reWriteByte); + // If re-verify step failed, mark error as verify failure + // and dont return error. Else, return error if not success + if (rc == ARIES_EEPROM_VERIFY_FAILURE) + { + matchError = ARIES_EEPROM_VERIFY_FAILURE; + } + else if (rc != ARIES_SUCCESS) + { + return rc; + } + rewriteFlag = true; + } + } + + // Rewrite address to start of next block, since it was reset in + // rewrite and verify step + if (rewriteFlag) + { + rc = ariesI2CMasterSendAddress( + device->i2cDriver, (addr + device->eeprom.blockWriteSize)); + CHECK_SUCCESS(rc); + } + + // Calcualte and update progress + uint8_t prog = 10 * addr / eepromEnd; + device->fwUpdateProg = (AriesFWUpdateProgressType) (ARIES_FW_UPDATE_PROGRESS_VERIFY_0 + prog); + } + } + else + { + ASTERA_INFO("Starting legacy mode EEPROM verify"); + uint8_t value[1] = {0}; + for (addr = 0; addr < eepromEnd; addr++) + { + addrMSB = addr / 65536; + addrI2C = addr % 65536; + + if (addrMSB != currentPage) + { + // Set updated page address + rc = ariesI2CMasterSetPage(device->i2cDriver, addrMSB); + CHECK_SUCCESS(rc); + currentPage = addrMSB; + // Send EEPROM address 0 after page update + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 2); + CHECK_SUCCESS(rc); + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 1); + CHECK_SUCCESS(rc); + firstByte = true; + } + else + { + firstByte = false; + } + + if (!(addrI2C % 8192)) + { + ASTERA_INFO("Slv: 0x%02x, Reg: 0x%04x, Mismatch count: %d", + (0x50 + addrMSB), addrI2C, mismatchCount); + } + + if (firstByte == true) + { + // Receive byte + // Address decided starting at what was set after setting + // page address + rc = ariesI2CMasterReceiveByte(device->i2cDriver, value); + CHECK_SUCCESS(rc); + } + else + { + // Receive continuous stream of bytes to speed up process + rc = ariesI2CMasterReceiveContinuousByte(device->i2cDriver, + value); + CHECK_SUCCESS(rc); + } + + expectedByte = values[addr]; + if (expectedByte != value[0]) + { + mismatchCount += 1; + reWriteByte[0] = expectedByte; + ASTERA_ERROR("Data mismatch"); + ASTERA_ERROR( + " (Addr: %d) Expected: 0x%02x, Received: 0x%02x", addr, + expectedByte, value[0]); + ASTERA_INFO(" Re-trying ..."); + rc = ariesI2CMasterRewriteAndVerifyByte(device->i2cDriver, addr, + reWriteByte); + + // If re-verify step failed, mark error as verify failure + // and dont return error. Else, return error if not success + if (rc == ARIES_EEPROM_VERIFY_FAILURE) + { + matchError = ARIES_EEPROM_VERIFY_FAILURE; + } + else if (rc != ARIES_SUCCESS) + { + return rc; + } + } + + // Calcualte and update progress + uint8_t prog = 10 * addr / eepromEnd; + device->fwUpdateProg = (AriesFWUpdateProgressType) (ARIES_FW_UPDATE_PROGRESS_VERIFY_0 + prog); + } + } + ASTERA_INFO("Ending verify"); + + // Stop timer + time(&end_t); + ASTERA_INFO("EEPROM verify time: %.2f seconds", difftime(end_t, start_t)); + + // Update device FW update progress state + device->fwUpdateProg = ARIES_FW_UPDATE_PROGRESS_VERIFY_DONE; + + // Assert HW/SW resets for I2C master interface + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + + usleep(2000); + + return matchError; +} + +/** + * @brief Verify the FW image in the EEPROM connected to the Retimer via + * checksum. + * + * In this case, no re-writes happen in case of a failure. + * It is recommended to attempt the rewrite the FW into the EEPROM again + * upon failure + * + * @param[in] device Struct containing device information + * @param[in] image Pointer to byte array containing the expected + * firmware image. The actual contents of the EEPROM + * will be compared against this. + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesVerifyEEPROMImageViaChecksum(AriesDeviceType* device, + uint8_t* image) +{ + int currentPage; + int addr; + uint8_t dataByte[1] = {0}; + AriesErrorType rc; + + // Set current page to 0 + currentPage = 0; + + if (!ariesFirmwareIsAtLeast(device, 1, 2, 3)) + { + ASTERA_ERROR("Checksum verify is not supported before firmware v1.2.3"); + return ARIES_FW_VER_NOT_SUPPORTED; + } + + ASTERA_INFO("Starting Main Micro assisted EEPROM verify via checksum"); + + // Deassert HW/SW resets + rc = ariesSetHwReset(device, 0); + CHECK_SUCCESS(rc); + + // Update device FW update progress state + device->fwUpdateProg = ARIES_FW_UPDATE_PROGRESS_VERIFY_0; + + // Reset I2C Master + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + rc = ariesSetI2CMasterReset(device, 0); + CHECK_SUCCESS(rc); + + // Set page address + rc = ariesI2CMasterSetPage(device->i2cDriver, 0); + CHECK_SUCCESS(rc); + + // Send EEPROM address 0 + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 2); + CHECK_SUCCESS(rc); + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 1); + CHECK_SUCCESS(rc); + + time_t start_t, end_t; + + // Calculate EEPROM end address + int eepromEnd; + int eepromWriteDelta; + int eepromEndLoc = ariesGetEEPROMImageEnd(image); + eepromEnd = eepromEndLoc; + + // Update EEPROM end index to match end of valid portion of EEPROM + if (eepromEndLoc == -1) + { + eepromEnd = ARIES_EEPROM_MAX_NUM_BYTES; + } + else + { + eepromEnd += 8; + eepromWriteDelta = eepromEnd % device->eeprom.blockWriteSize; + if (eepromWriteDelta) + { + eepromEnd += device->eeprom.blockWriteSize - eepromWriteDelta; + } + } + + // Start timer + time(&start_t); + + // Calculate expected checksum values for each block + uint8_t eepromBlockEnd = floor(eepromEnd / ARIES_EEPROM_BANK_SIZE); + uint16_t eepromBlockEndDelta = eepromEnd - + (eepromBlockEnd * ARIES_EEPROM_BANK_SIZE); + + uint32_t eepromBlockChecksum[ARIES_EEPROM_NUM_BANKS]; + uint16_t blockIdx; + uint32_t blockSum; + uint32_t byteIdx; + bool isPass = true; + for (blockIdx = 0; blockIdx < ARIES_EEPROM_NUM_BANKS; blockIdx++) + { + blockSum = 0; + if (blockIdx == eepromBlockEnd) + { + for (byteIdx = 0; byteIdx < eepromBlockEndDelta; byteIdx++) + { + blockSum += + image[(ARIES_EEPROM_BANK_SIZE * blockIdx) + byteIdx]; + } + } + else + { + for (byteIdx = 0; byteIdx < ARIES_EEPROM_BANK_SIZE; byteIdx++) + { + blockSum += + image[(ARIES_EEPROM_BANK_SIZE * blockIdx) + byteIdx]; + } + } + eepromBlockChecksum[blockIdx] = blockSum; + } + + uint8_t addrMSB; + bool eepromBlockEndFlag = false; + uint32_t checksum; + + for (addr = 0; addr < eepromEnd; addr += ARIES_EEPROM_BANK_SIZE) + { + addrMSB = addr / 65536; + eepromBlockEndFlag = false; + if (addrMSB != currentPage) + { + // Set updated page address + rc = ariesI2CMasterSetPage(device->i2cDriver, addrMSB); + CHECK_SUCCESS(rc); + currentPage = addrMSB; + // Send EEPROM address 0 after page update + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 2); + CHECK_SUCCESS(rc); + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 1); + CHECK_SUCCESS(rc); + + if (currentPage == eepromBlockEnd) + { + eepromBlockEndFlag = true; + } + } + + checksum = 0; + + if (eepromBlockEndFlag) + { + // partial-block checksum calc. + rc = ariesI2CMasterGetChecksum(device, eepromBlockEndDelta, + &checksum); + CHECK_SUCCESS(rc); + } + else + { + // full-block checksum calc. + rc = ariesI2CMasterGetChecksum(device, 0, &checksum); + CHECK_SUCCESS(rc); + } + + if (checksum != eepromBlockChecksum[currentPage]) + { + ASTERA_ERROR("Page %d: checksum did not match expected value", + currentPage); + ASTERA_ERROR(" Expected: %d", eepromBlockChecksum[currentPage]); + ASTERA_ERROR(" Received: %d", checksum); + isPass = false; + } + else + { + ASTERA_INFO("Page %d: checksums matched", currentPage); + } + + if (eepromBlockEndFlag) + { + break; + } + + // Calcualte and update progress + uint8_t prog = 10 * addr / eepromEnd; + device->fwUpdateProg = (AriesFWUpdateProgressType) (ARIES_FW_UPDATE_PROGRESS_VERIFY_0 + prog); + } + ASTERA_INFO("Ending verify"); + + // Stop timer + time(&end_t); + ASTERA_INFO("EEPROM verify time: %.2f seconds", difftime(end_t, start_t)); + + // Update device FW update progress state + device->fwUpdateProg = ARIES_FW_UPDATE_PROGRESS_VERIFY_DONE; + + // Assert HW/SW resets for I2C master interface + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + + usleep(2000); + + if (!isPass) + { + return ARIES_EEPROM_VERIFY_FAILURE; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Calculate block CRCs from data in EEPROM + * + * @param[in] device Struct containing device information + * @param[in, out] image Array containing FW info (in bytes) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesCheckEEPROMCrc(AriesDeviceType* device, uint8_t* image) +{ + AriesErrorType rc; + + uint8_t crcBytesEEPROM[ARIES_EEPROM_MAX_NUM_CRC_BLOCKS] = {0}; + uint8_t crcBytesImg[ARIES_EEPROM_MAX_NUM_CRC_BLOCKS] = {0}; + + uint8_t numCrcBytesEEPROM; + uint8_t numCrcBytesImg; + + rc = ariesCheckEEPROMImageCrcBytes(device, crcBytesEEPROM, + &numCrcBytesEEPROM); + CHECK_SUCCESS(rc); + ariesGetCrcBytesImage(image, crcBytesImg, &numCrcBytesImg); + + if (numCrcBytesImg != numCrcBytesEEPROM) + { + // FAIL + ASTERA_ERROR("CRC block size mismatch. Please check FW version"); + return ARIES_EEPROM_CRC_BLOCK_NUM_FAIL; + } + + uint8_t byteIdx; + for (byteIdx = 0; byteIdx < numCrcBytesEEPROM; byteIdx++) + { + if (crcBytesEEPROM[byteIdx] != crcBytesImg[byteIdx]) + { + // Mismatch + ASTERA_ERROR("CRC byte mismatch. Please check FW version"); + ASTERA_ERROR(" EEPROM CRC: %x, FILE CRC: %x", + crcBytesEEPROM[byteIdx], crcBytesImg[byteIdx]); + return ARIES_EEPROM_CRC_BYTE_FAIL; + } + } + + ASTERA_INFO("EEPROM Block CRCs match with expected FW image"); + + return ARIES_SUCCESS; +} + +/** + * @brief Calculate block CRCs from data in EEPROM + * + * @param[in] device Struct containing device information + * @param[in, out] crcBytes Array containing block crc bytes + * @param[in, out] numCrcBytes size of crcBytes (in bytes) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesCheckEEPROMImageCrcBytes(AriesDeviceType* device, + uint8_t* crcBytes, + uint8_t* numCrcBytes) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + // Deassert HW/SW resets + rc = ariesSetI2CMasterReset(device, 0); + CHECK_SUCCESS(rc); + + // Init I2C Master + rc = ariesI2CMasterInit(device->i2cDriver); + CHECK_SUCCESS(rc); + + // Set page address + rc = ariesI2CMasterSetPage(device->i2cDriver, 0); + CHECK_SUCCESS(rc); + + // Send EEPROM address 0 + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 2); + CHECK_SUCCESS(rc); + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 1); + CHECK_SUCCESS(rc); + + // Get block start address + int numBlocks = 0; + int blockStartAddr = 0; + rc = ariesGetEEPROMFirstBlock(device->i2cDriver, &blockStartAddr); + CHECK_SUCCESS(rc); + + uint8_t blockType; + int blockLength; + uint8_t crcByte; + + while (numBlocks < ARIES_EEPROM_MAX_NUM_CRC_BLOCKS) + { + rc = ariesGetEEPROMBlockType(device->i2cDriver, blockStartAddr, + &blockType); + CHECK_SUCCESS(rc); + + if (blockType != 0xff) + { + rc = ariesEEPROMGetBlockLength(device->i2cDriver, blockStartAddr, + &blockLength); + CHECK_SUCCESS(rc); + rc = ariesGetEEPROMBlockCrcByte(device->i2cDriver, blockStartAddr, + blockLength, &crcByte); + CHECK_SUCCESS(rc); + + crcBytes[numBlocks] = crcByte; + + blockStartAddr += blockLength + 13; + numBlocks++; + } + else + { + // Last Page + break; + } + } + + *numCrcBytes = numBlocks; + + // Assert HW/SW resets for I2C master interface + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + + usleep(2000); + + return ARIES_SUCCESS; +} + +/** + * @brief Read a byte from the EEPROM. + * + * Executes the sequence of SMBus transactions to read a single byte from the + * EEPROM connected to the retimer. + * + * @param[in] device Struct containing the device information + * @param[in] addr EEPROM address to read a byte from + * @param[in,out] value Data read from EEPROM (1 byte) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadEEPROMByte(AriesDeviceType* device, int addr, + uint8_t* value) +{ + AriesErrorType rc; + + // Deassert HW/SW resets + rc = ariesSetHwReset(device, 0); + CHECK_SUCCESS(rc); + + // Toggle SW reset for I2C master interface + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + rc = ariesSetI2CMasterReset(device, 0); + CHECK_SUCCESS(rc); + + // Set page address + uint8_t pageNum = floor(addr / ARIES_EEPROM_BANK_SIZE); + rc = ariesI2CMasterSetPage(device->i2cDriver, pageNum); + CHECK_SUCCESS(rc); + + // The EEPROM access + rc = ariesEEPROMGetRandomByte(device->i2cDriver, addr, value); + CHECK_SUCCESS(rc); + + // Assert HW/SW resets for I2C master interface + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + + usleep(2000); + + return ARIES_SUCCESS; +} + +/** + * @brief Write a byte to the EEPROM. + * + * Executes the sequence of SMBus transactions to write a single byte to the + * EEPROM connected to the retimer. + * + * @param[in] device Struct containing the device information + * @param[in] addr EEPROM address to write a byte to + * @param[in] value Data to write to EEPROM (1 byte) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteEEPROMByte(AriesDeviceType* device, int addr, + uint8_t* value) +{ + AriesErrorType rc; + + // Deassert HW/SW resets + rc = ariesSetHwReset(device, 0); + CHECK_SUCCESS(rc); + + // Toggle SW reset for I2C master interface + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + rc = ariesSetI2CMasterReset(device, 0); + CHECK_SUCCESS(rc); + + // Set page address + uint8_t pageNum = floor(addr / ARIES_EEPROM_BANK_SIZE); + rc = ariesI2CMasterSetPage(device->i2cDriver, pageNum); + CHECK_SUCCESS(rc); + + // The EEPROM access + rc = ariesI2CMasterSendByteBlockData(device->i2cDriver, addr, 1, value); + CHECK_SUCCESS(rc); + + // Assert HW/SW resets for I2C master interface + rc = ariesSetI2CMasterReset(device, 1); + CHECK_SUCCESS(rc); + + usleep(2000); + + return ARIES_SUCCESS; +} + +/** + * @brief Check connection health + * + * @param[in] device Pointer to Aries Device struct object + * @param[in] slaveAddress Desired Retimer I2C (7-bit) address in case ARP + * needs to be run + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesCheckConnectionHealth(AriesDeviceType* device, + uint8_t slaveAddress) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + device->arpEnable = false; + + rc = ariesReadByteData(device->i2cDriver, ARIES_CODE_LOAD_REG, dataByte); + if (rc != ARIES_SUCCESS) + { + // Failure to read code, run ARP + ASTERA_WARN("Failed to read code_load, Run ARP"); + // Perform Address Resolution Protocol (ARP) to set the Aries slave + // address. Aries slave will respond to 0x61 during ARP. + // NOTE: In most cases, Aries firmware will disable ARP and the Retimer + // will take on a fixed SMBus address: 0x20, 0x21, ..., 0x27. + int arpHandle = asteraI2COpenConnection(device->i2cBus, 0x61); + if (arpHandle < 0) + { + ASTERA_ERROR("Could not open i2c connection."); + return ARIES_I2C_OPEN_FAILURE; + } + rc = ariesRunArp(arpHandle, slaveAddress); // Run ARP, user addr + close(arpHandle); + if (rc != ARIES_SUCCESS) + { + ASTERA_ERROR("ARP connection unsuccessful"); + return ARIES_I2C_OPEN_FAILURE; + } + + // Update Aries SMBus driver + device->i2cDriver->handle = asteraI2COpenConnection(device->i2cBus, + slaveAddress); + device->i2cDriver->slaveAddr = slaveAddress; + device->arpEnable = true; + rc = ariesReadByteData(device->i2cDriver, ARIES_CODE_LOAD_REG, + dataByte); + if (rc != ARIES_SUCCESS) + { + ASTERA_ERROR("Failed to read code_load after ARP"); + return ARIES_I2C_OPEN_FAILURE; + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Perform an all-in-one health check on the device. + * + * Check if regular accesses to the EEPROM are working, and if the EEPROM has + * loaded correctly (i.e. Main Mircocode, Path Microcode and PMA code have + * loaded correctly). + * + * @param[in] device Pointer to Aries Device struct object + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesCheckDeviceHealth(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + device->deviceOkay = true; + + // Check if EEPROM has loaded successfully + rc = ariesReadByteData(device->i2cDriver, ARIES_CODE_LOAD_REG, dataByte); + if (rc != ARIES_SUCCESS) + { + ASTERA_ERROR("Reads to retimer aren't working"); + ASTERA_ERROR("Check slave address and/or connections to retimer"); + device->deviceOkay = false; + return rc; + } + + if (dataByte[0] < ARIES_LOAD_CODE) + { + ASTERA_ERROR("Device firmware load unsuccessful"); + ASTERA_ERROR("Must attempt firmware rewrite to EEPROM"); + device->deviceOkay = false; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Get the max recorded junction temperature. + * + * Read the maximum junction temperature and return in units of degrees + * Celsius. This value represents the maximum value from all temperature + * sensors on the Retimer. Returns a negative error code, else zero on + * success. + * + * @param[in] device Struct containing device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetMaxTemp(AriesDeviceType* device) +{ + return ariesReadPmaTempMax(device); +} + +/** + * @brief Get the current junction temperature. + * + * Read the current junction temperature and return in units of degrees + * Celsius. + * + * @param[in] device Struct containing device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetCurrentTemp(AriesDeviceType* device) +{ + if (device->fwVersion.isComplianceFW) + { + return ariesReadPmaAvgTempDirect(device); + } + return ariesReadPmaAvgTemp(device); +} + +/** + * @brief Set max data rate + * + * @param[in] device Struct containing device information + * @param[in] rate Max data rate to set + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesSetMaxDataRate(AriesDeviceType* device, + AriesMaxDataRateType rate) +{ + AriesErrorType rc; + uint8_t dataBytes[4] = {0}; + uint32_t val; + uint32_t mask; + + rc = ariesReadWideRegister(device->i2cDriver, ARIES_GLB_PARAM_REG0_ADDR, 4, + dataBytes); + CHECK_SUCCESS(rc); + val = dataBytes[0] + (dataBytes[1] << 8) + (dataBytes[2] << 16) + + (dataBytes[3] << 24); + + mask = ~(7 << 24) & 0xffffffff; + val &= mask; + val |= (rate << 24); + + dataBytes[0] = val & 0xff; + dataBytes[1] = (val >> 8) & 0xff; + dataBytes[2] = (val >> 16) & 0xff; + dataBytes[3] = (val >> 24) & 0xff; + + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_GLB_PARAM_REG0_ADDR, 4, + dataBytes); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Set the GPIO value + * + * @param[in] device Struct containing device information + * @param[in] gpioNum GPIO number [0:3] + * @param[in] value GPIO value (0) or (1) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesSetGPIO(AriesDeviceType* device, int gpioNum, bool value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + rc = ariesReadByteData(device->i2cDriver, 0x916, dataByte); + CHECK_SUCCESS(rc); + if (value == 1) + { + // Set the corresponding GPIO bit to 1 + dataByte[0] |= (1 << gpioNum); + } + else + { + // Clear the corresponding GPIO bit to 0 + dataByte[0] &= ~(1 << gpioNum); + } + + rc = ariesWriteByteData(device->i2cDriver, 0x916, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Get the GPIO value + * + * @param[in] device Struct containing device information + * @param[in] gpioNum GPIO number [0:3] + * @param[in, out] value Pointer to GPIO value (0) or (1) variable + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetGPIO(AriesDeviceType* device, int gpioNum, bool* value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + rc = ariesReadByteData(device->i2cDriver, 0x915, dataByte); + CHECK_SUCCESS(rc); + *value = (dataByte[0] >> gpioNum) & 1; + + return ARIES_SUCCESS; +} + +/** + * @brief Toggle the GPIO value + * + * @param[in] device Struct containing device information + * @param[in] gpioNum GPIO number [0:3] + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesToggleGPIO(AriesDeviceType* device, int gpioNum) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + rc = ariesReadByteData(device->i2cDriver, 0x916, dataByte); + CHECK_SUCCESS(rc); + + dataByte[0] ^= (1 << gpioNum); + + rc = ariesWriteByteData(device->i2cDriver, 0x916, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Set the GPIO direction + * + * @param[in] device Struct containing device information + * @param[in] gpioNum GPIO number [0:3] + * @param[in] value GPIO direction (0 = output) or (1 = input) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesSetGPIODirection(AriesDeviceType* device, int gpioNum, + bool value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + rc = ariesReadByteData(device->i2cDriver, 0x917, dataByte); + CHECK_SUCCESS(rc); + if (value == 1) + { + // Set the corresponding GPIO bit to 1 + dataByte[0] |= (1 << gpioNum); + } + else + { + // Clear the corresponding GPIO bit to 0 + dataByte[0] &= ~(1 << gpioNum); + } + + rc = ariesWriteByteData(device->i2cDriver, 0x917, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Get the GPIO direction + * + * @param[in] device Struct containing device information + * @param[in] gpioNum GPIO number [0:3] + * @param[in, out] value Pointer to GPIO direction (0 = output) or (1 = input) + * variable + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetGPIODirection(AriesDeviceType* device, int gpioNum, + bool* value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + rc = ariesReadByteData(device->i2cDriver, 0x917, dataByte); + CHECK_SUCCESS(rc); + *value = (dataByte[0] >> gpioNum) & 1; + + return ARIES_SUCCESS; +} + +/** + * @brief Enable Aries Test Mode for PRBS generation and checking + * + * @param[in] device Struct containing device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeEnable(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t dataWord[2] = {0}; + int side; + int lane; + int qs; + int qsLane; + + // Assert register PERST for Links[7:0] + dataByte[0] = 0x00; + rc = ariesWriteByteData(device->i2cDriver, ARIES_PERST_N, dataByte); + CHECK_SUCCESS(rc); + usleep(100000); // 100 ms + + // Enable RX terminations + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPipeRxTermSet(device, side, lane, true); + CHECK_SUCCESS(rc); + } + } + usleep(100000); // 100 ms + + // In some versions of FW, MPLLB output divider is overriden to /2. Undo + // this change. + if (device->revNumber == 1) // A0 only + { + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + qs = lane / 4; + qsLane = lane % 4; + // one control bit per PMA instance + if (qsLane == 0) + { + // MPLLB_TX_CLK_DIV_OVRD_EN = 0 + dataWord[0] = 0x00; + dataWord[1] = 0x00; + rc = ariesWriteWordPmaMainMicroIndirect( + device->i2cDriver, side, qs, + ARIES_PMA_SUP_DIG_MPLLB_OVRD_IN_0, dataWord); + CHECK_SUCCESS(rc); + } + // Also change ROPLL value back to /16 instead of /8 + dataWord[0] = 0x00; + dataWord[1] = 0x80; + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ANA_ROPLL_ANA_OUT_2, dataWord); + CHECK_SUCCESS(rc); + } + } + } + // Put Receivers into standby + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPipeRxStandbySet(device, side, lane, true); + CHECK_SUCCESS(rc); + } + } + usleep(10000); // 10 ms + + // Transition to Powerdown=0 (P0) + for (side = 0; side < 2; side++) + { + // One powerdown control for every grouping of two lanes + for (lane = 0; lane < 16; lane += 2) + { + rc = ariesPipePowerdownSet(device, side, lane, + ARIES_PIPE_POWERDOWN_P0); + CHECK_SUCCESS(rc); + usleep(10000); // 10 ms + rc = ariesPipePowerdownCheck(device, side, lane, + ARIES_PIPE_POWERDOWN_P0); + CHECK_SUCCESS(rc); + } + } + usleep(10000); // 10 ms + + // Disable PCS block align control + for (side = 0; side < 2; side++) + { + // One blockaligncontrol control for every grouping of two lanes + for (lane = 0; lane < 16; lane += 2) + { + rc = ariesPipeBlkAlgnCtrlSet(device, side, lane, false, true); + CHECK_SUCCESS(rc); + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Disable Aries Test Mode for PRBS generation and checking + * + * @param[in] device Struct containing device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeDisable(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t dataWord[2] = {0}; + int side; + int lane; + int qs; + int qsLane; + + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + qs = lane / 4; + qsLane = lane % 4; + // Undo RxReq override + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_ATE_OVRD_IN, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~(1 << 5); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_ATE_OVRD_IN, dataWord); + CHECK_SUCCESS(rc); + } + } + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + // Undo Rx inversion overrides + rc = ariesPMARxInvertSet(device, side, lane, false, false); + CHECK_SUCCESS(rc); + // Set de-emphasis back to -3.5 dB + rc = ariesPipeDeepmhasisSet(device, side, lane, 1, + ARIES_PIPE_DEEMPHASIS_PRESET_NONE, 0, + 48, 0); + CHECK_SUCCESS(rc); + // Turn off PRBS generators + rc = ariesPMABertPatGenConfig(device, side, lane, DISABLED); + CHECK_SUCCESS(rc); + // Turn off PRBS checkers + rc = ariesPMABertPatChkConfig(device, side, lane, DISABLED); + CHECK_SUCCESS(rc); + } + } + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + // Undo block align control override + rc = ariesPipeBlkAlgnCtrlSet(device, side, lane, false, false); + CHECK_SUCCESS(rc); + } + } + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + // Undo Tx/Rx data enable override + rc = ariesPMATxDataEnSet(device, side, lane, false); + CHECK_SUCCESS(rc); + rc = ariesPMARxDataEnSet(device, side, lane, false); + CHECK_SUCCESS(rc); + } + } + + // Rate change to Gen1 + rc = ariesTestModeRateChange(device, ARIES_GEN1); + CHECK_SUCCESS(rc); + + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + // Powerdown to P1 (P1 is value 2) + rc = ariesPipePowerdownSet(device, side, lane, + ARIES_PIPE_POWERDOWN_P1); + CHECK_SUCCESS(rc); + rc = ariesPipePowerdownCheck(device, side, lane, + ARIES_PIPE_POWERDOWN_P1); + CHECK_SUCCESS(rc); + } + } + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + // Undo Rxeqeval override + rc = ariesPipeRxEqEval(device, side, lane, false); + CHECK_SUCCESS(rc); + + // Undo Rxeqinprogress override + rc = ariesPipeRxEqInProgressSet(device, side, lane, false); + CHECK_SUCCESS(rc); + + // Undo Rxstandby override + rc = ariesPipeRxStandbySet(device, side, lane, false); + CHECK_SUCCESS(rc); + } + } + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + // Disable Rx terminations + rc = ariesPipeRxTermSet(device, side, lane, false); + CHECK_SUCCESS(rc); + } + } + + // De-assert register PERST + dataByte[0] = 0xff; + rc = ariesWriteByteData(device->i2cDriver, 0x604, dataByte); + CHECK_SUCCESS(rc); + usleep(100000); // 100 ms + + return ARIES_SUCCESS; +} + +/** + * @brief Set the desired Aries Test Mode data rate + * + * @param[in] device Struct containing device information + * @param[in] rate Desired rate (1, 2, ... 5) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeRateChange(AriesDeviceType* device, + AriesMaxDataRateType rate) +{ + AriesErrorType rc; + int side; + int lane; + + // Disable Tx and Rx so rate change is successful + rc = ariesTestModeRxConfig(device, DISABLED, false); + CHECK_SUCCESS(rc); + rc = ariesTestModeTxConfig(device, DISABLED, 0, false); + CHECK_SUCCESS(rc); + + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPMAVregVrefSet(device, side, lane, rate); + CHECK_SUCCESS(rc); + } + } + + for (side = 0; side < 2; side++) + { + // Rate is controlled for each grouping of two lanes, so only do this + // once per pair + for (lane = 0; lane < 16; lane += 2) + { + rc = ariesPipeRateChange(device, side, lane, rate); + CHECK_SUCCESS(rc); + } + usleep(100000); // 100 ms + // Confirm that every lane changed rate successfully + for (lane = 0; lane < 16; lane++) + { + // Confirm rate change by reading PMA registers + rc = ariesPipeRateCheck(device, side, lane, rate); + CHECK_SUCCESS(rc); + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries Test Mode transmitter configuration + * + * @param[in] device Struct containing device information + * @param[in] pattern Desired PRBS data pattern + * @param[in] preset Desired Tx preset setting + * @param[in] enable Enable (1) or disable (0) flag + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeTxConfig(AriesDeviceType* device, + AriesPRBSPatternType pattern, int preset, + bool enable) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int side; + int lane; + int rate; + int de; + AriesPRBSPatternType mode = DISABLED; + + // Decode the pattern argument + if (enable) + { + // Take Transmitters out of electrical idle + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPipeTxElecIdleSet(device, side, lane, false); + CHECK_SUCCESS(rc); + } + } + + // Set the generator mode (pattern) + mode = pattern; + + // Check any Path's rate (assumption is they're all the same) + // qs_2, pth_wrap_0 is absolute lane 8 + rc = ariesReadRetimerRegister( + device->i2cDriver, 0, 8, + ARIES_RET_PTH_GBL_MAC_PHY_RATE_AND_PCLK_RATE_ADDR, 1, dataByte); + CHECK_SUCCESS(rc); + rate = dataByte[0] & 0x7; + if (rate >= 2) // rate==2 is Gen3 + { + // For Gen3/4/5, use presets + de = ARIES_PIPE_DEEMPHASIS_DE_NONE; + if (preset < 0) + { + preset = 0; + } + else if (preset > 10) + { + preset = 10; + } + } + else + { + // For Gen1/2, use de-emphasis -3.5dB (default) + de = 1; + preset = ARIES_PIPE_DEEMPHASIS_PRESET_NONE; + } + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPipeDeepmhasisSet(device, side, lane, de, preset, 0, + 48, 0); + CHECK_SUCCESS(rc); + } + } + } + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPMABertPatGenConfig(device, side, lane, mode); + CHECK_SUCCESS(rc); + usleep(10000); // 10 ms + rc = ariesPMATxDataEnSet(device, side, lane, enable); + CHECK_SUCCESS(rc); + usleep(10000); // 10 ms + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries Test Mode transmitter single lane configuration + * + * @param[in] device Struct containing device information + * @param[in] pattern Desired PRBS data pattern + * @param[in] preset Desired Tx preset setting + * @param[in] enable Enable (1) or disable (0) flag + * @param[in] side Side of chip + * @param[in] lane Lane to configure + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeTxConfigLane(AriesDeviceType* device, + AriesPRBSPatternType pattern, + int preset, int side, int lane, + bool enable) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int rate; + int de; + AriesPRBSPatternType mode = DISABLED; + + // Decode the pattern argument + if (enable) + { + // Set the generator mode (pattern) + mode = pattern; + + // Check any Path's rate (assumption is they're all the same) + // qs_2, pth_wrap_0 is absolute lane 8 + rc = ariesReadRetimerRegister( + device->i2cDriver, 0, 8, + ARIES_RET_PTH_GBL_MAC_PHY_RATE_AND_PCLK_RATE_ADDR, 1, dataByte); + CHECK_SUCCESS(rc); + rate = dataByte[0] & 0x7; + if (rate >= 2) // rate==2 is Gen3 + { + // For Gen3/4/5, use presets + de = ARIES_PIPE_DEEMPHASIS_DE_NONE; + if (preset < 0) + { + preset = 0; + } + else if (preset > 10) + { + preset = 10; + } + } + else + { + // For Gen1/2, use de-emphasis -3.5dB (default) + de = 1; + preset = ARIES_PIPE_DEEMPHASIS_PRESET_NONE; + } + rc = ariesPipeDeepmhasisSet(device, side, lane, de, preset, 0, 48, 0); + CHECK_SUCCESS(rc); + } + rc = ariesPMABertPatGenConfig(device, side, lane, mode); + CHECK_SUCCESS(rc); + usleep(10000); // 10 ms + rc = ariesPMATxDataEnSet(device, side, lane, enable); + CHECK_SUCCESS(rc); + usleep(10000); // 10 ms + + return ARIES_SUCCESS; +} + +/** + * @brief Aries Test Mode receiver configuration + * + * @param[in] device Struct containing device information + * @param[in] pattern Desired PRBS data pattern + * @param[in] enable Enable (1) or disable (0) flag + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeRxConfig(AriesDeviceType* device, + AriesPRBSPatternType pattern, bool enable) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int side; + int lane; + int rate; + + if (enable == 0) + { + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPMAPCSRxReqBlock(device, side, lane, false); + CHECK_SUCCESS(rc); + rc = ariesPMARxDataEnSet(device, side, lane, false); + CHECK_SUCCESS(rc); + rc = ariesPMABertPatChkConfig(device, side, lane, DISABLED); + CHECK_SUCCESS(rc); + } + } + } + else + { + // Adapt the receivers for Gen3/4/5 + // Check any Path's rate (assumption is they're all the same) + // qs_2, pth_wrap_0 is absolute lane 8 + rc = ariesReadRetimerRegister( + device->i2cDriver, 0, 8, + ARIES_RET_PTH_GBL_MAC_PHY_RATE_AND_PCLK_RATE_ADDR, 1, dataByte); + CHECK_SUCCESS(rc); + rate = dataByte[0] & 0x7; + + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + // Set coarse adaptation + if (rate == 4) // Gen5 + { + rc = ariesPMAPCSRxRecalBankOvrdSet(device, side, lane, + false); + CHECK_SUCCESS(rc); + } + else // Gen-3/4 + { + rc = ariesPMAPCSRxRecalBankOvrdSet(device, side, lane, + true); + CHECK_SUCCESS(rc); + } + + // Enable Rx req block + rc = ariesPMAPCSRxReqBlock(device, side, lane, true); + CHECK_SUCCESS(rc); + + // Enable Rx data en + rc = ariesPMARxDataEnSet(device, side, lane, true); + CHECK_SUCCESS(rc); + usleep(10000); // 10 ms + } + } + + usleep(500000); // 500 ms + + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + ariesPipeRxStandbySet(device, side, lane, false); + } + } + + if (rate >= 2) // rate==2 is Gen3 + { + ASTERA_INFO("Run Rx adaptation...."); + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPipeRxAdapt(device, side, lane); + CHECK_SUCCESS(rc); + usleep(10000); // 10 ms + } + } + } + usleep(500000); // 500 ms + // Configure pattern checker + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPMABertPatChkConfig(device, side, lane, pattern); + CHECK_SUCCESS(rc); + usleep(10000); // 10 ms + rc = ariesPMABertPatChkToggleSync(device, side, lane); + CHECK_SUCCESS(rc); + rc = ariesPMABertPatChkToggleSync(device, side, lane); + CHECK_SUCCESS(rc); + } + } + // Detect/correct polarity + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPMABertPatChkDetectCorrectPolarity(device, side, + lane); + CHECK_SUCCESS(rc); + usleep(10000); // 10 ms + } + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries Test Mode read error count + * + * @param[in] device Struct containing device information + * @param[in, out] ecount Array containing error count data for each side and + * lane + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeRxEcountRead(AriesDeviceType* device, int* ecount) +{ + AriesErrorType rc; + int dataByte[1]; + int side; + int lane; + + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPMABertPatChkSts(device, side, lane, dataByte); + CHECK_SUCCESS(rc); + // ASTERA_INFO("Side:%d, Lane:%2d, ECOUNT = %d", side, lane, + // dataByte[0]); + int index = side * 16 + lane; + ecount[index] = dataByte[0]; + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries Test Mode clear error count + * + * Reads ecount values for each side and lane and populates an array + * + * @param[in] device Struct containing device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeRxEcountClear(AriesDeviceType* device) +{ + AriesErrorType rc; + int side; + int lane; + + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPMABertPatChkToggleSync(device, side, lane); + CHECK_SUCCESS(rc); + rc = ariesPMABertPatChkToggleSync(device, side, lane); + CHECK_SUCCESS(rc); + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries Test Mode clear single lane error count + * + * Reads ecount values for a single lane on one side and populates an array + * + * @param[in] device Struct containing device information + * @param[in] side Side of chip + * @param[in] lane Lane to clear error count for + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeRxEcountClearLane(AriesDeviceType* device, int side, + int lane) +{ + AriesErrorType rc; + + rc = ariesPMABertPatChkToggleSync(device, side, lane); + CHECK_SUCCESS(rc); + rc = ariesPMABertPatChkToggleSync(device, side, lane); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Aries Test Mode read FoM + * + * Reads FoM values for each side and lane and populates an array + * + * @param[in] device Struct containing device information + * @param[in, out] fom Array containing FoM data for each side and lane + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeRxFomRead(AriesDeviceType* device, int* fom) +{ + AriesErrorType rc; + int dataByte[1]; + int side; + int lane; + + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + rc = ariesPipeFomGet(device, side, lane, dataByte); + CHECK_SUCCESS(rc); + // ASTERA_INFO("Side:%d, Lane:%2d, FOM = 0x%x", side, lane, + // dataByte[0]); + int index = side * 16 + lane; + fom[index] = dataByte[0]; + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries Test Mode read Rx valid + * + * @param[in] device Struct containing device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeRxValidRead(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int side; + int lane; + int qs; + int qsLane; + bool rxvalid; + + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + qs = lane / 4; + qsLane = lane % 4; + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, 0x1033, dataWord); + CHECK_SUCCESS(rc); + rxvalid = (dataWord[0] >> 1) & 0x1; + ASTERA_INFO("Side:%d, Lane:%2d, PHY rxvalid = %d", side, lane, + rxvalid); + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries Test Mode inject error + * + * @param[in] device Struct containing device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesTestModeTxErrorInject(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int side; + int lane; + int qs; + int qsLane; + + for (side = 0; side < 2; side++) + { + for (lane = 0; lane < 16; lane++) + { + qs = lane / 4; + qsLane = lane % 4; + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, 0x1072, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] |= (1 << 4); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, 0x1072, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~(1 << 4); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, 0x1072, dataWord); + CHECK_SUCCESS(rc); + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries trigger FW reload on next secondary bus reset (SBR) + * + * This only works on Firmware version 1.28.x or later. + * + * @param[in] device Struct containing device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesFWReloadOnNextSBR(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + // Set glb_err_msk_reg2[1]=1 to trigger FW reload on next SBR. + // Aries FW will self-clear this bit once SBR-triggered FW reload starts. + rc = ariesReadByteData(device->i2cDriver, 0xe, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] |= 0x02; + rc = ariesWriteByteData(device->i2cDriver, 0xe, dataByte); + CHECK_SUCCESS(rc); + return ARIES_SUCCESS; +} + +/** + * @brief Aries get over temperature status + * + * This only works on Firmware version 1.28.x or later. + * + * @param[in] device Struct containing device information + * @param[out] value Pointer to bool with over temp status result + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesOvertempStatusGet(AriesDeviceType* device, bool* value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + // OVERTEMP_STATUS is glb_err_msk_reg2[2]. + rc = ariesReadByteData(device->i2cDriver, 0xe, dataByte); + CHECK_SUCCESS(rc); + *value = (dataByte[0] >> 2) & 1; + return ARIES_SUCCESS; +} + +/** + * @brief Aries get status of I2C bus self-clear event log + * + * When an errant I2C transaction (e.g. a transaction intervening between the + * Write and Read portions of a Aries Read transaction, or a transaction which + * does not follow the specified transaction format) causes the Aries I2C slave + * to get into a stuck state, the Aries FW will detect and clear this state. + * This API reads back status to show if such an event has occured (1) or + * not (0). This is a sticky status. + * + * @param[in] device Struct containing device information + * @param[out] status Pointer to return value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetI2CBusClearEventStatus(AriesDeviceType* device, + int* status) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + dataByte[0] = ARIES_GENERIC_STATUS_MASK_I2C_CLEAR_EVENT; + rc = ariesReadByteData(device->i2cDriver, ARIES_GENERIC_STATUS_ADDR, + dataByte); + CHECK_SUCCESS(rc); + if (dataByte[0] & ARIES_GENERIC_STATUS_MASK_I2C_CLEAR_EVENT) + *status = 1; + else + *status = 0; + return ARIES_SUCCESS; +} + +/** + * @brief Aries clear I2C bus self-clear event log status + * + * @param[in] device Struct containing device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesClearI2CBusClearEventStatus(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + dataByte[0] = ARIES_GENERIC_STATUS_MASK_I2C_CLEAR_EVENT; + rc = ariesWriteByteData(device->i2cDriver, ARIES_GENERIC_STATUS_CLR_ADDR, + dataByte); + CHECK_SUCCESS(rc); + return ARIES_SUCCESS; +} + +#ifdef __cplusplus +} +#endif diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_api.h b/common/recipes-lib/retimer-v2.16.2/files/aries_api.h new file mode 100755 index 000000000000..f75924ef6ea1 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_api.h @@ -0,0 +1,177 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_api.h + * @brief Definition of public functions for the SDK. + */ +#pragma once +#ifndef ASTERA_ARIES_SDK_API_H_ +#define ASTERA_ARIES_SDK_API_H_ + +#include "aries_globals.h" +#include "aries_error.h" +#include "aries_api_types.h" +#include "astera_log.h" +#include "aries_i2c.h" +#include "aries_link.h" +#include "aries_misc.h" + +#ifdef ARIES_MPW +#include "aries_mpw_reg_defines.h" +#else +#include "aries_a0_reg_defines.h" +#endif + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARIES_SDK_VERSION "2.16.2" + +const char* ariesGetSDKVersion(void); + +AriesErrorType ariesInitDevice(AriesDeviceType* device, uint8_t recoveryAddr); + +AriesErrorType ariesDeviceRevisionGet(AriesDeviceType* device); + +AriesErrorType ariesCreateLinkStructs(AriesDeviceType* device, + AriesLinkType* links); + +AriesErrorType ariesFWStatusCheck(AriesDeviceType* device); + +AriesErrorType ariesComplianceFWGet(AriesDeviceType* device); + +AriesErrorType ariesSetEEPROMPageSize(AriesDeviceType* device, int pageSize); + +AriesErrorType ariesSetBifurcationMode(AriesDeviceType* device, + AriesBifurcationType bifur); + +AriesErrorType ariesGetBifurcationMode(AriesDeviceType* device, + AriesBifurcationType* bifur); + +AriesErrorType ariesSetHwReset(AriesDeviceType* device, uint8_t reset); + +AriesErrorType ariesSetMMI2CMasterReset(AriesDeviceType* device, uint8_t reset); + +AriesErrorType ariesSetI2CMasterReset(AriesDeviceType* device, uint8_t reset); + +AriesErrorType ariesUpdateFirmware(AriesDeviceType* device, + const char* filename, + AriesFWImageFormatType fileType); + +AriesErrorType ariesUpdateFirmwareViaBuffer(AriesDeviceType* device, + uint8_t* image); + +AriesErrorType ariesFirmwareUpdateProgress(AriesDeviceType* device, + uint8_t* percentComplete); + +AriesErrorType ariesWriteEEPROMImage(AriesDeviceType* device, uint8_t* values, + bool legacyMode); + +AriesErrorType ariesVerifyEEPROMImage(AriesDeviceType* device, uint8_t* values, + bool legacyMode); + +AriesErrorType ariesVerifyEEPROMImageViaChecksum(AriesDeviceType* device, + uint8_t* image); + +AriesErrorType ariesCheckEEPROMCrc(AriesDeviceType* device, uint8_t* image); + +AriesErrorType ariesCheckEEPROMImageCrcBytes(AriesDeviceType* device, + uint8_t* crcBytes, + uint8_t* numCrcBytes); + +AriesErrorType ariesReadEEPROMByte(AriesDeviceType* device, int addr, + uint8_t* value); + +AriesErrorType ariesWriteEEPROMByte(AriesDeviceType* device, int addr, + uint8_t* value); + +AriesErrorType ariesCheckConnectionHealth(AriesDeviceType* device, + uint8_t slaveAddress); + +AriesErrorType ariesCheckDeviceHealth(AriesDeviceType* device); + +AriesErrorType ariesGetMaxTemp(AriesDeviceType* device); + +AriesErrorType ariesGetCurrentTemp(AriesDeviceType* device); + +AriesErrorType ariesSetMaxDataRate(AriesDeviceType* device, + AriesMaxDataRateType rate); + +AriesErrorType ariesSetGPIO(AriesDeviceType* device, int gpioNum, bool value); + +AriesErrorType ariesGetGPIO(AriesDeviceType* device, int gpioNum, bool* value); + +AriesErrorType ariesToggleGPIO(AriesDeviceType* device, int gpioNum); + +AriesErrorType ariesSetGPIODirection(AriesDeviceType* device, int gpioNum, + bool value); + +AriesErrorType ariesGetGPIODirection(AriesDeviceType* device, int gpioNum, + bool* value); + +AriesErrorType ariesTestModeEnable(AriesDeviceType* device); + +AriesErrorType ariesTestModeDisable(AriesDeviceType* device); + +AriesErrorType ariesTestModeRateChange(AriesDeviceType* device, + AriesMaxDataRateType rate); + +AriesErrorType ariesTestModeTxConfig(AriesDeviceType* device, + AriesPRBSPatternType pattern, int preset, + bool enable); + +AriesErrorType ariesTestModeTxConfigLane(AriesDeviceType* device, + AriesPRBSPatternType pattern, + int preset, int side, int lane, + bool enable); + +AriesErrorType ariesTestModeRxConfig(AriesDeviceType* device, + AriesPRBSPatternType pattern, bool enable); + +AriesErrorType ariesTestModeRxEcountRead(AriesDeviceType* device, int* ecount); + +AriesErrorType ariesTestModeRxEcountClear(AriesDeviceType* device); + +AriesErrorType ariesTestModeRxEcountClearLane(AriesDeviceType* device, int side, + int lane); + +AriesErrorType ariesTestModeRxFomRead(AriesDeviceType* device, int* fom); + +AriesErrorType ariesTestModeRxValidRead(AriesDeviceType* device); + +AriesErrorType ariesTestModeTxErrorInject(AriesDeviceType* device); + +AriesErrorType ariesFWReloadOnNextSBR(AriesDeviceType* device); + +AriesErrorType ariesOvertempStatusGet(AriesDeviceType* device, bool* value); + +AriesErrorType ariesGetI2CBusClearEventStatus(AriesDeviceType* device, + int* status); + +AriesErrorType ariesClearI2CBusClearEventStatus(AriesDeviceType* device); + +#ifdef __cplusplus +} +#endif + +#endif /* ASTERA_ARIES_SDK_API_H_ */ diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_api_types.h b/common/recipes-lib/retimer-v2.16.2/files/aries_api_types.h new file mode 100755 index 000000000000..643eccd1d000 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_api_types.h @@ -0,0 +1,573 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_api_types.h + * @brief Definition of enums and structs used by aries_api. + */ +#pragma once +#ifndef ASTERA_ARIES_SDK_API_TYPES_H_ +#define ASTERA_ARIES_SDK_API_TYPES_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Enumerator Definitions + */ + +/** + * @brief Enumeration of Aries product part numbers + */ +typedef enum AriesDevicePart +{ + ARIES_PTX16 = 0x0, /**< PTx16xx part numbers */ + ARIES_PTX08 = 0x1 /**< PTx08xx part numbers */ +} AriesDevicePartType; + +/** + * @brief Enumeration of Link bifurcation modes + */ +typedef enum AriesBifurcation +{ + ARIES_PTX16_X16 = 0x0, /**< PTx16 x16 mode */ + ARIES_PTX16_X8X8 = 0x3, /**< PTx16 x8x8 mode */ + ARIES_PTX16_X8X4X4 = 0x4, /**< PTx16 x8x4x4 mode */ + ARIES_PTX16_X4X4X8 = 0x5, /**< PTx16 x4x4x8 mode */ + ARIES_PTX16_X4X4X4X4 = 0x6, /**< PTx16 x4x4x4x4 mode */ + ARIES_PTX16_X2X2X2X2X2X2X2X2 = 0x7, /**< PTx16 x2x2x2x2x2x2x2x2 mode */ + ARIES_PTX08_X8 = 0x1, /**< PTx08 x8 mode */ + ARIES_PTX08_X4X4 = 0x1c, /**< PTx08 x4x4 mode */ + ARIES_PTX08_X2X2X4 = 0x1d, /**< PTx08 x2x2x4 mode */ + ARIES_PTX08_X4X2X2 = 0x1e, /**< PTx08 x4x2x2 mode */ + ARIES_PTX08_X2X2X2X2 = 0x1f /**< PTx08 x2x2x2x2 mode */ +} AriesBifurcationType; + +/** + * @brief Enumeration of Link states + */ +typedef enum AriesLinkStateEnum +{ + ARIES_STATE_RESET = 0x0, /**< Reset state */ + ARIES_STATE_PROTOCOL_RESET_0 = 0x1, /**< Protocol Reset state */ + ARIES_STATE_PROTOCOL_RESET_1 = 0x2, /**< Protocol Reset state */ + ARIES_STATE_PROTOCOL_RESET_2 = 0x3, /**< Protocol Reset state */ + ARIES_STATE_PROTOCOL_RESET_3 = 0x4, /**< Protocol Reset state */ + ARIES_STATE_DETECT_OR_DESKEW = 0x5, /**< Receiver Detect or Deskew state */ + ARIES_STATE_FWD = 0x6, /**< Forwarding state (i.e. L0) */ + ARIES_STATE_EQ_P2_0 = 0x7, /**< Equalization Phase 2 state */ + ARIES_STATE_EQ_P2_1 = 0x8, /**< Equalization Phase 2 state */ + ARIES_STATE_EQ_P3_0 = 0x9, /**< Equalization Phase 3 state */ + ARIES_STATE_EQ_P3_1 = 0xa, /**< Equalization Phase 3 state */ + ARIES_STATE_HOT_PLUG = 0xb, /**< Hot Plug state */ + ARIES_STATE_PROTOCOL_RESET_4 = 0xc, /**< Protocol Reset state */ + ARIES_STATE_OTHER = 0xd /**< Other (unexpected) state */ +} AriesLinkStateEnumType; + +/** + * @brief Enumeration of LTSSM logger verbosity + */ +typedef enum AriesLTSSMVerbosity +{ + ARIES_LTSSM_VERBOSITY_HIGH = 0x1 /**< High verbosity */ +} AriesLTSSMVerbosityType; + +/** + * @brief Enumeration of Link and Path LTSSM log types + */ +typedef enum AriesLTSSMLoggerEnum +{ + ARIES_LTSSM_LINK_LOGGER = 0xff, /**< Link-level logger */ + ARIES_LTSSM_DS_LN_0_1_LOG = 0x0, /**< Downstream, lanes 0 & 1 */ + ARIES_LTSSM_DS_LN_2_3_LOG = 0x2, /**< Downstream, lanes 2 & 3 */ + ARIES_LTSSM_DS_LN_4_5_LOG = 0x4, /**< Downstream, lanes 4 & 5 */ + ARIES_LTSSM_DS_LN_6_7_LOG = 0x6, /**< Downstream, lanes 6 & 7 */ + ARIES_LTSSM_DS_LN_8_9_LOG = 0x8, /**< Downstream, lanes 8 & 9 */ + ARIES_LTSSM_DS_LN_10_11_LOG = 0xa, /**< Downstream, lanes 10 & 11 */ + ARIES_LTSSM_DS_LN_12_13_LOG = 0xc, /**< Downstream, lanes 12 & 13 */ + ARIES_LTSSM_DS_LN_14_15_LOG = 0xe, /**< Downstream, lanes 14 & 15 */ + ARIES_LTSSM_US_LN_0_1_LOG = 0x1, /**< Upstream, lanes 0 & 1 */ + ARIES_LTSSM_US_LN_2_3_LOG = 0x3, /**< Upstream, lanes 2 & 3 */ + ARIES_LTSSM_US_LN_4_5_LOG = 0x5, /**< Upstream, lanes 4 & 5 */ + ARIES_LTSSM_US_LN_6_7_LOG = 0x7, /**< Upstream, lanes 6 & 7 */ + ARIES_LTSSM_US_LN_8_9_LOG = 0x9, /**< Upstream, lanes 8 & 9 */ + ARIES_LTSSM_US_LN_10_11_LOG = 0xb, /**< Upstream, lanes 10 & 11 */ + ARIES_LTSSM_US_LN_12_13_LOG = 0xd, /**< Upstream, lanes 12 & 13 */ + ARIES_LTSSM_US_LN_14_15_LOG = 0xf /**< Upstream, lanes 14 & 15 */ +} AriesLTSSMLoggerEnumType; + +/** + * @brief Enumeration of SRAM program memory check + */ +typedef enum AriesSramMemoryCheck +{ + ARIES_SRAM_MM_CHECK_IDLE = 0, /**< Idle state */ + ARIES_SRAM_MM_CHECK_IN_PROGRESS = 1, /**< Memory self-check in progress */ + ARIES_SRAM_MM_CHECK_PASS = 2, /**< Memory self-check has passed */ + ARIES_SRAM_MM_CHECK_FAIL = 3 /**< Memory self-check has failed */ +} AriesSramMemoryCheckType; + +/** + * @brief Enumeration of Retimer data rates + */ +typedef enum AriesMaxDataRate +{ + ARIES_GEN1 = 1, /**< PCIe Gen-1 datarate (2.5 GT/s) */ + ARIES_GEN2 = 2, /**< PCIe Gen-2 datarate (5 GT/s) */ + ARIES_GEN3 = 3, /**< PCIe Gen-3 datarate (8 GT/s) */ + ARIES_GEN4 = 4, /**< PCIe Gen-4 datarate (16 GT/s) */ + ARIES_GEN5 = 5 /**< PCIe Gen-5 datarate (32 GT/s) */ +} AriesMaxDataRateType; + +/** + * @brief Enumeration of Retimer PRBS patterns + */ +typedef enum AriesPRBSPattern +{ + DISABLED = 0, /**< Disabled */ + LFSR31 = 1, /**< X^31 + X^28 + 1 */ + LFSR23 = 2, /**< X^23 + X^18 + 1 */ + LFSR23_ALT = 3, /**< X^23 + X^21 + X^16 + X^8 + X^5 + X^2 + 1 */ + LFSR16 = 4, /**< X^16 + X^5 + X^4 + X^3 + 1 */ + LFSR15 = 5, /**< X^15 + X^14 + 1 */ + LFSR11 = 6, /**< X^11 + X^9 + 1 */ + LFSR9 = 7, /**< X^9 + X^5 + 1 */ + LFSR7 = 8, /**< X^7 + X^6 + 1 */ + FIXED_PAT0 = 9, /**< Fixed word (PAT0) (10 bits) */ + FIXED_PAT0_ALT = 10, /**< DC balanced word (PAT0, ~PAT0) (20 bits) */ + FIXED_PAT0_ALT_PAD = 11 /**< (000, PAT0, 3ff, ~PAT0) (40 bits) */ +} AriesPRBSPatternType; + +/** + * @brief Enumeration of I2C transaction format + */ +typedef enum AriesI2cFormat +{ + ARIES_I2C_FORMAT_INTEL, /**< Intel long format read/write transactions */ + ARIES_I2C_FORMAT_ASTERA, /**< Astera short format read/write transactions */ + ARIES_I2C_FORMAT_CMBND /**< Astera short format with combined read + transactions + (only supported on PT5xx with hosts that + support the SMBus block process call) */ +} AriesI2CFormatType; + +/** + * @brief Enumeration of Packet Error Checking (PEC) + */ +typedef enum AriesI2cPecEnable +{ + ARIES_I2C_PEC_ENABLE, /**< Enable PEC during I2C transactions */ + ARIES_I2C_PEC_DISABLE /**< Disable PEC during I2C transactions */ +} AriesI2CPECEnableType; + +/** + * @brief Enumeration of firmware image format + */ +typedef enum AriesFWImageFormat +{ + ARIES_FW_IMAGE_FORMAT_IHX, /**< Intel hex firmware image format */ + ARIES_FW_IMAGE_FORMAT_BIN /**< Binary firmware image format */ +} AriesFWImageFormatType; + +/** + * @brief Enumeration of firmware update progress + */ +typedef enum AriesFWUpdateProgress +{ + ARIES_FW_UPDATE_PROGRESS_START, /**< Firmware update start */ + ARIES_FW_UPDATE_PROGRESS_WRITE_0, /**< Firmware write 0% */ + ARIES_FW_UPDATE_PROGRESS_WRITE_10, /**< Firmware write 10% */ + ARIES_FW_UPDATE_PROGRESS_WRITE_20, /**< Firmware write 20% */ + ARIES_FW_UPDATE_PROGRESS_WRITE_30, /**< Firmware write 30% */ + ARIES_FW_UPDATE_PROGRESS_WRITE_40, /**< Firmware write 40% */ + ARIES_FW_UPDATE_PROGRESS_WRITE_50, /**< Firmware write 50% */ + ARIES_FW_UPDATE_PROGRESS_WRITE_60, /**< Firmware write 60% */ + ARIES_FW_UPDATE_PROGRESS_WRITE_70, /**< Firmware write 70% */ + ARIES_FW_UPDATE_PROGRESS_WRITE_80, /**< Firmware write 80% */ + ARIES_FW_UPDATE_PROGRESS_WRITE_90, /**< Firmware write 90% */ + ARIES_FW_UPDATE_PROGRESS_WRITE_DONE, /**< Firmware write done */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_0, /**< Firmware update verify 0% */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_10, /**< Firmware update verify 10% */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_20, /**< Firmware update verify 20% */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_30, /**< Firmware update verify 30% */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_40, /**< Firmware update verify 40% */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_50, /**< Firmware update verify 50% */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_60, /**< Firmware update verify 60% */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_70, /**< Firmware update verify 70% */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_80, /**< Firmware update verify 80% */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_90, /**< Firmware update verify 90% */ + ARIES_FW_UPDATE_PROGRESS_VERIFY_DONE, /**< Firmware update verify done */ + ARIES_FW_UPDATE_PROGRESS_COMPLETE /**< Firmware update complete */ +} AriesFWUpdateProgressType; + +/** + * @brief Enumeration of device orientation + */ +typedef enum AriesOrientation +{ + ARIES_ORIENTATION_NORMAL = 0, /**< Normal Orientation. Value is 0 */ + ARIES_ORIENTATION_REVERSED = 1 /**< Reversed Orientation. Value is 1 */ +} AriesOrientationType; + +/** + * @brief Enumeration of pseudo port + */ +typedef enum AriesPseudoPort +{ + ARIES_UP_STREAM_PSEUDO_PORT = 0, /**< USPP. Value is 0 */ + ARIES_DOWN_STREAM_PSEUDO_PORT = 1 /**< DSPP. Value is 1 */ +} AriesPseudoPortType; + +/* + * Structure Definitions + */ + +/** + * @brief Struct defining pesudo port physical info + */ +typedef struct AriesPseudoPortPins +{ + char rxPin[9]; /**< Rx pin location */ + char txPin[9]; /**< Tx pin location */ + int rxPackageInversion; /**< Rx package inversion flag */ + int txPackageInsersion; /**< Tx package inversion flag */ +} AriesPseudoPortPinsType; + +/** + * @brief Struct defining one set of pseudo port pins for a give lane + */ +typedef struct AriesPins +{ + int lane; /**< Lane number */ + AriesPseudoPortPinsType pinSet1; /**< pseudo port pins set upstream */ + AriesPseudoPortPinsType pinSet2; /**< pseudo port pins set downstream */ +} AriesPinsType; + +/** + * @brief Struct defining I2C/SMBus connection with an Aries device + */ +typedef struct AriesI2CDriver +{ + int handle; /**< File handle to I2C connection */ + int slaveAddr; /**< Slave Address */ + AriesI2CFormatType i2cFormat; /**< I2C format (Astera or Intel) */ + AriesI2CPECEnableType pecEnable; /**< Enable PEC */ + bool mmWideRegisterValid; /**< Flag for MM assisted Wide Register access */ + int lock; /**< Counter indicating if device reads are locked */ + bool lockInit; /**< Flag indicating if lock has been initialized */ +} AriesI2CDriverType; + +/** + * @brief Struct defining FW version loaded on an Aries device + */ +typedef struct AriesFWVersion +{ + uint8_t major; /**< FW version major release value */ + uint8_t minor; /**< FW version minor release value */ + uint16_t build; /**< FW version build release value */ + bool isComplianceFW; /**< Flag to indicate compliance FW */ +} AriesFWVersionType; + +/** + * @brief Struct defining EEPROM details + */ +typedef struct AriesEEPROM +{ + int pageCount; /**< Number of EEPROM pages */ + int pageSize; /**< Size of EEPROM pages */ + int maxBurstSize; /**< Max EEPROM burst size */ + int blockWriteSize; /**< MM EEPROM block write size */ + int blockBaseAddr; /**< MM EEPROM data block base address */ + int blockCmdModifier; /**< MM EEPROM command modifier code */ +} AriesEEPROMType; + +/** + * @brief Struct defining Aries Retimer device + */ +typedef struct AriesDevice +{ + AriesI2CDriverType* i2cDriver; /**< I2C driver to Aries */ + AriesFWVersionType fwVersion; /**< FW version loaded on Aries */ + int revNumber; /**< Aries revision num */ + int deviceId; /**< Aries device ID */ + int vendorId; /**< Aries vendor ID */ + int i2cBus; /**< I2C Bus connection to host BMC */ + uint8_t lotNumber[6]; /**< 6-byte silicon lot number */ + uint8_t chipID[12]; /**< 12-byte unique chip ID */ + bool arpEnable; /**< Flag indicating if ARP is enabled or not */ + bool codeLoadOkay; /**< Flag to indicate if code load reg value is + expected */ + bool mmHeartbeatOkay; /**< Flag indicating if Main Micro heartbeat is + good */ + int mm_print_info_struct_addr; /**< AL print info struct offset + (Main Micro) */ + int pm_print_info_struct_addr; /**< AL print info struct offset + (Path Micro) */ + int mm_gp_ctrl_sts_struct_addr; /**< GP ctrl status struct offset + (Main Micro) */ + int pm_gp_ctrl_sts_struct_addr; /**< GP ctrl status struct offset + (Path Micro) */ + int linkPathStructSize; /**< Num bytes in link path struct */ + int numLinks; /**< Number of bifurcated links on retimer */ + bool eFuseRead; /**< Flag to indicate if eFuse has been read */ + uint8_t tempCalCodePmaA[4]; /**< Temp calibration codes for PMA A */ + uint8_t tempCalCodePmaB[4]; /**< Temp calibration codes for PMA B */ + uint8_t tempCalCodeAvg; /**< Average temp calibration code */ + int tempCalCodeRefTempC; /**< Temp calibration code reference + temperature, in degrees C */ + uint8_t eFuseCPVersion; /**< eFuse CP program version */ + float tempAlertThreshC; /**< Temp alert (Celsius) */ + float tempWarnThreshC; /**< Temp warning (Celsius) */ + float maxTempC; /**< Max. temp seen across all temp sensors */ + float currentTempC; /**< Current average temp across all sensors */ + uint8_t minLinkFoMAlert; /**< Min. FoM value expected */ + bool deviceOkay; /**< Device state */ + bool overtempAlert; /**< Over temp alert indicated by reg */ + AriesDevicePartType partNumber; /**< Retimer part number - x16 or x8 */ + char fullPartNumber[8]; /**< Full device name string */ + AriesPinsType pins[16]; /**< Device physical pin information */ + AriesFWUpdateProgressType fwUpdateProg; /**< Firmware update progress */ + AriesEEPROMType eeprom; /**< Struct containing EEPROM information */ +} AriesDeviceType; + +/** + * @brief Struct defining detailed Transmitter status, including electrical + * parameters + */ +typedef struct AriesTxState +{ + int logicalLaneNum; /**< Captured logical Lane number */ + char* physicalPinName; /**< Physical pin name */ + int elecIdleStatus; /**< Electrical idle status */ + int de; /**< Current de-emphasis value (Gen-1/2 and above) */ + int pre; /**< Current pre-cursor value (Gen-3 and above) */ + int cur; /**< Current main-cursor value (Gen-3 and above) */ + float pst; /**< Current post-cursor value (Gen-3 and above) */ + int lastEqRate; /**< Last speed (e.g. 4 for Gen4) at which EQ was done */ + int lastPresetReq; /**< If final request was a preset, this is the value */ + int lastPreReq; /**< If final request was a coefficient, this is the pre */ + int lastCurReq; /**< If final request was a coefficient, this is the cur */ + int lastPstReq; /**< If final request was a coefficient, this is the pst */ +} AriesTxStateType; + +/** + * @brief Struct defining detailed Receiver status, including electrical + * parameters + */ +typedef struct AriesRxState +{ + int logicalLaneNum; /**< Captured logical Lane number */ + char* physicalPinName; /**< Physical pin name */ + int termination; /**< Termination enable (1) or disable (0) status */ + int polarity; /**< Polarity normal (0) or inverted (1) status */ + float attdB; /**< Rx Attenuator, in DB */ + float vgadB; /**< Rx VGA, in DB */ + float ctleBoostdB; /**< CTLE boost, in dB */ + int ctlePole; /**< CTLE pole code setting */ + int adaptIq; /**< Adapt IQ value */ + int adaptIqB0; /**< Adapt IQ value for bank 0 */ + int adaptDoneB0; /**< Adapt done value for bank 0 */ + int adaptIqB1; /**< Adapt IQ value for bank 1 */ + int adaptDoneB1; /**< Adapt done value for bank 1 */ + int adaptIqB2; /**< Adapt IQ value for bank 2 */ + int adaptDoneB2; /**< Adapt done value for bank 2 */ + float dfe1; /**< DFE tap 1 value, in mV */ + float dfe2; /**< DFE tap 2 value, in mV */ + float dfe3; /**< DFE tap 3 value, in mV */ + float dfe4; /**< DFE tap 4 value, in mV */ + float dfe5; /**< DFE tap 5 value, in mV */ + float dfe6; /**< DFE tap 6 value, in mV */ + float dfe7; /**< DFE tap 7 value, in mV */ + float dfe8; /**< DFE tap 8 value, in mV */ + int FoM; /**< Current FoM value */ + int lastEqRate; /**< Last speed (e.g. 4 for Gen4) at which EQ was done */ + int lastPresetNumReq; /**< Number of preset requests in previous cycle */ + int lastPresetReq[10]; /**< Array of preset requests in previous cycle */ + int lastPresetReqFom[10]; /**< Array of preset request FoM values from + previous cycle */ +} AriesRxStateType; + +/** + * @brief Struct defining detailed Pseudo Port status, including electrical + * parameters + * + * Note that each Aries Link has two Pseudo Ports, and Upstream Pseudo Port + * (USPP) and a Downstream Pseudo Port (DSPP). + */ +typedef struct AriesPseudoPortState +{ + AriesTxStateType txState[16]; /**< Detailed Tx state */ + AriesRxStateType rxState[16]; /**< Detailed Tx state */ +} AriesPseudoPortStateType; + +/** + * @brief Struct defining detailed "Retimer core" status + * + * Note that the "Retimer core" is sandwiched between the Upstream and + * Downstream Pseudo Ports. + */ +typedef struct AriesRetimerCoreState +{ + float usppTempC[16]; /**< USPP Lane temperature (in degrees Celsius) */ + float dsppTempC[16]; /**< DSPP Lane temperature (in degrees Celsius) */ + int dsDeskewNs[16]; /**< Downstream Path deskew (in nanoseconds) */ + int usDeskewNs[16]; /**< Upstream Path deskew (in nanoseconds) */ + bool usppTempAlert[16]; /**< USPP lane temperature alert */ + bool dsppTempAlert[16]; /**< DSPP lane temperature alert */ + int usppPathHWState[16]; /**< Upstream Path HW state */ + int dsppPathHWState[16]; /**< Downstream Path HW state */ + int usppPathFWState[16]; /**< Upstream Path FW state */ + int dsppPathFWState[16]; /**< Downstream Path FW state */ +} AriesRetimerCoreStateType; + +/** + * @brief Struct defining detailed Link status, including electrical + * parameters + * + * Note that each Aries Retimer can support multiple multi-Lane Links. + */ +typedef struct AriesLinkState +{ + float usppSpeed; /**< USPP speed */ + float dsppSpeed; /**< DSPP speed */ + int width; /**< Width of the Link */ + int detWidth; /**< Detected width of the Link */ + int curWidth; /**< Current width of the Link */ + AriesLinkStateEnumType state; /**< Current Link state */ + int rate; /**< Current data rate (1=Gen1, 5=Gen5) */ + bool linkOkay; /**< Link is healthy (true) or not (false) */ + AriesPseudoPortStateType usppState; /**< Current USPP state */ + AriesRetimerCoreStateType coreState; /**< Current RT core state */ + AriesPseudoPortStateType dsppState; /**< Current DSPP state */ + int linkMinFoM; /**< Minimum FoM value across all Lanes on USPP and DSPP */ + char* linkMinFoMRx; /**< Receiver corresponding to minimum FoM */ + int recoveryCount; /**< Count of entries to Recovery since L0 at current + speed */ +} AriesLinkStateType; + +/** + * @brief Struct defining an Aries Retimer Link configuration + * + * Note that each Aries Retimer can support multiple multi-Lane Links. + */ +typedef struct AriesLinkConfig +{ + AriesDevicePartType partNumber; /**< Aries part number */ + int maxWidth; /**< Maximum Link width (e.g. 16) */ + int startLane; /**< Starting (physical) Lane number */ + float tempAlertThreshC; /**< Temperature alert threshold, in Celsius */ + int linkId; /**< Link identifier (starts from 0) */ +} AriesLinkConfigType; + +/** + * @brief Struct defining an Aries Link + * + * Note that each Aries Retimer can support multiple multi-Lane Links. + */ +typedef struct AriesLink +{ + AriesDeviceType* device; /**< Pointer to device struct */ + AriesLinkConfigType config; /**< Struct with link config settings */ + AriesLinkStateType state; /**< Struct with link stats returned back */ +} AriesLinkType; + +/** + * @brief Struct defining an Aries LTSSM Logger entry + */ +typedef struct AriesLTSSMEntry +{ + uint8_t data; /**< Data entry from LTSSM module */ + int logType; /**< Log type i.e. if Main Mirco (0xff) or Path Micro ID */ + int offset; /**< Logger offset inside LTSSM module */ +} AriesLTSSMEntryType; + +/** + * @brief Struct defining an Aries EEPROM delta value + */ +typedef struct AriesEEPROMDelta +{ + int address; /**< Data address in image */ + uint8_t data; /**< Data value */ +} AriesEEPROMDeltaType; + +/** + * @brief Struct defining paramaters for a given link inside link set + */ +typedef struct AriesBifurcationLinkParams +{ + int startLane; /**< Start lane for a link */ + int linkWidth; /**< Width of a this link */ + int linkId; /**< Link num inside link set */ +} AriesBifurcationLinkParamsType; + +/** + * @brief Struct defining paramaters for the entire link + */ +typedef struct AriesBifurcationParams +{ + int numLinks; /**< Num links part of this link set. + Max links inside a link set is 8 */ + AriesBifurcationLinkParamsType links[8]; /**< Link properties in link set */ +} AriesBifurcationParamsType; + +/** + * @brief Struct defining data read from receiver lanes during margining + */ +typedef struct AriesRxMarginData +{ + float eyeWidthLeft; /**< Eye lower width bounds */ + float eyeWidthRight; /**< Eye upper width bounds */ + float eyeHeightDown; /**< Eye lower height bounds */ + float eyeHeightUp; /**< Eye upper height bounds */ + uint8_t presetBefore; /**< Preset request applied before margining */ + uint8_t fomBefore; /**< Lane FoM value from before margining */ + uint8_t + recovCount; /**< Number of recoveries that occurred during margining */ + uint8_t presetAfter; /**< Preset request applied after margining */ + uint8_t fomAfter; /**< Lane FoM value from after margining */ + int dfe1; /**< DFE tap 1 value (dB) */ + int dfe2; /**< DFE tap 2 value (dB)*/ + uint8_t iq; /**< Rx adapt IQ value */ +} AriesRxMarginDataType; + +/** + * @brief Struct defining receiver margining + */ +typedef struct AriesRxMargin +{ + AriesDeviceType* device; /**< Device we are margining */ + AriesDevicePartType partNumber; /**< Device part number */ + AriesOrientationType orientation; /**< Margin device orientation */ + bool do1XAnd0XCapture; /**< Capture both the 1X and 0X eye results */ + int errorCountLimit; /**< Max number of errors per lane till margining + stops */ + uint8_t errorCount[2][16]; /**< Array to store error counts for each port + and lane */ + AriesRxMarginDataType marginData[2][16]; /**< Array to store margining data + for each port and lane */ + uint8_t eyeResults[2][16][15][15]; /**< Array to store eye diagram data for + each port and lane */ +} AriesRxMarginType; + +#ifdef __cplusplus +} +#endif + +#endif // ASTERA_ARIES_SDK_API_TYPES_H_ diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_error.h b/common/recipes-lib/retimer-v2.16.2/files/aries_error.h new file mode 100755 index 000000000000..d192a354abce --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_error.h @@ -0,0 +1,98 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_error.h + * @brief Definition of error types for the SDK. + */ +#pragma once +#ifndef ASTERA_ARIES_SDK_ERROR_H_ +#define ASTERA_ARIES_SDK_ERROR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Used to avoid warnings in case of unused parameters in function pointers + */ +#define ARIES_UNUSED(x) (void)(x) + +/** + * @brief Aries Error enum + * + * Enumeration of return values from the aries* functions within the SDK. + * Value of 0 is a generic success response + * Values less than 1 are specific error return codes + */ +typedef enum +{ + /* General purpose errors */ + /** Success return value, no error occurred */ + ARIES_SUCCESS = 0, + /** Generic failure code */ + ARIES_FAILURE = -1, + /** Invalid function argument */ + ARIES_INVALID_ARGUMENT = -2, + /** Failed to open connection to I2C slave */ + ARIES_I2C_OPEN_FAILURE = -3, + + /* I2C Access failures*/ + /** I2C read transaction to Aries failure */ + ARIES_I2C_BLOCK_READ_FAILURE = -4, + /** I2C write transaction to Aries failure */ + ARIES_I2C_BLOCK_WRITE_FAILURE = -5, + /** Indirect SRAM access mechanism did not provide expected status */ + ARIES_FAILURE_SRAM_IND_ACCESS_TIMEOUT = -6, + + /* EEPROM Write and Read thru access failures */ + /** Main Micro status update to program EEPROM failed */ + ARIES_EEPROM_MM_STATUS_BUSY = -7, + /** Data read back from EEPROM did not match expectations */ + ARIES_EEPROM_VERIFY_FAILURE = -8, + + /* Main Micro access failures */ + /** Failure in PMA access via Main Micro */ + ARIES_PMA_MM_ACCESS_FAILURE = -9, + + /* Link and logger configuration failures */ + /** Invalid link configuration */ + ARIES_LINK_CONFIG_INVALID = -10, + /** Invalid LTSSM Logger entry */ + ARIES_LTSSM_INVALID_ENTRY = -11, + + /** SDK Function not yet implemented */ + ARIES_FUNCTION_NOT_IMPLEMENTED = -12, + + /** EEPROM write error */ + ARIES_EEPROM_WRITE_ERROR = -13, + + /** EEPROM CRC block fail */ + ARIES_EEPROM_CRC_BLOCK_NUM_FAIL = -14, + /** EEPROM CRC block byte fail */ + ARIES_EEPROM_CRC_BYTE_FAIL = -15, + + /** Temperature read value not ready */ + ARIES_TEMP_READ_NOT_READY = -16, + + /** The current firmware version does not support this feature */ + ARIES_FW_VER_NOT_SUPPORTED = -17 +} AriesErrorType; + +#ifdef __cplusplus +} +#endif + +#endif /* ASTERA_ARIES_SDK_ERROR_H_ */ diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_globals.h b/common/recipes-lib/retimer-v2.16.2/files/aries_globals.h new file mode 100755 index 000000000000..5fc132625a11 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_globals.h @@ -0,0 +1,192 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_globals.h + * @brief Definition of enums and structs globally used by the SDK. + */ +#pragma once +#ifndef ASTERA_ARIES_SDK_GLOBALS_H +#define ASTERA_ARIES_SDK_GLOBALS_H + +/////////////////////////////////////// +////////// EEPROM parameters ////////// +/////////////////////////////////////// + +/** EEPROM Page Count */ +#define ARIES_EEPROM_PAGE_COUNT 1024 +/** EEPROM Page Size */ +#define ARIES_EEPROM_PAGE_SIZE 256 +/** Max Burst Size */ +#define ARIES_MAX_BURST_SIZE 256 +/** EEPROM MM block write size */ +#define ARIES_EEPROM_BLOCK_WRITE_SIZE_WIDE 16 +#define ARIES_EEPROM_BLOCK_WRITE_SIZE_NOWIDE 32 +#define ARIES_EEPROM_BLOCK_WRITE_SIZE_MAX 32 // 32 bytes max +#define ARIES_EEPROM_BLOCK_WRITE_SIZE_NUM_BLOCKS_MAX 8 // eight 4-byte blocks +/** EEPROM MM data block base address */ +#define ARIES_EEPROM_BLOCK_BASE_ADDR_WIDE 0x410 +#define ARIES_EEPROM_BLOCK_BASE_ADDR_NOWIDE 0x88e7 +/** EEPROM MM command modifier code */ +#define ARIES_EEPROM_BLOCK_CMD_MODIFIER_WIDE 0x0 +#define ARIES_EEPROM_BLOCK_CMD_MODIFIER_NOWIDE 0x80 +/** EEPROM MM block checksum write size */ +#define ARIES_EEPROM_MM_BLOCK_CHECKSUM_WRITE_SIZE 8 + +/** Total EEPROM size (in bytes) */ +#define ARIES_EEPROM_MAX_NUM_BYTES 262144 + +/** Value indicating FW was loaded properly */ +#define ARIES_LOAD_CODE 0xe + +/** Time delay between checking MM status of EEPROM write (microseconds) */ +// #define ARIES_MM_STATUS_TIME 1000 +#define ARIES_MM_STATUS_TIME 5000 +/** Time between resetting command byte in EEPROM read */ +#define ARIES_I2C_MASTER_CMD_RST 2000 +// #define ARIES_I2C_MASTER_CMD_RST 1000 +/** Delay after writing data in rewrite and re-verify step */ +#define ARIES_I2C_MASTER_WRITE_DELAY 50000 +// #define ARIES_I2C_MASTER_WRITE_DELAY 5000 +/** Time delay after sending EEPROM read command to Main Micro */ +#define ARIES_MM_READ_CMD_WAIT 10000 +/** Time allocated to calculate checksum (secs) */ +#define ARIES_MM_CALC_CHECKSUM_WAIT 6 +/** Time allocated to calculate checksum (micro secs) */ +#define ARIES_MM_CALC_CHECKSUM_TRY_TIME 10000 +/** Time allocated for Rx adaptation (microseconds) */ +#define ARIES_PIPE_RXEQEVAL_TIME_US 100000 +/** Time allocated for PMA register access to complete (microseconds) */ +#define ARIES_PMA_REG_ACCESS_TIME_US 100 + +/** Num banks per EEPROM (num slaves) */ +#define ARIES_EEPROM_NUM_BANKS 4 +/** EEPROM Bank Size (num slaves) */ +#define ARIES_EEPROM_BANK_SIZE 0x10000 + +/** Main Micro codes for writing and reading EEPROM via MM-assist */ +#define ARIES_MM_EEPROM_WRITE_REG_CODE 1 +#define ARIES_MM_EEPROM_WRITE_END_CODE 2 +#define ARIES_MM_EEPROM_READ_END_CODE 3 +#define ARIES_MM_EEPROM_READ_REG_CODE 4 + +/** Main Micro code for checksum computation */ +#define ARIES_MM_EEPROM_CHECKSUM_CODE 16 + +/** Main Micro code for checksum computation (partial) */ +#define ARIES_MM_EEPROM_CHECKSUM_PARTIAL_CODE 17 + +/** Num EEPROM CRC blocks */ +#define ARIES_EEPROM_MAX_NUM_CRC_BLOCKS 10 + +////////////////////////////////////// +////////// Delay parameters ////////// +////////////////////////////////////// + +/** Wait time after completing an EEPROM block write-thru */ +#define ARIES_DATA_BLOCK_PROGRAM_TIME_USEC 10000 +/** Wait time after setting manual training */ +#define ARIES_MAN_TRAIN_WAIT_SEC 0.01 + +////////////////////////////////////// +////////// Memory parameters ////////// +////////////////////////////////////// + +/** Main Micro num of print class enables in logger module */ +#define ARIES_MM_PRINT_INFO_NUM_PRINT_CLASS_EN 8 + +/** Main Micro print buffer size */ +#define ARIES_MM_PRINT_INFO_NUM_PRINT_BUFFER_SIZE 512 + +/** Path Micro num of print class enables in logger module */ +#define ARIES_PM_PRINT_INFO_NUM_PRINT_CLASS_EN 16 + +/** Path Micro print buffer size */ +#define ARIES_PM_PRINT_INFO_NUM_PRINT_BUFFER_SIZE 256 + +/////////////////////////////////////////// +////////// Sides and quad slices ////////// +/////////////////////////////////////////// + +/** Num. sides for PMA */ +#define ARIES_NUM_SIDES 2 + +/** Num. quad slices per slice */ +#define ARIES_NUM_QUAD_SLICES 4 + +////////////////////////////////////////////////////////////// +////////// Main-micro-assisted access command codes ////////// +////////////////////////////////////////////////////////////// + +/** PMA 0, 1 read */ +#define ARIES_MM_RD_PID_IND_PMA0 0x6 +#define ARIES_MM_RD_PID_IND_PMA1 0x7 + +/** PMA 0, 1, and broadcast write */ +#define ARIES_MM_WR_PID_IND_PMA0 0xf +#define ARIES_MM_WR_PID_IND_PMA1 0x10 +#define ARIES_MM_WR_PID_IND_PMAX 0x11 + +/** Wide register reads */ +#define ARIES_MM_RD_WIDE_REG_2B 0x1d +#define ARIES_MM_RD_WIDE_REG_3B 0x1e +#define ARIES_MM_RD_WIDE_REG_4B 0x1f +#define ARIES_MM_RD_WIDE_REG_5B 0x20 + +/** Wide register writes */ +#define ARIES_MM_WR_WIDE_REG_2B 0x21 +#define ARIES_MM_WR_WIDE_REG_3B 0x22 +#define ARIES_MM_WR_WIDE_REG_4B 0x23 +#define ARIES_MM_WR_WIDE_REG_5B 0x24 + +/////////////////////////////////////////////////////////// +//////////// Temperature measurement constants //////////// +/////////////////////////////////////////////////////////// + +/** Aries max temp ADC code CSR */ +#define ARIES_ALL_TIME_MAX_TEMP_ADC_CSR 0x424 + +/** Aries current max temp ADC code CSR */ +#define ARIES_CURRENT_MAX_TEMP_ADC_CSR 0x428 + +/** Aries current average temp ADC code CSR */ +#define ARIES_CURRENT_AVG_TEMP_ADC_CSR 0x42c + +/** Aries temp calibration uncertainty offset */ +#define ARIES_TEMP_CALIBRATION_OFFSET 3 + +////////////////////////////////////////////////////////// +///////////////////// Miscellaneous ////////////////////// +////////////////////////////////////////////////////////// + +/** Maximum path length */ +#define ARIES_PATH_MAX 4096 + +/** CRC8 polynomial used in PEC computation */ +#define ARIES_CRC8_POLYNOMIAL 0x107 + +/** Max number of links a single retimer can support*/ +#define ARIES_MAX_NUM_LINKS 8 + +/////////////////////////////////////////////////////////// +///////////////////// PIPE Interface ////////////////////// +/////////////////////////////////////////////////////////// +#define ARIES_PIPE_POWERDOWN_P0 0 +#define ARIES_PIPE_POWERDOWN_P1 2 + +#define ARIES_PIPE_DEEMPHASIS_DE_NONE -1 +#define ARIES_PIPE_DEEMPHASIS_PRESET_NONE -1 + +#endif /* ASTERA_ARIES_SDK_GLOBALS_H */ diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_i2c.c b/common/recipes-lib/retimer-v2.16.2/files/aries_i2c.c new file mode 100755 index 000000000000..53dc4dcc8a50 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_i2c.c @@ -0,0 +1,2400 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_i2c.c + * @brief Implementation of public functions for the SDK I2C interface. + */ + +#include "aries_i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set Slave address to user-specified value: new7bitSmbusAddr + * + * @param[in] handle Handle to I2C driver + * @param[in] new7bitSmbusAddr Desired I2C (7-bit) address of Retimer + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesRunArp(int handle, uint8_t new7bitSmbusAddr) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t dataBytes19[19] = {0}; + + dataByte[0] = 0xc9; + rc = asteraI2CWriteBlockData(handle, 0x2, 1, dataByte); + CHECK_SUCCESS(rc); + + dataByte[0] = 0xc0; + rc = asteraI2CWriteBlockData(handle, 0x1, 1, dataByte); + CHECK_SUCCESS(rc); + + // This read will get first device repsonse in ARP mode + rc = asteraI2CReadBlockData(handle, 0x3, 19, dataBytes19); + if (rc != 19) + { + return ARIES_I2C_BLOCK_READ_FAILURE; + } + + // The PEC byte is calculated from 20 bytes of data including the ARP slave + // address byte (0x61<<1 = 0xc2), the command code (0x4), and the remainder + // of the Assign Address command. + uint8_t new8bitSmbusAddr = new7bitSmbusAddr << 1; + uint8_t pec_data[21] = { + 0xc2, 4, 17, 1, 9, 29, 250, 0, 1, 0, 21, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}; // last byte is new 8-bit SMBus + // address + pec_data[19] = new8bitSmbusAddr; + uint8_t crc = ariesGetPecByte(pec_data, 21); + + // Program slave address of device to 0x55 + int i; + for (i = 0; i < 18; i++) + { + dataBytes19[i] = pec_data[i + 2]; + } + dataBytes19[18] = crc; // PEC byte + + rc = asteraI2CWriteBlockData(handle, 0x4, 19, dataBytes19); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Write multiple bytes of data at specified address to Aries over I2C. + * This function returns a negative error code if unsuccessful, else zero on + * success. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 17-bit address to write + * @param[in] numBytes Number of bytes to write + * @param[in] values Pointer to array of data (bytes) you wish to write + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteBlockData(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values) +{ + AriesErrorType rc; + // Max of (2+currBytes):Intel and (3+currBytes):Astera + uint8_t writeBuf[7] = {0}; + uint8_t wrBufLength; + + uint8_t cmdCode; + uint8_t pecEn; + uint8_t rsvd; + uint8_t funcCode; + uint8_t start; + uint8_t end; + + uint8_t addr15To8; + uint8_t addr7To0; + + uint8_t currBytes = 0; + uint8_t remainingBytes = numBytes; + if (i2cDriver->i2cFormat == ARIES_I2C_FORMAT_INTEL) + { + // Addresses in this format can be 16 bit only + if (address > 0xffff) + { + ASTERA_ERROR("Address cannot be more than 16 bits in Intel format"); + return ARIES_INVALID_ARGUMENT; + } + + // If byte count is greater than 4, perform multiple iterations + while (remainingBytes > 0) + { + if (remainingBytes > 4) + { + currBytes = 4; + remainingBytes -= 4; + } + else + { + currBytes = remainingBytes; + remainingBytes = 0; + } + + pecEn = 0; + rsvd = 0; + funcCode = 1; + start = 1; + end = 1; + + if (i2cDriver->pecEnable == ARIES_I2C_PEC_ENABLE) + { + pecEn = 1; + } + + ASTERA_TRACE("Reading from address: 0x%08x", address); + + wrBufLength = 2 + currBytes; + + // Construct Command code + cmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 0); + + addr15To8 = (address & 0xff00) >> 8; // Get bits 15:8 + addr7To0 = address & 0xff; // Get bits 7:0 + + // Construct data buffer + writeBuf[0] = addr7To0; + writeBuf[1] = addr15To8; + + ASTERA_TRACE("Write:"); + ASTERA_TRACE(" cmdCode = 0x%02x", cmdCode); + ASTERA_TRACE(" writeBuf[0] = 0x%02x", writeBuf[0]); + ASTERA_TRACE(" writeBuf[1] = 0x%02x", writeBuf[1]); + + // Fill up data buffer + int pos; + int bufPos; + for (pos = 0; pos < currBytes; pos++) + { + bufPos = pos + 2; + writeBuf[bufPos] = values[pos]; + ASTERA_TRACE(" writeBuf[%d] = 0x%02x", bufPos, + writeBuf[bufPos]); + } + + rc = asteraI2CWriteBlockData(i2cDriver->handle, cmdCode, + wrBufLength, writeBuf); + CHECK_SUCCESS(rc); + + // Increment iteration count + values += currBytes; + address += currBytes; + } + } + else if (i2cDriver->i2cFormat == ARIES_I2C_FORMAT_ASTERA || + i2cDriver->i2cFormat == ARIES_I2C_FORMAT_CMBND) + { + // If byte count is greater than 4, perform multiple iterations + while (remainingBytes > 0) + { + if (remainingBytes > 4) + { + currBytes = 4; + remainingBytes -= 4; + } + else + { + currBytes = remainingBytes; + remainingBytes = 0; + } + + pecEn = 0; + rsvd = 0; + funcCode = 3; + start = 1; + end = 1; + + if (i2cDriver->pecEnable == ARIES_I2C_PEC_ENABLE) + { + pecEn = 1; + } + + ASTERA_TRACE("Writing to address: 0x%08x", address); + + wrBufLength = 3 + currBytes; + + // Construct Command code + cmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 0); + + // Construct Config & Offset Upper byte + uint8_t config; + uint8_t addr16; + addr16 = (address & 0x10000) >> 16; // Get 16bit of addr + addr15To8 = (address & 0xff00) >> 8; // Get bits 15:8 + addr7To0 = address & 0xff; // Get bits 7:0 + + uint8_t cfg_type = 1; + uint8_t bdcst = 0; + uint8_t burstLen = currBytes - 1; + + config = (cfg_type << 6) + (bdcst << 4) + (burstLen << 1) + + (addr16 << 0); + + // Construct data buffer + writeBuf[0] = config; + writeBuf[1] = addr15To8; + writeBuf[2] = addr7To0; + + ASTERA_TRACE("Write:"); + ASTERA_TRACE(" cmdCode = 0x%02x", cmdCode); + ASTERA_TRACE(" byteCount = 0x%02x", wrBufLength); + ASTERA_TRACE(" writeBuf[0] = 0x%02x", writeBuf[0]); + ASTERA_TRACE(" writeBuf[1] = 0x%02x", writeBuf[1]); + ASTERA_TRACE(" writeBuf[2] = 0x%02x", writeBuf[2]); + + // Fill up data buffer + int pos; + int bufPos; + for (pos = 0; pos < currBytes; pos++) + { + bufPos = pos + 3; + writeBuf[bufPos] = values[pos]; + ASTERA_TRACE(" writeBuf[%d] = 0x%02x", bufPos, + writeBuf[bufPos]); + } + + // This translates to actual low level library call + // Function definition part of user application + rc = asteraI2CWriteBlockData(i2cDriver->handle, cmdCode, + wrBufLength, writeBuf); + CHECK_SUCCESS(rc); + values += currBytes; + address += currBytes; + } + } + else + { + return ARIES_INVALID_ARGUMENT; + } + return ARIES_SUCCESS; +} + +/** + * @brief Write a data byte at specified address to Aries over I2C. + * This function returns a negative error code if unsuccessful, else zero on + * success. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 17-bit address to write + * @param[in] value Pointer to single element array of data you wish + * to write + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteByteData(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t* value) +{ + return ariesWriteBlockData(i2cDriver, address, 1, value); +} + +/** + * @brief Read multiple bytes of data at specified address from Aries over I2C. + * This function returns a negative error code if unsuccessful, else zero on + * success. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 17-bit address from which to read + * @param[in] numBytes Number of bytes to read + * @param[out] values Pointer to array of read data (bytes) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadBlockData(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values) +{ + AriesErrorType rc; + AriesErrorType lc; + int readBytes; + + uint8_t writeBuf[3] = {0}; // Max of (2):Intel and (3):Astera + uint8_t readBuf[18] = {0}; // Max of (2+4):Intel and (16):Astera, or 18 if + // using I2C block read + + uint8_t wrCmdCode; + uint8_t rdCmdCode; + uint8_t pecEn; + uint8_t rsvd; + uint8_t funcCode; + uint8_t start; + uint8_t end; + + uint8_t addr15To8; // Get bits 15:8 + uint8_t addr7To0; // Get bits 7:0 + + uint8_t tryIndex; + uint8_t readTryCount = 3; + + uint8_t currBytes = 0; + uint8_t remainingBytes = numBytes; + if (i2cDriver->i2cFormat == ARIES_I2C_FORMAT_INTEL) + { + // Addresses in this format can be 16 bit only + if (address > 0xffff) + { + ASTERA_ERROR("Address cannot be more than 16 bits in Intel format"); + return ARIES_INVALID_ARGUMENT; + } + + // If byte count is greater than 4, perform multiple iterations + while (remainingBytes > 0) + { + if (remainingBytes > 4) + { + currBytes = 4; + remainingBytes -= 4; + } + else + { + currBytes = remainingBytes; + remainingBytes = 0; + } + + pecEn = 0; + rsvd = 0; + funcCode = 0; + start = 1; + end = 0; + + if (i2cDriver->pecEnable == ARIES_I2C_PEC_ENABLE) + { + pecEn = 1; + } + + ASTERA_TRACE("Reading from address: 0x%08x", address); + + // Construct command code + wrCmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 0); + + addr15To8 = (address & 0xff00) >> 8; + addr7To0 = address & 0xff; + + // Construct data buffer + writeBuf[0] = addr7To0; + writeBuf[1] = addr15To8; + + ASTERA_TRACE("Write:"); + ASTERA_TRACE(" cmdCode = 0x%02x", wrCmdCode); + ASTERA_TRACE(" byteCount = 0x02"); + ASTERA_TRACE(" writeBuf[0] = 0x%02x", writeBuf[0]); + ASTERA_TRACE(" writeBuf[1] = 0x%02x", writeBuf[1]); + + // Set up I2C lock, to keep write and read atomic + // Do not unblock here on return of error since this error code + // would mean that the block call did not happen + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // Perform read operation + // Try upto 3 times before issuing a block read failure + for (tryIndex = 0; tryIndex < readTryCount; tryIndex++) + { + rc = asteraI2CWriteBlockData(i2cDriver->handle, wrCmdCode, 2, + writeBuf); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + start = 0; + end = 1; + funcCode = 0; + + rdCmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 0); + + ASTERA_TRACE("Read:"); + ASTERA_TRACE(" cmdCode = 0x%02x", rdCmdCode); + + readBytes = asteraI2CReadBlockData(i2cDriver->handle, rdCmdCode, + currBytes, readBuf); + if (readBytes != currBytes) + { + ASTERA_TRACE("Expected %d bytes but got %d", currBytes, + readBytes); + ASTERA_TRACE("Perform read again..."); + + if (tryIndex == (readTryCount - 1)) + { + ASTERA_ERROR("Incorrect num. bytes returned by read"); + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_I2C_BLOCK_READ_FAILURE; + } + } + else + { + break; + } + } + + // Unlock previous lock set before write + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + // Print the values + ASTERA_TRACE("Values read:"); + int pos; + for (pos = 0; pos < currBytes; pos++) + { + values[pos] = readBuf[2 + pos]; + ASTERA_TRACE(" readBuf[%d] = 0x%02x", pos, values[pos]); + } + // Increment iteration count + values += currBytes; + address += currBytes; + } + } + else if (i2cDriver->i2cFormat == ARIES_I2C_FORMAT_ASTERA) + { + // If byte count is greater than 16, perform multiple iterations + while (remainingBytes > 0) + { + if (remainingBytes > 16) + { + currBytes = 16; + remainingBytes -= 16; + } + else + { + currBytes = remainingBytes; + remainingBytes = 0; + } + + pecEn = 0; + rsvd = 0; + funcCode = 2; + start = 1; + end = 0; + + if (i2cDriver->pecEnable == ARIES_I2C_PEC_ENABLE) + { + pecEn = 1; + } + + ASTERA_TRACE("Reading from address: 0x%08x", address); + + // Construct command code + wrCmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 0); + + // Construct Config & Offset Upper byte + uint8_t config; + uint8_t addr16; + addr16 = (address & 0x10000) >> 16; // Get 16bit of addr + addr15To8 = (address & 0xff00) >> 8; // Get bits 15:8 + addr7To0 = address & 0xff; // Get bits 7:0 + + uint8_t cfg_type = 0; + uint8_t bdcst = 0; + uint8_t burstLen = currBytes - 1; + + config = (cfg_type << 6) + (bdcst << 4) + (burstLen << 1) + + (addr16 << 0); + + // Construct data buffer + writeBuf[0] = config; + writeBuf[1] = addr15To8; + writeBuf[2] = addr7To0; + + ASTERA_TRACE("Write:"); + ASTERA_TRACE(" cmdCode = 0x%02x", wrCmdCode); + ASTERA_TRACE(" byteCount = 0x03"); + ASTERA_TRACE(" writeBuf[0] = 0x%02x", writeBuf[0]); + ASTERA_TRACE(" writeBuf[1] = 0x%02x", writeBuf[1]); + ASTERA_TRACE(" writeBuf[2] = 0x%02x", writeBuf[2]); + + // Set up I2C lock, to keep write and read atomic + // Do not unblock here on return of error since this error code + // would mean that the block call did not happen + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // Perform read operation + // Try upto 3 times before issuing a block read failure + for (tryIndex = 0; tryIndex < readTryCount; tryIndex++) + { + // First write address you wish to read from + rc = asteraI2CWriteBlockData(i2cDriver->handle, wrCmdCode, 3, + writeBuf); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + funcCode = 2; + start = 0; + end = 1; + rdCmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 0); + + ASTERA_TRACE("Read:"); + ASTERA_TRACE(" cmdCode = 0x%02x", rdCmdCode); + + // Then issue read + // First byte returned is length + readBytes = asteraI2CReadBlockData(i2cDriver->handle, rdCmdCode, + currBytes + 1 + pecEn, + readBuf); +#ifdef SMBUS_BLOCK_READ_UNSUPPORTED + readBytes -= 1 + pecEn; +#endif + if (readBytes != currBytes) + { + ASTERA_TRACE("Expected %d bytes but got %d", currBytes, + readBytes); + ASTERA_TRACE("Perform read again..."); + + if (tryIndex == (readTryCount - 1)) + { + ASTERA_ERROR("Incorrect num. bytes returned by read"); + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_I2C_BLOCK_READ_FAILURE; + } + } + else + { + break; + } + } + + // Unlock previous lock set before write + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); +#ifdef SMBUS_BLOCK_READ_UNSUPPORTED + // Verify PEC checksum manually if using I2C block write + if (pecEn) + { + // Include write portion of read (addr and wr bit, and cmd Code) + // and extra read portion (addr and rd bit) + uint8_t rdStreamLen = 4 + currBytes + pecEn; + uint8_t rdStream[21]; + uint8_t rdPec; + + rdStream[0] = (i2cDriver->slaveAddr << 1) + 0; + rdStream[1] = rdCmdCode; + rdStream[2] = (i2cDriver->slaveAddr << 1) + 1; + + int pos; + for (pos = 3; pos < rdStreamLen; pos++) + { + rdStream[pos] = readBuf[pos - 3]; + } + + rdPec = ariesGetPecByte(rdStream, rdStreamLen); + if (rdPec != 0) + { + ASTERA_ERROR("PEC value not as expected"); + return ARIES_I2C_BLOCK_READ_FAILURE; + } + } + int i; + for (i = 0; i < readBytes; i++) + { + readBuf[i] = readBuf[i + 1]; + } +#endif + // Print the values + ASTERA_TRACE("Values read:"); + int pos; + for (pos = 0; pos < currBytes; pos++) + { + values[pos] = readBuf[pos]; + ASTERA_TRACE(" readBuf[%d] = 0x%02x", pos, values[pos]); + } + values += currBytes; + address += currBytes; + } + } + else if (i2cDriver->i2cFormat == ARIES_I2C_FORMAT_CMBND) + { + return ariesWriteReadBlockData(i2cDriver, address, numBytes, values); + } + else + { + return ARIES_INVALID_ARGUMENT; + } + return ARIES_SUCCESS; +} + +/** + * @brief Read a data byte at specified address from Aries over I2C. + * This function returns a negative error code if unsuccessful, else zero on + * success. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 17-bit address from which to read + * @param[out] values Pointer to single element array of read data (byte) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadByteData(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t* values) +{ + return ariesReadBlockData(i2cDriver, address, 1, values); +} + +/** + * @brief Read multiple data bytes to Aries over I2C using combined transaction + * format. This function returns a negative error code if unsuccessful, else + * zero on success. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 17-bit address to read + * @param[in] numBytes Number of bytes to read + * @param[in] values Pointer to array of data (bytes) you wish to read + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteReadBlockData(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values) +{ + int readBytes; + + uint8_t writeReadBuf[16]; // Max of (currBytes):Astera + + uint8_t wrrdCmdCode; + uint8_t pecEn; + uint8_t rsvd; + uint8_t funcCode; + uint8_t start; + uint8_t end; + + uint8_t config; + uint8_t cfg_type; + uint8_t bdcst; + uint8_t burstLen; + uint8_t addr16; + uint8_t addr15To8; + uint8_t addr7To0; + + uint8_t currBytes = 0; + uint8_t remainingBytes = numBytes; + // If byte count is greater than 16, perform multiple iterations + while (remainingBytes > 0) + { + if (remainingBytes > 16) + { + currBytes = 16; + remainingBytes -= 16; + } + else + { + currBytes = remainingBytes; + remainingBytes = 0; + } + + pecEn = 0; + rsvd = 0; + funcCode = 2; + start = 1; + end = 0; + + if (i2cDriver->pecEnable == ARIES_I2C_PEC_ENABLE) + { + pecEn = 1; + } + + ASTERA_TRACE("Reading from address: 0x%08x", address); + + // Construct command code + wrrdCmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 1); + + // Construct Config & Offset Upper byte + addr16 = (address & 0x10000) >> 16; // Get 16bit of addr + addr15To8 = (address & 0xff00) >> 8; // Get bits 15:8 + addr7To0 = address & 0xff; // Get bits 7:0 + + cfg_type = 0; + bdcst = 0; + burstLen = currBytes - 1; + + config = (cfg_type << 6) + (bdcst << 4) + (burstLen << 1) + + (addr16 << 0); + + // Construct data buffer + writeReadBuf[0] = config; + writeReadBuf[1] = addr15To8; + writeReadBuf[2] = addr7To0; + + ASTERA_TRACE("WriteRead:"); + ASTERA_TRACE(" cmdCode = 0x%02x", wrrdCmdCode); + ASTERA_TRACE(" byteCount = 0x03"); + ASTERA_TRACE(" writeReadBuf[0] = 0x%02x", writeReadBuf[0]); + ASTERA_TRACE(" writeReadBuf[1] = 0x%02x", writeReadBuf[1]); + ASTERA_TRACE(" writeReadBuf[2] = 0x%02x", writeReadBuf[2]); + + // Perform combined write read operation + readBytes = asteraI2CWriteReadBlockData(i2cDriver->handle, wrrdCmdCode, + 3, writeReadBuf); + if (readBytes != currBytes) + { + ASTERA_TRACE("Expected %d bytes but got %d", currBytes, readBytes); + return ARIES_I2C_BLOCK_READ_FAILURE; + } + + // Print the values + ASTERA_TRACE("Values read:"); + int pos; + for (pos = 0; pos < currBytes; pos++) + { + values[pos] = writeReadBuf[pos]; + ASTERA_TRACE(" writeReadBuf[%d] = 0x%02x", pos, values[pos]); + } + values += currBytes; + address += currBytes; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Read multiple (up to eight) data bytes from micro SRAM over I2C for + * A0. Returns a negative error code, or zero on success + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] microIndStructOffset Micro Indirect Struct Offset + * @param[in] address 16-bit address from which to read + * @param[in] numBytes Number of bytes to read + * @param[out] values Pointer to single element array of read data + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadBlockDataMainMicroIndirectA0( + AriesI2CDriverType* i2cDriver, uint32_t microIndStructOffset, + uint32_t address, uint8_t numBytes, uint8_t* values) +{ + AriesErrorType rc; + AriesErrorType lc; + uint8_t dataByte[1] = {0}; + uint8_t eepromAddrBytes[3] = {0}; + int byteIndex; + + // Grab lock + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // No multi-byte indirect support here. Hence read a byte at a time + for (byteIndex = 0; byteIndex < numBytes; byteIndex++) + { + // Write eeprom addr + int eepromAccAddr = address - AL_MAIN_SRAM_DMEM_OFFSET + byteIndex; + eepromAddrBytes[0] = eepromAccAddr & 0xff; + eepromAddrBytes[1] = (eepromAccAddr >> 8) & 0xff; + eepromAddrBytes[2] = (eepromAccAddr >> 16) & 0xff; + rc = ariesWriteBlockData(i2cDriver, microIndStructOffset, 3, + eepromAddrBytes); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Write eeprom cmd + dataByte[0] = AL_TG_RD_LOC_IND_SRAM; + rc = ariesWriteByteData(i2cDriver, (microIndStructOffset + 4), + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Test successfull access + uint8_t status = 0xff; + uint8_t count = 0; + while ((status != 0) && (count < 0xff)) + { + rc = ariesReadByteData(i2cDriver, (microIndStructOffset + 4), + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + status = dataByte[0]; + count += 1; + } + + if (status != 0) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_FAILURE_SRAM_IND_ACCESS_TIMEOUT; + } + else + { + rc = ariesReadByteData(i2cDriver, (microIndStructOffset + 3), + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + values[(byteIndex)] = dataByte[0]; + } + } + + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + return ARIES_SUCCESS; +} + +/** + * @brief Read multiple (up to eight) data bytes from micro SRAM over I2C. + * Returns a negative error code, or zero on success + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] microIndStructOffset Micro Indirect Struct Offset + * @param[in] address 16-bit address from which to read + * @param[in] numBytes Number of bytes to read + * @param[out] values Pointer to single element array of read data + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadBlockDataMainMicroIndirectMPW( + AriesI2CDriverType* i2cDriver, uint32_t microIndStructOffset, + uint32_t address, uint8_t numBytes, uint8_t* values) +{ + AriesErrorType rc; + AriesErrorType lc; + + if (numBytes > 8) + { + return ARIES_INVALID_ARGUMENT; + } + else + { + // Set aries transaction lock + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + uint8_t curOffset = 0; + uint8_t remainingBytes = numBytes; + uint8_t tmpValue[1]; + while (remainingBytes > 0) + { + uint8_t curRemainingBytes; + if (remainingBytes > 4) + { + curRemainingBytes = 4; + remainingBytes -= 4; + } + else + { + curRemainingBytes = remainingBytes; + remainingBytes = 0; + } + + // Set indirect access command + tmpValue[0] = ((curRemainingBytes - 1) << 5) | + ((((address + curOffset) >> 16) & 0x1) << 1); + rc = ariesWriteByteData(i2cDriver, microIndStructOffset, tmpValue); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Set address upper byte + tmpValue[0] = ((address + curOffset) >> 8) & 0xff; + rc = ariesWriteByteData(i2cDriver, (microIndStructOffset + 1), + tmpValue); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Set address lower byte (this triggers indirect access) + tmpValue[0] = (address + curOffset) & 0xff; + rc = ariesWriteByteData(i2cDriver, (microIndStructOffset + 2), + tmpValue); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Test successfull access + uint8_t status = 0; + uint8_t rdata[1]; + uint8_t count = 0; + while ((status == 0) && (count < 0xff)) + { + rc = ariesReadByteData(i2cDriver, (microIndStructOffset + 0xb), + rdata); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + status = rdata[0] & 0x1; + count += 1; + } + if ((status == 0) || (count == 0xff)) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_FAILURE_SRAM_IND_ACCESS_TIMEOUT; + } + else + { + uint8_t i; + for (i = 0; i < curRemainingBytes; i++) + { + rc = ariesReadByteData( + i2cDriver, (microIndStructOffset + 3 + i), tmpValue); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + values[curOffset + i] = tmpValue[0]; + } + } + curOffset += curRemainingBytes; + } + + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + return ARIES_SUCCESS; + } +} + +/** + * @brief Write multiple data bytes to specified address from micro SRAM over + * I2C for A0. Returns a negative error code, or zero on success + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] microIndStructOffset Micro Indirect Struct Offset + * @param[in] address 16-bit address from which to write + * @param[in] numBytes Number of bytes to write + * @param[out] values Pointer to array of data (bytes) you wish to write + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteBlockDataMainMicroIndirectA0( + AriesI2CDriverType* i2cDriver, uint32_t microIndStructOffset, + uint32_t address, uint8_t numBytes, uint8_t* values) +{ + AriesErrorType rc; + AriesErrorType lc; + int byteIndex; + uint8_t dataByte[1] = {0}; + + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // No multi-byte indirect support here. Hence read a byte at a time + for (byteIndex = 0; byteIndex < numBytes; byteIndex++) + { + // Write eeprom addr + uint8_t eepromAddrBytes[3]; + int eepromAccAddr = address - AL_MAIN_SRAM_DMEM_OFFSET + byteIndex; + eepromAddrBytes[0] = eepromAccAddr & 0xff; + eepromAddrBytes[1] = (eepromAccAddr >> 8) & 0xff; + eepromAddrBytes[2] = (eepromAccAddr >> 16) & 0xff; + rc = ariesWriteBlockData(i2cDriver, microIndStructOffset, 3, + eepromAddrBytes); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Write data + dataByte[0] = values[byteIndex]; + rc = ariesWriteByteData(i2cDriver, (microIndStructOffset + 3), + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Write eeprom cmd + uint8_t eepromCmdByte[1]; + eepromCmdByte[0] = AL_TG_WR_LOC_IND_SRAM; + rc = ariesWriteByteData(i2cDriver, (microIndStructOffset + 4), + eepromCmdByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Test successfull access + uint8_t status = 0xff; + uint8_t rdata[1]; + uint8_t count = 0; + while ((status != 0) && (count < 0xff)) + { + rc = ariesReadByteData(i2cDriver, (microIndStructOffset + 4), + rdata); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + status = rdata[0]; + count += 1; + } + + if (status != 0) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_FAILURE_SRAM_IND_ACCESS_TIMEOUT; + } + } + + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + return ARIES_SUCCESS; +} + +/** + * @brief Write multiple (up to eight) data bytes to specified address from + * micro SRAM over I2C. Returns a negative error code, or zero on success + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] microIndStructOffset Micro Indirect Struct Offset + * @param[in] address 16-bit address from which to write + * @param[in] numBytes Number of bytes to write + * @param[out] values Pointer to array of data (bytes) you wish to write + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteBlockDataMainMicroIndirectMPW( + AriesI2CDriverType* i2cDriver, uint32_t microIndStructOffset, + uint32_t address, uint8_t numBytes, uint8_t* values) +{ + uint8_t tmpValue[1]; + uint8_t curOffset = 0; + AriesErrorType rc; + AriesErrorType lc; + if (numBytes > 8) + { + return ARIES_INVALID_ARGUMENT; + } + else + { + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + uint8_t remainingBytes = numBytes; + while (remainingBytes > 0) + { + uint8_t curRemainingBytes; + if (remainingBytes > 4) + { + curRemainingBytes = 4; + remainingBytes -= 4; + } + else + { + curRemainingBytes = remainingBytes; + remainingBytes = 0; + } + + // Set indirect access command + tmpValue[0] = ((curRemainingBytes - 1) << 5) | + ((((address + curOffset) >> 16) & 0x1) << 1) | 0x1; + rc = ariesWriteByteData(i2cDriver, microIndStructOffset, tmpValue); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Set the data byte(s) + uint8_t i; + for (i = 0; i < curRemainingBytes; i++) + { + tmpValue[0] = values[curOffset + i]; + rc = ariesWriteByteData(i2cDriver, microIndStructOffset + 3 + i, + tmpValue); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + } + + // Set address upper byte + tmpValue[0] = ((address + curOffset) >> 8) & 0xff; + rc = ariesWriteByteData(i2cDriver, microIndStructOffset + 1, + tmpValue); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Set address lower byte (this triggers indirect access) + tmpValue[0] = (address + curOffset) & 0xff; + rc = ariesWriteByteData(i2cDriver, microIndStructOffset + 2, + tmpValue); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Test successfull access + uint8_t status = 0; + uint8_t rdata[1]; + uint8_t count = 0; + while ((status == 0) && (count < 0xff)) + { + rc = ariesReadByteData(i2cDriver, microIndStructOffset + 0xb, + rdata); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + status = rdata[0] & 0x1; + count += 1; + } + if ((status == 0) || (count == 0xff)) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_FAILURE_SRAM_IND_ACCESS_TIMEOUT; + } + curOffset += curRemainingBytes; + } + + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + return ARIES_SUCCESS; + } +} + +/** + * @brief Read one byte of data from specified address from Main micro SRAM + * over I2C. Returns a negative error code, else the number byte data read. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 16-bit address from which to read + * @param[out] value Pointer to single element array of read data + * (one byte) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadByteDataMainMicroIndirect(AriesI2CDriverType* i2cDriver, + uint32_t address, + uint8_t* value) +{ + AriesErrorType rc; +#ifdef ARIES_MPW + rc = ariesReadBlockDataMainMicroIndirectMPW(i2cDriver, 0xe00, address, 1, + value); +#else + rc = ariesReadBlockDataMainMicroIndirectA0(i2cDriver, 0xd99, address, 1, + value); +#endif + return rc; +} + +/** + * @brief Read multiple (up to eight) data bytes from specified address from + * Main micro SRAM over I2C. Returns a negative error code, else zero on + * success + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 16-bit address from which to read + * @param[in] numBytes Number of bytes to read (maximum 8 bytes) + * @param[out] values Pointer to array storing up to 8 bytes of data + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesReadBlockDataMainMicroIndirect(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values) +{ + AriesErrorType rc; +#ifdef ARIES_MPW + rc = ariesReadBlockDataMainMicroIndirectMPW(i2cDriver, 0xe00, address, + numBytes, values); +#else + rc = ariesReadBlockDataMainMicroIndirectA0(i2cDriver, 0xd99, address, + numBytes, values); +#endif + return rc; +} + +/** + * @brief Write a data byte at specified address to Main micro SRAM Aries + * over I2C. Returns a negative error code, else zero on success. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 16-bit address to write + * @param[in] value Byte data to write + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesWriteByteDataMainMicroIndirect(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t* value) +{ + AriesErrorType rc; +#ifdef ARIES_MPW + rc = ariesWriteBlockDataMainMicroIndirectMPW(i2cDriver, 0xe00, address, 1, + value); +#else + rc = ariesWriteBlockDataMainMicroIndirectA0(i2cDriver, 0xd99, address, 1, + value); +#endif + return rc; +} + +/** + * @brief Write multiple (up to eight) data bytes at specified address to + * Main micro SRAM over I2C. Returns a negative error code, else zero on + * success. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 16-bit address to write + * @param[in] numBytes Number of bytes to write (maximum 16 bytes) + * @param[in] values Byte array which will be written + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesWriteBlockDataMainMicroIndirect(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values) +{ + AriesErrorType rc; +#ifdef ARIES_MPW + rc = ariesWriteBlockDataMainMicroIndirectMPW(i2cDriver, 0xe00, address, + numBytes, values); +#else + rc = ariesWriteBlockDataMainMicroIndirectA0(i2cDriver, 0xd99, address, + numBytes, values); +#endif + return rc; +} + +/** + * @brief Read multiple (up to eight) data bytes at specified address from + * Path micro SRAM over I2C. Returns a negative error code, else the number + * of bytes read. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] pathID Path micro ID (e.g. 0, 1, ..., 15) + * @param[in] address 16-bit address from which to read + * @param[in] numBytes Number of bytes to read (maximum 16 bytes) + * @param[out] values Pointer to array storing up to 16 bytes of data + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesReadBlockDataPathMicroIndirect(AriesI2CDriverType* i2cDriver, + uint8_t pathID, uint32_t address, + uint8_t numBytes, uint8_t* values) +{ + if (pathID > 15) + { + return ARIES_INVALID_ARGUMENT; + } + + AriesErrorType rc; + uint32_t microIndStructOffset = 0x4200 + (pathID * ARIES_PATH_WRP_STRIDE); + + rc = ariesReadBlockDataMainMicroIndirectMPW(i2cDriver, microIndStructOffset, + address, numBytes, values); + + return rc; +} + +/** + * @brief Read a data byte at specified address from Path micro SRAM over I2C. + * Returns a negative error code, else zero on success. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] pathID Path micro ID (e.g. 0, 1, ..., 15) + * @param[in] address 16-bit address from which to read + * @param[out] value Pointer to array of one byte of read data + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadByteDataPathMicroIndirect(AriesI2CDriverType* i2cDriver, + uint8_t pathID, + uint32_t address, + uint8_t* value) +{ + AriesErrorType rc; + rc = ariesReadBlockDataPathMicroIndirect(i2cDriver, pathID, address, 1, + value); + return rc; +} + +/** + * @brief Write multiple (up to eight) data bytes at specified address to + * Path micro SRAM over I2C. Returns a negative error code, else zero on + * success. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] pathID Path micro ID (e.g. 0, 1, ..., 15) + * @param[in] address 16-bit address to write + * @param[in] numBytes Number of bytes to write (maximum 16 bytes) + * @param[in] values Byte array which will be written + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesWriteBlockDataPathMicroIndirect(AriesI2CDriverType* i2cDriver, + uint8_t pathID, uint32_t address, + uint8_t numBytes, uint8_t* values) +{ + if (pathID > 15) + { + return ARIES_INVALID_ARGUMENT; + } + + AriesErrorType rc; + uint32_t microIndStructOffset = 0x4200 + (pathID * ARIES_PATH_WRP_STRIDE); + + rc = ariesWriteBlockDataMainMicroIndirectMPW( + i2cDriver, microIndStructOffset, address, numBytes, values); + + return rc; +} + +/** + * @brief Write one byte of data byte at specified address to Path micro SRAM + * Aries over I2C. Returns a negative error code, else zero on success. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] pathID Path micro ID (e.g. 0, 1, ..., 15) + * @param[in] address 16-bit address to write + * @param[in] value Byte data to write + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesWriteByteDataPathMicroIndirect(AriesI2CDriverType* i2cDriver, + uint8_t pathID, uint32_t address, + uint8_t* value) +{ + AriesErrorType rc; + rc = ariesWriteBlockDataPathMicroIndirect(i2cDriver, pathID, address, 1, + value); + return rc; +} + +/** + * @brief Read 2 bytes of data from PMA register over I2C + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0) or A (1) + * @param[in] quadSlice PMA num: 0, 1, 2, or 3 + * @param[in] address 16-bit address from which to read + * @param[in] values Byte array which will be written + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadWordPmaIndirect(AriesI2CDriverType* i2cDriver, int side, + int quadSlice, uint16_t address, + uint8_t* values) +{ + uint8_t cmd; + uint8_t addr15To8; + uint8_t addr7To0; + uint8_t dataByte[1] = {0}; + AriesErrorType rc; + AriesErrorType lc; + int regAddr; + + // Set value for command register based on PMA side + // A = 1, B = 0 + cmd = 0; + if (side == 0) // B + { + cmd |= (0x1 << 1); + } + else if (side == 1) // A + { + cmd |= (0x2 << 1); + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + // Split address into 2 bytes + addr15To8 = (address >> 8) & 0xff; + addr7To0 = address & 0xff; + + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // Write Cmd reg + dataByte[0] = cmd; + regAddr = ARIES_PMA_QS0_CMD_ADDRESS + (quadSlice * ARIES_QS_STRIDE); + rc = ariesWriteByteData(i2cDriver, regAddr, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Write upper bytes of address + dataByte[0] = addr15To8; + regAddr = ARIES_PMA_QS0_ADDR_1_ADDRESS + (quadSlice * ARIES_QS_STRIDE); + rc = ariesWriteByteData(i2cDriver, regAddr, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Write lower bytes of address + dataByte[0] = addr7To0; + regAddr = ARIES_PMA_QS0_ADDR_0_ADDRESS + (quadSlice * ARIES_QS_STRIDE); + rc = ariesWriteByteData(i2cDriver, regAddr, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + usleep(ARIES_PMA_REG_ACCESS_TIME_US); + + // Read data (lower and upper bits) + // Lower bits at data0 and upper bits at data1 + regAddr = ARIES_PMA_QS0_DATA_0_ADDRESS + (quadSlice * ARIES_QS_STRIDE); + rc = ariesReadByteData(i2cDriver, regAddr, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + values[0] = dataByte[0]; + + regAddr = ARIES_PMA_QS0_DATA_1_ADDRESS + (quadSlice * ARIES_QS_STRIDE); + rc = ariesReadByteData(i2cDriver, regAddr, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + values[1] = dataByte[0]; + + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + return ARIES_SUCCESS; +} + +/** + * @brief Write 2 bytes of data from PMA register over I2C + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0), A (1), or broadcast to both (2) + * @param[in] quadSlice PMA num: 0, 1, 2, or 3 + * @param[in] address 16-bit address to write + * @param[in] values Byte array which will be written + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteWordPmaIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, + uint16_t address, uint8_t* values) +{ + uint8_t cmd; + uint8_t addr15To8; + uint8_t addr7To0; + uint8_t dataByte[1] = {0}; + AriesErrorType rc; + AriesErrorType lc; + int regAddr; + + // Set value for command register based on PMA side + // A = 1, B = 0, Both = 2 + cmd = 1; + if (side == 0) + { + cmd |= (0x1 << 1); + } + else if (side == 1) + { + cmd |= (0x2 << 1); + } + else if (side == 2) + { + cmd |= (0x3 << 1); + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + // Split address into 2 bytes + addr15To8 = (address >> 8) & 0xff; + addr7To0 = address & 0xff; + + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // Write command reg + dataByte[0] = cmd; + regAddr = ARIES_PMA_QS0_CMD_ADDRESS + (quadSlice * ARIES_QS_STRIDE); + rc = ariesWriteByteData(i2cDriver, regAddr, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Write data word, LSByte (data0) and MSByte (data1) + dataByte[0] = values[0]; + regAddr = ARIES_PMA_QS0_DATA_0_ADDRESS + (quadSlice * ARIES_QS_STRIDE); + rc = ariesWriteByteData(i2cDriver, regAddr, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + dataByte[0] = values[1]; + regAddr = ARIES_PMA_QS0_DATA_1_ADDRESS + (quadSlice * ARIES_QS_STRIDE); + rc = ariesWriteByteData(i2cDriver, regAddr, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Write address (upper bits) + dataByte[0] = addr15To8; + regAddr = ARIES_PMA_QS0_ADDR_1_ADDRESS + (quadSlice * ARIES_QS_STRIDE); + rc = ariesWriteByteData(i2cDriver, regAddr, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Write address (lower bits) + dataByte[0] = addr7To0; + regAddr = ARIES_PMA_QS0_ADDR_0_ADDRESS + (quadSlice * ARIES_QS_STRIDE); + rc = ariesWriteByteData(i2cDriver, regAddr, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + usleep(ARIES_PMA_REG_ACCESS_TIME_US); + + return ARIES_SUCCESS; +} + +/** + * @brief Read 2 bytes of data from PMA lane register over I2C + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0) or A (1) + * @param[in] quadSlice PMA num: 0, 1, 2, or 3 + * @param[in] lane PMA lane number + * @param[in] regOffset 16-bit ref offset from which to read + * @param[in] values Byte array to store read data + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadWordPmaLaneIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, int lane, + uint16_t regOffset, uint8_t* values) +{ + AriesErrorType rc; + uint16_t address; + if (lane < 4) + { + // 0x200 is the lane offset in a PMA + address = regOffset + (lane * ARIES_PMA_LANE_STRIDE); + } + else + { + // This is not a lane type read + // Treat this as a regular Pma read + address = regOffset; + } + + rc = ariesReadWordPmaIndirect(i2cDriver, side, quadSlice, address, values); + CHECK_SUCCESS(rc); + return ARIES_SUCCESS; +} + +/** + * @brief Write 2 bytes of data to PMA lane register over I2C + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0), A (1), or broadcast to both (2) + * @param[in] quadSlice PMA num: 0, 1, 2, or 3 + * @param[in] lane PMA lane number + * @param[in] regOffset 16-bit ref offset from which to read + * @param[in] values Byte array which will be written + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteWordPmaLaneIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, int lane, + uint16_t regOffset, + uint8_t* values) +{ + AriesErrorType rc; + uint16_t address; + if (lane < 4) + { + // 0x200 is the lane offset in a PMA + address = regOffset + (lane * ARIES_PMA_LANE_STRIDE); + } + else + { + // This is not a lane type read + // Treat this as a regular Pma read + address = regOffset; + } + + rc = ariesWriteWordPmaIndirect(i2cDriver, side, quadSlice, address, values); + CHECK_SUCCESS(rc); + return ARIES_SUCCESS; +} + +/** + * @brief Read 2 bytes of data from PMA register over I2C using the + * 'main-micro-assisted' indirect method. This method is necessary during + * mission-mode (non-PRBS-test-mode) operation. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0) or A (1) + * @param[in] quadSlice PMA num: 0, 1, 2, or 3 + * @param[in] pmaAddr 16-bit PMA reg offset from which to read + * @param[in,out] data Byte array which will be contain data read + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadWordPmaMainMicroIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, + uint16_t pmaAddr, + uint8_t* data) +{ + AriesErrorType rc; + AriesErrorType lc; + + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // Write address (3 bytes) + uint8_t addr[3]; + uint32_t address = ((uint32_t)(quadSlice * 4) << 20) | (uint32_t)pmaAddr; + addr[0] = address & 0xff; + addr[1] = (address >> 8) & 0xff; + addr[2] = (address >> 16) & 0xff; + rc = ariesWriteBlockData(i2cDriver, ARIES_MM_ASSIST_REG_ADDR_OFFSET, 3, + addr); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Set command + uint8_t dataByte[1] = {0}; + if (side == 0) + { + dataByte[0] = ARIES_MM_RD_PID_IND_PMA0; + } + else + { + dataByte[0] = ARIES_MM_RD_PID_IND_PMA1; + } + + rc = ariesWriteBlockData(i2cDriver, ARIES_MM_ASSIST_CMD_OFFSET, 1, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Check access status + int count = 0; + int status = 1; + while ((status != 0) && (count < 100)) + { + rc = ariesReadBlockData(i2cDriver, ARIES_MM_ASSIST_CMD_OFFSET, 1, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + status = dataByte[0]; + + usleep(ARIES_MM_STATUS_TIME); + count += 1; + } + + if (status != 0) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_PMA_MM_ACCESS_FAILURE; + } + + // Read 2 bytes of data + rc = ariesReadBlockData(i2cDriver, ARIES_MM_ASSIST_DATA_OFFSET, 1, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + data[0] = dataByte[0]; + rc = ariesReadBlockData(i2cDriver, ARIES_MM_ASSIST_STS_OFFSET, 1, dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + data[1] = dataByte[0]; + + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + return ARIES_SUCCESS; +} + +/** + * @brief Write 2 bytes of data to PMA register over I2C using the + * 'main-micro-assisted' indirect method. This method is necessary during + * mission-mode (non-PRBS-test-mode) operation. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0), A (1), or broadcast to both (2) + * @param[in] quadSlice PMA num: 0, 1, 2, or 3 + * @param[in] pmaAddr 16-bit PMA reg offset from which to read + * @param[in,out] data Byte array which will be written + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteWordPmaMainMicroIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, + uint16_t pmaAddr, + uint8_t* data) +{ + AriesErrorType rc; + AriesErrorType lc; + + uint8_t dataByte[1] = {0}; + + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // Write address (3 bytes) + uint8_t addr[3]; + uint32_t address = ((uint32_t)(quadSlice * 4) << 20) | (uint32_t)pmaAddr; + addr[0] = address & 0xff; + addr[1] = (address >> 8) & 0xff; + addr[2] = (address >> 16) & 0xff; + rc = ariesWriteBlockData(i2cDriver, ARIES_MM_ASSIST_REG_ADDR_OFFSET, 3, + addr); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Write 2 bytes of data + dataByte[0] = data[0]; + rc = ariesWriteBlockData(i2cDriver, ARIES_MM_ASSIST_DATA_OFFSET, 1, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + dataByte[0] = data[1]; + rc = ariesWriteBlockData(i2cDriver, ARIES_MM_ASSIST_STS_OFFSET, 1, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Set command + if (side == 0) + { + dataByte[0] = ARIES_MM_WR_PID_IND_PMA0; + } + else if (side == 1) + { + dataByte[0] = ARIES_MM_WR_PID_IND_PMA1; + } + else + { + dataByte[0] = ARIES_MM_WR_PID_IND_PMAX; + } + + rc = ariesWriteBlockData(i2cDriver, ARIES_MM_ASSIST_CMD_OFFSET, 1, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Check access status + int count = 0; + int status = 1; + while ((status != 0) && (count < 100)) + { + rc = ariesReadBlockData(i2cDriver, ARIES_MM_ASSIST_CMD_OFFSET, 1, + dataByte); + CHECK_SUCCESS(rc); + status = dataByte[0]; + + usleep(ARIES_MM_STATUS_TIME); + count += 1; + } + + if (status != 0) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_PMA_MM_ACCESS_FAILURE; + } + + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + return ARIES_SUCCESS; +} + +/** + * @brief Read 2 bytes of data from PMA lane register over I2C using the + * 'main-micro-assisted' indirect method. This method is necessary during + * mission-mode (non-PRBS-test-mode) operation. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0) or A (1) + * @param[in] quadSlice PMA num: 0, 1, 2, or 3 + * @param[in] lane PMA lane number + * @param[in] regOffset 16-bit PMA reg offset from which to read + * @param[in,out] values Byte array which will be contain data read + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesReadWordPmaLaneMainMicroIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, int lane, + uint16_t regOffset, uint8_t* values) +{ + AriesErrorType rc; + uint16_t address; + if (lane < 4) + { + // 0x200 is the lane offset in a PMA + address = regOffset + (lane * ARIES_PMA_LANE_STRIDE); + } + else + { + // This is not a lane type read + // Treat this as a regular Pma read + address = regOffset; + } + + rc = ariesReadWordPmaMainMicroIndirect(i2cDriver, side, quadSlice, address, + values); + CHECK_SUCCESS(rc); + return ARIES_SUCCESS; +} + +/** + * @brief Write 2 bytes of data to PMA lane register over I2C using the + * 'main-micro-assisted' indirect method. This method is necessary during + * mission-mode (non-PRBS-test-mode) operation. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0), A (1), or broadcast to both (2) + * @param[in] quadSlice PMA num: 0, 1, 2, or 3 + * @param[in] lane PMA lane number + * @param[in] regOffset 16-bit ref offset from which to read + * @param[in] values Byte array which will be written + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesWriteWordPmaLaneMainMicroIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, int lane, + uint16_t regOffset, uint8_t* values) +{ + AriesErrorType rc; + uint16_t address; + if (lane < 4) + { + // 0x200 is the lane offset in a PMA + address = regOffset + (lane * ARIES_PMA_LANE_STRIDE); + } + else + { + // This is not a lane type read + // Treat this as a regular Pma read + address = regOffset; + } + + rc = ariesWriteWordPmaMainMicroIndirect(i2cDriver, side, quadSlice, address, + values); + CHECK_SUCCESS(rc); + return ARIES_SUCCESS; +} + +/** + * @brief Read Modify Write 2 bytes of data to PMA lane register over I2C using + * the 'main-micro-assisted' indirect method. This method is necessary during + * mission-mode (non-PRBS-test-mode) operation. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0), A (1), or broadcast to both (2) + * @param[in] quadSlice PMA num: 0, 1, 2, or 3 + * @param[in] quadSliceLane PMA lane number + * @param[in] pmaAddr Address you wish to write to + * @param[in] offset Offset from the start of the word you want to write + * in + * @param[in] value The value to write + * @param[in] width The width of the field you are writing to + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadWriteWordPmaLaneMainMicroIndirect( + AriesI2CDriverType* i2cDriver, int side, int quadSlice, int quadSliceLane, + uint16_t pmaAddr, int offset, uint16_t value, int width) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + + rc = ariesReadWordPmaLaneMainMicroIndirect( + i2cDriver, side, quadSlice, quadSliceLane, pmaAddr, dataWord); + CHECK_SUCCESS(rc) + + uint16_t mask = 0xFFFF; + mask <<= (width + offset); + mask += (1 << offset) - 1; + + dataWord[0] &= mask; + dataWord[1] &= mask >> 8; + + uint16_t orMask = 0; + uint16_t valueMask = ((1 << width) - 1); + orMask = (value & valueMask) << offset; + + if (value >= (1 << width)) + { + ASTERA_WARN( + "Trying to write a value greater than the width of the bitfield. Truncation occurred."); + } + + dataWord[0] |= orMask; + dataWord[1] |= orMask >> 8; + + rc = ariesWriteWordPmaLaneMainMicroIndirect( + i2cDriver, side, quadSlice, quadSliceLane, pmaAddr, dataWord); + CHECK_SUCCESS(rc) + + return ARIES_SUCCESS; +} + +/** + * @brief Read N bytes of data from a Retimer (gbl, ln0, or ln1) CSR. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0), A (1) + * @param[in] lane Absolute lane number (15:0) + * @param[in] baseAddr 16-bit base address + * @param[in] numBytes Number of bytes to read + * @param[in] data Byte array which will be contain data read + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadRetimerRegister(AriesI2CDriverType* i2cDriver, int side, + int lane, uint16_t baseAddr, + uint8_t numBytes, uint8_t* data) +{ + AriesErrorType rc; + uint8_t ret_pth_ln = lane % 2; + uint8_t pth_wrap; + uint8_t qs = floor(lane / 4); + uint32_t addr; + if (side == 1) // Side "A" - Even paths + { + pth_wrap = (2 * floor((lane % 4) / 2)); + } + else if (side == 0) // Side "B" - Odd paths + { + pth_wrap = (2 * floor((lane % 4) / 2)) + 1; + } + else + { + return ARIES_INVALID_ARGUMENT; + } + addr = baseAddr + ret_pth_ln * ARIES_PATH_LANE_STRIDE + + pth_wrap * ARIES_PATH_WRP_STRIDE + qs * ARIES_QS_STRIDE; + rc = ariesReadBlockData(i2cDriver, addr, numBytes, data); + CHECK_SUCCESS(rc); + return ARIES_SUCCESS; +} + +/** + * @brief Write N bytes of data to a Retimer (gbl, ln0, or ln1) CSR. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] side PMA Side B (0), A (1) + * @param[in] lane Absolute lane number (15:0) + * @param[in] baseAddr 16-bit base address + * @param[in] numBytes Number of bytes to write + * @param[in] data Byte array containing data to be written + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteRetimerRegister(AriesI2CDriverType* i2cDriver, + int side, int lane, uint16_t baseAddr, + uint8_t numBytes, uint8_t* data) +{ + AriesErrorType rc; + uint8_t ret_pth_ln = lane % 2; + uint8_t pth_wrap; + uint8_t qs = floor(lane / 4); + uint32_t addr; + if (side == 1) // Side "A" - Even paths + { + pth_wrap = (2 * floor((lane % 4) / 2)); + } + else if (side == 0) // Side "B" - Odd paths + { + pth_wrap = (2 * floor((lane % 4) / 2)) + 1; + } + else + { + return ARIES_INVALID_ARGUMENT; + } + addr = baseAddr + ret_pth_ln * ARIES_PATH_LANE_STRIDE + + pth_wrap * ARIES_PATH_WRP_STRIDE + qs * ARIES_QS_STRIDE; + rc = ariesWriteBlockData(i2cDriver, addr, numBytes, data); + CHECK_SUCCESS(rc); + return ARIES_SUCCESS; +} + +/** + * @brief Read the contents of an Aries wide register. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 16-bit base address + * @param[in] width Size of wide register in bytes + * @param[in] values Byte array containing data to be written + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadWideRegister(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t width, + uint8_t* values) +{ + if (i2cDriver->mmWideRegisterValid) + { + AriesErrorType rc; + AriesErrorType lc; + uint8_t dataByte[1] = {0}; + uint8_t addr[3]; + + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // Write address (3 bytes) + addr[0] = address & 0xff; + addr[1] = (address >> 8) & 0xff; + addr[2] = (address >> 16) & 0x01; + rc = ariesWriteBlockData(i2cDriver, ARIES_MM_ASSIST_SPARE_0_OFFSET, 3, + addr); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Set command based on width + if (width == 2) + { + dataByte[0] = ARIES_MM_RD_WIDE_REG_2B; + } + else if (width == 3) + { + dataByte[0] = ARIES_MM_RD_WIDE_REG_3B; + } + else if (width == 4) + { + dataByte[0] = ARIES_MM_RD_WIDE_REG_4B; + } + else if (width == 5) + { + dataByte[0] = ARIES_MM_RD_WIDE_REG_5B; + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + rc = ariesWriteByteData(i2cDriver, ARIES_MM_ASSIST_CMD_OFFSET, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Check access status + int count = 0; + int status = 0xff; + while ((status != 0) && (count < 100)) + { + rc = ariesReadByteData(i2cDriver, ARIES_MM_ASSIST_CMD_OFFSET, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + status = dataByte[0]; + + usleep(ARIES_MM_STATUS_TIME); + count += 1; + } + + if (status != 0) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_PMA_MM_ACCESS_FAILURE; + } + + // Read N bytes of data based on width + rc = ariesReadBlockData(i2cDriver, ARIES_MM_ASSIST_SPARE_3_OFFSET, + width, values); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + } + else + { + return ariesReadBlockData(i2cDriver, address, width, values); + } + + return ARIES_SUCCESS; +} + +/** + * @brief Write the contents of an Aries wide register. + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @param[in] address 16-bit base address + * @param[in] width Size of wide register in bytes + * @param[in] values Byte array containing data to be written + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesWriteWideRegister(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t width, + uint8_t* values) +{ + if (i2cDriver->mmWideRegisterValid) + { + AriesErrorType rc; + AriesErrorType lc; + uint8_t dataByte[1] = {0}; + uint8_t addr[3]; + + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // Write address (3 bytes) + addr[0] = address & 0xff; + addr[1] = (address >> 8) & 0xff; + addr[2] = (address >> 16) & 0x01; + rc = ariesWriteBlockData(i2cDriver, ARIES_MM_ASSIST_SPARE_0_OFFSET, 3, + addr); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Write N bytes of data based on width + rc = ariesWriteBlockData(i2cDriver, ARIES_MM_ASSIST_SPARE_3_OFFSET, + width, values); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Set command based on width + if (width == 2) + { + dataByte[0] = ARIES_MM_WR_WIDE_REG_2B; + } + else if (width == 3) + { + dataByte[0] = ARIES_MM_WR_WIDE_REG_3B; + } + else if (width == 4) + { + dataByte[0] = ARIES_MM_WR_WIDE_REG_4B; + } + else if (width == 5) + { + dataByte[0] = ARIES_MM_WR_WIDE_REG_5B; + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + rc = ariesWriteByteData(i2cDriver, ARIES_MM_ASSIST_CMD_OFFSET, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Check access status + int count = 0; + int status = 0xff; + while ((status != 0) && (count < 100)) + { + rc = ariesReadByteData(i2cDriver, ARIES_MM_ASSIST_CMD_OFFSET, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + status = dataByte[0]; + + usleep(ARIES_MM_STATUS_TIME); + count += 1; + } + + if (status != 0) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_PMA_MM_ACCESS_FAILURE; + } + + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + } + else + { + return ariesWriteBlockData(i2cDriver, address, width, values); + } + + return ARIES_SUCCESS; +} + +/** + * @brief Set lock on bus (Aries transaction) + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLock(AriesI2CDriverType* i2cDriver) +{ + AriesErrorType rc; + + if (i2cDriver->lock >= 1) + { + i2cDriver->lock++; + rc = ARIES_SUCCESS; + } + else + { + rc = asteraI2CBlock(i2cDriver->handle); + i2cDriver->lock = 1; + } + + return rc; +} + +/** + * @brief Unlock bus after lock (Aries transaction) + * + * @param[in] i2cDriver I2C driver responsible for the transaction(s) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesUnlock(AriesI2CDriverType* i2cDriver) +{ + AriesErrorType rc; + + if (i2cDriver->lock > 1) + { + i2cDriver->lock--; + rc = ARIES_SUCCESS; + } + else + { + rc = asteraI2CUnblock(i2cDriver->handle); + i2cDriver->lock = 0; + } + + return rc; +} + +#ifdef __cplusplus +} +#endif diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_i2c.h b/common/recipes-lib/retimer-v2.16.2/files/aries_i2c.h new file mode 100755 index 000000000000..0368ab2300c2 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_i2c.h @@ -0,0 +1,210 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_i2c.h + * @brief Definition of I2C/SMBus types for the SDK. + */ +#pragma once +#ifndef ASTERA_ARIES_SDK_I2C_H_ +#define ASTERA_ARIES_SDK_I2C_H_ + +#include "aries_globals.h" +#include "aries_error.h" +#include "aries_api_types.h" +#include "astera_log.h" +#include "aries_misc.h" + +#ifdef ARIES_MPW +#include "aries_mpw_reg_defines.h" +#else +#include "aries_a0_reg_defines.h" +#endif + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK_SUCCESS(rc) \ + { \ + if (rc != ARIES_SUCCESS) \ + { \ + ASTERA_ERROR("Unexpected return code: %d", rc); \ + return rc; \ + } \ + } + +int asteraI2COpenConnection(int i2cBus, int slaveAddress); + +AriesErrorType asteraI2CWriteBlockData(int handle, uint8_t cmdCode, uint8_t numBytes, + uint8_t* buf); + +AriesErrorType asteraI2CReadBlockData(int handle, uint8_t cmdCode, uint8_t numBytes, + uint8_t* buf); + +AriesErrorType asteraI2CWriteReadBlockData(int handle, uint8_t cmdCode, uint8_t numBytes, + uint8_t* buf); + +AriesErrorType asteraI2CBlock(int handle); + +AriesErrorType asteraI2CUnblock(int handle); + +AriesErrorType ariesRunArp(int handle, uint8_t new7bitSmbusAddr); + +AriesErrorType ariesWriteBlockData(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values); + +AriesErrorType ariesWriteByteData(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t* value); + +AriesErrorType ariesReadBlockData(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values); + +AriesErrorType ariesReadByteData(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t* value); + +AriesErrorType ariesWriteReadBlockData(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values); + +AriesErrorType ariesReadBlockDataMainMicroIndirectA0( + AriesI2CDriverType* i2cDriver, uint32_t microIndStructOffset, + uint32_t address, uint8_t numBytes, uint8_t* values); + +AriesErrorType ariesReadBlockDataMainMicroIndirectMPW( + AriesI2CDriverType* i2cDriver, uint32_t microIndStructOffset, + uint32_t address, uint8_t numBytes, uint8_t* values); + +AriesErrorType ariesWriteBlockDataMainMicroIndirectA0( + AriesI2CDriverType* i2cDriver, uint32_t microIndStructOffset, + uint32_t address, uint8_t numBytes, uint8_t* values); + +AriesErrorType ariesWriteBlockDataMainMicroIndirectMPW( + AriesI2CDriverType* i2cDriver, uint32_t microIndStructOffset, + uint32_t address, uint8_t numBytes, uint8_t* values); + +AriesErrorType ariesReadByteDataMainMicroIndirect(AriesI2CDriverType* i2cDriver, + uint32_t address, + uint8_t* values); + +AriesErrorType + ariesReadBlockDataMainMicroIndirect(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values); + +AriesErrorType + ariesWriteByteDataMainMicroIndirect(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t* value); + +AriesErrorType + ariesWriteBlockDataMainMicroIndirect(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values); + +AriesErrorType ariesReadByteDataPathMicroIndirect(AriesI2CDriverType* i2cDriver, + uint8_t pathID, + uint32_t address, + uint8_t* value); + +AriesErrorType + ariesReadBlockDataPathMicroIndirect(AriesI2CDriverType* i2cDriver, + uint8_t pathID, uint32_t address, + uint8_t numBytes, uint8_t* values); + +AriesErrorType + ariesWriteByteDataPathMicroIndirect(AriesI2CDriverType* i2cDriver, + uint8_t pathID, uint32_t address, + uint8_t* value); + +AriesErrorType + ariesWriteBlockDataPathMicroIndirect(AriesI2CDriverType* i2cDriver, + uint8_t pathID, uint32_t address, + uint8_t numBytes, uint8_t* values); + +AriesErrorType ariesReadWordPmaIndirect(AriesI2CDriverType* i2cDriver, int side, + int quadSlice, uint16_t address, + uint8_t* values); + +AriesErrorType ariesWriteWordPmaIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, + uint16_t address, uint8_t* values); + +AriesErrorType ariesReadWordPmaLaneIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, int lane, + uint16_t regOffset, + uint8_t* values); + +AriesErrorType ariesWriteWordPmaLaneIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, int lane, + uint16_t regOffset, + uint8_t* values); + +AriesErrorType ariesReadWordPmaMainMicroIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, + uint16_t pmaAddr, + uint8_t* data); + +AriesErrorType ariesWriteWordPmaMainMicroIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, + uint16_t pmaAddr, + uint8_t* data); + +AriesErrorType + ariesReadWordPmaLaneMainMicroIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, int lane, + uint16_t regOffset, uint8_t* values); + +AriesErrorType + ariesWriteWordPmaLaneMainMicroIndirect(AriesI2CDriverType* i2cDriver, + int side, int quadSlice, int lane, + uint16_t regOffset, uint8_t* values); + +AriesErrorType ariesReadWriteWordPmaLaneMainMicroIndirect( + AriesI2CDriverType* i2cDriver, int side, int quadSlice, int quadSliceLane, + uint16_t pmaAddr, int offset, uint16_t value, int width); + +AriesErrorType ariesReadRetimerRegister(AriesI2CDriverType* i2cDriver, int side, + int lane, uint16_t baseAddr, + uint8_t numBytes, uint8_t* data); + +AriesErrorType ariesWriteRetimerRegister(AriesI2CDriverType* i2cDriver, + int side, int lane, uint16_t baseAddr, + uint8_t numBytes, uint8_t* data); + +AriesErrorType ariesReadWideRegister(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t width, + uint8_t* values); + +AriesErrorType ariesWriteWideRegister(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t width, + uint8_t* values); + +AriesErrorType ariesLock(AriesI2CDriverType* i2cDriver); + +AriesErrorType ariesUnlock(AriesI2CDriverType* i2cDriver); + +#ifdef __cplusplus +} +#endif + +#endif /* ASTERA_ARIES_SDK_I2C_H_ */ diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_link.c b/common/recipes-lib/retimer-v2.16.2/files/aries_link.c new file mode 100755 index 000000000000..8ec99227b2e5 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_link.c @@ -0,0 +1,2407 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_link.c + * @brief Implementation of public Link level functions for the SDK. + */ + +#include "aries_link.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set the PCIe Protocol Reset. + * + * PCIe Protocol (fundamental) Reset may be triggered from the PERST_N + * hardware pin or from a register. This function sets the register to assert + * PCIe reset (1) or de-assert PCIe reset (0). User must wait 1 ms between + * an assertion and de-assertion. + * + * @param[in] link Link object (containing link id) + * @param[in] reset Reset assert (1) or de-assert (0) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesSetPcieReset(AriesLinkType* link, uint8_t reset) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + if (reset == 1) + { + rc = ariesReadByteData(link->device->i2cDriver, 0x604, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] &= ~(1 << link->config.linkId); + rc = ariesWriteByteData(link->device->i2cDriver, 0x604, dataByte); + CHECK_SUCCESS(rc); + } + else if (reset == 0) + { + rc = ariesReadByteData(link->device->i2cDriver, 0x604, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] |= (1 << link->config.linkId); + rc = ariesWriteByteData(link->device->i2cDriver, 0x604, dataByte); + CHECK_SUCCESS(rc); + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Perform an all-in-one health check on a given Link. + * + * Check critical parameters like junction temperature, Link LTSSM state, + * and per-lane eye height/width against certain alert thresholds. Update + * link.linkOkay member (bool). + * + * @param[in] link Pointer to Aries Link struct object + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesCheckLinkHealth(AriesLinkType* link) +{ + AriesErrorType rc; + + link->state.linkOkay = true; + + rc = ariesGetLinkState(link); + CHECK_SUCCESS(rc); + + // Get current temperature and check against threshold + rc = ariesGetCurrentTemp(link->device); + CHECK_SUCCESS(rc); + if ((link->device->currentTempC + ARIES_TEMP_CALIBRATION_OFFSET) >= + link->device->tempAlertThreshC) + { + ASTERA_ERROR( + "Temperature alert! Current (average) temp observed is above threshold"); + ASTERA_ERROR( + " Cur Temp observed (+uncertainty) = %f", + (link->device->currentTempC + ARIES_TEMP_CALIBRATION_OFFSET)); + ASTERA_ERROR(" Alert threshold = %f", link->device->tempAlertThreshC); + link->state.linkOkay = false; + } + else if ((link->device->currentTempC + ARIES_TEMP_CALIBRATION_OFFSET) >= + link->device->tempWarnThreshC) + { + ASTERA_WARN( + "Temperature warn! Current (average) temp observed is above threshold"); + ASTERA_WARN( + " Cur Temp observed (+uncertainty) = %f", + (link->device->currentTempC + ARIES_TEMP_CALIBRATION_OFFSET)); + ASTERA_WARN(" Warn threshold = %f", link->device->tempWarnThreshC); + } + + // Get max temp stat and check against threshold + rc = ariesGetMaxTemp(link->device); + CHECK_SUCCESS(rc); + if ((link->device->maxTempC + ARIES_TEMP_CALIBRATION_OFFSET) >= + link->device->tempAlertThreshC) + { + ASTERA_ERROR( + "Temperature alert! All-time max temp observed is above threshold"); + ASTERA_ERROR(" Max Temp observed (+uncertainty) = %f", + (link->device->maxTempC + ARIES_TEMP_CALIBRATION_OFFSET)); + ASTERA_ERROR(" Alert threshold = %f", link->device->tempAlertThreshC); + link->state.linkOkay = false; + } + else if ((link->device->maxTempC + ARIES_TEMP_CALIBRATION_OFFSET) >= + link->device->tempWarnThreshC) + { + ASTERA_WARN( + "Temperature warn! All-time max temp observed is above threshold"); + ASTERA_WARN(" Max Temp observed (+uncertainty) = %f", + (link->device->maxTempC + ARIES_TEMP_CALIBRATION_OFFSET)); + ASTERA_WARN(" Warn threshold = %f", link->device->tempWarnThreshC); + } + + // Check over-temp alert + bool overtempFlag; + rc = ariesOvertempStatusGet(link->device, &overtempFlag); + CHECK_SUCCESS(rc); + if (overtempFlag) + { + link->device->overtempAlert = true; + } + else + { + link->device->overtempAlert = false; + } + + // Get orientation - normal or reversed + // 0 is normal, 1 is reversed + int orientation; + rc = ariesGetPortOrientation(link->device, &orientation); + CHECK_SUCCESS(rc); + + int upstreamSide; + int downstreamSide; + + if (orientation == 0) + { + upstreamSide = 1; + downstreamSide = 0; + } + else + { + upstreamSide = 0; + downstreamSide = 1; + } + + // Check Link State + // Set linkOkay to false if link is not in FWd state + if (link->state.state != ARIES_STATE_FWD) + { + link->state.linkOkay = false; + } + + // Check link FoM values + int laneIndex; + int absLane; + + int pathID; + int lane; + + int startLane = ariesGetStartLane(link); + + uint8_t dataWord[2] = {0}; + uint8_t minFoM = 0xff; + uint8_t thisLaneFoM; + char* minFoMRx = "A_PER0"; + for (laneIndex = 0; laneIndex < link->state.width; laneIndex++) + { + absLane = startLane + laneIndex; + pathID = floor(absLane / 4) * 4; + lane = absLane % 4; + + rc = ariesGetMinFoMVal(link->device, upstreamSide, pathID, lane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_ADAPT_FOM, + dataWord); + CHECK_SUCCESS(rc); + + // FoM value is 7:0 of word + thisLaneFoM = dataWord[0]; + if (thisLaneFoM <= minFoM) + { + minFoM = thisLaneFoM; + if (orientation == 0) + { + minFoMRx = link->device->pins[absLane].pinSet1.rxPin; + } + else + { + minFoMRx = link->device->pins[absLane].pinSet2.rxPin; + } + } + + rc = ariesGetMinFoMVal(link->device, downstreamSide, pathID, lane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_ADAPT_FOM, + dataWord); + CHECK_SUCCESS(rc); + + // FoM value is 7:0 of word + thisLaneFoM = dataWord[0]; + if (thisLaneFoM <= minFoM) + { + minFoM = thisLaneFoM; + if (orientation == 0) + { + minFoMRx = link->device->pins[absLane].pinSet2.rxPin; + } + else + { + minFoMRx = link->device->pins[absLane].pinSet1.rxPin; + } + } + } + + // For Gen-2 and below, FoM values do not make sense. Hence we set it to + // default values + if (link->state.rate >= 3) + { + link->state.linkMinFoM = minFoM; + link->state.linkMinFoMRx = minFoMRx; + } + else + { + link->state.linkMinFoM = 0xff; + link->state.linkMinFoMRx = ""; + } + + // Trigger alerts for Gen 3 and above only + if ((link->state.linkMinFoM <= link->device->minLinkFoMAlert) && + (link->state.rate >= 3)) + { + link->state.linkOkay = false; + ASTERA_ERROR("Lane FoM alert! %s FoM below threshold (Val: 0x%02x)", + link->state.linkMinFoMRx, link->state.linkMinFoM); + } + + // Capture the recovery count + int recoveryCount = 0; + rc = ariesGetLinkRecoveryCount(link, &recoveryCount); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Read the current width of the link + * + * @param[in] link Pointer to Aries Link struct object + * @param[out] currentWidth Pointer to integer to store the current link + * width + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLinkGetCurrentWidth(AriesLinkType* link, int* currentWidth) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint32_t addressOffset; + // Check state of each path micro in the link. If it is in FWD we add to + // current width + *currentWidth = 0; + + int i; + int startLane = ariesGetStartLane(link); + for (i = startLane; i < link->config.maxWidth + startLane; i++) + { + addressOffset = ARIES_QS_0_CSR_OFFSET + + (ARIES_PATH_WRAPPER_1_CSR_OFFSET * i); + + rc = ariesReadByteData(link->device->i2cDriver, addressOffset + 0xb7, + dataByte); + CHECK_SUCCESS(rc); + + if (dataByte[0] == 0x13) // FWD state + { + (*currentWidth)++; + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Get link recovery counter value + * + * @param[in] link Link struct created by user + * @param[out] recoveryCount Recovery count returned + * @return AriesErrorType - Aries error code + * + */ +AriesErrorType ariesGetLinkRecoveryCount(AriesLinkType* link, + int* recoveryCount) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint32_t address; + + // Get current recovery count value + address = link->device->mm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_LNK_RECOV_ENTRIES_PTR_OFFSET + + link->config.linkId; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, address, + dataByte); + CHECK_SUCCESS(rc); + + // Update the value in the struct + link->state.recoveryCount = dataByte[0]; + + *recoveryCount = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief Clear link recovery counter value + * + * @param[in] link Link struct created by user + * @return AriesErrorType - Aries error code + * + */ +AriesErrorType ariesClearLinkRecoveryCount(AriesLinkType* link) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint32_t address; + + // Set current recovery count value + dataByte[0] = 0; + address = link->device->mm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_LNK_RECOV_ENTRIES_PTR_OFFSET + + link->config.linkId; + rc = ariesWriteByteDataMainMicroIndirect(link->device->i2cDriver, address, + dataByte); + CHECK_SUCCESS(rc); + + // Update the value in the struct + link->state.recoveryCount = 0; + + return ARIES_SUCCESS; +} + +/** + * @brief Get the current detailed Link state, including electrical parameters. + * + * Read the current Link state and return the parameters for the current links + * Returns a negative error code, else zero on success. + * + * @param[in,out] link Pointer to Aries Link struct object + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLinkState(AriesLinkType* link) +{ + AriesErrorType rc; + AriesBifurcationType bifMode; + uint8_t dataByte[1] = {0}; + uint8_t dataWord[2] = {0}; + uint32_t baseAddress; + uint32_t addressOffset; + int linkNum; + int linkIdx; + + // Get current bifurcation settings + rc = ariesGetBifurcationMode(link->device, &bifMode); + CHECK_SUCCESS(rc); + + // Get link number in bifurcation mode + // Iterate over links in bifurcation lookup table and find start lane + // Lane number is part of bifurcation link properties + bool laneFound = false; + for (linkIdx = 0; linkIdx < bifurcationModes[bifMode].numLinks; linkIdx++) + { + if (link->config.startLane == + (bifurcationModes[bifMode].links[linkIdx].startLane)) + { + linkNum = bifurcationModes[bifMode].links[linkIdx].linkId; + laneFound = true; + break; + } + } + + if (laneFound == false) + { + return ARIES_LINK_CONFIG_INVALID; + } + + int timeout = 0; + bool ready = 0; + + ariesGetGPIO(link->device, 0, &ready); + while ((++timeout < 10) && !ready) + { + if (timeout == 1) + { + ASTERA_INFO( + "Waiting for FW load to complete before fetching link state..."); + } + sleep(1); + ariesGetGPIO(link->device, 0, &ready); + } + + if (!ready) + { + ASTERA_ERROR( + "ariesGetLinkState timed out while waiting for FW load to complete."); + return ARIES_FAILURE; + } + + // Get link path struct offsets + // The link path struct sits on top of the MM link struct + // Hence need to compute this offset first, before getting link struct + // parameters + addressOffset = ARIES_MAIN_MICRO_FW_INFO + + ARIES_MM_LINK_STRUCT_ADDR_OFFSET + + (linkNum * ARIES_LINK_ADDR_EL_SIZE); + + rc = ariesReadBlockDataMainMicroIndirect(link->device->i2cDriver, + addressOffset, 2, dataWord); + CHECK_SUCCESS(rc); + int linkStructAddr = dataWord[0] + (dataWord[1] << 8); + + // Compute offset, at which link struct members are available + baseAddress = AL_MAIN_SRAM_DMEM_OFFSET + linkStructAddr + + (link->device->linkPathStructSize * 2); + + // Read link width + addressOffset = baseAddress + ARIES_LINK_STRUCT_WIDTH_OFFSET; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, + addressOffset, dataByte); + CHECK_SUCCESS(rc); + link->state.width = dataByte[0]; + + // Read detected width + addressOffset = baseAddress + ARIES_LINK_STRUCT_DETECTED_WIDTH_OFFSET; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, + addressOffset, dataByte); + CHECK_SUCCESS(rc); + link->state.detWidth = dataByte[0]; + + // Read current width + int currentWidth = 0; + rc = ariesLinkGetCurrentWidth(link, ¤tWidth); + CHECK_SUCCESS(rc); + link->state.curWidth = currentWidth; + + // Read current state + // Current state is offset at 10 from base address + addressOffset = baseAddress + ARIES_LINK_STRUCT_STATE_OFFSET; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, + addressOffset, dataByte); + CHECK_SUCCESS(rc); + link->state.state = (AriesLinkStateEnumType) dataByte[0]; + + // Read current rate + // Current rate is offset at 6 from base address + // Rate value is offset by 1, i.e. 0: Gen1, 1: Gen2, ... 4: Gen5 + // Hence update rate value by 1 + addressOffset = baseAddress + ARIES_LINK_STRUCT_RATE_OFFSET; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, + addressOffset, dataByte); + CHECK_SUCCESS(rc); + link->state.rate = dataByte[0] + 1; + + return ARIES_SUCCESS; +} + +/** + * @brief Get the current detailed Link state, including electrical parameters. + * + * Read the current Link state and return the parameters for the current links + * Returns a negative error code, else zero on success. + * + * @param[in,out] link Pointer to Aries Link struct object + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLinkStateDetailed(AriesLinkType* link) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + float usppSpeed = 0.0; + float dsppSpeed = 0.0; + + // Update link state parameters + rc = ariesCheckLinkHealth(link); + CHECK_SUCCESS(rc); + + int width = link->state.width; + int startLane = ariesGetStartLane(link); + int laneIndex; + int upstreamSide; + int downstreamSide; + int upstreamDirection; + int downstreamDirection; + int upstreamPinSet; + int downstreamPinSet; + int usppTxDirection; + int dsppTxDirection; + int usppRxDirection; + int dsppRxDirection; + + // Get orientation - normal or reversed + // 0 is normal, 1 is reversed + int orientation; + rc = ariesGetPortOrientation(link->device, &orientation); + CHECK_SUCCESS(rc); + + if (orientation == 0) // Normal Orientation + { + upstreamSide = 1; + downstreamSide = 0; + upstreamDirection = 1; + downstreamDirection = 0; + upstreamPinSet = 0; + downstreamPinSet = 1; + usppRxDirection = 0; + usppTxDirection = 1; + dsppRxDirection = 1; + dsppTxDirection = 0; + } + else // Reversed orientation + { + upstreamSide = 0; + downstreamSide = 1; + upstreamDirection = 0; + downstreamDirection = 1; + upstreamPinSet = 1; + downstreamPinSet = 0; + usppRxDirection = 1; + usppTxDirection = 0; + dsppRxDirection = 0; + dsppTxDirection = 1; + } + + // Get Current Speeds + // Upstream path speed + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int absLane = startLane + laneIndex; + int rxTerm; + rc = ariesGetLinkRxTerm(link, upstreamSide, absLane, &rxTerm); + CHECK_SUCCESS(rc); + if (rxTerm == 1) + { + rc = ariesGetLinkCurrentSpeed(link, absLane, upstreamDirection, + &usppSpeed); + CHECK_SUCCESS(rc); + break; + } + } + + // Downstream path speed + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int absLane = startLane + laneIndex; + int rxTerm; + rc = ariesGetLinkRxTerm(link, downstreamSide, absLane, &rxTerm); + CHECK_SUCCESS(rc); + if (rxTerm == 1) + { + rc = ariesGetLinkCurrentSpeed(link, absLane, downstreamDirection, + &dsppSpeed); + CHECK_SUCCESS(rc); + break; + } + } + + link->state.usppSpeed = usppSpeed; + link->state.dsppSpeed = dsppSpeed; + + // Get Logical Lane for both upstream and downstream + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int laneNum; + int absLane = startLane + laneIndex; + + // Upstream direction (RX and TX) + rc = ariesGetLogicalLaneNum(link, absLane, usppRxDirection, &laneNum); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].logicalLaneNum = laneNum; + rc = ariesGetLogicalLaneNum(link, absLane, usppTxDirection, &laneNum); + CHECK_SUCCESS(rc); + link->state.usppState.txState[laneIndex].logicalLaneNum = laneNum; + + // Downstream direction (RX and TX) + rc = ariesGetLogicalLaneNum(link, absLane, dsppRxDirection, &laneNum); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].logicalLaneNum = laneNum; + rc = ariesGetLogicalLaneNum(link, absLane, dsppTxDirection, &laneNum); + CHECK_SUCCESS(rc); + link->state.dsppState.txState[laneIndex].logicalLaneNum = laneNum; + } + + //////////////////////// USPP & DSPP Parameters //////////////////////// + + // Physical Pin Info + // This is from a fixed array defined in aries_globals.c + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int absLane = startLane + laneIndex; + + if (orientation == 0) + { + link->state.usppState.rxState[laneIndex].physicalPinName = + link->device->pins[absLane].pinSet1.rxPin; + link->state.usppState.txState[laneIndex].physicalPinName = + link->device->pins[absLane].pinSet1.txPin; + link->state.dsppState.rxState[laneIndex].physicalPinName = + link->device->pins[absLane].pinSet2.rxPin; + link->state.dsppState.txState[laneIndex].physicalPinName = + link->device->pins[absLane].pinSet2.txPin; + } + else + { + link->state.dsppState.rxState[laneIndex].physicalPinName = + link->device->pins[absLane].pinSet1.rxPin; + link->state.dsppState.txState[laneIndex].physicalPinName = + link->device->pins[absLane].pinSet1.txPin; + link->state.usppState.rxState[laneIndex].physicalPinName = + link->device->pins[absLane].pinSet2.rxPin; + link->state.usppState.txState[laneIndex].physicalPinName = + link->device->pins[absLane].pinSet2.txPin; + } + } + + // Current TX Pre-Cursor value (valid for Gen-3 and above) + // else 0 + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + if ((usppSpeed == 2.5) || (usppSpeed == 5)) + { + link->state.usppState.txState[laneIndex].pre = 0; + link->state.dsppState.txState[laneIndex].pre = 0; + } + else + { + // Physical Path N drives the TX de-emphasis values for Path 1-N + // Hence upstream stats will be in downstream path + int txPre; + int absLane = startLane + laneIndex; + // Upstream direction + rc = ariesGetTxPre(link, absLane, downstreamDirection, &txPre); + CHECK_SUCCESS(rc); + link->state.usppState.txState[laneIndex].pre = txPre; + + // Downstream parameters + rc = ariesGetTxPre(link, absLane, upstreamDirection, &txPre); + CHECK_SUCCESS(rc); + link->state.dsppState.txState[laneIndex].pre = txPre; + } + } + + // Get TX Current Cursor value (valid for Gen-3 and above) + // else 0 + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + if ((usppSpeed == 2.5) || (usppSpeed == 5)) + { + link->state.usppState.txState[laneIndex].cur = 0; + link->state.dsppState.txState[laneIndex].cur = 0; + } + else + { + // Physical Path N drives the TX de-emphasis values for Path 1-N + // Hence upstream stats will be in downstream path + int txCur; + int absLane = startLane + laneIndex; + // Upstream parameters + rc = ariesGetTxCur(link, absLane, downstreamDirection, &txCur); + CHECK_SUCCESS(rc); + link->state.usppState.txState[laneIndex].cur = txCur; + + // Downstream parameters + rc = ariesGetTxCur(link, absLane, upstreamDirection, &txCur); + CHECK_SUCCESS(rc); + link->state.dsppState.txState[laneIndex].cur = txCur; + } + } + + // Get RX Polarity + // Get RX pseudoport param - opposite paths + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int rxPolarity; + int absLane = startLane + laneIndex; + // Upstream parameters + rc = ariesGetRxPolarityCode(link, absLane, usppRxDirection, + upstreamPinSet, &rxPolarity); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].polarity = rxPolarity; + + // Downstream parameters + rc = ariesGetRxPolarityCode(link, absLane, dsppRxDirection, + downstreamPinSet, &rxPolarity); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].polarity = rxPolarity; + } + + // Get TX Post Cursor Value (valid for gen-3 and above) + // else get post val from tx-pre val + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + // Physical Path N drives the TX de-emphasis values for Path 1-N + // Hence upstream stats will be in downstream path + if ((usppSpeed == 2.5) || (usppSpeed == 5)) + { + // For gen 1 and 2, pre-cursor mode has de-emphasis value + // Compute pst from this value + int txPre; + float txPst; + int absLane = startLane + laneIndex; + + // Upstream parameters (USPP) + rc = ariesGetTxPre(link, absLane, downstreamDirection, &txPre); + CHECK_SUCCESS(rc); + if (txPre == 0) + { + txPst = -6; + } + else if (txPre == 1) + { + txPst = -3.5; + } + else + { + txPst = 0; + } + link->state.usppState.txState[laneIndex].pst = txPst; + // De-emphasis value is in tx pre cursor val + link->state.usppState.txState[laneIndex].de = txPre; + + // Downstream parameters (DSPP) + rc = ariesGetTxPre(link, absLane, upstreamDirection, &txPre); + CHECK_SUCCESS(rc); + if (txPre == 0) + { + txPst = -6; + } + else if (txPre == 1) + { + txPst = -3.5; + } + else + { + txPst = 0; + } + link->state.dsppState.txState[laneIndex].pst = txPst; + // De-emphasis value is in tx pre cursor val + link->state.dsppState.txState[laneIndex].de = txPre; + } + else + { + int txPst; + int absLane = startLane + laneIndex; + // Upstream parameters + rc = ariesGetTxPst(link, absLane, downstreamDirection, &txPst); + CHECK_SUCCESS(rc); + link->state.usppState.txState[laneIndex].pst = txPst; + // De-emphasis value does not apply here + link->state.usppState.txState[laneIndex].de = 0; + + // Downstream parameters + rc = ariesGetTxPst(link, absLane, upstreamDirection, &txPst); + CHECK_SUCCESS(rc); + link->state.dsppState.txState[laneIndex].pst = txPst; + // De-emphasis value does not apply here + link->state.dsppState.txState[laneIndex].de = 0; + } + } + + // Get RX TERM (Calculate based on pseudo-port path) + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int rxTerm; + int absLane = startLane + laneIndex; + + // Upstream path + rc = ariesGetLinkRxTerm(link, upstreamSide, absLane, &rxTerm); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].termination = rxTerm; + // Downstream path + rc = ariesGetLinkRxTerm(link, downstreamSide, absLane, &rxTerm); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].termination = rxTerm; + } + + // Get RX ATT, VGA, CTLE Boost + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int boostCode; + int attCode; + float attValDb; + int vgaCode; + float boostValDb; + int absLane = startLane + laneIndex; + + // Upstream parameters + rc = ariesGetRxCtleBoostCode(link, upstreamSide, absLane, &boostCode); + CHECK_SUCCESS(rc); + rc = ariesGetRxAttCode(link, upstreamSide, absLane, &attCode); + CHECK_SUCCESS(rc); + attValDb = attCode * -1.5; + rc = ariesGetRxVgaCode(link, upstreamSide, absLane, &vgaCode); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].attdB = attValDb; + link->state.usppState.rxState[laneIndex].vgadB = vgaCode * 0.9; + boostValDb = ariesGetRxBoostValueDb(boostCode, attValDb, vgaCode); + link->state.usppState.rxState[laneIndex].ctleBoostdB = boostValDb; + + // Downstream parameters + rc = ariesGetRxCtleBoostCode(link, downstreamSide, absLane, &boostCode); + CHECK_SUCCESS(rc); + rc = ariesGetRxAttCode(link, downstreamSide, absLane, &attCode); + CHECK_SUCCESS(rc); + attValDb = attCode * -1.5; + rc = ariesGetRxVgaCode(link, downstreamSide, absLane, &vgaCode); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].attdB = attValDb; + link->state.dsppState.rxState[laneIndex].vgadB = vgaCode * 0.9; + boostValDb = ariesGetRxBoostValueDb(boostCode, attValDb, vgaCode); + link->state.dsppState.rxState[laneIndex].ctleBoostdB = boostValDb; + } + + // Get RX Pole Code + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int poleCode; + int absLane = startLane + laneIndex; + + // Upstream parameters + rc = ariesGetRxCtlePoleCode(link, upstreamSide, absLane, &poleCode); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].ctlePole = poleCode; + + // Downstream parameters + rc = ariesGetRxCtlePoleCode(link, downstreamSide, absLane, &poleCode); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].ctlePole = poleCode; + } + + // Get RX ADAPT IQ Value + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int iqValue; + int doneValue; + int absLane = startLane + laneIndex; + + // Upstream parameters + rc = ariesGetRxAdaptIq(link, upstreamSide, absLane, &iqValue); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].adaptIq = iqValue; + // Bank 0 + rc = ariesGetRxAdaptIqBank(link, upstreamSide, absLane, 0, &iqValue); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].adaptIqB0 = iqValue; + rc = ariesGetRxAdaptDoneBank(link, upstreamSide, absLane, 0, + &doneValue); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].adaptDoneB0 = doneValue; + // Bank 1 + rc = ariesGetRxAdaptIqBank(link, upstreamSide, absLane, 1, &iqValue); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].adaptIqB1 = iqValue; + rc = ariesGetRxAdaptDoneBank(link, upstreamSide, absLane, 1, + &doneValue); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].adaptDoneB1 = doneValue; + // Bank 2 + rc = ariesGetRxAdaptIqBank(link, upstreamSide, absLane, 2, &iqValue); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].adaptIqB2 = iqValue; + rc = ariesGetRxAdaptDoneBank(link, upstreamSide, absLane, 2, + &doneValue); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].adaptDoneB2 = doneValue; + + // Downstream parameters + rc = ariesGetRxAdaptIq(link, downstreamSide, absLane, &iqValue); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].adaptIq = iqValue; + // Bank 0 + rc = ariesGetRxAdaptIqBank(link, downstreamSide, absLane, 0, &iqValue); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].adaptIqB0 = iqValue; + rc = ariesGetRxAdaptDoneBank(link, downstreamSide, absLane, 0, + &doneValue); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].adaptDoneB0 = doneValue; + // Bank 1 + rc = ariesGetRxAdaptIqBank(link, downstreamSide, absLane, 1, &iqValue); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].adaptIqB1 = iqValue; + rc = ariesGetRxAdaptDoneBank(link, downstreamSide, absLane, 1, + &doneValue); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].adaptDoneB1 = doneValue; + // Bank 2 + rc = ariesGetRxAdaptIqBank(link, downstreamSide, absLane, 2, &iqValue); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].adaptIqB2 = iqValue; + rc = ariesGetRxAdaptDoneBank(link, downstreamSide, absLane, 2, + &doneValue); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].adaptDoneB2 = doneValue; + } + + // Get RX DFE Values + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int tap = 1; + int code; + float dfe; + int absLane = startLane + laneIndex; + + for (tap = 1; tap <= 8; tap++) + { + // Upstream parameters + rc = ariesGetRxDfeCode(link, upstreamSide, absLane, tap, &code); + CHECK_SUCCESS(rc); + + switch (tap) + { + case 1: + dfe = code * 1.85; + link->state.usppState.rxState[laneIndex].dfe1 = dfe; + break; + case 2: + dfe = code * 0.35; + link->state.usppState.rxState[laneIndex].dfe2 = dfe; + break; + case 3: + dfe = code * 0.7; + link->state.usppState.rxState[laneIndex].dfe3 = dfe; + break; + case 4: + dfe = code * 0.35; + link->state.usppState.rxState[laneIndex].dfe4 = dfe; + break; + case 5: + dfe = code * 0.35; + link->state.usppState.rxState[laneIndex].dfe5 = dfe; + break; + case 6: + dfe = code * 0.35; + link->state.usppState.rxState[laneIndex].dfe6 = dfe; + break; + case 7: + dfe = code * 0.35; + link->state.usppState.rxState[laneIndex].dfe7 = dfe; + break; + case 8: + dfe = code * 0.35; + link->state.usppState.rxState[laneIndex].dfe8 = dfe; + break; + default: + ASTERA_ERROR("Invalid DFE tap argument"); + return ARIES_INVALID_ARGUMENT; + break; + } + + // Downstream parameters + rc = ariesGetRxDfeCode(link, downstreamSide, absLane, tap, &code); + CHECK_SUCCESS(rc); + + switch (tap) + { + case 1: + dfe = code * 1.85; + link->state.dsppState.rxState[laneIndex].dfe1 = dfe; + break; + case 2: + dfe = code * 0.35; + link->state.dsppState.rxState[laneIndex].dfe2 = dfe; + break; + case 3: + dfe = code * 0.7; + link->state.dsppState.rxState[laneIndex].dfe3 = dfe; + break; + case 4: + dfe = code * 0.35; + link->state.dsppState.rxState[laneIndex].dfe4 = dfe; + break; + case 5: + dfe = code * 0.35; + link->state.dsppState.rxState[laneIndex].dfe5 = dfe; + break; + case 6: + dfe = code * 0.35; + link->state.dsppState.rxState[laneIndex].dfe6 = dfe; + break; + case 7: + dfe = code * 0.35; + link->state.dsppState.rxState[laneIndex].dfe7 = dfe; + break; + case 8: + dfe = code * 0.35; + link->state.dsppState.rxState[laneIndex].dfe8 = dfe; + break; + } + } + } + + // Get Temperature Values + // Each PMA has a temp sensor. Read that and store value accordingly in + // lane indexed array + // ariesReadPmaTemp() returns max temp in 16 readings + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int absLane = startLane + laneIndex; + + // startLane = link->config.startLane; + int pmaNum = ariesGetPmaNumber(absLane); + + // Upstream values + float utemp; + rc = ariesReadPmaTemp(link->device, upstreamSide, pmaNum, &utemp); + CHECK_SUCCESS(rc); + + link->state.coreState.usppTempC[laneIndex] = utemp; + + // Downstream values + float dtemp; + rc = ariesReadPmaTemp(link->device, downstreamSide, pmaNum, &dtemp); + CHECK_SUCCESS(rc); + link->state.coreState.dsppTempC[laneIndex] = dtemp; + + link->state.coreState.usppTempAlert[laneIndex] = false; + link->state.coreState.dsppTempAlert[laneIndex] = false; + + float thresh = link->device->tempAlertThreshC; + if (utemp >= thresh) + { + link->state.coreState.usppTempAlert[laneIndex] = true; + } + if (dtemp >= thresh) + { + link->state.coreState.dsppTempAlert[laneIndex] = true; + } + } + + // Get Path HW State + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int absLane = startLane + laneIndex; + int state; + rc = ariesGetPathHWState(link, absLane, upstreamDirection, &state); + CHECK_SUCCESS(rc); + link->state.coreState.usppPathHWState[laneIndex] = state; + rc = ariesGetPathHWState(link, absLane, downstreamDirection, &state); + CHECK_SUCCESS(rc); + link->state.coreState.dsppPathHWState[laneIndex] = state; + } + + // Get DESKEW status (in nanoseconds) + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int status; + int absLane = startLane + laneIndex; + + // Upstream Path + rc = ariesGetDeskewClks(link, absLane, upstreamDirection, &status); + CHECK_SUCCESS(rc); + + int clkPeriod; + if (usppSpeed == 32) + { + clkPeriod = 1; // ns + } + else if (usppSpeed == 16) + { + clkPeriod = 2; // ns + } + else if (usppSpeed == 8) + { + clkPeriod = 4; // ns + } + else if (usppSpeed == 5) + { + clkPeriod = 8; // ns + } + else + { + // GEN 1, speed = 2.5 + clkPeriod = 16; // ns + } + + link->state.coreState.usDeskewNs[laneIndex] = status * clkPeriod; + + // Downstream Values + rc = ariesGetDeskewClks(link, absLane, downstreamDirection, &status); + CHECK_SUCCESS(rc); + + // Get Clock Speed (ns) + if (dsppSpeed == 32) + { + clkPeriod = 1; // ns + } + else if (dsppSpeed == 16) + { + clkPeriod = 2; // ns + } + else if (dsppSpeed == 8) + { + clkPeriod = 4; // ns + } + else if (dsppSpeed == 5) + { + clkPeriod = 8; // ns + } + else + { + // GEN 1, speed = 2.5 + clkPeriod = 16; // ns + } + + link->state.coreState.dsDeskewNs[laneIndex] = status * clkPeriod; + } + + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int absLane = startLane + laneIndex; + int pathID = floor(absLane / 4) * 4; + int lane = absLane % 4; + + rc = ariesGetMinFoMVal(link->device, upstreamSide, pathID, lane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_ADAPT_FOM, + dataWord); + CHECK_SUCCESS(rc); + // FoM value is 7:0 of word. + link->state.usppState.rxState[laneIndex].FoM = dataWord[0]; + + rc = ariesGetMinFoMVal(link->device, downstreamSide, pathID, lane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_ADAPT_FOM, + dataWord); + CHECK_SUCCESS(rc); + // FoM value is 7:0 of word. + link->state.dsppState.rxState[laneIndex].FoM = dataWord[0]; + } + + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int absLane = laneIndex + startLane; + int state; + // FW state + rc = ariesGetPathFWState(link, absLane, usppRxDirection, &state); + CHECK_SUCCESS(rc); + link->state.coreState.usppPathFWState[laneIndex] = state; + // FW state + rc = ariesGetPathFWState(link, absLane, dsppRxDirection, &state); + CHECK_SUCCESS(rc); + link->state.coreState.dsppPathFWState[laneIndex] = state; + } + + // Get RX EQ parameters + for (laneIndex = 0; laneIndex < width; laneIndex++) + { + int absLane = startLane + laneIndex; + int speed; + int presetReq; + int preReq; + int curReq; + int pstReq; + int numReq; + int i; + int req; + int fom; + + ///////////////////// Upstream parameters /////////////////// + + // EQ Speed RX Param + rc = ariesGetLastEqSpeed(link, absLane, usppRxDirection, &speed); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].lastEqRate = speed; + + // EQ Speed TX Param + rc = ariesGetLastEqSpeed(link, absLane, usppTxDirection, &speed); + CHECK_SUCCESS(rc); + link->state.usppState.txState[laneIndex].lastEqRate = speed; + + // Get EQ Preset values + rc = ariesGetLastEqReqPreset(link, absLane, usppTxDirection, + &presetReq); + CHECK_SUCCESS(rc); + link->state.usppState.txState[laneIndex].lastPresetReq = presetReq; + + // If final request was a preset, this is the pre value + rc = ariesGetLastEqReqPre(link, absLane, usppTxDirection, &preReq); + CHECK_SUCCESS(rc); + link->state.usppState.txState[laneIndex].lastPreReq = preReq; + + // If final request was a preset, this is the current value + rc = ariesGetLastEqReqCur(link, absLane, usppTxDirection, &curReq); + CHECK_SUCCESS(rc); + link->state.usppState.txState[laneIndex].lastCurReq = curReq; + + // If final request was a preset, this is the post value + rc = ariesGetLastEqReqPst(link, absLane, usppTxDirection, &pstReq); + CHECK_SUCCESS(rc); + link->state.usppState.txState[laneIndex].lastPstReq = pstReq; + + // Preset requests + rc = ariesGetLastEqNumPresetReq(link, absLane, usppRxDirection, + &numReq); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].lastPresetNumReq = numReq; + for (i = 0; i < numReq; ++i) + { + rc = ariesGetLastEqPresetReq(link, absLane, usppRxDirection, i, + &req); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].lastPresetReq[i] = req; + } + + // Preset request FOM values + for (i = 0; i < numReq; ++i) + { + rc = ariesGetLastEqPresetReqFOM(link, absLane, usppRxDirection, i, + &fom); + CHECK_SUCCESS(rc); + link->state.usppState.rxState[laneIndex].lastPresetReqFom[i] = fom; + } + + ///////////////////// Downstream parameters /////////////////// + + // Downstream EQ speed param (RX) + rc = ariesGetLastEqSpeed(link, absLane, dsppRxDirection, &speed); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].lastEqRate = speed; + + // Downstream EQ speed param (TX) + rc = ariesGetLastEqSpeed(link, absLane, dsppTxDirection, &speed); + CHECK_SUCCESS(rc); + link->state.dsppState.txState[laneIndex].lastEqRate = speed; + + // Get EQ Preset values + rc = ariesGetLastEqReqPreset(link, absLane, dsppTxDirection, + &presetReq); + CHECK_SUCCESS(rc); + link->state.dsppState.txState[laneIndex].lastPresetReq = presetReq; + + // If final request was a preset, this is the pre value + rc = ariesGetLastEqReqPre(link, absLane, dsppTxDirection, &preReq); + CHECK_SUCCESS(rc); + link->state.dsppState.txState[laneIndex].lastPreReq = preReq; + + // If final request was a preset, this is the cur value + rc = ariesGetLastEqReqCur(link, absLane, dsppTxDirection, &curReq); + CHECK_SUCCESS(rc); + link->state.dsppState.txState[laneIndex].lastCurReq = curReq; + + // If final request was a preset, this is the post value + rc = ariesGetLastEqReqPst(link, absLane, dsppTxDirection, &pstReq); + CHECK_SUCCESS(rc); + link->state.dsppState.txState[laneIndex].lastPstReq = pstReq; + + rc = ariesGetLastEqNumPresetReq(link, absLane, dsppRxDirection, + &numReq); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].lastPresetNumReq = numReq; + + // Preset requests + for (i = 0; i < numReq; ++i) + { + rc = ariesGetLastEqPresetReq(link, absLane, dsppRxDirection, i, + &req); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].lastPresetReq[i] = req; + } + + // Preset request FOM values + for (i = 0; i < numReq; ++i) + { + rc = ariesGetLastEqPresetReqFOM(link, absLane, dsppRxDirection, i, + &fom); + CHECK_SUCCESS(rc); + link->state.dsppState.rxState[laneIndex].lastPresetReqFom[i] = fom; + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Initialize LTSSM logger. + * + * Configure the LTSSM logger for one-batch or continuous mode and set the + * print classes (and per-Link enables for main microcontroller log). + * Returns a negative error code, else zero on success. + * + * @param[in] link Pointer to Aries Link struct object + * @param[in] oneBatchModeEn Enable one-batch mode (1) or continuous mode (0) + * @param[in] verbosity Logger verbosity control + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLTSSMLoggerInit(AriesLinkType* link, uint8_t oneBatchModeEn, + AriesLTSSMVerbosityType verbosity) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t dataBytes8[8] = {0}; + uint32_t baseAddress; + uint32_t address; + int absLane; + + int startLane = ariesGetStartLane(link); + + // Initialize Main Micro logger + baseAddress = link->device->mm_print_info_struct_addr; + + // Set One Batch Mode + dataByte[0] = oneBatchModeEn; + address = baseAddress + ARIES_PRINT_INFO_STRUCT_ONE_BATCH_MODE_EN_OFFSET; + rc = ariesWriteByteDataMainMicroIndirect(link->device->i2cDriver, address, + dataByte); + CHECK_SUCCESS(rc); + + // Set print enables + int pcIndex; + switch (verbosity) + { + case ARIES_LTSSM_VERBOSITY_HIGH: + for (pcIndex = 0; pcIndex < ARIES_MM_PRINT_INFO_NUM_PRINT_CLASS_EN; + pcIndex++) + { + dataBytes8[pcIndex] = 0xff; + } + break; + default: + ASTERA_ERROR("Invalid LTSSM logger verbosity"); + return ARIES_INVALID_ARGUMENT; + break; + } + address = baseAddress + ARIES_MM_PRINT_INFO_STRUCT_PRINT_CLASS_EN_OFFSET; + rc = ariesWriteBlockDataMainMicroIndirect(link->device->i2cDriver, address, + 8, dataBytes8); + CHECK_SUCCESS(rc); + + // Initialize Path Micro logger + baseAddress = link->device->pm_print_info_struct_addr; + + // Use laneIndex as path since lane is split between 2 paths and min lanes + // per link is two. For example - lanes 2 and 3 would be path 2 and 3. + int laneIndex; + for (laneIndex = 0; laneIndex < link->state.width; laneIndex++) + { + // absLane = link->config.startLane + laneIndex; + absLane = startLane + laneIndex; + + // Downstream paths + // Set One Batch Mode + dataByte[0] = oneBatchModeEn; + address = baseAddress + + ARIES_PRINT_INFO_STRUCT_ONE_BATCH_MODE_EN_OFFSET; + rc = ariesWriteByteDataPathMicroIndirect(link->device->i2cDriver, + absLane, address, dataByte); + CHECK_SUCCESS(rc); + + // Set Print Class enables + // Can only write micros 8 bytes at a time. Hence we have to spilt the + // writes. There are 16 entires, so split writes into 2 + int pcCount; + for (pcCount = 0; pcCount < 2; pcCount++) + { + address = baseAddress + + ARIES_PM_PRINT_INFO_STRUCT_PRINT_CLASS_EN_OFFSET + + (pcCount * 8); + for (pcIndex = 0; pcIndex < 8; pcIndex++) + { + dataBytes8[pcIndex] = 0xff; + } + rc = ariesWriteBlockDataPathMicroIndirect( + link->device->i2cDriver, absLane, address, 8, dataBytes8); + CHECK_SUCCESS(rc); + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Enable or disable LTSSM logger. + * + * @param[in] link Pointer to Aries Link struct object + * @param[in] printEn Enable (1) or disable (0) printing for this Link + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLTSSMLoggerPrintEn(AriesLinkType* link, uint8_t printEn) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint32_t baseAddress; + uint32_t address; + int absLane; + + int startLane = ariesGetStartLane(link); + + // Do for Main Micro + baseAddress = link->device->mm_print_info_struct_addr; + address = baseAddress + ARIES_PRINT_INFO_STRUCT_PRINT_EN_OFFSET; + dataByte[0] = printEn; + rc = ariesWriteByteDataMainMicroIndirect(link->device->i2cDriver, address, + dataByte); + CHECK_SUCCESS(rc); + + // Do for Path Micros + // Use laneIndex as path since lane is split between 2 paths and min lanes + // per link is two. For example - lanes 2 and 3 would be path 2 and 3. + baseAddress = link->device->pm_print_info_struct_addr; + + // Use laneIndex as path since lane is split between 2 paths and min lanes + // per link is two. For example - lanes 2 and 3 would be path 2 and 3. + int laneIndex; + for (laneIndex = 0; laneIndex < link->state.width; laneIndex++) + { + // absLane = link->config.startLane + laneIndex; + absLane = startLane + laneIndex; + address = baseAddress + ARIES_PRINT_INFO_STRUCT_PRINT_EN_OFFSET; + dataByte[0] = printEn; + + rc = ariesWriteByteDataPathMicroIndirect(link->device->i2cDriver, + absLane, address, dataByte); + CHECK_SUCCESS(rc); + } + + return ARIES_SUCCESS; +} + +/** + * @brief Clear LTSSM Logger + * + * @param[in] link Pointer to Aries Link struct object + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLTSSMLoggerClear(AriesLinkType* link) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint32_t baseAddress; + uint32_t address; + int offset; + + // Stop Micros from printing + rc = ariesLTSSMLoggerPrintEn(link, 0); + CHECK_SUCCESS(rc); + + dataByte[0] = 0; + // Main Micro Log clear + for (offset = 0; offset < ARIES_MM_PRINT_INFO_NUM_PRINT_BUFFER_SIZE; + offset++) + { + baseAddress = link->device->mm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_PRINT_BUFFER_OFFSET; + address = baseAddress + offset; + rc = ariesWriteByteDataMainMicroIndirect(link->device->i2cDriver, + address, dataByte); + CHECK_SUCCESS(rc); + } + + // Path Micros Log Clear + int startLane = ariesGetStartLane(link); + int absLane; + int laneIndex; + for (laneIndex = 0; laneIndex < link->state.width; laneIndex++) + { + absLane = startLane + laneIndex; + + for (offset = 0; offset < ARIES_PM_PRINT_INFO_NUM_PRINT_BUFFER_SIZE; + offset++) + { + baseAddress = link->device->pm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_PRINT_BUFFER_OFFSET; + address = baseAddress + offset; + rc = ariesWriteByteDataPathMicroIndirect( + link->device->i2cDriver, absLane, address, dataByte); + CHECK_SUCCESS(rc); + } + } + + // Enable Macros to print + rc = ariesLTSSMLoggerPrintEn(link, 1); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Read an entry from the LTSSM logger. + * + * The LTSSM log entry starting at offset is returned, and offset + * is updated to point to the next entry. Returns a negative error code, + * including ARIES_LTSSM_INVALID_ENTRY if the the end of the log is reached, + * else zero on success. + * + * @param[in] link Pointer to Aries Link struct object + * @param[in] logType The specific log to read from + * @param[in,out] offset Pointer to the log offset value + * @param[out] entry Pointer to Aries LTSSM Logger Entry struct returned + * by this function + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLTSSMLoggerReadEntry(AriesLinkType* link, + AriesLTSSMLoggerEnumType logType, + int* offset, + AriesLTSSMEntryType* entry) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint32_t baseAddress; + uint32_t address; + + // Main Micro entry + if (logType == ARIES_LTSSM_LINK_LOGGER) + { + baseAddress = link->device->mm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_PRINT_BUFFER_OFFSET; + address = baseAddress + *offset; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, + address, dataByte); + CHECK_SUCCESS(rc); + entry->data = dataByte[0]; + entry->offset = *offset; + (*offset)++; + } + else + { + baseAddress = link->device->pm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_PRINT_BUFFER_OFFSET; + address = baseAddress + *offset; + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, + logType, address, dataByte); + CHECK_SUCCESS(rc); + entry->data = dataByte[0]; + entry->offset = *offset; + (*offset)++; + } + return ARIES_SUCCESS; +} + +/** + * @brief Dump all the debug info from the retimer + * + * @param[in] link Pointer to Aries Link struct object + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLinkDumpDebugInfo(AriesLinkType* link) +{ + char filename[80]; + char timestamp[18]; + time_t currtime; + struct tm* timeinfo; + AriesErrorType rc; + + time(&currtime); + timeinfo = localtime(&currtime); + if (timeinfo == NULL) + { + ASTERA_ERROR("Couldn't get localtime"); + return ARIES_FAILURE; + } + strftime(timestamp, 18, "%G%m%d-%H%M", timeinfo); + // Capture detailed debug information + // This function prints Aries chip ID, FW version, SDK version, + // and the detailed link state information to a file + snprintf(filename, 80, "link_state_detailed_bus%d_addr0x%x_link%d_%s", + link->device->i2cBus, link->device->i2cDriver->slaveAddr, + link->config.linkId, timestamp); + rc = ariesLinkPrintDetailedState(link, ".", filename); + CHECK_SUCCESS(rc); + // This function prints Aries LTSSM logs to a file + snprintf(filename, 80, "ltssm_micro_log_bus%d_addr0x%x_link%d_%s", + link->device->i2cBus, link->device->i2cDriver->slaveAddr, + link->config.linkId, timestamp); + rc = ariesLinkPrintMicroLogs(link, ".", filename); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +// Deprecated! Will be removed in future release +AriesErrorType ariesPrintMicroLogs(AriesLinkType* link, const char* basepath, + const char* filename); +AriesErrorType ariesPrintMicroLogs(AriesLinkType* link, const char* basepath, + const char* filename) +{ + ASTERA_WARN("ariesPrintMicroLogs() has been deprecated " + "and will be removed in a future major version release " + "of the Aries C SDK! " + "Please use ariesLinkPrintMicroLogs() instead"); + return ariesLinkPrintMicroLogs(link, basepath, filename); +} + +/** + * @brief Print logs from all micros in retimer to a file + * + * @param[in] link Pointer to Aries Link struct object + * @param[in] basepath root directory path + * @param[in] filename name of file to write micro logs to + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLinkPrintMicroLogs(AriesLinkType* link, + const char* basepath, + const char* filename) +{ + AriesErrorType rc; + char filepath[ARIES_PATH_MAX]; + FILE* fp; + + if (!link || !basepath || !filename) + { + ASTERA_ERROR("Invalid link or basepath or filename passed"); + return ARIES_INVALID_ARGUMENT; + } + + if (strlen(basepath) == 0 || strlen(filename) == 0) + { + ASTERA_ERROR("Can't load a file without the basepath or filename"); + return ARIES_INVALID_ARGUMENT; + } + + int startLane = ariesGetStartLane(link); + + // Write micro logs output to a file + // File is printed as a python dict so that we can post-process it to a + // readable format as log messages + snprintf(filepath, ARIES_PATH_MAX, "%s/%s.py", basepath, filename); + + fp = fopen(filepath, "w"); + if (fp == NULL) + { + ASTERA_ERROR("Could not open the specified filepath"); + return ARIES_INVALID_ARGUMENT; + } + + fprintf(fp, "# AUTOGENERATED FILE. DO NOT EDIT #\n"); + fprintf(fp, "# GENERATED WTIH C SDK VERSION %s #\n\n\n", + ariesGetSDKVersion()); + + fprintf(fp, "fw_version_major = %d\n", link->device->fwVersion.major); + fprintf(fp, "fw_version_minor = %d\n", link->device->fwVersion.minor); + fprintf(fp, "fw_version_build = %d\n", link->device->fwVersion.build); + + // Initialise logger + rc = ariesLTSSMLoggerInit(link, 0, ARIES_LTSSM_VERBOSITY_HIGH); + if (rc != ARIES_SUCCESS) + { + fprintf( + fp, + "# Encountered error during ariesLinkPrintMicroLogs->ariesLTSSMLoggerInit. Closing file\n"); + fclose(fp); + return rc; + } + + // Enable Macros to print + rc = ariesLTSSMLoggerPrintEn(link, 1); + if (rc != ARIES_SUCCESS) + { + fprintf( + fp, + "# Encountered error during ariesLinkPrintMicroLogs->ariesLTSSMLoggerPrintEn. Closing file\n"); + fclose(fp); + return rc; + } + + fprintf(fp, "aries_micro_logs = [\n"); + fprintf(fp, " {\n"); + fprintf(fp, " 'log_type': %d,\n", ARIES_LTSSM_LINK_LOGGER); + fprintf(fp, " 'log': [ # Open MM log\n"); + // Print Main micro log + rc = ariesPrintLog(link, ARIES_LTSSM_LINK_LOGGER, fp); + fprintf(fp, " ], # Close MM log\n"); + fprintf(fp, " },\n"); + if (rc != ARIES_SUCCESS) + { + fprintf( + fp, + "# Encountered error during ariesLinkPrintMicroLogs->ariesPrintLog. Closing file\n"); + fclose(fp); + return rc; + } + + // Print path micro logs + int laneNum; + int laneIdx; + for (laneIdx = 0; laneIdx < link->state.width; laneIdx++) + { + laneNum = laneIdx + startLane; + fprintf(fp, " {\n"); + fprintf(fp, " 'log_type': %d,\n", laneNum); + fprintf(fp, " 'log': [ # Open PM %d log\n", laneNum); + rc |= ariesPrintLog(link, (AriesLTSSMLoggerEnumType) laneNum, fp); + fprintf(fp, " ], # Close PM %d log\n", laneNum); + fprintf(fp, " },\n"); + } + fprintf(fp, "]\n"); + + if (rc != ARIES_SUCCESS) + { + fprintf( + fp, + "# Encountered error during ariesLinkPrintMicroLogs->ariesPrintLog. Closing file\n"); + fclose(fp); + return rc; + } + fclose(fp); + + return ARIES_SUCCESS; +} + +// Deprecated! Will be removed in future release +AriesErrorType ariesPrintLinkDetailedState(AriesLinkType* link, + const char* basepath, + const char* filename); + +AriesErrorType ariesPrintLinkDetailedState(AriesLinkType* link, + const char* basepath, + const char* filename) +{ + ASTERA_WARN("ariesPrintLinkDetailedState() has been deprecated " + "and will be removed in a future major version release " + "of the Aries C SDK! " + "Please use ariesLinkPrintDetailedState() instead"); + return ariesLinkPrintDetailedState(link, basepath, filename); +} + +/** + * @brief Capture the detailed link state and print it to file + * + * @param[in] link Pointer to Aries Link struct object + * @param[in] basepath root directory path + * @param[in] filename name of file to write micro logs to + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLinkPrintDetailedState(AriesLinkType* link, + const char* basepath, + const char* filename) +{ + AriesErrorType rc; + char filepath[ARIES_PATH_MAX]; + FILE* fp; + + if (!link || !basepath || !filename) + { + ASTERA_ERROR("Invalid link or basepath or filename passed"); + return ARIES_INVALID_ARGUMENT; + } + + if (strlen(basepath) == 0 || strlen(filename) == 0) + { + ASTERA_ERROR("Can't load a file without the basepath or filename"); + return ARIES_INVALID_ARGUMENT; + } + + // Check overall device health + rc = ariesCheckDeviceHealth(link->device); + CHECK_SUCCESS(rc); + + // Check firmware state + rc = ariesFWStatusCheck(link->device); + CHECK_SUCCESS(rc); + + // Get Link state + rc = ariesGetLinkStateDetailed(&link[0]); + CHECK_SUCCESS(rc); + + int startLane = ariesGetStartLane(link); + + // Write link state detailed output to a file + // File is printed as a python dict so that we can post-process it to a + // readable format as a table + snprintf(filepath, ARIES_PATH_MAX, "%s/%s.py", basepath, filename); + + fp = fopen(filepath, "w"); + if (fp == NULL) + { + ASTERA_ERROR("Could not open the specified filepath"); + return ARIES_INVALID_ARGUMENT; + } + + fprintf(fp, "# AUTOGENERATED FILE. DO NOT EDIT #\n"); + fprintf(fp, "# GENERATED WTIH C SDK VERSION %s #\n\n\n", + ariesGetSDKVersion()); + + // Aries device struct parameters + fprintf(fp, "aries_device = {}\n"); + fprintf(fp, "aries_device['c_sdk_version'] = \"%s\"\n", + ariesGetSDKVersion()); + fprintf(fp, "aries_device['fw_version_major'] = %d\n", + link->device->fwVersion.major); + fprintf(fp, "aries_device['fw_version_minor'] = %d\n", + link->device->fwVersion.minor); + fprintf(fp, "aries_device['fw_version_build'] = %d\n", + link->device->fwVersion.build); + int b = 0; + for (b = 0; b < 12; b += 1) + { + fprintf(fp, "aries_device['chipID_%d'] = %d\n", b, + link->device->chipID[b]); + } + for (b = 0; b < 6; b += 1) + { + fprintf(fp, "aries_device['lotNumber_%d'] = %d\n", b, + link->device->lotNumber[b]); + } + fprintf(fp, "aries_device['fullPartNumber'] = \"%s\"\n", + link->device->fullPartNumber); + fprintf(fp, "aries_device['revNumber'] = %d\n", link->device->revNumber); + fprintf(fp, "aries_device['deviceOkay'] = %s\n", + link->device->deviceOkay ? "True" : "False"); + fprintf(fp, "aries_device['allTimeMaxTempC'] = %f\n", + link->device->maxTempC); + fprintf(fp, "aries_device['currentTempC'] = %f\n", + link->device->currentTempC); + fprintf(fp, "aries_device['tempAlertThreshC'] = %f\n\n", + link->device->tempAlertThreshC); + + // Aries link struct parameters + fprintf(fp, "aries_link = {}\n"); + fprintf(fp, "aries_link['link_id'] = %d\n", link->config.linkId); + fprintf(fp, "aries_link['linkOkay'] = %s\n", + link->state.linkOkay ? "True" : "False"); + fprintf(fp, "aries_link['state'] = %d\n", link->state.state); + fprintf(fp, "aries_link['width'] = %d\n", link->state.width); + fprintf(fp, "aries_link['detWidth'] = %d\n", link->state.detWidth); + fprintf(fp, "aries_link['curWidth'] = %d\n", link->state.curWidth); + fprintf(fp, "aries_link['linkMinFoM'] = %d\n", link->state.linkMinFoM); + fprintf(fp, "aries_link['linkMinFoMRx'] = '%s'\n", + link->state.linkMinFoMRx); + fprintf(fp, "aries_link['recoveryCount'] = %d\n", + link->state.recoveryCount); + fprintf(fp, "aries_link['uspp_speed'] = %2.1f\n", link->state.usppSpeed); + fprintf(fp, "aries_link['dspp_speed'] = %2.1f\n", link->state.dsppSpeed); + fprintf(fp, "aries_link['uspp'] = {}\n"); + fprintf(fp, "aries_link['uspp']['tx'] = {}\n"); + fprintf(fp, "aries_link['uspp']['rx'] = {}\n"); + fprintf(fp, "aries_link['dspp'] = {}\n"); + fprintf(fp, "aries_link['dspp']['tx'] = {}\n"); + fprintf(fp, "aries_link['dspp']['rx'] = {}\n"); + fprintf(fp, "aries_link['rt_core'] = {}\n"); + fprintf(fp, "aries_link['rt_core']['us'] = {}\n"); + fprintf(fp, "aries_link['rt_core']['ds'] = {}\n"); + + int phyLaneNum; + int laneIndex; + int i; + for (laneIndex = 0; laneIndex < link->state.width; laneIndex++) + { + phyLaneNum = laneIndex + startLane; + + fprintf(fp, + "\n\n### Stats for logical lane %d (physical lane %d) ###\n", + laneIndex, phyLaneNum); + + fprintf(fp, "aries_link['uspp']['tx'][%d] = {}\n", laneIndex); + fprintf(fp, "aries_link['uspp']['tx'][%d]['logical_lane'] = %d\n", + laneIndex, + link->state.usppState.txState[laneIndex].logicalLaneNum); + fprintf(fp, "aries_link['uspp']['tx'][%d]['physical_pin'] = '%s'\n", + laneIndex, + link->state.usppState.txState[laneIndex].physicalPinName); + fprintf(fp, "aries_link['uspp']['tx'][%d]['de'] = %d\n", laneIndex, + link->state.usppState.txState[laneIndex].de); + fprintf(fp, "aries_link['uspp']['tx'][%d]['pre'] = %d\n", laneIndex, + link->state.usppState.txState[laneIndex].pre); + fprintf(fp, "aries_link['uspp']['tx'][%d]['cur'] = %d\n", laneIndex, + link->state.usppState.txState[laneIndex].cur); + fprintf(fp, "aries_link['uspp']['tx'][%d]['pst'] = %f\n", laneIndex, + link->state.usppState.txState[laneIndex].pst); + fprintf(fp, "aries_link['uspp']['tx'][%d]['last_eq_rate'] = %d\n", + laneIndex, link->state.usppState.txState[laneIndex].lastEqRate); + fprintf(fp, "aries_link['uspp']['tx'][%d]['last_preset_req'] = %d\n", + laneIndex, + link->state.usppState.txState[laneIndex].lastPresetReq); + fprintf(fp, "aries_link['uspp']['tx'][%d]['last_pre_req'] = %d\n", + laneIndex, link->state.usppState.txState[laneIndex].lastPreReq); + fprintf(fp, "aries_link['uspp']['tx'][%d]['last_cur_req'] = %d\n", + laneIndex, link->state.usppState.txState[laneIndex].lastCurReq); + fprintf(fp, "aries_link['uspp']['tx'][%d]['last_pst_req'] = %d\n", + laneIndex, link->state.usppState.txState[laneIndex].lastPstReq); + fprintf(fp, "\n"); + + fprintf(fp, "aries_link['uspp']['rx'][%d] = {}\n", laneIndex); + fprintf(fp, "aries_link['uspp']['rx'][%d]['logical_lane'] = %d\n", + laneIndex, + link->state.usppState.rxState[laneIndex].logicalLaneNum); + fprintf(fp, "aries_link['uspp']['rx'][%d]['physical_pin'] = '%s'\n", + laneIndex, + link->state.usppState.rxState[laneIndex].physicalPinName); + fprintf(fp, "aries_link['uspp']['rx'][%d]['termination'] = %d\n", + laneIndex, + link->state.usppState.rxState[laneIndex].termination); + fprintf(fp, "aries_link['uspp']['rx'][%d]['polarity'] = %d\n", + laneIndex, link->state.usppState.rxState[laneIndex].polarity); + fprintf(fp, "aries_link['uspp']['rx'][%d]['att_db'] = %f\n", laneIndex, + link->state.usppState.rxState[laneIndex].attdB); + fprintf(fp, "aries_link['uspp']['rx'][%d]['ctle_boost_db'] = %f\n", + laneIndex, + link->state.usppState.rxState[laneIndex].ctleBoostdB); + fprintf(fp, "aries_link['uspp']['rx'][%d]['ctle_pole'] = %d\n", + laneIndex, link->state.usppState.rxState[laneIndex].ctlePole); + fprintf(fp, "aries_link['uspp']['rx'][%d]['vga_db'] = %f\n", laneIndex, + link->state.usppState.rxState[laneIndex].vgadB); + fprintf(fp, "aries_link['uspp']['rx'][%d]['adapt_iq'] = %d\n", + laneIndex, link->state.usppState.rxState[laneIndex].adaptIq); + fprintf(fp, "aries_link['uspp']['rx'][%d]['adapt_iq_b0'] = %d\n", + laneIndex, link->state.usppState.rxState[laneIndex].adaptIqB0); + fprintf(fp, "aries_link['uspp']['rx'][%d]['adapt_done_b0'] = %d\n", + laneIndex, + link->state.usppState.rxState[laneIndex].adaptDoneB0); + fprintf(fp, "aries_link['uspp']['rx'][%d]['adapt_iq_b1'] = %d\n", + laneIndex, link->state.usppState.rxState[laneIndex].adaptIqB1); + fprintf(fp, "aries_link['uspp']['rx'][%d]['adapt_done_b1'] = %d\n", + laneIndex, + link->state.usppState.rxState[laneIndex].adaptDoneB1); + fprintf(fp, "aries_link['uspp']['rx'][%d]['adapt_iq_b2'] = %d\n", + laneIndex, link->state.usppState.rxState[laneIndex].adaptIqB2); + fprintf(fp, "aries_link['uspp']['rx'][%d]['adapt_done_b2'] = %d\n", + laneIndex, + link->state.usppState.rxState[laneIndex].adaptDoneB2); + fprintf(fp, "aries_link['uspp']['rx'][%d]['dfe'] = {}\n", laneIndex); + fprintf(fp, "aries_link['uspp']['rx'][%d]['dfe'][1] = %f\n", laneIndex, + link->state.usppState.rxState[laneIndex].dfe1); + fprintf(fp, "aries_link['uspp']['rx'][%d]['dfe'][2] = %f\n", laneIndex, + link->state.usppState.rxState[laneIndex].dfe2); + fprintf(fp, "aries_link['uspp']['rx'][%d]['dfe'][3] = %f\n", laneIndex, + link->state.usppState.rxState[laneIndex].dfe3); + fprintf(fp, "aries_link['uspp']['rx'][%d]['dfe'][4] = %f\n", laneIndex, + link->state.usppState.rxState[laneIndex].dfe4); + fprintf(fp, "aries_link['uspp']['rx'][%d]['dfe'][5] = %f\n", laneIndex, + link->state.usppState.rxState[laneIndex].dfe5); + fprintf(fp, "aries_link['uspp']['rx'][%d]['dfe'][6] = %f\n", laneIndex, + link->state.usppState.rxState[laneIndex].dfe6); + fprintf(fp, "aries_link['uspp']['rx'][%d]['dfe'][7] = %f\n", laneIndex, + link->state.usppState.rxState[laneIndex].dfe7); + fprintf(fp, "aries_link['uspp']['rx'][%d]['dfe'][8] = %f\n", laneIndex, + link->state.usppState.rxState[laneIndex].dfe8); + fprintf(fp, "aries_link['uspp']['rx'][%d]['last_eq_rate'] = %d\n", + laneIndex, link->state.usppState.rxState[laneIndex].lastEqRate); + fprintf(fp, + "aries_link['uspp']['rx'][%d]['last_preset_num_req'] = %d\n", + laneIndex, + link->state.usppState.rxState[laneIndex].lastPresetNumReq); + int last_preset = + link->state.usppState.rxState[laneIndex].lastPresetNumReq - 1; + fprintf(fp, "aries_link['uspp']['rx'][%d]['last_preset_req'] = %d\n", + laneIndex, + link->state.usppState.rxState[laneIndex] + .lastPresetReq[last_preset]); + fprintf(fp, + "aries_link['uspp']['rx'][%d]['last_preset_req_fom'] = %d\n", + laneIndex, + link->state.usppState.rxState[laneIndex] + .lastPresetReqFom[last_preset]); + for (i = 1; + i < link->state.usppState.rxState[laneIndex].lastPresetNumReq; ++i) + { + fprintf( + fp, + "aries_link['uspp']['rx'][%d]['last_preset_req_m%d'] = %d\n", + laneIndex, i, + link->state.usppState.rxState[laneIndex] + .lastPresetReq[last_preset - i]); + fprintf( + fp, + "aries_link['uspp']['rx'][%d]['last_preset_req_fom_m%d'] = %d\n", + laneIndex, i, + link->state.usppState.rxState[laneIndex] + .lastPresetReqFom[last_preset - i]); + } + fprintf(fp, "\n"); + + fprintf(fp, "aries_link['rt_core']['us'][%d] = {}\n", laneIndex); + fprintf(fp, "aries_link['rt_core']['us'][%d]['tj_c'] = %2.2f\n", + laneIndex, link->state.coreState.usppTempC[laneIndex]); + fprintf(fp, "aries_link['rt_core']['us'][%d]['skew_ns'] = %d\n", + laneIndex, link->state.coreState.usDeskewNs[laneIndex]); + fprintf(fp, "aries_link['rt_core']['us'][%d]['tj_c_alert'] = %d\n", + laneIndex, link->state.coreState.usppTempAlert[laneIndex]); + fprintf(fp, "aries_link['rt_core']['us'][%d]['pth_fw_state'] = %d\n", + laneIndex, link->state.coreState.usppPathFWState[laneIndex]); + fprintf(fp, "aries_link['rt_core']['us'][%d]['pth_hw_state'] = %d\n", + laneIndex, link->state.coreState.usppPathHWState[laneIndex]); + + fprintf(fp, "aries_link['rt_core']['ds'][%d] = {}\n", laneIndex); + fprintf(fp, "aries_link['rt_core']['ds'][%d]['tj_c'] = %2.2f\n", + laneIndex, link->state.coreState.dsppTempC[laneIndex]); + fprintf(fp, "aries_link['rt_core']['ds'][%d]['skew_ns'] = %d\n", + laneIndex, link->state.coreState.dsDeskewNs[laneIndex]); + fprintf(fp, "aries_link['rt_core']['ds'][%d]['tj_c_alert'] = %d\n", + laneIndex, link->state.coreState.dsppTempAlert[laneIndex]); + fprintf(fp, "aries_link['rt_core']['ds'][%d]['pth_fw_state'] = %d\n", + laneIndex, link->state.coreState.dsppPathFWState[laneIndex]); + fprintf(fp, "aries_link['rt_core']['ds'][%d]['pth_hw_state'] = %d\n", + laneIndex, link->state.coreState.dsppPathHWState[laneIndex]); + fprintf(fp, "\n"); + + fprintf(fp, "aries_link['dspp']['tx'][%d] = {}\n", laneIndex); + fprintf(fp, "aries_link['dspp']['tx'][%d]['logical_lane'] = %d\n", + laneIndex, + link->state.dsppState.txState[laneIndex].logicalLaneNum); + fprintf(fp, "aries_link['dspp']['tx'][%d]['physical_pin'] = '%s'\n", + laneIndex, + link->state.dsppState.txState[laneIndex].physicalPinName); + fprintf(fp, "aries_link['dspp']['tx'][%d]['de'] = %d\n", laneIndex, + link->state.dsppState.txState[laneIndex].de); + fprintf(fp, "aries_link['dspp']['tx'][%d]['pre'] = %d\n", laneIndex, + link->state.dsppState.txState[laneIndex].pre); + fprintf(fp, "aries_link['dspp']['tx'][%d]['cur'] = %d\n", laneIndex, + link->state.dsppState.txState[laneIndex].cur); + fprintf(fp, "aries_link['dspp']['tx'][%d]['pst'] = %f\n", laneIndex, + link->state.dsppState.txState[laneIndex].pst); + fprintf(fp, "aries_link['dspp']['tx'][%d]['last_eq_rate'] = %d\n", + laneIndex, link->state.dsppState.txState[laneIndex].lastEqRate); + fprintf(fp, "aries_link['dspp']['tx'][%d]['last_preset_req'] = %d\n", + laneIndex, + link->state.dsppState.txState[laneIndex].lastPresetReq); + fprintf(fp, "aries_link['dspp']['tx'][%d]['last_pre_req'] = %d\n", + laneIndex, link->state.dsppState.txState[laneIndex].lastPreReq); + fprintf(fp, "aries_link['dspp']['tx'][%d]['last_cur_req'] = %d\n", + laneIndex, link->state.dsppState.txState[laneIndex].lastCurReq); + fprintf(fp, "aries_link['dspp']['tx'][%d]['last_pst_req'] = %d\n", + laneIndex, link->state.dsppState.txState[laneIndex].lastPstReq); + fprintf(fp, "\n"); + + fprintf(fp, "aries_link['dspp']['rx'][%d] = {}\n", laneIndex); + fprintf(fp, "aries_link['dspp']['rx'][%d]['logical_lane'] = %d\n", + laneIndex, + link->state.dsppState.rxState[laneIndex].logicalLaneNum); + fprintf(fp, "aries_link['dspp']['rx'][%d]['physical_pin'] = '%s'\n", + laneIndex, + link->state.dsppState.rxState[laneIndex].physicalPinName); + fprintf(fp, "aries_link['dspp']['rx'][%d]['termination'] = %d\n", + laneIndex, + link->state.dsppState.rxState[laneIndex].termination); + fprintf(fp, "aries_link['dspp']['rx'][%d]['polarity'] = %d\n", + laneIndex, link->state.dsppState.rxState[laneIndex].polarity); + fprintf(fp, "aries_link['dspp']['rx'][%d]['att_db'] = %f\n", laneIndex, + link->state.dsppState.rxState[laneIndex].attdB); + fprintf(fp, "aries_link['dspp']['rx'][%d]['ctle_boost_db'] = %f\n", + laneIndex, + link->state.dsppState.rxState[laneIndex].ctleBoostdB); + fprintf(fp, "aries_link['dspp']['rx'][%d]['ctle_pole'] = %d\n", + laneIndex, link->state.dsppState.rxState[laneIndex].ctlePole); + fprintf(fp, "aries_link['dspp']['rx'][%d]['vga_db'] = %f\n", laneIndex, + link->state.dsppState.rxState[laneIndex].vgadB); + fprintf(fp, "aries_link['dspp']['rx'][%d]['adapt_iq'] = %d\n", + laneIndex, link->state.dsppState.rxState[laneIndex].adaptIq); + fprintf(fp, "aries_link['dspp']['rx'][%d]['adapt_iq_b0'] = %d\n", + laneIndex, link->state.dsppState.rxState[laneIndex].adaptIqB0); + fprintf(fp, "aries_link['dspp']['rx'][%d]['adapt_done_b0'] = %d\n", + laneIndex, + link->state.dsppState.rxState[laneIndex].adaptDoneB0); + fprintf(fp, "aries_link['dspp']['rx'][%d]['adapt_iq_b1'] = %d\n", + laneIndex, link->state.dsppState.rxState[laneIndex].adaptIqB1); + fprintf(fp, "aries_link['dspp']['rx'][%d]['adapt_done_b1'] = %d\n", + laneIndex, + link->state.dsppState.rxState[laneIndex].adaptDoneB1); + fprintf(fp, "aries_link['dspp']['rx'][%d]['adapt_iq_b2'] = %d\n", + laneIndex, link->state.dsppState.rxState[laneIndex].adaptIqB2); + fprintf(fp, "aries_link['dspp']['rx'][%d]['adapt_done_b2'] = %d\n", + laneIndex, + link->state.dsppState.rxState[laneIndex].adaptDoneB2); + fprintf(fp, "aries_link['dspp']['rx'][%d]['dfe'] = {}\n", laneIndex); + fprintf(fp, "aries_link['dspp']['rx'][%d]['dfe'][1] = %f\n", laneIndex, + link->state.dsppState.rxState[laneIndex].dfe1); + fprintf(fp, "aries_link['dspp']['rx'][%d]['dfe'][2] = %f\n", laneIndex, + link->state.dsppState.rxState[laneIndex].dfe2); + fprintf(fp, "aries_link['dspp']['rx'][%d]['dfe'][3] = %f\n", laneIndex, + link->state.dsppState.rxState[laneIndex].dfe3); + fprintf(fp, "aries_link['dspp']['rx'][%d]['dfe'][4] = %f\n", laneIndex, + link->state.dsppState.rxState[laneIndex].dfe4); + fprintf(fp, "aries_link['dspp']['rx'][%d]['dfe'][5] = %f\n", laneIndex, + link->state.dsppState.rxState[laneIndex].dfe5); + fprintf(fp, "aries_link['dspp']['rx'][%d]['dfe'][6] = %f\n", laneIndex, + link->state.dsppState.rxState[laneIndex].dfe6); + fprintf(fp, "aries_link['dspp']['rx'][%d]['dfe'][7] = %f\n", laneIndex, + link->state.dsppState.rxState[laneIndex].dfe7); + fprintf(fp, "aries_link['dspp']['rx'][%d]['dfe'][8] = %f\n", laneIndex, + link->state.dsppState.rxState[laneIndex].dfe8); + fprintf(fp, "aries_link['dspp']['rx'][%d]['last_eq_rate'] = %d\n", + laneIndex, link->state.dsppState.rxState[laneIndex].lastEqRate); + fprintf(fp, + "aries_link['dspp']['rx'][%d]['last_preset_num_req'] = %d\n", + laneIndex, + link->state.dsppState.rxState[laneIndex].lastPresetNumReq); + last_preset = + link->state.dsppState.rxState[laneIndex].lastPresetNumReq - 1; + fprintf(fp, "aries_link['dspp']['rx'][%d]['last_preset_req'] = %d\n", + laneIndex, + link->state.dsppState.rxState[laneIndex] + .lastPresetReq[last_preset]); + fprintf(fp, + "aries_link['dspp']['rx'][%d]['last_preset_req_fom'] = %d\n", + laneIndex, + link->state.dsppState.rxState[laneIndex] + .lastPresetReqFom[last_preset]); + for (i = 1; + i < link->state.dsppState.rxState[laneIndex].lastPresetNumReq; ++i) + { + fprintf( + fp, + "aries_link['dspp']['rx'][%d]['last_preset_req_m%d'] = %d\n", + laneIndex, i, + link->state.dsppState.rxState[laneIndex] + .lastPresetReq[last_preset - i]); + fprintf( + fp, + "aries_link['dspp']['rx'][%d]['last_preset_req_fom_m%d'] = %d\n", + laneIndex, i, + link->state.dsppState.rxState[laneIndex] + .lastPresetReqFom[last_preset - i]); + } + fprintf(fp, "\n"); + } + fclose(fp); + + return ARIES_SUCCESS; +} + +/** + * @brief Print the micro logger entries + * + * Low level api used by ariesLinkPrintMicroLogs() not generally used directly + * + * @param[in] link Pointer to Aries Link struct object + * @param[in] log log type + * @param[in] fp file pointer to output file for logger entries + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesPrintLog(AriesLinkType* link, AriesLTSSMLoggerEnumType log, + FILE* fp) +{ + AriesLTSSMEntryType ltssmEntry; + int offset = 0; + int oneBatchModeEn; + int oneBatchWrEn; + int batchComplete; + int currFmtID; + int currWriteOffset; + AriesErrorType rc; + int full = 0; + + ltssmEntry.logType = log; + + // Buffer size different for main and path micros + int bufferSize; + if (log == ARIES_LTSSM_LINK_LOGGER) + { + bufferSize = ARIES_MM_PRINT_INFO_NUM_PRINT_BUFFER_SIZE; + } + else + { + bufferSize = ARIES_PM_PRINT_INFO_NUM_PRINT_BUFFER_SIZE; + } + + // Get One batch mode enable + rc = ariesGetLoggerOneBatchModeEn(link, log, &oneBatchModeEn); + CHECK_SUCCESS(rc); + + // Get One batch write enable + rc = ariesGetLoggerOneBatchWrEn(link, log, &oneBatchWrEn); + CHECK_SUCCESS(rc); + + if (oneBatchModeEn == 0) + { + // Stop Micros from printing + rc = ariesLTSSMLoggerPrintEn(link, 0); + CHECK_SUCCESS(rc); + + // In this mode we print the logger from current write offset + // and reset the offset to zero once we reach the end of the buffer + // Capture current write offset + rc = ariesGetLoggerWriteOffset(link, log, &currWriteOffset); + CHECK_SUCCESS(rc); + // Start offset from the current (paused) offset + offset = currWriteOffset; + + // Print logger from current offset + while (offset < bufferSize) + { + rc = ariesLTSSMLoggerReadEntry(link, log, &offset, <ssmEntry); + CHECK_SUCCESS(rc); + fprintf(fp, " {\n"); + fprintf(fp, " 'data': %d,\n", ltssmEntry.data); + fprintf(fp, " 'offset': %d\n", ltssmEntry.offset); + fprintf(fp, " },\n"); + } + + // Wrap around and continue reading the log entries + if (currWriteOffset != 0) + { + // Reset offset to start from beginning + offset = 0; + + // Print logger from start of print buffer + while (offset < currWriteOffset) + { + // Read logger entry + rc = ariesLTSSMLoggerReadEntry(link, log, &offset, <ssmEntry); + CHECK_SUCCESS(rc); + fprintf(fp, " {\n"); + fprintf(fp, " 'data': %d,\n", ltssmEntry.data); + fprintf(fp, " 'offset': %d\n", + ltssmEntry.offset); + fprintf(fp, " },\n"); + } + } + + // Enable Macros to print + rc = ariesLTSSMLoggerPrintEn(link, 1); + CHECK_SUCCESS(rc); + } + else + { + // Check if batch is complete + batchComplete = (oneBatchModeEn == 1) && (oneBatchWrEn == 0); + + // Read format ID at current offset + rc = ariesGetLoggerFmtID(link, log, offset, &currFmtID); + CHECK_SUCCESS(rc); + + if (batchComplete == 1) + { + full = 1; + while ((currFmtID != 0) && (offset < bufferSize)) + { + // Get logger entry + rc = ariesLTSSMLoggerReadEntry(link, log, &offset, <ssmEntry); + CHECK_SUCCESS(rc); + fprintf(fp, " {\n"); + fprintf(fp, " 'data': %d,\n", ltssmEntry.data); + fprintf(fp, " 'offset': %d\n", + ltssmEntry.offset); + fprintf(fp, " },\n"); + // Read Fmt ID + rc = ariesGetLoggerFmtID(link, log, offset, &currFmtID); + CHECK_SUCCESS(rc); + } + } + else + { + // Print from start of the buffer until the end + while ((currFmtID != 0) && (offset < bufferSize) && + (offset < currWriteOffset)) + { + // Get logger entry + rc = ariesLTSSMLoggerReadEntry(link, log, &offset, <ssmEntry); + CHECK_SUCCESS(rc); + fprintf(fp, " {\n"); + fprintf(fp, " 'data': %d,\n", ltssmEntry.data); + fprintf(fp, " 'offset': %d\n", + ltssmEntry.offset); + fprintf(fp, " },\n"); + // Read Fmt ID + rc = ariesGetLoggerFmtID(link, log, offset, &currFmtID); + CHECK_SUCCESS(rc); + // Read current write offset + rc = ariesGetLoggerWriteOffset(link, log, &currWriteOffset); + CHECK_SUCCESS(rc); + } + } + } + + if (full == 0) + { + ASTERA_INFO("There is more to print ..."); + } + + return ARIES_SUCCESS; +} + +/** + * @brief Set the list of presets that the retimer should request + * + * This modifies the list of presets that the retimer will request when + * performing Phase 2 and 3 of Equalization + * + * @param[in] link Pointer to Aries Link struct object + * @param[in] port Port to apply new settings to (USPP or DSPP) + * @param[in] rate PCIe rate to apply settings to (Gen-3, Gen-4, or + * Gen-5) + * @param[in] laneMask Logical lane mask of lanes in the link to update + * @param[in] presetOneHotList The one-hot preset request list; set the + * bit corresponding to the preset you want + * to enable; for example to request + * presets 4, 5, and 9 the presetOneHotList + * would be 0x230 + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLinkPresetRequestListSet(AriesLinkType* link, + AriesPseudoPortType port, + AriesMaxDataRateType rate, + uint16_t laneMask, + uint16_t presetOneHotList) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + uint32_t baseAddress; + uint32_t sliceOffset; + uint8_t laneOffset; + uint8_t rateOffset; + uint32_t address; + int orientation; + int laneIndex; + int startLane; + int absLane; + + rc = ariesGetPortOrientation(link->device, &orientation); + CHECK_SUCCESS(rc); + + if ((link->config.partNumber == ARIES_PTX16 && + ((port == ARIES_UP_STREAM_PSEUDO_PORT && + orientation == ARIES_ORIENTATION_NORMAL) || + (port == ARIES_DOWN_STREAM_PSEUDO_PORT && + orientation == ARIES_ORIENTATION_REVERSED))) || + (link->config.partNumber == ARIES_PTX08 && + ((port == ARIES_UP_STREAM_PSEUDO_PORT && + orientation == ARIES_ORIENTATION_REVERSED) || + (port == ARIES_DOWN_STREAM_PSEUDO_PORT && + orientation == ARIES_ORIENTATION_NORMAL)))) + { + baseAddress = ARIES_MISC_AL_PSEUDO_PORT_0_CSR_OFFSET; + } + else if ((link->config.partNumber == ARIES_PTX16 && + ((port == ARIES_UP_STREAM_PSEUDO_PORT && + orientation == ARIES_ORIENTATION_REVERSED) || + (port == ARIES_DOWN_STREAM_PSEUDO_PORT && + orientation == ARIES_ORIENTATION_NORMAL))) || + (link->config.partNumber == ARIES_PTX08 && + ((port == ARIES_UP_STREAM_PSEUDO_PORT && + orientation == ARIES_ORIENTATION_NORMAL) || + (port == ARIES_DOWN_STREAM_PSEUDO_PORT && + orientation == ARIES_ORIENTATION_REVERSED)))) + { + baseAddress = ARIES_MISC_AL_PSEUDO_PORT_1_CSR_OFFSET; + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + if (rate == ARIES_GEN5) + { + rateOffset = 0x0; + } + else if (rate == ARIES_GEN4) + { + rateOffset = 0x2; + } + else if (rate == ARIES_GEN3) + { + rateOffset = 0x4; + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + startLane = ariesGetStartLane(link); + for (laneIndex = 0; laneIndex < link->state.width; laneIndex++) + { + if ((laneMask >> laneIndex) & 0x1) + { + absLane = startLane + laneIndex; + + if ((laneIndex % 2) == 0) + { + laneOffset = 0x8; + } + else + { + laneOffset = 0x1b; + } + + sliceOffset = (absLane / 2) * 0x2e; + + address = baseAddress + sliceOffset + laneOffset + rateOffset; + + dataWord[0] = presetOneHotList & 0xff; + dataWord[1] = (presetOneHotList >> 8) & 0x7; + rc = ariesWriteWideRegister(link->device->i2cDriver, address, 2, + dataWord); + CHECK_SUCCESS(rc); + } + } + + return ARIES_SUCCESS; +} + +#ifdef __cplusplus +} +#endif diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_link.h b/common/recipes-lib/retimer-v2.16.2/files/aries_link.h new file mode 100755 index 000000000000..ad1604438c27 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_link.h @@ -0,0 +1,97 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_link.h + * @brief Definition of Link level functions for the SDK. + */ +#pragma once +#ifndef ASTERA_ARIES_SDK_LINK_H_ +#define ASTERA_ARIES_SDK_LINK_H_ + +#include "aries_globals.h" +#include "aries_error.h" +#include "aries_api_types.h" +#include "astera_log.h" +#include "aries_api.h" +#include "aries_misc.h" + +#ifdef ARIES_MPW +#include "aries_mpw_reg_defines.h" +#else +#include "aries_a0_reg_defines.h" +#endif + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +AriesErrorType ariesSetPcieReset(AriesLinkType* link, uint8_t reset); + +AriesErrorType ariesCheckLinkHealth(AriesLinkType* link); + +AriesErrorType ariesLinkGetCurrentWidth(AriesLinkType* link, int* currentWidth); + +AriesErrorType ariesGetLinkRecoveryCount(AriesLinkType* link, + int* recoveryCount); + +AriesErrorType ariesClearLinkRecoveryCount(AriesLinkType* link); + +AriesErrorType ariesGetLinkState(AriesLinkType* link); + +AriesErrorType ariesGetLinkStateDetailed(AriesLinkType* link); + +AriesErrorType ariesLTSSMLoggerInit(AriesLinkType* link, uint8_t oneBatchModeEn, + AriesLTSSMVerbosityType verbosity); + +AriesErrorType ariesLTSSMLoggerPrintEn(AriesLinkType* link, uint8_t printEn); + +AriesErrorType ariesLTSSMLoggerClear(AriesLinkType* link); + +AriesErrorType ariesLTSSMLoggerReadEntry(AriesLinkType* link, + AriesLTSSMLoggerEnumType logType, + int* offset, + AriesLTSSMEntryType* entry); + +AriesErrorType ariesLinkDumpDebugInfo(AriesLinkType* link); + +AriesErrorType ariesLinkPrintMicroLogs(AriesLinkType* link, + const char* basepath, + const char* filename); + +AriesErrorType ariesLinkPrintDetailedState(AriesLinkType* link, + const char* basepath, + const char* filename); + +AriesErrorType ariesPrintLog(AriesLinkType* link, AriesLTSSMLoggerEnumType log, + FILE* fp); + +AriesErrorType ariesLinkPresetRequestListSet(AriesLinkType* link, + AriesPseudoPortType port, + AriesMaxDataRateType rate, + uint16_t laneMask, + uint16_t presetOneHotList); + +#ifdef __cplusplus +} +#endif + +#endif /* ASTERA_ARIES_SDK_LINK_H_ */ diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_margin.c b/common/recipes-lib/retimer-v2.16.2/files/aries_margin.c new file mode 100755 index 000000000000..5b2346089806 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_margin.c @@ -0,0 +1,1886 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_margin.c + * @brief Implementation of receiver margining functions for the SDK. + */ + +#include "aries_margin.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Margin Command for No Command + * + * Sending this out of band doesn't do anything + * + * @param[in] marginDevice Struct containing Margin Device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginNoCommand(AriesRxMarginType* marginDevice) +{ + (void)marginDevice; + ASTERA_WARN("Sending this out of band doesn't do anything"); + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to access Retimer register + * + * Register access is not implemented yet + * + * @param[in] marginDevice Struct containing Margin Device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginAccessRetimerRegister(AriesRxMarginType* marginDevice) +{ + (void)marginDevice; + ASTERA_WARN("Register access is not implemented yet"); + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to print and store margin control capabilities + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[out] capabilities Array to store margin device capabilities + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesMarginReportMarginControlCapabilities(AriesRxMarginType* marginDevice, + int* capabilities) +{ + (void)marginDevice; + ASTERA_INFO("Reporting Margin Control Capabilities:"); + ASTERA_INFO(" M_VOLTAGE_SUPPORTED = %d", VOLTAGESUPPORTED); + ASTERA_INFO(" M_IND_UP_DOWN_VOLTAGE = %d", INDUPDOWNVOLTAGE); + ASTERA_INFO(" M_IND_LEFT_RIGHT_TIMING = %d", INDLEFTRIGHTTIMING); + ASTERA_INFO(" M_SAMPLE_REPORTING_METHOD = %d", SAMPLEREPORTINGMETHOD); + ASTERA_INFO(" M_IND_ERROR_SAMPLER = %d", INDERRORSAMPLER); + capabilities[0] = VOLTAGESUPPORTED; + capabilities[1] = INDUPDOWNVOLTAGE; + capabilities[2] = INDLEFTRIGHTTIMING; + capabilities[3] = SAMPLEREPORTINGMETHOD; + capabilities[4] = INDERRORSAMPLER; + + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to report the number of voltage steps + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[out] numVoltageSteps variable to store number of voltage steps in + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginReportNumVoltageSteps(AriesRxMarginType* marginDevice, + int* numVoltageSteps) +{ + (void)marginDevice; + *numVoltageSteps = NUMVOLTAGESTEPS; + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to report the number of timing steps + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[out] numTimingSteps variable to store number of timing steps in + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginReportNumTimingSteps(AriesRxMarginType* marginDevice, + int* numTimingSteps) +{ + (void)marginDevice; + *numTimingSteps = NUMTIMINGSTEPS; + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to report the max timing offset + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[out] maxTimingOffset variable to store max timing offset in + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginReportMaxTimingOffset(AriesRxMarginType* marginDevice, + int* maxTimingOffset) +{ + (void)marginDevice; + *maxTimingOffset = MAXTIMINGOFFSET; + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to report the max voltage offset + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[out] maxVoltageOffset variable to store max voltage offset in + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesMarginReportMaxVoltageOffset(AriesRxMarginType* marginDevice, + int* maxVoltageOffset) +{ + (void)marginDevice; + *maxVoltageOffset = MAXVOLTAGEOFFSET; + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to report sampling rate voltage + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[out] samplingRateVoltage variable to store samplingRateVoltage in + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesMarginReportSamplingRateVoltage(AriesRxMarginType* marginDevice, + int* samplingRateVoltage) +{ + (void)marginDevice; + *samplingRateVoltage = SAMPLINGRATEVOLTAGE; + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to report sampling rate timing + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[out] samplingRateTiming variable to store samplingRateTiming in; + * @return AriesErrorType - Aries error code + */ +AriesErrorType + ariesMarginReportSamplingRateTiming(AriesRxMarginType* marginDevice, + int* samplingRateTiming) +{ + (void)marginDevice; + *samplingRateTiming = SAMPLINGRATETIMING; + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to report sample count + * + * @param[in] marginDevice Struct containing Margin Device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginReportSampleCount(AriesRxMarginType* marginDevice) +{ + (void)marginDevice; + ASTERA_WARN("We support sampling rates instead of sample count"); + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to report the max lanes + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[out] maxLanes variable to store maxLanes in + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginReportMaxLanes(AriesRxMarginType* marginDevice, + int* maxLanes) +{ + (void)marginDevice; + *maxLanes = MAXLANES; + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to set the ErrorCountLimit + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] limit New ErrorCountLimit + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginSetErrorCountLimit(AriesRxMarginType* marginDevice, + int limit) +{ + if (limit > 63) + { + ASTERA_WARN( + "Error Count Limit cannot be greater than 63. Setting it to 63"); + marginDevice->errorCountLimit = 63; + } + else + { + ASTERA_DEBUG("Error Count Limit is %d", limit); + marginDevice->errorCountLimit = limit; + } + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to go to normal settings + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] laneCount length of physical lane list + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginGoToNormalSettings(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int* lane, int laneCount) +{ + AriesErrorType rc; + + rc = ariesMarginPmaRxMarginStop(marginDevice, port, lane, laneCount); + CHECK_SUCCESS(rc); + + // rc = ariesMarginClearErrorLog(marginDevice, port, lane, laneCount); + // CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to clears error count of a given port and lane + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] laneCount length of physical lane list + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginClearErrorLog(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int laneCount) +{ + int ln; + for (ln = 0; ln < laneCount; ln++) + { + marginDevice->errorCount[port][ln] = 0; + } + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to Sets the margin sampler to a specific time offset + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] direction Direction to move the sampler (0: left, 1: right) + * @param[in] steps Number of steps to move the sampler + * @param[in] laneCount length of physical lane list + * @param[in] dwell Time between starting sampling and reading sampling + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginStepMarginToTimingOffset( + AriesRxMarginType* marginDevice, AriesPseudoPortType port, int* lane, + int* direction, int* steps, int laneCount, float dwell) +{ + AriesErrorType rc; + int ln; + + for (ln = 0; ln < laneCount; ln++) + { + if (direction[ln] != 0 && direction[ln] != 1) + { + ASTERA_ERROR("Unsupported direction argument, must be 1 or 0"); + return ARIES_INVALID_ARGUMENT; + } + if (steps[ln] > NUMTIMINGSTEPS) + { + ASTERA_ERROR( + "Unsupported Lane Margining command: Exceeded NumTimingSteps"); + return ARIES_INVALID_ARGUMENT; + } + } + + rc = ariesMarginPmaRxMarginTiming(marginDevice, port, lane, direction, + steps, laneCount); + CHECK_SUCCESS(rc); + usleep((int)(dwell * 1000000)); + rc = ariesMarginPmaRxMarginGetECount(marginDevice, port, lane, laneCount); + CHECK_SUCCESS(rc); + if (marginDevice->do1XAnd0XCapture) + { + rc = ariesMarginPmaRxMarginTiming(marginDevice, port, lane, direction, + steps, laneCount); + CHECK_SUCCESS(rc); + usleep((int)(dwell * 100000)); + rc = ariesMarginPmaRxMarginGetECount(marginDevice, port, lane, + laneCount); + CHECK_SUCCESS(rc); + } + for (ln = 0; ln < laneCount; ln++) + { + if (marginDevice->errorCount[port][ln] > 63) + { + marginDevice->errorCount[port][ln] = 63; + } + if (marginDevice->errorCount[port][ln] > marginDevice->errorCountLimit) + { + ASTERA_DEBUG( + "Error count on port %d lane %d exceeded error count limit: %d > %d", + port, lane[ln], marginDevice->errorCount[port][ln], + marginDevice->errorCountLimit); + ASTERA_DEBUG("Port %d lane %d is going back to default settings", + port, lane[ln]); + rc = ariesMarginGoToNormalSettings(marginDevice, port, lane + ln, + 1); + CHECK_SUCCESS(rc); + } + else + { + ASTERA_DEBUG( + "Margining time is in progress on port %d lane %d. Current error count is: %d", + port, lane[ln], marginDevice->errorCount[port][ln]); + } + } + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command to Sets the margin sampler to a specific voltage offset + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] direction Direction to move the sampler (0: up, 1: down) + * @param[in] steps Number of steps to move the sampler + * @param[in] laneCount length of physical lane list + * @param[in] dwell Time between starting sampling and reading sampling + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginStepMarginToVoltageOffset( + AriesRxMarginType* marginDevice, AriesPseudoPortType port, int* lane, + int* direction, int* steps, int laneCount, float dwell) +{ + AriesErrorType rc; + int ln; + + for (ln = 0; ln < laneCount; ln++) + { + if (direction[ln] != 0 && direction[ln] != 1) + { + ASTERA_ERROR("Unsupported direction argument, must be 0 or 1"); + return ARIES_INVALID_ARGUMENT; + } + if (steps[ln] > NUMVOLTAGESTEPS) + { + ASTERA_ERROR( + "Unsupported Lane Margining command: Exceeded NumTimingSteps"); + return ARIES_INVALID_ARGUMENT; + } + } + + rc = ariesMarginPmaRxMarginVoltage(marginDevice, port, lane, direction, + steps, laneCount); + CHECK_SUCCESS(rc); + usleep((int)(dwell * 100000)); + rc = ariesMarginPmaRxMarginGetECount(marginDevice, port, lane, laneCount); + CHECK_SUCCESS(rc); + if (marginDevice->do1XAnd0XCapture) + { + for (ln = 0; ln < laneCount; ln++) + { + direction[ln] = 1 - direction[ln]; + } + rc = ariesMarginPmaRxMarginVoltage(marginDevice, port, lane, direction, + steps, laneCount); + CHECK_SUCCESS(rc); + usleep((int)(dwell * 100000)); + rc = ariesMarginPmaRxMarginGetECount(marginDevice, port, lane, + laneCount); + CHECK_SUCCESS(rc); + } + for (ln = 0; ln < laneCount; ln++) + { + if (marginDevice->errorCount[port][ln] > 63) + { + marginDevice->errorCount[port][ln] = 63; + } + if (marginDevice->errorCount[port][ln] > marginDevice->errorCountLimit) + { + ASTERA_DEBUG( + "Error count on port %d lane %d exceeded error count limit: %d > %d", + port, lane[ln], marginDevice->errorCount[port][ln], + marginDevice->errorCountLimit); + ASTERA_DEBUG("Port %d lane %d is going back to default settings", + port, lane[ln]); + rc = ariesMarginGoToNormalSettings(marginDevice, port, lane + ln, + 1); + CHECK_SUCCESS(rc); + } + else + { + ASTERA_DEBUG( + "Margining voltage is in progress on port %d lane %d. Current error count is: %d", + port, lane[ln], marginDevice->errorCount[port][ln]); + } + } + return ARIES_SUCCESS; +} + +/** + * @brief Margin Command for vendor defined + * + * Vendor defined function is not implemented yet + * + * @param[in] marginDevice Struct containing Margin Device information + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginVendorDefined(AriesRxMarginType* marginDevice) +{ + (void)marginDevice; + ASTERA_WARN("Vendor defined function is not implemented yet"); + return ARIES_SUCCESS; +} + +/** + * @brief Stops the margining + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] laneCount length of physical lane list + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginPmaRxMarginStop(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int* lane, + int laneCount) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int side, quadSlice, quadSliceLane; + int ln; + + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesMarginDeterminePmaSideAndQs( + marginDevice, port, lane[ln], &side, &quadSlice, &quadSliceLane); + CHECK_SUCCESS(rc); + + rc = ariesReadWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, dataWord); + CHECK_SUCCESS(rc); + uint8_t en = (dataWord[0] >> 1) & 0x1; // enable has offset of 1 + + if (en == 1) + { + // Setting OVRD enable to 0 for rx IQ + // Offset of 12, width of 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_7, 12, 0, 1); + CHECK_SUCCESS(rc); + // Setting OVRD enable to 0 for margin vdac + // Offset of 11, width of 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 11, 0, 1); + CHECK_SUCCESS(rc); + // Setting OVRD enable to 0 for margin in progress + // Offset of 13, width of 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 13, 0, 1); + CHECK_SUCCESS(rc); + + rc = ariesMarginPmaRxReqAckHandshake(marginDevice, port, lane + ln, + 1); + CHECK_SUCCESS(rc); + + // Setting OVRD enable to 0 for margin error clear + // Offset of 1, width of 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 1, 0, 1); + CHECK_SUCCESS(rc); + } + } + return ARIES_SUCCESS; +} + +/** + * @brief Sets the PMA registers to margin a specified time value + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] direction Direction to move (0:left, 1:right) + * @param[in] steps Number of steps to move + * @param[in] laneCount length of physical lane list + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginPmaRxMarginTiming(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int* lane, + int* direction, int* steps, + int laneCount) +{ + AriesErrorType rc; + int ln; + + // Determine pma side and qs from device port and lane + int side, quadSlice, quadSliceLane; + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesMarginDeterminePmaSideAndQs( + marginDevice, port, lane[ln], &side, &quadSlice, &quadSliceLane); + CHECK_SUCCESS(rc); + + // positive deltaValues move the slicer to the left + // assume the direction is left and if it is right we will overwrite + // deltaValue later + uint8_t deltaValue = steps[ln]; + + // negative deltaValues move the slicer to the right + // if the direction is right, and we want to move at least 1 step then + // take the negation of our steps, so we move the opposite direction + if (direction[ln] == 1 && steps[ln] > 0) // right and not 0 steps + { + deltaValue = ((~steps[ln] + 1) & 0x7f); + } + + // Setting IQ OVRD to 1 and setting the IQ value + // offset 12, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_7, 12, 1, 1); + CHECK_SUCCESS(rc); + // offset 5, width 7 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_7, 5, deltaValue, 7); + CHECK_SUCCESS(rc); + + // set Rx margin error clear ovrd en to 1 and Rx margin error clear ovrd + // to 1 ALS-105: Stop a dummy req being generated by keeping Margin + // error clear high in case iq value is unchanged form last command + // offset 1, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 1, 1, 1); + CHECK_SUCCESS(rc); + // offset 0, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 0, 1, 1); + CHECK_SUCCESS(rc); + + // set rxX margin in prog + // offset 13, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 13, 1, 1); + CHECK_SUCCESS(rc); + // offset 12, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 12, 1, 1); + CHECK_SUCCESS(rc); + } + rc = ariesMarginPmaRxReqAckHandshake(marginDevice, port, lane, laneCount); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Sets the PMA registers to margin a specified voltage value + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] direction Direction to move (0:up, 1:down) + * @param[in] steps Number of steps to move + * @param[in] laneCount length of physical lane list + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginPmaRxMarginVoltage(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int* lane, int* direction, + int* steps, int laneCount) +{ + AriesErrorType rc; + int side, quadSlice, quadSliceLane; + int ln; + + for (ln = 0; ln < laneCount; ln++) + { + // Determine pma side and qs from device port and lane + rc = ariesMarginDeterminePmaSideAndQs( + marginDevice, port, lane[ln], &side, &quadSlice, &quadSliceLane); + CHECK_SUCCESS(rc); + + // positive vdacValues move the slicer up + // assume the direction is up and if it is down we will overwrite + // deltaValue later + + uint16_t vdacValue = steps[ln]; + + // negative vdacValues move the slicer down + // if the direction is down, and we want to move at least 1 step then + // take the negation of our steps, so we move the opposite direction + // (aka down) + if (direction[ln] == 1 && steps[ln] > 0) // down or not 0 steps + { + vdacValue = ((~steps[ln] + 1) & 0x1ff); + } + + // set VDAC override to 1 and set the VDAC value + // offset 11, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 11, 1, 1); + CHECK_SUCCESS(rc); + // offset 2, width 9 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 2, vdacValue, 9); + CHECK_SUCCESS(rc); + + // set Rx margin error clear ovrd en to 1 and Rx margin error clear ovrd + // to 1 ALS-105: Stop a dummy req being generated by keeping Margin + // error clear high in case iq value is unchanged form last command + // offset 1, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 1, 1, 1); + CHECK_SUCCESS(rc); + // offset 0, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 0, 1, 1); + CHECK_SUCCESS(rc); + + // set rxX margin in prog + // offset 13, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 13, 1, 1); + CHECK_SUCCESS(rc); + // offset 12, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 12, 1, 1); + CHECK_SUCCESS(rc); + } + + rc = ariesMarginPmaRxReqAckHandshake(marginDevice, port, lane, laneCount); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Sets the PMA registers to margin a specified voltage and time value + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] timeDirection Direction to move on time axis (0: left, 1: right) + * @param[in] timeSteps Number of steps to move on time axis + * @param[in] voltageDirection Direction to move on voltage axis (0: up, 1: + * down) + * @param[in] voltageSteps Number of steps to move on the voltage axis + * @param[in] laneCount length of physical lane list + * @param[in] dwell Time to wait between starting margining and reading error + * count + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginPmaTimingVoltageOffset( + AriesRxMarginType* marginDevice, AriesPseudoPortType port, int* lane, + int* timeDirection, int* timeSteps, int* voltageDirection, + int* voltageSteps, int laneCount, float dwell) +{ + AriesErrorType rc; + int side, quadSlice, quadSliceLane; + uint16_t deltaValue = 0, vdacValue = 0; + int ln; + + for (ln = 0; ln < laneCount; ln++) + { + // Determine pma side and qs from device port and lane + rc = ariesMarginDeterminePmaSideAndQs( + marginDevice, port, lane[ln], &side, &quadSlice, &quadSliceLane); + CHECK_SUCCESS(rc); + + // get the 1X eye capture + deltaValue = timeSteps[ln]; + vdacValue = voltageSteps[ln]; + + if (timeDirection[ln] == 1 && timeSteps[ln] != 0) + { + deltaValue = ((~timeSteps[ln] + 1) & 0x7f); + } + + if (voltageDirection[ln] == 1 && timeSteps[ln] != 0) + { + vdacValue = ((~voltageSteps[ln] + 1) & 0x1ff); + } + + // set IQ override to 1 and set the IQ value + // offset 12, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_7, 12, 1, 1); + CHECK_SUCCESS(rc); + // offset 5, width 7 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_7, 5, deltaValue, 7); + CHECK_SUCCESS(rc); + // set VDAC override to 1 and set the VDAC value + // offset 11, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 11, 1, 1); + CHECK_SUCCESS(rc); + // offset 2, width 9 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 2, vdacValue, 9); + CHECK_SUCCESS(rc); + + // set Rx margin error clear ovrd en to 1 and Rx margin error clear ovrd + // to 1 ALS-105: Stop a dummy req being generated by keeping Margin + // error clear high in case iq value is unchanged form last command + // offset 1, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 1, 1, 1); + CHECK_SUCCESS(rc); + // offset 0, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 0, 1, 1); + CHECK_SUCCESS(rc); + + // set rxX margin in prog + // offset 13, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 13, 1, 1); + CHECK_SUCCESS(rc); + // offset 12, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 12, 1, 1); + CHECK_SUCCESS(rc); + } + + rc = ariesMarginPmaRxReqAckHandshake(marginDevice, port, lane, laneCount); + CHECK_SUCCESS(rc); + + usleep((int)(dwell * 100000)); + + rc = ariesMarginPmaRxMarginGetECount(marginDevice, port, lane, laneCount); + CHECK_SUCCESS(rc); + + if (marginDevice->do1XAnd0XCapture) + { + for (ln = 0; ln < laneCount; ln++) + { + voltageDirection[ln] = 1 - voltageDirection[ln]; + + if (timeDirection[ln] == 1 && timeSteps[ln] != 0) + { + deltaValue = ((~timeSteps[ln] + 1) & 0x7f); + } + + if (voltageDirection[ln] == 1 && timeSteps[ln] != 0) + { + vdacValue = ((~voltageSteps[ln] + 1) & 0x1ff); + } + + // set IQ override to 1 and set the IQ value + // offset 12, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_7, 12, 1, 1); + CHECK_SUCCESS(rc); + // offset 5, with 7 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_7, 5, deltaValue, 7); + CHECK_SUCCESS(rc); + // set VDAC override to 1 and set the VDAC value + // offset 11, with 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 11, 1, 1); + CHECK_SUCCESS(rc); + // offset 2, width 9 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 2, vdacValue, 9); + CHECK_SUCCESS(rc); + + // set Rx margin error clear ovrd en to 1 and Rx margin error clear + // ovrd to 1 ALS-105: Stop a dummy req being generated by keeping + // Margin error clear high in case iq value is unchanged form last + // command offset 1, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 1, 1, 1); + CHECK_SUCCESS(rc); + // offset 0, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 0, 1, 1); + CHECK_SUCCESS(rc); + + // set rxX margin in prog + // offset 13, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 13, 1, 1); + CHECK_SUCCESS(rc); + // offset 12, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9, 12, 1, 1); + CHECK_SUCCESS(rc); + } + + rc = ariesMarginPmaRxReqAckHandshake(marginDevice, port, lane, + laneCount); + CHECK_SUCCESS(rc); + + usleep((int)(dwell * 100000)); + + rc = ariesMarginPmaRxMarginGetECount(marginDevice, port, lane, + laneCount); + CHECK_SUCCESS(rc); + } + + for (ln = 0; ln < laneCount; ln++) + { + if (marginDevice->errorCount[port][ln] > 63) + marginDevice->errorCount[port][ln] = 63; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Gets the number of errors that have occurred on a specific port and + * lane + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] laneCount length of physical lane list + * @return AriesErrorType - Aries Error Code + */ +AriesErrorType ariesMarginPmaRxMarginGetECount(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int* lane, int laneCount) +{ + // Determine pma side and quad slice + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int side, quadSlice, quadSliceLane; + int ln; + + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesMarginDeterminePmaSideAndQs( + marginDevice, port, lane[ln], &side, &quadSlice, &quadSliceLane); + CHECK_SUCCESS(rc); + + // Get error count from error count register + rc = ariesReadWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_RX_CTL_RX_MARGIN_ERROR, dataWord); + CHECK_SUCCESS(rc); + + uint8_t eCount = + dataWord[0] & + 0x3f; // eCount is only 6 bits wide. We want the first 6 bits. + + // update errorCount array + marginDevice->errorCount[port][ln] += eCount; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Perform Rx Request/Ack Handshake + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] laneCount length of physical lane list + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginPmaRxReqAckHandshake(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int* lane, int laneCount) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int side, quadSlice, quadSliceLane; + int ln; + + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesMarginDeterminePmaSideAndQs( + marginDevice, port, lane[ln], &side, &quadSlice, &quadSliceLane); + CHECK_SUCCESS(rc); + + // Assert rxX_req + // Force 0 + // Offset 5, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_ATE_OVRD_IN, 5, 1, 1); + CHECK_SUCCESS(rc); + // Offset 4, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_ATE_OVRD_IN, 4, 0, 1); + CHECK_SUCCESS(rc); + + // Force 1 + // Offset 5, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_ATE_OVRD_IN, 5, 1, 1); + CHECK_SUCCESS(rc); + // Offset 4, width 1 + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_ATE_OVRD_IN, 4, 1, 1); + CHECK_SUCCESS(rc); + } + + // Check for ack + uint8_t ack = 0x0; + int count = 0; + while (!ack && count <= 0x3fff) + { + ack = 1; + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesReadWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_PCS_OUT, dataWord); + CHECK_SUCCESS(rc); + if (!(dataWord[0] & 0x1)) + { + ack = 0; + } + } + count += 1; + } + if (!ack) + { + ASTERA_ERROR("During Rx req handshake, ACK timed out"); + } + // Set rxX_req override to 0 + // Offset 5, width 1 + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesMarginDeterminePmaSideAndQs( + marginDevice, port, lane[ln], &side, &quadSlice, &quadSliceLane); + CHECK_SUCCESS(rc); + rc = ariesReadWriteWordPmaLaneMainMicroIndirect( + marginDevice->device->i2cDriver, side, quadSlice, quadSliceLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_ATE_OVRD_IN, 5, 0, 1); + CHECK_SUCCESS(rc); + } + return ARIES_SUCCESS; +} + +/** + * @brief Determines the pma side and quadslice of a port and lane + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Physical device lane on the Retimer + * @param[out] side Side the port and lane are in + * @param[out] quadSlice Quad slice the port and lane are in + * @param[out] quadSliceLane Quad slice lane the port and lane are in + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesMarginDeterminePmaSideAndQs(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int lane, int* side, + int* quadSlice, + int* quadSliceLane) +{ + if (marginDevice->partNumber == ARIES_PTX16) + { + if (marginDevice->orientation == + ARIES_ORIENTATION_NORMAL) // Normal Orientation + { + if (port == ARIES_UP_STREAM_PSEUDO_PORT) // USPP + { + *side = 1; // Side is a + } + else if (port == ARIES_DOWN_STREAM_PSEUDO_PORT) // DSPP + { + *side = 0; // Side is b + } + else + { + ASTERA_ERROR("Invalid port %d", port); + return ARIES_INVALID_ARGUMENT; + } + } + else if (marginDevice->orientation == + ARIES_ORIENTATION_REVERSED) // Reversed Orientation + { + if (port == ARIES_UP_STREAM_PSEUDO_PORT) // USPP + { + *side = 0; // Side is b + } + else if (port == ARIES_DOWN_STREAM_PSEUDO_PORT) // DSPP + { + *side = 1; // Side is a + } + else + { + ASTERA_ERROR("Invalid port: %d", port); + return ARIES_INVALID_ARGUMENT; + } + } + else + { + ASTERA_ERROR("Invalid Orientation: %d", marginDevice->orientation); + return ARIES_INVALID_ARGUMENT; + } + *quadSlice = lane / 4; + } + else if (marginDevice->partNumber == ARIES_PTX08) + { + if (marginDevice->orientation == + ARIES_ORIENTATION_NORMAL) // Normal Orientation + { + if (port == ARIES_UP_STREAM_PSEUDO_PORT) // USPP + { + *side = 0; // Side is b + } + else if (port == ARIES_DOWN_STREAM_PSEUDO_PORT) // DSPP + { + *side = 1; // Side is a + } + else + { + ASTERA_ERROR("Invalid port %d", port); + return ARIES_INVALID_ARGUMENT; + } + } + else if (marginDevice->orientation == + ARIES_ORIENTATION_REVERSED) // Reversed Orientation + { + if (port == ARIES_UP_STREAM_PSEUDO_PORT) // USPP + { + *side = 1; // Side is a + } + else if (port == ARIES_DOWN_STREAM_PSEUDO_PORT) // DSPP + { + *side = 0; // Side is b + } + else + { + ASTERA_ERROR("Invalid port %d", port); + return ARIES_INVALID_ARGUMENT; + } + } + else + { + ASTERA_ERROR("Invalid Orientation: %d", marginDevice->orientation); + return ARIES_INVALID_ARGUMENT; + } + *quadSlice = lane / 4 + 1; + } + else + { + ASTERA_ERROR("Failed to match board Part Number: %d", + marginDevice->partNumber); + return ARIES_INVALID_ARGUMENT; + } + + *quadSliceLane = lane % 4; + + return ARIES_SUCCESS; +} + +/** + * @brief Gets the recovery count of a lane + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] laneCount Length of physical lane list + * @param[out] recoveryCount Variable to store the recovery count in + */ +AriesErrorType ariesGetLaneRecoveryCount(AriesRxMarginType* marginDevice, + int* lane, int laneCount, + uint8_t* recoveryCount) +{ + AriesErrorType rc; + AriesBifurcationType bifMode; + uint8_t dataByte[1] = {0}; + int linkId; + int address; + int ln; + + // Get current bifurcation settings + rc = ariesGetBifurcationMode(marginDevice->device, &bifMode); + CHECK_SUCCESS(rc); + + // Use bifurcation settings and lane to determine linkId + for (ln = 0; ln < laneCount; ln++) + { + ariesGetLinkId(bifMode, lane[ln], &linkId); + address = marginDevice->device->mm_print_info_struct_addr; + address += ARIES_PRINT_INFO_STRUCT_LNK_RECOV_ENTRIES_PTR_OFFSET + + linkId; + + // Read recovery count + rc = ariesReadByteDataMainMicroIndirect(marginDevice->device->i2cDriver, + address, dataByte); + CHECK_SUCCESS(rc); + recoveryCount[ln] = dataByte[0] / 2; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Clears the recovery count of a lane + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] laneCount Length of physical lane list + */ +AriesErrorType ariesClearLaneRecoveryCount(AriesRxMarginType* marginDevice, + int* lane, int laneCount) +{ + AriesErrorType rc; + AriesBifurcationType bifMode; + uint8_t dataByte[1] = {0}; + int linkId; + int address; + int ln; + + // Get current bifurcation settings + rc = ariesGetBifurcationMode(marginDevice->device, &bifMode); + CHECK_SUCCESS(rc); + + // Use bifurcation settings and lane to determine linkId + for (ln = 0; ln < laneCount; ln++) + { + ariesGetLinkId(bifMode, lane[ln], &linkId); + address = marginDevice->device->mm_print_info_struct_addr; + address += ARIES_PRINT_INFO_STRUCT_LNK_RECOV_ENTRIES_PTR_OFFSET + + linkId; + + // Reset recovery count + dataByte[0] = 0; + rc = ariesWriteByteDataMainMicroIndirect( + marginDevice->device->i2cDriver, address, dataByte); + CHECK_SUCCESS(rc); + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries margin implementation to read the number of preset requests + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] laneCount length of physical lane list + * @param[out] numReq pointer to integer to store number of preset requests + */ +AriesErrorType ariesGetLaneLastEqNumPresetReq(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int* lane, int laneCount, + int* numReq) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int ln, qs, qs_lane, abs_lane; + + for (ln = 0; ln < laneCount; ln++) + { + int direction; + rc = ariesMarginDeterminePmaSideAndQs(marginDevice, port, lane[ln], + &direction, &qs, &qs_lane); + CHECK_SUCCESS(rc); + abs_lane = qs * 4 + qs_lane; + int pathID = ((abs_lane / 2) * 2) + (1 - direction); + int pathLane = abs_lane % 2; + int address = marginDevice->device->pm_gp_ctrl_sts_struct_addr; + if (pathLane == 0) + { + address += ARIES_CTRL_STS_STRUCT_LAST_EQ_NUM_PRESET_REQS_LN0; + } + else + { + address += ARIES_CTRL_STS_STRUCT_LAST_EQ_NUM_PRESET_REQS_LN1; + } + + // Read the number of last preset requests + rc = ariesReadBlockDataPathMicroIndirect( + marginDevice->device->i2cDriver, pathID, address, 1, dataByte); + CHECK_SUCCESS(rc); + numReq[ln] = dataByte[0]; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries margin implementation to get the last preset requests + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] laneCount length of physical lane list + * @param[out] presets list of integers representing each preset request + */ +AriesErrorType ariesGetLaneLastEqPresetReq(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int* lane, + int laneCount, uint8_t* presets) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int ln, direction, qs, qs_lane, abs_lane, pathID, address; + + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesMarginDeterminePmaSideAndQs(marginDevice, port, lane[ln], + &direction, &qs, &qs_lane); + CHECK_SUCCESS(rc); + + abs_lane = qs * 4 + qs_lane; + rc = ariesGetLaneLastEqNumPresetReq(marginDevice, port, &lane[ln], 1, + &address); + CHECK_SUCCESS(rc); + address -= 1; + + address += marginDevice->device->pm_gp_ctrl_sts_struct_addr; + if (abs_lane % 2 == 0) + { + address += ARIES_CTRL_STS_STRUCT_LAST_EQ_PRESET_REQS_LN0; + } + else + { + address += ARIES_CTRL_STS_STRUCT_LAST_EQ_PRESET_REQS_LN1; + } + + // Read the last preset request + pathID = ((abs_lane / 2) * 2) + (1 - direction); + rc = ariesReadByteDataPathMicroIndirect(marginDevice->device->i2cDriver, + pathID, address, dataByte); + CHECK_SUCCESS(rc); + presets[ln] = dataByte[0]; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Aries margin implementation to get the last preset request FOM + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] laneCount length of physical lane list + * @param[out] foms list of integers representing FOM of last preset request + */ +AriesErrorType ariesGetLaneLastEqPresetReqFOM(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int* lane, int laneCount, + uint8_t* foms) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int ln, direction, qs, qs_lane, abs_lane, pathID, address; + + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesMarginDeterminePmaSideAndQs(marginDevice, port, lane[ln], + &direction, &qs, &qs_lane); + CHECK_SUCCESS(rc); + + abs_lane = qs * 4 + qs_lane; + rc = ariesGetLaneLastEqNumPresetReq(marginDevice, port, &lane[ln], 1, + &address); + CHECK_SUCCESS(rc); + address -= 1; + + address += marginDevice->device->pm_gp_ctrl_sts_struct_addr; + if (abs_lane % 2 == 0) + { + address += ARIES_CTRL_STS_STRUCT_LAST_EQ_FOMS_LN0; + } + else + { + address += ARIES_CTRL_STS_STRUCT_LAST_EQ_FOMS_LN1; + } + + // Read the last preset request FOM + pathID = ((abs_lane / 2) * 2) + (1 - direction); + rc = ariesReadByteDataPathMicroIndirect(marginDevice->device->i2cDriver, + pathID, address, dataByte); + CHECK_SUCCESS(rc); + foms[ln] = dataByte[0]; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Determines eye stats for a given port and lane using binary search + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] lane Pointer to list of physical device lanes on the Retimer + * @param[in] laneCount length of physical lane list + * @param[in] dwell Time to wait before checking error count in a lane + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesCheckEye(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int* lane, int laneCount, + float dwell) +{ + AriesErrorType rc; + int low[16]; + int high[16]; + int steps[16]; + int direction[16]; + int ln, left_right, down_up; + + rc = ariesClearLaneRecoveryCount(marginDevice, lane, laneCount); + CHECK_SUCCESS(rc); + + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesGetLaneLastEqPresetReq( + marginDevice, port, lane + ln, 1, + &marginDevice->marginData[port][ln].presetBefore); + CHECK_SUCCESS(rc); + } + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesGetLaneLastEqPresetReqFOM( + marginDevice, port, lane + ln, 1, + &marginDevice->marginData[port][ln].fomBefore); + CHECK_SUCCESS(rc); + } + + for (left_right = 0; left_right < 2; left_right++) + { // 0:left, 1:right + rc = ariesMarginGoToNormalSettings(marginDevice, port, lane, laneCount); + CHECK_SUCCESS(rc); + for (ln = 0; ln < laneCount; ln++) + { + low[ln] = 0; + high[ln] = NUMTIMINGSTEPS; + direction[ln] = left_right; + } + while (memcmp(low, high, sizeof(int) * laneCount)) + { + for (ln = 0; ln < laneCount; ln++) + { + steps[ln] = (low[ln] + high[ln] + 1) / 2; + ASTERA_DEBUG( + "Checking timing offset lane %d direction %d steps %d", + lane[ln], left_right, steps[ln]); + } + rc = ariesMarginClearErrorLog(marginDevice, port, laneCount); + CHECK_SUCCESS(rc); + + rc = ariesMarginStepMarginToTimingOffset( + marginDevice, port, lane, direction, steps, laneCount, dwell); + CHECK_SUCCESS(rc); + for (ln = 0; ln < laneCount; ln++) + { + if (low[ln] != high[ln]) + { + if (marginDevice->errorCount[port][ln] > + marginDevice->errorCountLimit) + { + // can't be here anymore. We saw too many errors here + high[ln] = steps[ln] - 1; + if (high[ln] < low[ln]) + { + low[ln] = high[ln]; + } + } + else + { + low[ln] = steps[ln]; + if (steps[ln] == NUMTIMINGSTEPS) + { + ASTERA_DEBUG( + "We reached maximum timing offset, exiting margining"); + } + } + if (direction[ln] == 0) + { + marginDevice->marginData[port][ln].eyeWidthLeft = + low[ln] / (float)NUMTIMINGSTEPS * + (float)MAXTIMINGOFFSET; + } + else + { + marginDevice->marginData[port][ln].eyeWidthRight = + low[ln] / (float)NUMTIMINGSTEPS * + (float)MAXTIMINGOFFSET; + } + } + } + } + } + + // voltage + for (down_up = 0; down_up < 2; down_up++) + { // 0:up, 1:down + rc = ariesMarginGoToNormalSettings(marginDevice, port, lane, laneCount); + CHECK_SUCCESS(rc); + for (ln = 0; ln < laneCount; ln++) + { + low[ln] = 0; + high[ln] = NUMVOLTAGESTEPS; + direction[ln] = down_up; + } + while (memcmp(low, high, sizeof(int) * laneCount)) + { + for (ln = 0; ln < laneCount; ln++) + { + steps[ln] = (low[ln] + high[ln] + 1) / 2; + ASTERA_DEBUG( + "Checking voltage offset lane %d direction %d steps %d", + lane[ln], down_up, steps[ln]); + } + rc = ariesMarginClearErrorLog(marginDevice, port, laneCount); + CHECK_SUCCESS(rc); + + rc = ariesMarginStepMarginToVoltageOffset( + marginDevice, port, lane, direction, steps, laneCount, dwell); + CHECK_SUCCESS(rc); + for (ln = 0; ln < laneCount; ln++) + { + if (low[ln] != high[ln]) + { + if (marginDevice->errorCount[port][ln] > + marginDevice->errorCountLimit) + { + // can't be here anymore. We saw too many errors here + high[ln] = steps[ln] - 1; + if (high[ln] < low[ln]) + { + low[ln] = high[ln]; + } + } + else + { + low[ln] = steps[ln]; + if (steps[ln] == NUMVOLTAGESTEPS) + { + ASTERA_DEBUG( + "We reached maximum timing offset, exiting margining"); + } + } + if (direction[ln] == 0) + { + marginDevice->marginData[port][ln].eyeHeightDown = + low[ln] / (float)NUMVOLTAGESTEPS * + (float)MAXVOLTAGEOFFSET * 10; + } + else + { + marginDevice->marginData[port][ln].eyeHeightUp = + low[ln] / (float)NUMVOLTAGESTEPS * + (float)MAXVOLTAGEOFFSET * 10; + } + } + } + } + } + + rc = ariesMarginGoToNormalSettings(marginDevice, port, lane, laneCount); + CHECK_SUCCESS(rc); + rc = ariesMarginClearErrorLog(marginDevice, port, laneCount); + CHECK_SUCCESS(rc); + + // Check recovery count and preset values. + for (ln = 0; ln < laneCount; ln++) + { + rc = ariesGetLaneRecoveryCount( + marginDevice, lane + ln, 1, + &marginDevice->marginData[port][ln].recovCount); + rc |= ariesGetLaneLastEqPresetReq( + marginDevice, port, lane + ln, 1, + &marginDevice->marginData[port][ln].presetAfter); + rc |= ariesGetLaneLastEqPresetReqFOM( + marginDevice, port, lane + ln, 1, + &marginDevice->marginData[port][ln].fomAfter); + CHECK_SUCCESS(rc); + } + + for (ln = 0; ln < laneCount; ln++) + { + if (marginDevice->marginData[port][ln].recovCount != 0) + { + ASTERA_WARN("%d recoveries occured while margining lane %d", + marginDevice->marginData[port][ln].recovCount, + lane[ln]); + } + } + AriesLinkType tmp; + tmp.device = marginDevice->device; + int dfe1; + int dfe2; + int iq; + for (ln = 0; ln < laneCount; ln++) + { + ariesGetRxDfeCode(&tmp, port, lane[ln], 1, &dfe1); + marginDevice->marginData[port][ln].dfe1 = dfe1 * 1.85; + ariesGetRxDfeCode(&tmp, port, lane[ln], 2, &dfe2); + marginDevice->marginData[port][ln].dfe2 = dfe2 * 0.35; + ariesGetRxAdaptIq(&tmp, port, lane[ln], &iq); + marginDevice->marginData[port][ln].iq = iq; + ASTERA_INFO("Eye stats for port %d lane %d", port, lane[ln]); + ASTERA_INFO(" Width = -%.2fUI to %.2fUI", + marginDevice->marginData[port][ln].eyeWidthLeft / 100.0, + marginDevice->marginData[port][ln].eyeHeightUp / 100.0); + ASTERA_INFO(" Height = -%.0fmV to %.0fmV", + marginDevice->marginData[port][ln].eyeHeightDown, + marginDevice->marginData[port][ln].eyeHeightUp); + } + + // Print results + return ARIES_SUCCESS; +} + +/** + * @brief Calculates the eye for each lane on the device's port in parallel + * and outputs it to a file + * + * Calculates the eye for each lane in range start_lane - start_lane+width, + * and outputs timing UI, voltage UI, and additional debug info to a .csv. + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] width Width of the device (x16 or x8) + * @param[in] filename Name of the file for the data to be stored in + * @param[in] startLane Lane to start at on the Retimer + * @param[in] dwell Time to wait before checking error count + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLogEye(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int width, + const char* filename, int startLane, float dwell) +{ + AriesErrorType rc; + + if (width == 0) + { + if (marginDevice->partNumber == ARIES_PTX16) + { + width = 16; + } + else if (marginDevice->partNumber == ARIES_PTX08) + { + width = 8; + } + } + + char filepath[ARIES_PATH_MAX]; + snprintf(filepath, ARIES_PATH_MAX, "%s_%d.csv", filename, port); + + int lanes[16]; + int ln; + for (ln = 0; ln < width; ln++) + { + lanes[ln] = startLane + ln; + } + rc = ariesCheckEye(marginDevice, port, lanes, width, dwell); + CHECK_SUCCESS(rc); + FILE* fp; + fp = fopen(filepath, "w"); + if (fp == NULL) + { + ASTERA_ERROR("Could not open file %s", filepath); + return ARIES_FAILURE; + } + // Adding header + fprintf( + fp, + "Lane,Timing_neg_UI%%,Timing_pos_UI%%,Timing_tot_UI%%,Voltage_neg_mV,Voltage_pos_mV,Voltage_tot_mV,"); + fprintf( + fp, + "Recoveries,Preset_before,FoM_before,Preset_after,FoM_after,DFE1,DFE2,IQ\n"); + for (ln = 0; ln < width; ln++) + { + float eyeWidthLeft = marginDevice->marginData[port][ln].eyeWidthLeft; + float eyeWidthRight = marginDevice->marginData[port][ln].eyeWidthRight; + float eyeHeightDown = marginDevice->marginData[port][ln].eyeHeightDown; + float eyeHeightUp = marginDevice->marginData[port][ln].eyeHeightUp; + int recoveries = marginDevice->marginData[port][ln].recovCount; + int preset = marginDevice->marginData[port][ln].presetBefore; + int fom = marginDevice->marginData[port][ln].fomBefore; + int preset2 = marginDevice->marginData[port][ln].presetAfter; + int fom2 = marginDevice->marginData[port][ln].fomAfter; + int dfe1 = marginDevice->marginData[port][ln].dfe1; + int dfe2 = marginDevice->marginData[port][ln].dfe2; + int iq = marginDevice->marginData[port][ln].iq; + fprintf(fp, "%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,", lanes[ln], + eyeWidthLeft, eyeWidthRight, eyeWidthLeft + eyeWidthRight, + eyeHeightDown, eyeHeightUp, eyeHeightUp + eyeHeightDown); + + fprintf(fp, "%d,P%d,%2xh,P%d,%2xh,%dmV,%dmV,%d\n", recoveries, preset, + fom, preset2, fom2, dfe1, dfe2, iq); + } + + fclose(fp); + return ARIES_SUCCESS; +} + +/** + * @brief Calculates the eye for each lane on the device's port one at a time + * and outputs it to a file + * + * Calculates the eye for each lane in range start_lane - start_lane+width, + * and outputs timing UI, voltage UI, and additional debug info to a .csv. + * Slower and less optimal than ariesLogEye due to checking lanes one at a time + * rather than in parallel, but useful to debug lanes one at a time. + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer (USPP or DSPP) + * @param[in] width Width of the device (x16 or x8) + * @param[in] filename Name of the file for the data to be stored in + * @param[in] startLane Lane to start at on the Retimer + * @param[in] dwell Time to wait before checking error count + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesLogEyeSerial(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int width, + const char* filename, int startLane, + float dwell) +{ + AriesErrorType rc; + + if (width == 0) + { + if (marginDevice->partNumber == ARIES_PTX16) + { + width = 16; + } + else if (marginDevice->partNumber == ARIES_PTX08) + { + width = 8; + } + } + + char filepath[ARIES_PATH_MAX]; + snprintf(filepath, ARIES_PATH_MAX, "%s_%d.csv", filename, port); + + FILE* fp; + fp = fopen(filepath, "w"); + if (fp == NULL) + { + ASTERA_ERROR("Could not open file %s", filepath); + return ARIES_FAILURE; + } + // Adding header + fprintf( + fp, + "Lane,Timing_neg_UI%%,Timing_pos_UI%%,Timing_tot_UI%%,Voltage_neg_mV,Voltage_pos_mV,Voltage_tot_mV,"); + fprintf(fp, "Recoveries,Preset_before,FoM_before,Preset_after,FoM_after\n"); + + int ln; + for (ln = startLane; ln < startLane + width; ln++) + { + rc = ariesCheckEye(marginDevice, port, &ln, 1, dwell); + if (rc != ARIES_SUCCESS) + { + fclose(fp); + return rc; + } + + float eyeWidthLeft = marginDevice->marginData[port][0].eyeWidthLeft; + float eyeWidthRight = marginDevice->marginData[port][0].eyeWidthRight; + float eyeHeightDown = marginDevice->marginData[port][0].eyeHeightDown; + float eyeHeightUp = marginDevice->marginData[port][0].eyeHeightUp; + int recoveries = marginDevice->marginData[port][0].recovCount; + int preset = marginDevice->marginData[port][0].presetBefore; + int fom = marginDevice->marginData[port][0].fomBefore; + int preset2 = marginDevice->marginData[port][0].presetAfter; + int fom2 = marginDevice->marginData[port][0].fomAfter; + int dfe1 = marginDevice->marginData[port][0].dfe1; + int dfe2 = marginDevice->marginData[port][0].dfe2; + int iq = marginDevice->marginData[port][0].iq; + fprintf(fp, "%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,", ln, eyeWidthLeft, + eyeWidthRight, eyeWidthLeft + eyeWidthRight, eyeHeightDown, + eyeHeightUp, eyeHeightUp + eyeHeightDown); + + fprintf(fp, "%d,P%d,%2xh,P%d,%2xh,%dmV,%dmV,%d\n", recoveries, preset, + fom, preset2, fom2, dfe1, dfe2, iq); + } + + fclose(fp); + return ARIES_SUCCESS; +} + +/** + * @brief Creates a full 2D eye diagram for a given port and lane + * + * @param[in] marginDevice Struct containing Margin Device information + * @param[in] port Port to margin on the Retimer(USPP or DSPP) + * @param[in] lane Physical device lane on the Retimer + * @param[in] rate data rate of the Retimer (Gen3: 3, Gen4: 4, Gen5: 5) + * @param[in] dwell Time to wait before checking error count + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesEyeDiagram(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int lane, int rate, + float dwell) +{ + AriesErrorType rc; + + int* timingOffsets = (int*)malloc(sizeof(int) * NUMTIMINGSTEPS * 2 + 1); + if (timingOffsets == NULL) + { + return ARIES_FAILURE; + } + int voltageOffsets[] = {70, 60, 50, 40, 30, 20, 10, 0, + -10, -20, -30, -40, -50, -60, -70}; + int timingOffset; + if (rate == 3) + { + for (timingOffset = 0; timingOffset < NUMTIMINGSTEPS + 1; + timingOffset++) + { + timingOffsets[timingOffset] = (-NUMTIMINGSTEPS * 2) + + (4 * timingOffset); + } + } + else if (rate >= 4 && rate < 6) + { + for (timingOffset = 0; timingOffset < NUMTIMINGSTEPS + 1; + timingOffset++) + { + timingOffsets[timingOffset] = (-NUMTIMINGSTEPS) + + (2 * timingOffset); + } + } + else + { + ASTERA_ERROR("%d is not a valid rate", rate); + free(timingOffsets); + return ARIES_INVALID_ARGUMENT; + } + + char filepath[ARIES_PATH_MAX]; + snprintf(filepath, ARIES_PATH_MAX, "eye_diagram_%d_lane%d.csv", port, lane); + + // Clear error count + rc = ariesClearLaneRecoveryCount(marginDevice, &lane, 1); + if (rc != ARIES_SUCCESS) + { + free(timingOffsets); + return rc; + } + + FILE* fp; + fp = fopen(filepath, "w"); + int voltageOffset; + for (voltageOffset = 0; voltageOffset < 15; voltageOffset++) + { + fprintf(fp, "%3d,,", voltageOffsets[voltageOffset]); + for (timingOffset = 0; timingOffset < NUMTIMINGSTEPS + 1; + timingOffset++) + { + rc = ariesMarginClearErrorLog(marginDevice, port, 1); + if (rc != ARIES_SUCCESS) + { + free(timingOffsets); + return rc; + } + + int timeDirection = 0; + if (timingOffsets[timingOffset] < 0) + { + timeDirection = 0; // left + } + else + { + timeDirection = 1; // right + } + int timeSteps = abs(timingOffsets[timingOffset]); + + int voltageDirection = 0; + if (voltageOffsets[voltageOffset] < 0) + { + voltageDirection = 0; + } + else + { + voltageDirection = 1; + } + int voltageSteps = abs(voltageOffsets[voltageOffset]); + + ASTERA_DEBUG("Checking offset x=%d,y=%d", + timingOffsets[timingOffset], + voltageOffsets[voltageOffset]); + rc = ariesMarginPmaTimingVoltageOffset( + marginDevice, port, &lane, &timeDirection, &timeSteps, + &voltageDirection, &voltageSteps, 1, dwell); + if (rc != ARIES_SUCCESS) + { + fclose(fp); + free(timingOffsets); + return rc; + } + marginDevice->eyeResults[port][lane][timingOffset][voltageOffset] = + marginDevice->errorCount[port][0]; + fprintf(fp, "%3d,", marginDevice->errorCount[port][0]); + } + fprintf(fp, "\n"); + } + fprintf(fp, "\n"); + fprintf(fp, " ,,"); + for (timingOffset = 0; timingOffset < NUMTIMINGSTEPS + 1; timingOffset++) + { + fprintf(fp, "%3d,", timingOffsets[timingOffset]); + } + fprintf(fp, "\n"); + fclose(fp); + + uint8_t recovCount; + rc = ariesGetLaneRecoveryCount(marginDevice, &lane, 1, &recovCount); + CHECK_SUCCESS(rc); + + // Check recovery count again. if they are different then a recovery + // happened + if (recovCount != 0) + { + ASTERA_WARN("%d recoveries occured while margining this lane", + recovCount / 2); + } + + return ARIES_SUCCESS; +} + +#ifdef __cplusplus +} +#endif diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_margin.h b/common/recipes-lib/retimer-v2.16.2/files/aries_margin.h new file mode 100755 index 000000000000..9a0baf013ca2 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_margin.h @@ -0,0 +1,174 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_margin.h + * @brief Definition of receiver margining types for the SDK. + */ +#pragma once +#ifndef ASTERA_ARIES_SDK_MARGIN_H_ +#define ASTERA_ARIES_SDK_MARGIN_H_ + +#include "aries_globals.h" +#include "aries_error.h" +#include "aries_api_types.h" +#include "astera_log.h" +#include "aries_api.h" +#include "aries_i2c.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Constants describing margining actions +#define NUMTIMINGSTEPS 14 +#define MAXTIMINGOFFSET 40 +#define NUMVOLTAGESTEPS 108 +#define MAXVOLTAGEOFFSET 20 +#define SAMPLINGRATEVOLTAGE 31 +#define SAMPLINGRATETIMING 31 +#define MAXLANES 15 +#define VOLTAGESUPPORTED true +#define INDUPDOWNVOLTAGE true +#define INDLEFTRIGHTTIMING true +#define SAMPLEREPORTINGMETHOD true +#define INDERRORSAMPLER true + +AriesErrorType ariesMarginNoCommand(AriesRxMarginType* marginDevice); + +AriesErrorType + ariesMarginAccessRetimerRegister(AriesRxMarginType* marginDevice); + +AriesErrorType + ariesMarginReportMarginControlCapabilities(AriesRxMarginType* marginDevice, + int* capabilities); + +AriesErrorType ariesMarginReportNumVoltageSteps(AriesRxMarginType* marginDevice, + int* numVoltageSteps); + +AriesErrorType ariesMarginReportNumTimingSteps(AriesRxMarginType* marginDevice, + int* numTimingSteps); + +AriesErrorType ariesMarginReportMaxTimingOffset(AriesRxMarginType* marginDevice, + int* maxTimingOffset); + +AriesErrorType + ariesMarginReportMaxVoltageOffset(AriesRxMarginType* marginDevice, + int* maxVoltageOffset); + +AriesErrorType + ariesMarginReportSamplingRateVoltage(AriesRxMarginType* marginDevice, + int* samplingRateVoltage); + +AriesErrorType + ariesMarginReportSamplingRateTiming(AriesRxMarginType* marginDevice, + int* samplingRateTiming); + +AriesErrorType ariesMarginReportSampleCount(AriesRxMarginType* marginDevice); + +AriesErrorType ariesMarginReportMaxLanes(AriesRxMarginType* marginDevice, + int* maxLanes); + +AriesErrorType ariesMarginSetErrorCountLimit(AriesRxMarginType* marginDevice, + int limit); + +AriesErrorType ariesMarginGoToNormalSettings(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int* lane, int laneCount); + +AriesErrorType ariesMarginClearErrorLog(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int laneCount); + +AriesErrorType ariesMarginStepMarginToTimingOffset( + AriesRxMarginType* marginDevice, AriesPseudoPortType port, int* lane, + int* direction, int* steps, int laneCount, float dwell); + +AriesErrorType ariesMarginStepMarginToVoltageOffset( + AriesRxMarginType* marginDevice, AriesPseudoPortType port, int* lane, + int* direction, int* steps, int laneCount, float dwell); + +AriesErrorType ariesMarginVendorDefined(AriesRxMarginType* marginDevice); + +AriesErrorType ariesMarginPmaRxMarginStop(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int* lane, + int laneCount); + +AriesErrorType ariesMarginPmaRxMarginTiming(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int* lane, + int* direction, int* steps, + int laneCount); + +AriesErrorType ariesMarginPmaRxMarginVoltage(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int* lane, int* direction, + int* steps, int laneCount); + +AriesErrorType ariesMarginPmaTimingVoltageOffset( + AriesRxMarginType* marginDevice, AriesPseudoPortType port, int* lane, + int* timeDirection, int* timeSteps, int* voltageDirection, + int* voltageSteps, int laneCount, float dwell); + +AriesErrorType ariesMarginPmaRxMarginGetECount(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int* lane, int laneCount); + +AriesErrorType ariesMarginPmaRxReqAckHandshake(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int* lane, int laneCount); + +AriesErrorType ariesMarginDeterminePmaSideAndQs(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, + int lane, int* side, + int* quadSlice, + int* quadSliceLane); + +AriesErrorType ariesGetLaneRecoveryCount(AriesRxMarginType* marginDevice, + int* lane, int laneCount, + uint8_t* recoveryCount); + +AriesErrorType ariesClearLaneRecoveryCount(AriesRxMarginType* marginDevice, + int* lane, int laneCount); + +AriesErrorType ariesCheckEye(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int* lane, int laneCount, + float dwell); + +AriesErrorType ariesLogEye(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int width, + const char* filename, int startLane, float dwell); + +AriesErrorType ariesLogEyeSerial(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int width, + const char* filename, int startLane, + float dwell); + +AriesErrorType ariesEyeDiagram(AriesRxMarginType* marginDevice, + AriesPseudoPortType port, int lane, int rate, + float dwell); + +#ifdef __cplusplus +} +#endif + +#endif /* ASTERA_ARIES_SDK_MARGIN_H_ */ diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_misc.c b/common/recipes-lib/retimer-v2.16.2/files/aries_misc.c new file mode 100755 index 000000000000..b6debda0f605 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_misc.c @@ -0,0 +1,5956 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_misc.c + * @brief Implementation of helper functions for the SDK. + */ + +#include "aries_misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +AriesBifurcationParamsType bifurcationModes[36] = { + /** Links in bifurcation string read right to left */ + /** Bifurcation properties for x16 */ + {1, {{0, 16, 0}}}, + /** Bifurcation properties for x8 */ + {1, {{0, 8, 0}}}, + /** Bifurcation properties for x4 */ + {1, {{0, 4, 0}}}, + /** Bifurcation properties for x8x8 */ + {2, {{0, 8, 0}, {8, 8, 1}}}, + /** Bifurcation properties for x8x4x4 */ + {3, {{0, 4, 0}, {4, 4, 1}, {8, 8, 2}}}, + /** Bifurcation properties for x4x4x8 */ + {3, {{0, 8, 0}, {8, 4, 1}, {12, 4, 2}}}, + /** Bifurcation properties for x4x4x4x4 */ + {4, {{0, 4, 0}, {4, 4, 1}, {8, 4, 2}, {12, 4, 3}}}, + /** Bifurcation properties for x2x2x2x2x2x2x2x2 */ + {8, + {{0, 2, 0}, + {2, 2, 1}, + {4, 2, 2}, + {6, 2, 3}, + {8, 2, 4}, + {10, 2, 5}, + {12, 2, 6}, + {14, 2, 7}}}, + /** Bifurcation properties for x8x4x2x2 */ + {4, {{0, 2, 0}, {2, 2, 1}, {4, 4, 2}, {8, 8, 3}}}, + /** Bifurcation properties for x8x2x2x4 */ + {4, {{0, 4, 0}, {4, 2, 1}, {6, 2, 2}, {8, 8, 3}}}, + /** Bifurcation properties for x2x2x4x8 */ + {4, {{0, 8, 0}, {8, 4, 1}, {12, 2, 2}, {14, 2, 3}}}, + /** Bifurcation properties for x4x2x2x8 */ + {4, {{0, 8, 0}, {8, 2, 1}, {10, 2, 2}, {12, 4, 3}}}, + /** Bifurcation properties for x2x2x2x2x8 */ + {5, {{0, 8, 0}, {8, 2, 1}, {10, 2, 2}, {12, 2, 3}, {14, 2, 4}}}, + /** Bifurcation properties for x8x2x2x2x2 */ + {5, {{0, 2, 0}, {2, 2, 1}, {4, 2, 2}, {6, 2, 3}, {8, 8, 4}}}, + /** Bifurcation properties for x2x2x4x4x4 */ + {5, {{0, 4, 0}, {4, 4, 1}, {8, 4, 2}, {12, 2, 3}, {14, 2, 4}}}, + /** Bifurcation properties for x4x2x2x4x4 */ + {5, {{0, 4, 0}, {4, 4, 1}, {8, 2, 2}, {10, 2, 3}, {12, 4, 4}}}, + /** Bifurcation properties for x4x4x2x2x4 */ + {5, {{0, 4, 0}, {4, 2, 1}, {6, 2, 2}, {8, 4, 3}, {12, 4, 4}}}, + /** Bifurcation properties for x4x4x4x2x2 */ + {5, {{0, 2, 0}, {2, 2, 1}, {4, 4, 2}, {8, 4, 3}, {12, 4, 4}}}, + /** Bifurcation properties for x2x2x2x2x4x4 */ + {6, {{0, 4, 0}, {4, 4, 1}, {8, 2, 2}, {10, 2, 3}, {12, 2, 4}, {14, 2, 5}}}, + /** Bifurcation properties for x2x2x4x2x2x4 */ + {6, {{0, 4, 0}, {4, 2, 1}, {6, 2, 2}, {8, 4, 3}, {12, 2, 4}, {14, 2, 5}}}, + /** Bifurcation properties for x4x2x2x2x2x4 */ + {6, {{0, 4, 0}, {4, 2, 1}, {6, 2, 2}, {8, 2, 3}, {10, 2, 4}, {12, 4, 5}}}, + /** Bifurcation properties for x2x2x4x4x2x2 */ + {6, {{0, 2, 0}, {2, 2, 1}, {4, 4, 2}, {8, 4, 3}, {12, 2, 4}, {14, 2, 5}}}, + /** Bifurcation properties for x4x2x2x4x2x2 */ + {6, {{0, 2, 0}, {2, 2, 1}, {4, 4, 2}, {8, 2, 3}, {10, 2, 4}, {12, 4, 5}}}, + /** Bifurcation properties for x4x4x2x2x2x2 */ + {6, {{0, 2, 0}, {2, 2, 1}, {4, 2, 2}, {6, 2, 3}, {8, 4, 4}, {12, 4, 5}}}, + /** Bifurcation properties for x2x2x2x2x2x2x4 */ + {7, + {{0, 4, 0}, + {4, 2, 1}, + {6, 2, 2}, + {8, 2, 3}, + {10, 2, 4}, + {12, 2, 5}, + {14, 2, 6}}}, + /** Bifurcation properties for x2x2x2x2x4x2x2 */ + {7, + {{0, 2, 0}, + {2, 2, 1}, + {4, 4, 2}, + {8, 2, 3}, + {10, 2, 4}, + {12, 2, 5}, + {14, 2, 6}}}, + /** Bifurcation properties for x2x2x4x2x2x2x2 */ + {7, + {{0, 2, 0}, + {2, 2, 1}, + {4, 2, 2}, + {6, 2, 3}, + {8, 4, 4}, + {12, 2, 5}, + {14, 2, 6}}}, + /** Bifurcation properties for x4x2x2x2x2x2x2 */ + {7, + {{0, 2, 0}, + {2, 2, 1}, + {4, 2, 2}, + {6, 2, 3}, + {8, 2, 4}, + {10, 2, 5}, + {12, 4, 6}}}, + /** Bifurcation properties for x4x4 */ + {2, {{0, 4, 0}, {4, 4, 1}}}, + /** Bifurcation properties for x2x2x4 */ + {3, {{0, 4, 0}, {4, 2, 1}, {6, 2, 2}}}, + /** Bifurcation properties for x4x2x2 */ + {3, {{0, 2, 0}, {2, 2, 1}, {4, 4, 2}}}, + /** Bifurcation properties for x2x2x2x2 */ + {4, {{0, 2, 0}, {2, 2, 1}, {4, 2, 2}, {6, 2, 3}}}, + /** Bifurcation properties for x2x2 */ + {2, {{0, 2, 0}, {2, 2, 1}}}, + /** Bifurcation properties for x4x8x4 */ + {3, {{0, 4, 0}, {4, 8, 1}, {12, 4, 2}}}, + /** Bifurcation properties for x2 */ + {1, {{0, 2, 0}}}}; + +char* deviceTypes[3] = {"PT5", "PT4", "ET4"}; + +/** + * @brief Read FW version info from the Main Micro space + * + * @param[in] i2cDriver Aries i2c driver + * @param[in] offset Offset inside FW struct for member + * @param[in,out] dataVal - data captured from registeres + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadFwVersion(AriesI2CDriverType* i2cDriver, int offset, + uint8_t* dataVal) +{ + AriesErrorType rc; + int addr = ARIES_MAIN_MICRO_FW_INFO + offset; + if (offset == ARIES_MM_FW_VERSION_BUILD) + { + rc = ariesReadBlockDataMainMicroIndirect(i2cDriver, addr, 2, dataVal); + CHECK_SUCCESS(rc); + } + else + { + rc = ariesReadByteDataMainMicroIndirect(i2cDriver, addr, dataVal); + CHECK_SUCCESS(rc); + } + return ARIES_SUCCESS; +} + +int ariesFirmwareIsAtLeast(AriesDeviceType* device, uint8_t major, + uint8_t minor, uint16_t build) +{ + if ((device->fwVersion.major == 0) && (device->fwVersion.minor == 0) && + (device->fwVersion.build == 0)) + { + return false; + } + else if ((device->fwVersion.major > major) || + ((device->fwVersion.major == major) && + (device->fwVersion.minor > minor)) || + ((device->fwVersion.major == major) && + (device->fwVersion.minor == minor) && + (device->fwVersion.build >= build))) + { + return true; + } + else + { + return false; + } +} + +/** + * @brief I2C Master init for EEPROM Write-Thru + */ +AriesErrorType ariesI2CMasterInit(AriesI2CDriverType* i2cDriver) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t dataWord[2] = {0}; + + // Disable I2C master + dataByte[0] = 0; + rc = ariesI2CMasterWriteCtrlReg(i2cDriver, 0x6c, 1, dataByte); + CHECK_SUCCESS(rc); + // I2C Control register, 1MHz speed + dataWord[0] = 0xe5; + dataWord[1] = 0xf; + rc = ariesI2CMasterWriteCtrlReg(i2cDriver, 0x0, 2, dataWord); + CHECK_SUCCESS(rc); + // Target Address Register + dataByte[0] = 0x50; + rc = ariesI2CMasterWriteCtrlReg(i2cDriver, 0x04, 1, dataByte); + CHECK_SUCCESS(rc); + // Receive FIFO Threshold Register + dataByte[0] = 0; + rc = ariesI2CMasterWriteCtrlReg(i2cDriver, 0x38, 1, dataByte); + CHECK_SUCCESS(rc); + // Transmit FIFO Threshold Register + dataByte[0] = 4; + rc = ariesI2CMasterWriteCtrlReg(i2cDriver, 0x3c, 1, dataByte); + CHECK_SUCCESS(rc); + // SDA Hold Time, 100ns + dataByte[0] = 100; + rc = ariesI2CMasterWriteCtrlReg(i2cDriver, 0x7c, 1, dataByte); + CHECK_SUCCESS(rc); + // Enable I2C master + dataByte[0] = 1; + rc = ariesI2CMasterWriteCtrlReg(i2cDriver, 0x6c, 1, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Write to I2C Master ctrl register + */ +AriesErrorType ariesI2CMasterWriteCtrlReg(AriesI2CDriverType* i2cDriver, + uint32_t address, + uint8_t lengthDataBytes, + uint8_t* values) +{ + AriesErrorType rc; + uint8_t addr[1] = {0}; + uint8_t tmpVal[1] = {0}; + addr[0] = address; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, addr); + CHECK_SUCCESS(rc); + uint8_t dataBytes[4] = {0}; + uint16_t dataAddress[4] = { + ARIES_I2C_MST_DATA0_ADDR, ARIES_I2C_MST_DATA1_ADDR, + ARIES_I2C_MST_DATA2_ADDR, ARIES_I2C_MST_DATA3_ADDR}; + + int i = 0; + for (i = 0; i < lengthDataBytes; i++) + { + dataBytes[i] = values[i]; + } + + for (i = 0; i < 4; i++) + { + tmpVal[0] = dataBytes[i]; + rc = ariesWriteByteData(i2cDriver, dataAddress[i], tmpVal); + CHECK_SUCCESS(rc); + } + + // self.wr_csr_cmd(1) + uint8_t cmd[1] = {0}; + cmd[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, cmd); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Set I2C Master frequency + * + * @param[in] i2cDriver Aries i2c driver + * @param[in] frequencyHz I2C Master frequency in Hz + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesI2CMasterSetFrequency(AriesI2CDriverType* i2cDriver, + int frequencyHz) +{ + AriesErrorType rc; + + // Check if desired frequency is within supported range + // return invalid arg error if not + if (frequencyHz > 1000000) + { + ASTERA_ERROR("Cannot set I2C Master frequency greater than 1MHz"); + return ARIES_INVALID_ARGUMENT; + } + else if (frequencyHz < 400000) + { + ASTERA_ERROR("Cannot set I2C Master frequency less than 400KHz"); + return ARIES_INVALID_ARGUMENT; + } + + int defaultSclLowCnt = 0x28a; + int defaultSclHighCnt = 0x12c; + int defaultFreqHz = 935000; + + int newSclLowCnt = (defaultFreqHz / frequencyHz) * defaultSclLowCnt; + int newSclHighCnt = (defaultFreqHz / frequencyHz) * defaultSclHighCnt; + + uint8_t dataWord[2] = {0}; + uint8_t dataByte[1] = {0}; + + // Reset I2C IP + // self.csr.misc.hw_rst = 0x200 + dataWord[0] = 0x0; + dataWord[1] = 0x2; + rc = ariesWriteWideRegister(i2cDriver, ARIES_HW_RST_ADDR, 2, dataWord); + CHECK_SUCCESS(rc); + + // Set IC_ENABLE=0 to allow changing the LCNT/HCNT settings + // self.csr.muc.al_main_ext_csr.i2c_mst_addr = 0x6c + dataByte[0] = 0x6c; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + // self.csr.muc.al_main_ext_csr.i2c_mst_data_0 = 0 + dataByte[0] = 0x0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + // self.csr.muc.al_main_ext_csr.i2c_mst_cmd = 1 + dataByte[0] = 0x1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + // Unreset I2C IP + // self.csr.misc.hw_rst = 0x0 + dataWord[0] = 0x0; + dataWord[1] = 0x0; + rc = ariesWriteWideRegister(i2cDriver, ARIES_HW_RST_ADDR, 2, dataWord); + CHECK_SUCCESS(rc); + + // Set IC_FS_SCL_HCNT + // self.csr.muc.al_main_ext_csr.i2c_mst_addr = 0x1c + dataByte[0] = 0x1c; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + // self.csr.muc.al_main_ext_csr.i2c_mst_data_0 = new_fs_scl_hcnt & 0xff + dataByte[0] = newSclHighCnt & 0xff; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + // self.csr.muc.al_main_ext_csr.i2c_mst_data_1 = (new_fs_scl_hcnt >> 8) & + // 0xff + dataByte[0] = (newSclHighCnt >> 8) & 0xff; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA1_ADDR, dataByte); + CHECK_SUCCESS(rc); + // self.csr.muc.al_main_ext_csr.i2c_mst_cmd = 1 + dataByte[0] = 0x1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + // Set IC_FS_SCL_LCNT + // self.csr.muc.al_main_ext_csr.i2c_mst_addr = 0x20 + dataByte[0] = 0x20; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + // self.csr.muc.al_main_ext_csr.i2c_mst_data_0 = new_fs_scl_lcnt & 0xff + dataByte[0] = newSclLowCnt & 0xff; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + // self.csr.muc.al_main_ext_csr.i2c_mst_data_1 = (new_fs_scl_lcnt >> 8) & + // 0xff + dataByte[0] = (newSclLowCnt >> 8) & 0xff; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA1_ADDR, dataByte); + CHECK_SUCCESS(rc); + // self.csr.muc.al_main_ext_csr.i2c_mst_cmd = 1 + dataByte[0] = 0x1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + // Set IC_ENABLE=1 + // self.csr.muc.al_main_ext_csr.i2c_mst_addr = 0x6c + dataByte[0] = 0x6c; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + // self.csr.muc.al_main_ext_csr.i2c_mst_data_0 = 1 + dataByte[0] = 0x1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + // self.csr.muc.al_main_ext_csr.i2c_mst_cmd = 1 + dataByte[0] = 0x1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Get the location where the EEPROM image ends, which is after a + * specific sequence - a55aa555aff. This can be used to help reduce the overall + * fw load time + * + * @param[in] data Full EEPROM image as a byte array + * @param[in] numBytes Number of bytes to write (<= 256k) + * @return location - Index until which we have to write + */ +int ariesGetEEPROMImageEnd(uint8_t* data) +{ + int dataIdx = 0; + int seqIdx = 0; + int location; + + uint8_t sequence[11] = {0xa5, 0x5a, 0xa5, 0x5a, 0xff, 0x0, + 0x0, 0x0, 0x0, 0xff, 0xff}; + int seqLen = 11; + + while ((dataIdx < ARIES_EEPROM_MAX_NUM_BYTES) && (seqIdx < seqLen)) + { + // Increment both pointers if element matches + if (data[dataIdx] == sequence[seqIdx]) + { + dataIdx++; + seqIdx++; + // Check if sequence is fully traversed + if (seqIdx == seqLen) + { + location = dataIdx; + return location; + } + } + else + { + dataIdx = dataIdx - seqIdx + 1; + seqIdx = 0; + } + } + // If we cant find the end return max num of bytes + return ARIES_EEPROM_MAX_NUM_BYTES; +} + +/** + * @brief Write multiple blocks of data to the EEPROM with help from + * Main Micro + * + * @param[in] device Aries Device struct + * @param[in] address EEPROM address + * @param[in] numBytes Number of bytes to write (<= 256k) + * @param[in] values EEPROM data to be written as a byte array + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesI2CMasterMultiBlockWrite(AriesDeviceType* device, + uint16_t address, int numBytes, + uint8_t* values) +{ + uint8_t dataByte[1] = {0}; + // Data is arranged in four-byte chunks + uint8_t dataBytes[4] = {0}; + uint8_t addr15To8; + uint8_t addr7To0; + AriesErrorType rc; + + // IC Data command + dataByte[0] = 0x10; + rc = ariesWriteByteData(device->i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, + dataByte); + CHECK_SUCCESS(rc); + + // Prepare Flag Byte + dataByte[0] = 0; + rc = ariesWriteByteData(device->i2cDriver, ARIES_I2C_MST_DATA1_ADDR, + dataByte); + CHECK_SUCCESS(rc); + + // Send Address + addr15To8 = (address >> 8) & 0xff; + addr7To0 = address & 0xff; + dataByte[0] = addr15To8; + rc = ariesWriteByteData(device->i2cDriver, ARIES_I2C_MST_DATA0_ADDR, + dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesWriteByteData(device->i2cDriver, ARIES_I2C_MST_CMD_ADDR, + dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = addr7To0; + rc = ariesWriteByteData(device->i2cDriver, ARIES_I2C_MST_DATA0_ADDR, + dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesWriteByteData(device->i2cDriver, ARIES_I2C_MST_CMD_ADDR, + dataByte); + CHECK_SUCCESS(rc); + + int numIters = numBytes / device->eeprom.blockWriteSize; + int iterIdx; + int numBlocks = device->eeprom.blockWriteSize / 4; // 4-byte blocks + int blockIdx; + int byteIdx; + + int offset = 0; + uint8_t cmd; + int try; + int numTries = 30; + bool mmBusy = false; + + for (iterIdx = 0; iterIdx < numIters; iterIdx++) + { + // determine MM-assist command + cmd = ARIES_MM_EEPROM_WRITE_REG_CODE; + if (iterIdx == (numIters - 1)) + { + cmd = ARIES_MM_EEPROM_WRITE_END_CODE; + } + cmd = cmd | device->eeprom.blockCmdModifier; + + // Write data + for (blockIdx = 0; blockIdx < numBlocks; blockIdx++) + { + // prepare the data + for (byteIdx = 0; byteIdx < 4; byteIdx++) + { + dataBytes[byteIdx] = values[(offset + blockIdx * 4 + byteIdx)]; + } + // write the data to Retimer holding registers + rc = ariesWriteBlockData( + device->i2cDriver, device->eeprom.blockBaseAddr + 4 * blockIdx, + 4, dataBytes); + CHECK_SUCCESS(rc); + } + + // Write cmd + dataByte[0] = cmd; + rc = ariesWriteByteData(device->i2cDriver, + ARIES_MM_EEPROM_ASSIST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + // Verify Command returned back to zero + mmBusy = true; + for (try = 0; try < numTries; try++) + { + rc = ariesReadByteData(device->i2cDriver, + ARIES_MM_EEPROM_ASSIST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + if (dataByte[0] == 0) + { + mmBusy = false; + break; + } + usleep(ARIES_MM_STATUS_TIME); + } + + // If status not reset to 0, return BUSY error + if (mmBusy) + { + ASTERA_TRACE( + "ERROR: Main Micro busy writing data block to EEPROM. Did not commit write"); + return ARIES_EEPROM_MM_STATUS_BUSY; + } + + offset += device->eeprom.blockWriteSize; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Rewrite a byte of data and verify it got written to the EEPROM with + * help from Main Micro + * + * @param[in] i2cDriver Aries i2c driver + * @param[in] address EEPROM address to rewrite and verify + * @param[in] value Data to write to EEPROM (1 byte) + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesI2CMasterRewriteAndVerifyByte(AriesI2CDriverType* i2cDriver, + int address, uint8_t* value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + // Write byte + rc = ariesI2CMasterSendByteBlockData(i2cDriver, address, 1, value); + CHECK_SUCCESS(rc); + usleep(ARIES_I2C_MASTER_WRITE_DELAY); + + rc = ariesI2CMasterSendAddress(i2cDriver, address); + CHECK_SUCCESS(rc); + + // Read byte + dataByte[0] = 0x3; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA1_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 0x1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + usleep(ARIES_I2C_MASTER_CMD_RST); + dataByte[0] = 0x0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + rc = ariesReadByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + + if (value[0] == dataByte[0]) + { + ASTERA_INFO(" Re-write succeeded"); + rc = ARIES_SUCCESS; + } + else + { + ASTERA_INFO(" Re-write failed. Expected %d but got %d", value[0], + dataByte[0]); + rc = ARIES_EEPROM_VERIFY_FAILURE; + } + + return rc; +} + +/** + * @brief Uses I2C master to set the address pointer in the EEPROM + * + * @param[in] i2cDriver Aries i2c driver + * @param[in] address EEPROM address to send + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesI2CMasterSendAddress(AriesI2CDriverType* i2cDriver, + int address) +{ + uint8_t dataByte[1] = {0}; + uint8_t addr15To8; + uint8_t addr7To0; + AriesErrorType rc; + + dataByte[0] = 0x10; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + // Prepare Flag Byte + dataByte[0] = 0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA1_ADDR, dataByte); + CHECK_SUCCESS(rc); + + // Program address + addr15To8 = (address >> 8) & 0xff; + addr7To0 = address & 0xff; + dataByte[0] = addr15To8; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = addr7To0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Uses I2C master to write multiple bytes to EEPROM + * + * @param[in] i2cDriver Aries i2c driver + * @param[in] address EEPROM address to send + * @param[in] numBytes Number of bytes to be sent + * @param[in] value Data bytes to send + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesI2CMasterSendByteBlockData(AriesI2CDriverType* i2cDriver, + int address, int numBytes, + uint8_t* value) +{ + // Write CSR Address + uint8_t dataByte[1] = {0}; + uint8_t addr15To8; + uint8_t addr7To0; + int byteIndex = 0; + AriesErrorType rc; + + dataByte[0] = 0x10; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + // Prepare Flag Byte + dataByte[0] = 0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA1_ADDR, dataByte); + CHECK_SUCCESS(rc); + + addr15To8 = (address >> 8) & 0xff; + addr7To0 = address & 0xff; + dataByte[0] = addr15To8; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = addr7To0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + // Write all data bytes except last + for (byteIndex = 0; byteIndex < (numBytes - 1); byteIndex++) + { + dataByte[0] = value[byteIndex]; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + } + + // Write last byte + dataByte[0] = 2; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA1_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = value[(numBytes - 1)]; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Uses I2C master to write a byte to EEPROM + * + * @param[in] i2cDriver Aries i2c driver + * @param[in] value Data byte + * @param[in] flag I2C flag + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesI2CMasterSendByte(AriesI2CDriverType* i2cDriver, + uint8_t* value, int flag) +{ + uint8_t dataByte[1] = {0}; + AriesErrorType rc; + + dataByte[0] = 0x10; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, value); + CHECK_SUCCESS(rc); + dataByte[0] = flag << 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA1_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 0x1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Uses I2C master to read multiple bytes to EEPROM + * + * @param[in] device Aries Device struct + * @param[out] dataBytes Data bytes recieved + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesI2CMasterReceiveByteBlock(AriesDeviceType* device, + uint8_t* dataBytes) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + // Data is transfered in 4-byte burst transactions + uint8_t data[4] = {0}; + bool mmBusy; + + // Read data from EEPROM and terminate transaction + dataByte[0] = ARIES_MM_EEPROM_READ_END_CODE | + device->eeprom.blockCmdModifier; + rc = ariesWriteByteData(device->i2cDriver, ARIES_MM_EEPROM_ASSIST_CMD_ADDR, + dataByte); + CHECK_SUCCESS(rc); + + usleep(ARIES_MM_READ_CMD_WAIT); + + int numTries = 30; + int tryIndex; + mmBusy = true; + for (tryIndex = 0; tryIndex < numTries; tryIndex++) + { + rc = ariesReadByteData(device->i2cDriver, + ARIES_MM_EEPROM_ASSIST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + if (dataByte[0] == 0) + { + mmBusy = false; + break; + } + usleep(ARIES_MM_STATUS_TIME); + } + + // If status not reset to 0, return BUSY error + if (mmBusy) + { + ASTERA_ERROR("ERROR: Main Micro busy. Read data not ready"); + return ARIES_EEPROM_MM_STATUS_BUSY; + } + + int byteIdx; + int dataByteIdx; + for (byteIdx = 0; byteIdx < device->eeprom.blockWriteSize; byteIdx += 4) + { + rc = ariesReadBlockData(device->i2cDriver, + (device->eeprom.blockBaseAddr + byteIdx), 4, + data); + CHECK_SUCCESS(rc); + for (dataByteIdx = 0; dataByteIdx < 4; dataByteIdx++) + { + dataBytes[(byteIdx + dataByteIdx)] = data[dataByteIdx]; + } + } + return ARIES_SUCCESS; +} + +/** + * @brief Calculate checksum of this EEPROM block + * + * @param[in] i2cDriver Aries i2c driver + * @param[in] checksum Checksum value returned by function + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesI2CMasterGetChecksum(AriesDeviceType* device, + uint16_t blockEnd, uint32_t* checksum) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t dataBytes[4] = {0}; + int try; + uint8_t byteIdx; + uint8_t dataByteIdx; + int numTries = 500; + uint8_t commandCode; + + if (blockEnd != 0) + { + // Set up end index for partial checksum calculation + dataBytes[0] = blockEnd & 0xff; + dataBytes[1] = (blockEnd >> 8) & 0xff; + dataBytes[2] = 0; + dataBytes[3] = 0; + rc = ariesWriteBlockData(device->i2cDriver, + device->eeprom.blockBaseAddr, 4, dataBytes); + CHECK_SUCCESS(rc); + commandCode = ARIES_MM_EEPROM_CHECKSUM_PARTIAL_CODE; + } + else + { + // Full-block checksum + commandCode = ARIES_MM_EEPROM_CHECKSUM_CODE; + } + + bool mmBusy = true; + dataByte[0] = commandCode | device->eeprom.blockCmdModifier; + rc = ariesWriteByteData(device->i2cDriver, ARIES_MM_EEPROM_ASSIST_CMD_ADDR, + dataByte); + CHECK_SUCCESS(rc); + + sleep(ARIES_MM_CALC_CHECKSUM_WAIT); + + for (try = 0; try < numTries; try++) + { + rc = ariesReadByteData(device->i2cDriver, + ARIES_MM_EEPROM_ASSIST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + if (dataByte[0] == 0) + { + mmBusy = false; + break; + } + usleep(ARIES_MM_CALC_CHECKSUM_TRY_TIME); + } + + if (mmBusy) + { + ASTERA_ERROR( + "ERROR: Main Micro busy calculating block checksum. Read data not ready"); + return ARIES_EEPROM_MM_STATUS_BUSY; + } + + // Compute checksum + *checksum = 0; + + for (byteIdx = 0; byteIdx < ARIES_EEPROM_MM_BLOCK_CHECKSUM_WRITE_SIZE; + byteIdx += 4) + { + rc = ariesReadBlockData(device->i2cDriver, + (device->eeprom.blockBaseAddr + byteIdx), 4, + dataBytes); + CHECK_SUCCESS(rc); + for (dataByteIdx = 0; dataByteIdx < 4; dataByteIdx++) + { + *checksum |= (dataBytes[dataByteIdx] + << (8 * (dataByteIdx + byteIdx))); + } + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesI2CMasterReceiveByte(AriesI2CDriverType* i2cDriver, + uint8_t* value) +{ + uint8_t dataByte[1] = {0}; + AriesErrorType rc; + + dataByte[0] = 0x10; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 0x3; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA1_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 0x1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + usleep(ARIES_I2C_MASTER_CMD_RST); + dataByte[0] = 0x0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + rc = ariesReadByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, value); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType + ariesI2CMasterReceiveContinuousByte(AriesI2CDriverType* i2cDriver, + uint8_t* value) +{ + uint8_t dataByte[1] = {0}; + AriesErrorType rc; + + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + dataByte[0] = 0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + rc = ariesReadByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, value); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesI2CMasterSetPage(AriesI2CDriverType* i2cDriver, int page) +{ + uint8_t tar[1] = {0}; + AriesErrorType rc; + + // Power-on default value is 0x50 + // rc = ariesReadByteData(i2cDriver, 0xd0c, tar); + // CHECK_SUCCESS(rc); + tar[0] = 0x50 | (page & 3); + + uint8_t dataByte[1] = {0}; + + dataByte[0] = 0; + rc = ariesI2CMasterWriteCtrlReg(i2cDriver, 0x6c, 1, dataByte); + CHECK_SUCCESS(rc); + rc = ariesI2CMasterWriteCtrlReg(i2cDriver, 0x04, 1, tar); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesI2CMasterWriteCtrlReg(i2cDriver, 0x6c, 1, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Get temp calibration codes, lot ID, and chip ID from eFuse. + * + * @param[in] device Aries Device struct + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetTempCalibrationCodes(AriesDeviceType* device) +{ + AriesErrorType rc; + AriesErrorType lc; + uint8_t dataByte[1] = {0}; + uint8_t dataBytes5[5] = {0}; + uint8_t invalid; + uint8_t flag; + uint8_t offset; + uint8_t calCode; + uint8_t deviceType; + uint8_t deviceWidth; + uint8_t eFuseCPChuckTempC; + + // Only read from eFuse once - if we haven't done so already + if (device->eFuseRead == 0) + { + // eFuse read procedure + lc = ariesLock(device->i2cDriver); + CHECK_SUCCESS(lc); + // 1. Switch to refclk/8 clock for TCK + // self.csr.misc.efuse_cntl.sms_clk_sel = 1 + rc = ariesReadWideRegister(device->i2cDriver, ARIES_EFUSE_CNTL, 5, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // Assert bit 25 + dataBytes5[3] |= (1 << 1); + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_EFUSE_CNTL, 5, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // 2. Assert efuse_load + // self.csr.misc.sms_efuse_cntl.sms_efuse_load = 1 + rc = ariesReadByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // Assert bit 7 + dataByte[0] |= (1 << 7); + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // 3. Assert smart_test + // self.csr.misc.efuse_cntl.smart_test = 1 + rc = ariesReadWideRegister(device->i2cDriver, ARIES_EFUSE_CNTL, 5, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // Assert bit 24 + dataBytes5[3] |= (1 << 0); + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_EFUSE_CNTL, 5, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // 4. De-assert smart_test + // self.csr.misc.efuse_cntl.smart_test = 0 + rc = ariesReadWideRegister(device->i2cDriver, ARIES_EFUSE_CNTL, 5, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // De-assert bit 24 + dataBytes5[3] &= ~(1 << 0); + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_EFUSE_CNTL, 5, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // 5. De-assert efuse_load + // self.csr.misc.sms_efuse_cntl.sms_efuse_load = 0 + rc = ariesReadByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // De-assert bit 7 + dataByte[0] &= ~(1 << 7); + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Read eFuse “primary page invalid” bit and adjust offset accordingly + // Set address + dataByte[0] = 63; + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // Read data + rc = ariesReadWideRegister(device->i2cDriver, ARIES_SMS_EFUSE_STS, 2, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + invalid = dataBytes5[0]; + + if (invalid & 0x80) + { + offset = 64; + } + else + { + offset = 0; + } + + // Determine calibration codes + // Set address + dataByte[0] = 48 + offset; + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + // Read data + rc = ariesReadWideRegister(device->i2cDriver, ARIES_SMS_EFUSE_STS, 2, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + flag = dataBytes5[0]; + + // Compute PMA A calibration codes + int qs; + for (qs = 0; qs < 4; qs++) + { + if (flag & 0x4) + { + dataByte[0] = 34 + (qs * 4) + offset; + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + rc = ariesReadWideRegister(device->i2cDriver, + ARIES_SMS_EFUSE_STS, 2, dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + calCode = dataBytes5[0]; + if (calCode == 0) + { + calCode = 84; + } + } + else + { + calCode = 84; + } + device->tempCalCodePmaA[qs] = calCode; + } + + // Compute PMA B calibration codes + for (qs = 0; qs < 4; qs++) + { + if (flag & 0x04) + { + dataByte[0] = 32 + (qs * 4) + offset; + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + rc = ariesReadWideRegister(device->i2cDriver, + ARIES_SMS_EFUSE_STS, 2, dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + calCode = dataBytes5[0]; + if (calCode == 0) + { + calCode = 84; + } + } + else + { + calCode = 84; + } + device->tempCalCodePmaB[qs] = calCode; + } + + // Calcualte the average PMA calibration code + if (device->partNumber == ARIES_PTX16) + { + device->tempCalCodeAvg = + (device->tempCalCodePmaA[0] + device->tempCalCodePmaA[1] + + device->tempCalCodePmaA[2] + device->tempCalCodePmaA[3] + + device->tempCalCodePmaB[0] + device->tempCalCodePmaB[1] + + device->tempCalCodePmaB[2] + device->tempCalCodePmaB[3] + + 8 / 2) / + 8; // Add denominator/2 to cause integer rounding + } + else if (device->partNumber == ARIES_PTX08) + { + device->tempCalCodeAvg = + (device->tempCalCodePmaA[1] + device->tempCalCodePmaA[2] + + device->tempCalCodePmaB[1] + device->tempCalCodePmaB[2] + + 4 / 2) / + 4; // Add denominator/2 to cause integer rounding + } + else + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_INVALID_ARGUMENT; + } + + // Read 12-byte chip ID + int b = 0; + for (b = 0; b < 12; b++) + { + // Chip ID starts at byte 0 in eFuse + dataByte[0] = 0 + b + offset; + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + rc = ariesReadWideRegister(device->i2cDriver, ARIES_SMS_EFUSE_STS, + 2, dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + device->chipID[b] = dataBytes5[0]; + } + + // Read 6-byte lot number + for (b = 0; b < 6; b++) + { + // Lot number starts at byte 16 in eFuse + dataByte[0] = 16 + b + offset; + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + rc = ariesReadWideRegister(device->i2cDriver, ARIES_SMS_EFUSE_STS, + 2, dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + device->lotNumber[b] = dataBytes5[0]; + } + + // Read DUT part number + dataByte[0] = 15 + offset; + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + rc = ariesReadWideRegister(device->i2cDriver, ARIES_SMS_EFUSE_STS, 2, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + deviceType = (dataBytes5[0] >> 6) & 0x3; + deviceWidth = dataBytes5[0] & 0x3F; + if (deviceType <= 2) + { + snprintf(device->fullPartNumber, 8, "%s", deviceTypes[deviceType]); + } + else + { + sprintf(device->fullPartNumber, "ptx"); + } + if (deviceWidth == 1) + { + sprintf(device->fullPartNumber + 3, "161"); + } + else if (deviceWidth == 2) + { + if (deviceType == 1) + { + sprintf(device->fullPartNumber + 3, "080"); + } + else + { + sprintf(device->fullPartNumber + 3, "081"); + } + } + else + { + if (device->partNumber == ARIES_PTX08) + { + sprintf(device->fullPartNumber + 3, "08xx"); + } + else + { + sprintf(device->fullPartNumber + 3, "16xx"); + } + } + + // CP Program Version is byte 49 in eFuse + dataByte[0] = 49 + offset; + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + rc = ariesReadWideRegister(device->i2cDriver, ARIES_SMS_EFUSE_STS, 2, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + device->eFuseCPVersion = dataBytes5[0]; + + // Wafer sort chuck temperature is byte 62 in eFuse + // CP Program Version 14 and later store chuck temperature + dataByte[0] = 62 + offset; + rc = ariesWriteByteData(device->i2cDriver, ARIES_SMS_EFUSE_CNTL, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + rc = ariesReadWideRegister(device->i2cDriver, ARIES_SMS_EFUSE_STS, 2, + dataBytes5); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + eFuseCPChuckTempC = dataBytes5[0]; + // Use CP Program Version and eFuse chuck temperature to store temp + // calibration reference temperature. + if (device->eFuseCPVersion >= 14) + { + device->tempCalCodeRefTempC = eFuseCPChuckTempC; + } + else if (device->eFuseCPVersion >= 11) + { + device->tempCalCodeRefTempC = 85; + } + else + { + device->tempCalCodeRefTempC = 110; + } + + // Done reading from eFuse + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + device->eFuseRead = 1; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Get max. temp reading across all PMAs + * + * @param[in] device Aries Device struct + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadPmaTempMax(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataBytes[4] = {0}; + int adcCode; + + if (!ariesFirmwareIsAtLeast(device, 1, 1, 31)) + { + return ARIES_TEMP_READ_NOT_READY; + } + + rc = ariesReadWideRegister(device->i2cDriver, + ARIES_ALL_TIME_MAX_TEMP_ADC_CSR, 4, dataBytes); + CHECK_SUCCESS(rc); + adcCode = (dataBytes[3] << 24) + (dataBytes[2] << 16) + + (dataBytes[1] << 8) + dataBytes[0]; + + if (adcCode == 0 || adcCode >= 0x3ff) + { + return ARIES_TEMP_READ_NOT_READY; + } + + device->maxTempC = ariesTsenseADCToDegrees(device, adcCode, + device->tempCalCodeAvg); + + return ARIES_SUCCESS; +} + +/** + * @brief Get current avg. temp reading across all PMAs + * + * @param[in] device Aries Device struct + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadPmaAvgTemp(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataBytes[4] = {0}; + int adcCode; + + if (!ariesFirmwareIsAtLeast(device, 1, 0, 42)) + { + return ARIES_TEMP_READ_NOT_READY; + } + + rc = ariesReadWideRegister(device->i2cDriver, + ARIES_CURRENT_AVG_TEMP_ADC_CSR, 4, dataBytes); + CHECK_SUCCESS(rc); + adcCode = (dataBytes[3] << 24) + (dataBytes[2] << 16) + + (dataBytes[1] << 8) + dataBytes[0]; + + if (adcCode == 0 || adcCode >= 0x3ff) + { + return ARIES_TEMP_READ_NOT_READY; + } + + device->currentTempC = ariesTsenseADCToDegrees(device, adcCode, + device->tempCalCodeAvg); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesReadPmaAvgTempDirect(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int side; + int qs; + int adcCode; + uint8_t calCode; + float temperature_C = 0; + + for (side = 0; side < 2; side++) + { + for (qs = 0; qs < 4; qs++) + { + // Unfreeze FW + rc = ariesReadWordPmaIndirect( + device->i2cDriver, side, qs, + ARIES_PMA_RAWLANE_DIG_FSM_FSM_OVRD_CTL + 0x4000, dataWord); + CHECK_SUCCESS(rc); + dataWord[1] |= 0x40; + rc = ariesWriteWordPmaIndirect( + device->i2cDriver, side, qs, + ARIES_PMA_RAWLANE_DIG_FSM_FSM_OVRD_CTL + 0x4000, dataWord); + CHECK_SUCCESS(rc); + + // Temp Sensor Enable + rc = ariesReadWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_BG, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] |= 0x08; + rc = ariesWriteWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_BG, dataWord); + CHECK_SUCCESS(rc); + + rc = ariesReadWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_RTUNE_CTRL, + dataWord); + CHECK_SUCCESS(rc); + dataWord[0] |= 0x60; + dataWord[0] &= ~0x18; + rc = ariesWriteWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_RTUNE_CTRL, + dataWord); + CHECK_SUCCESS(rc); + + rc = ariesReadWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_DIG_RTUNE_DEBUG, + dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~0x18; + rc = ariesWriteWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_DIG_RTUNE_DEBUG, + dataWord); + CHECK_SUCCESS(rc); + usleep(10000); + + rc = ariesReadWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_RTUNE_CTRL, + dataWord); + CHECK_SUCCESS(rc); + dataWord[0] |= 0x80; + dataWord[0] &= ~0x04; + rc = ariesWriteWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_RTUNE_CTRL, + dataWord); + CHECK_SUCCESS(rc); + + // Temp Sensor Read + rc = ariesReadWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_DIG_RTUNE_DEBUG, + dataWord); + CHECK_SUCCESS(rc); + dataWord[0] |= 0x02; + rc = ariesWriteWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_DIG_RTUNE_DEBUG, + dataWord); + CHECK_SUCCESS(rc); + usleep(10000); + + rc = ariesReadWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_DIG_RTUNE_STAT, + dataWord); + CHECK_SUCCESS(rc); + + adcCode = (dataWord[1] << 8) + dataWord[0]; + + if (adcCode == 0 || adcCode >= 0x3ff) + { + return ARIES_TEMP_READ_NOT_READY; + } + + if (side == 0) + { + calCode = device->tempCalCodePmaB[qs]; + } + else + { + calCode = device->tempCalCodePmaA[qs]; + } + + temperature_C += ariesTsenseADCToDegrees(device, adcCode, calCode); + + rc = ariesReadWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_DIG_RTUNE_DEBUG, + dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~0x02; + rc = ariesWriteWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_DIG_RTUNE_DEBUG, + dataWord); + CHECK_SUCCESS(rc); + + // Temp Sensor Disable + rc = ariesReadWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_BG, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~0x08; + rc = ariesWriteWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_BG, dataWord); + CHECK_SUCCESS(rc); + + rc = ariesReadWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_RTUNE_CTRL, + dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~0xe0; + rc = ariesWriteWordPmaIndirect(device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_RTUNE_CTRL, + dataWord); + CHECK_SUCCESS(rc); + + // Freeze FW + rc = ariesReadWordPmaIndirect( + device->i2cDriver, side, qs, + ARIES_PMA_RAWLANE_DIG_FSM_FSM_OVRD_CTL, dataWord); + CHECK_SUCCESS(rc); + dataWord[1] &= ~0x40; + rc = ariesWriteWordPmaIndirect( + device->i2cDriver, side, qs, + ARIES_PMA_RAWLANE_DIG_FSM_FSM_OVRD_CTL, dataWord); + CHECK_SUCCESS(rc); + } + } + + // Take average of all 8 sensors + temperature_C = temperature_C / 8; + + // Convert ACD code to deg C + device->currentTempC = temperature_C; + + return ARIES_SUCCESS; +} + +/** + * @brief Enable thermal shutdown in Aries + * + * @param[in] device Aries Device struct + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesEnableThermalShutdown(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataBytes[4] = {0}; + + dataBytes[0] = 1; + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_EN_THERMAL_SHUTDOWN, 4, + dataBytes); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Disable thermal shutdown in Aries + * + * @param[in] device Aries Device struct + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesDisableThermalShutdown(AriesDeviceType* device) +{ + AriesErrorType rc; + uint8_t dataBytes[4] = {0}; + + dataBytes[0] = 0; + rc = ariesWriteWideRegister(device->i2cDriver, ARIES_EN_THERMAL_SHUTDOWN, 4, + dataBytes); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Get PMA Temp reading + * + * @param[in] device Aries Device struct + * @param[in] side PMA side + * @param[in] qs PMA Quad Slice + * @param[in/out] temperature_C PMA Temperature reading + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesReadPmaTemp(AriesDeviceType* device, int side, int qs, + float* temperature_C) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + + uint32_t pmaCsr = ARIES_MAIN_MICRO_FW_INFO + + ARIES_MM_PMA_TJ_ADC_CODE_OFFSET; + int adcCode; + uint8_t calCode; + + rc = ariesReadBlockDataMainMicroIndirect( + device->i2cDriver, (pmaCsr + (side * 8) + (qs * 2)), 2, dataWord); + CHECK_SUCCESS(rc); + + adcCode = (dataWord[1] << 8) + dataWord[0]; + + if (adcCode == 0 || adcCode >= 0x3ff) + { + return ARIES_TEMP_READ_NOT_READY; + } + + if (side == 0) + { + calCode = device->tempCalCodePmaB[qs]; + } + else + { + calCode = device->tempCalCodePmaA[qs]; + } + + *temperature_C = ariesTsenseADCToDegrees(device, adcCode, calCode); + + return ARIES_SUCCESS; +} + +/** + * @brief Get port orientation + * + * @param[in] i2cDriver i2c Driver struct + * @param[out] orientation port orientation + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetPortOrientation(AriesDeviceType* device, + int* orientation) +{ + AriesErrorType rc; + uint8_t dataBytes[4] = {0}; + + rc = ariesReadWideRegister(device->i2cDriver, 0x10, 4, dataBytes); + CHECK_SUCCESS(rc); + + // Oriention Val is bit 8 + *orientation = dataBytes[1] & (0x01); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesSetPortOrientation(AriesDeviceType* device, + uint8_t orientation) +{ + AriesErrorType rc; + uint8_t dataBytes[4] = {0}; + + rc = ariesReadWideRegister(device->i2cDriver, 0x10, 4, dataBytes); + CHECK_SUCCESS(rc); + + // Orientation is bit 8 in this reg + dataBytes[1] &= 0xfe; + dataBytes[1] |= (orientation & 0x1); + + rc = ariesWriteWideRegister(device->i2cDriver, 0x10, 4, dataBytes); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +/** + * @brief Get PMA ID + * + * @param[in] absLane Absolute lane number + * @return int - pma id. + */ +int ariesGetPmaNumber(int absLane) +{ + int pmaNum; + pmaNum = absLane / 4; + return pmaNum; +} + +/** + * @brief Get PMA lane ID + * + * @param[in] absLane Absolute lane number + * @return int - pma lane id. + */ +int ariesGetPmaLane(int absLane) +{ + int pmaLane; + pmaLane = absLane % 4; + return pmaLane; +} + +/** + * @brief Get absolute Path ID + * + * @param[in] lane start lane of link + * @param[in] direction upstream/downsteram direction + * @return int - path id. + */ +int ariesGetPathID(int lane, int direction) +{ + int pathID; + pathID = ((lane / 2) * 2) + direction; + return pathID; +} + +/** + * @brief Get Path lane ID + * + * @param[in] lane start lane of link + * @return int - path lane id. + */ +int ariesGetPathLaneID(int lane) +{ + int pathLaneID; + pathLaneID = lane % 2; + return pathLaneID; +} + +int ariesGetStartLane(AriesLinkType* link) +{ + int startLane; + startLane = link->config.startLane; + if (link->config.partNumber == ARIES_PTX08) + { + startLane += 4; + } + + return startLane; +} + +/** + * @brief Dump PMA registers to a file + * + * @param device Aries Device struct + * @param pma_addr Array of uint16_t register addresses to log + * @param len Length of pma_addr array + * @param filename File to write register dump to + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesDumpPMARegs(AriesDeviceType* device, uint16_t* pma_addr, + int len, char* filename) +{ + AriesErrorType rc; + FILE* fp; + uint16_t dataWord; + int i, side, qs; + + fp = fopen(filename, "w"); + if (fp == NULL) + { + ASTERA_ERROR("Could not open file %s", filename); + return ARIES_FAILURE; + } + if (device->partNumber == ARIES_PTX16) + { + fprintf( + fp, + "addr,pma_a_0,pma_a_1,pma_a_2,pma_a_3,pma_b_0,pma_b_1,pma_b_2,pma_b_3"); + } + else + { + fprintf(fp, "addr,pma_a_1,pma_a_2,pma_b_1,pma_b_2"); + } + + for (i = 0; i < len; i++) + { + fprintf(fp, "\n0x%x", pma_addr[i]); + if (device->partNumber == ARIES_PTX16) + { + for (side = 0; side < 2; side++) + { + for (qs = 0; qs < 4; qs++) + { + rc = ariesReadWordPmaMainMicroIndirect( + device->i2cDriver, 1 - side, qs, pma_addr[i], + (uint8_t*)&dataWord); + if (rc != ARIES_SUCCESS) + { + fclose(fp); + return rc; + } + fprintf(fp, ",0x%x", dataWord); + } + } + } + else + { + for (side = 0; side < 2; side++) + { + for (qs = 1; qs < 3; qs++) + { + rc = ariesReadWordPmaMainMicroIndirect( + device->i2cDriver, 1 - side, qs, pma_addr[i], + (uint8_t*)&dataWord); + if (rc != ARIES_SUCCESS) + { + fclose(fp); + return rc; + } + fprintf(fp, ",0x%x", dataWord); + } + } + } + } + fclose(fp); + return ARIES_SUCCESS; +} + +void ariesGetQSPathInfo(int lane, int direction, int* qs, int* qsPath, + int* qsPathLane) +{ + int pathID = ariesGetPathID(lane, direction); + *qs = pathID / 4; + *qsPath = pathID % 4; + *qsPathLane = lane % 2; +} + +/** + * @brief Get link termination status + * + * @param[in] link Link struct created by user + * @param[in] side pma side - a (0) or b (1) + * @param[in] abslane link lane number + * @param[out] term term value returned + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLinkRxTerm(AriesLinkType* link, int side, int absLane, + int* term) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + AriesI2CDriverType* i2cDriver; + i2cDriver = link->device->i2cDriver; + + int pmaNum = ariesGetPmaNumber(absLane); + int pmaLane = ariesGetPmaLane(absLane); + + // Read from PMAX_LANE0_DIG_ASIC_RX_OVRD_IN_3 + rc = ariesReadWordPmaLaneMainMicroIndirect( + i2cDriver, side, pmaNum, pmaLane, ARIES_PMA_LANE_DIG_ASIC_RX_OVRD_IN_3, + dataWord); + CHECK_SUCCESS(rc); + + // TERM_EN_OVRD_EN is bit 7 + uint8_t termEnOverrideEn = (dataWord[0] & 0x80) >> 7; + // TERM_EN is bit 6 + uint8_t termEn = (dataWord[0] & 0x40) >> 6; + + if (termEnOverrideEn == 1) + { + *term = termEn; + } + else + { + // Read from LANE0_DIG_ASIC_RX_ASIC_IN_1 + rc = ariesReadWordPmaLaneMainMicroIndirect( + i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_LANE_DIG_ASIC_RX_ASIC_IN_1, dataWord); + CHECK_SUCCESS(rc); + // RX_TERM_EN is bit 2 + *term = (dataWord[0] & 0x04) >> 2; + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesGetLinkId(AriesBifurcationType bifMode, int lane, + int* linkNum) +{ + int i; + for (i = 0; i < bifurcationModes[bifMode].numLinks; i++) + { + lane -= bifurcationModes[bifMode].links[i].linkWidth; + if (lane < 0) + { + *linkNum = i; + return ARIES_SUCCESS; + } + } + + return ARIES_FAILURE; +} + +AriesErrorType ariesGetLinkCurrentSpeed(AriesLinkType* link, int lane, + int direction, float* speed) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int pcieGen; + + // Based on the lane info, determine QS and Path info + int qs; + int qsPath; + int qsPathLane; + ariesGetQSPathInfo(lane, direction, &qs, &qsPath, &qsPathLane); + + // Compute reg address + // Calculate QS, Path, Path lane relative offsets first, + // and then compute actual address + int qsOff = ARIES_QS_0_CSR_OFFSET + (qs * ARIES_QS_STRIDE); + int pathOff = ARIES_PATH_WRAPPER_0_CSR_OFFSET + + (qsPath * ARIES_PATH_WRP_STRIDE); + + // Compute actual address based on offsets above + uint32_t address = qsOff + pathOff + ARIES_PATH_GLOBAL_CSR_OFFSET + + ARIES_GBL_CSR_MAC_PHY_RATE_AND_PCLK_RATE; + + rc = ariesReadByteData(link->device->i2cDriver, address, dataByte); + CHECK_SUCCESS(rc); + + // rate val is last 3 bits of reg + pcieGen = (dataByte[0] & 0x07) + 1; + + // Get speed from PCIE gen value + switch (pcieGen) + { + case 1: + *speed = 2.5; + break; + case 2: + *speed = 5.0; + break; + case 3: + *speed = 8.0; + break; + case 4: + *speed = 16.0; + break; + case 5: + *speed = 32.0; + break; + default: + *speed = 0.0; + } + return ARIES_SUCCESS; +} + +/** + * @brief Get Logical Lane Num + * + * @param[in] link Link struct created by user + * @param[in] lane link lane number + * @param[in] direction link direction + * @param[in/out] laneNum logical lane number + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLogicalLaneNum(AriesLinkType* link, int lane, + int direction, int* laneNum) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + // Based on the lane info, determine QS and Path info + int qs; + int qsPath; + int qsPathLane; + ariesGetQSPathInfo(lane, direction, &qs, &qsPath, &qsPathLane); + + // Compute reg address + // Calculate QS, Path, Path lane relative offsets first, + // and then compute actual address + int qsOff = ARIES_QS_0_CSR_OFFSET + (qs * ARIES_QS_STRIDE); + int pathOff = ARIES_PATH_WRAPPER_0_CSR_OFFSET + + (qsPath * ARIES_PATH_WRP_STRIDE); + int pathLaneOff = ARIES_PATH_LANE_0_CSR_OFFSET + + (qsPathLane * ARIES_PATH_LANE_STRIDE); + + uint32_t address = qsOff + pathOff + pathLaneOff + ARIES_LN_CAPT_NUM; + + rc = ariesReadByteData(link->device->i2cDriver, address, dataByte); + CHECK_SUCCESS(rc); + *laneNum = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief Get TX pre cursor value + * + * @param[in] link Link struct created by user + * @param[in] lane link lane number + * @param[in] direction link direction + * @param[in/out] txPre tx pre cursor value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetTxPre(AriesLinkType* link, int lane, int direction, + int* txPre) +{ + AriesErrorType rc; + uint8_t dataBytes3[3] = {0}; + int tmpVal; + + // Based on the lane info, determine QS and Path info + int qs; + int qsPath; + int qsPathLane; + ariesGetQSPathInfo(lane, direction, &qs, &qsPath, &qsPathLane); + + // Compute reg address + // Calculate QS, Path, Path lane relative offsets first, + // and then compute actual address + int qsOff = ARIES_QS_0_CSR_OFFSET + (qs * ARIES_QS_STRIDE); + int pathOff = ARIES_PATH_WRAPPER_0_CSR_OFFSET + + (qsPath * ARIES_PATH_WRP_STRIDE); + int pathLaneOff = ARIES_PATH_LANE_0_CSR_OFFSET + + (qsPathLane * ARIES_PATH_LANE_STRIDE); + + // Compute Reg Offset + uint32_t address = qsOff + pathOff + pathLaneOff + + ARIES_MAC_PHY_TXDEEMPH_OB; + + rc = ariesReadBlockData(link->device->i2cDriver, address, 3, dataBytes3); + CHECK_SUCCESS(rc); + tmpVal = (dataBytes3[0] + (dataBytes3[1] << 8) + (dataBytes3[2] << 16)); + *txPre = tmpVal & 0x3f; + + return ARIES_SUCCESS; +} + +AriesErrorType ariesGetPathHWState(AriesLinkType* link, int lane, int direction, + int* state) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + // Based on the lane info, determine QS and Path info + int qs; + int qsPath; + int qsPathLane; + ariesGetQSPathInfo(lane, direction, &qs, &qsPath, &qsPathLane); + + int qsOff = ARIES_QS_0_CSR_OFFSET + (qs * ARIES_QS_STRIDE); + int pathOff = ARIES_PATH_WRAPPER_0_CSR_OFFSET + + (qsPath * ARIES_PATH_WRP_STRIDE); + + uint32_t address = qsOff + pathOff + ARIES_RET_PTH_NEXT_STATE_OFFSET; + + rc = ariesReadBlockData(link->device->i2cDriver, address, 1, dataByte); + CHECK_SUCCESS(rc); + *state = dataByte[0]; + + return ARIES_SUCCESS; +} + +/** + * @brief Get TX current cursor value + * + * @param[in] link Link struct created by user + * @param[in] lane link lane number + * @param[in] direction link direction + * @param[in/out] txCur tx current cursor value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetTxCur(AriesLinkType* link, int lane, int direction, + int* txCur) +{ + AriesErrorType rc; + uint8_t dataBytes3[3] = {0}; + int tmpVal; + + // Based on the lane info, determine QS and Path info + int qs; + int qsPath; + int qsPathLane; + ariesGetQSPathInfo(lane, direction, &qs, &qsPath, &qsPathLane); + + // Compute reg address + // Calculate QS, Path, Path lane relative offsets first, + // and then compute actual address + int qsOff = ARIES_QS_0_CSR_OFFSET + (qs * ARIES_QS_STRIDE); + int pathOff = ARIES_PATH_WRAPPER_0_CSR_OFFSET + + (qsPath * ARIES_PATH_WRP_STRIDE); + int pathLaneOff = ARIES_PATH_LANE_0_CSR_OFFSET + + (qsPathLane * ARIES_PATH_LANE_STRIDE); + + // Compute Reg Offset + uint32_t address = qsOff + pathOff + pathLaneOff + + ARIES_MAC_PHY_TXDEEMPH_OB; + + rc = ariesReadBlockData(link->device->i2cDriver, address, 3, dataBytes3); + CHECK_SUCCESS(rc); + tmpVal = (dataBytes3[0] + (dataBytes3[1] << 8) + (dataBytes3[2] << 16)); + *txCur = (tmpVal & 0xfc0) >> 6; + + return ARIES_SUCCESS; +} + +/** + * @brief Get TX port cursor value + * + * @param[in] link Link struct created by user + * @param[in] lane link lane number + * @param[in] direction link direction + * @param[in/out] txPst tx pst value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetTxPst(AriesLinkType* link, int lane, int direction, + int* txPst) +{ + AriesErrorType rc; + uint8_t dataBytes3[3] = {0}; + int tmpVal; + + // Based on the lane info, determine QS and Path info + int qs; + int qsPath; + int qsPathLane; + ariesGetQSPathInfo(lane, direction, &qs, &qsPath, &qsPathLane); + + // Compute reg address + // Calculate QS, Path, Path lane relative offsets first, + // and then compute actual address + int qsOff = ARIES_QS_0_CSR_OFFSET + (qs * ARIES_QS_STRIDE); + int pathOff = ARIES_PATH_WRAPPER_0_CSR_OFFSET + + (qsPath * ARIES_PATH_WRP_STRIDE); + int pathLaneOff = ARIES_PATH_LANE_0_CSR_OFFSET + + (qsPathLane * ARIES_PATH_LANE_STRIDE); + + // Compute Reg Offset + uint32_t address = qsOff + pathOff + pathLaneOff + + ARIES_MAC_PHY_TXDEEMPH_OB; + + rc = ariesReadBlockData(link->device->i2cDriver, address, 3, dataBytes3); + CHECK_SUCCESS(rc); + tmpVal = (dataBytes3[0] + (dataBytes3[1] << 8) + (dataBytes3[2] << 16)); + *txPst = (tmpVal & 0x3f000) >> 12; + + return ARIES_SUCCESS; +} + +/** + * @brief Get RX polarity code + * + * @param[in] link Link struct created by user + * @param[in] lane link lane number + * @param[in] direction link direction + * @param[in] pinSet location in pins array + * @param[in/out] polarity polarity code + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetRxPolarityCode(AriesLinkType* link, int lane, + int direction, int pinSet, int* polarity) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + // Based on the lane info, determine QS and Path info + int qs; + int qsPath; + int qsPathLane; + ariesGetQSPathInfo(lane, direction, &qs, &qsPath, &qsPathLane); + + // Compute reg address + // Calculate QS, Path, Path lane relative offsets first, + // and then compute actual address + int qsOff = ARIES_QS_0_CSR_OFFSET + (qs * ARIES_QS_STRIDE); + int pathOff = ARIES_PATH_WRAPPER_0_CSR_OFFSET + + (qsPath * ARIES_PATH_WRP_STRIDE); + int pathLaneOff = ARIES_PATH_LANE_0_CSR_OFFSET + + (qsPathLane * ARIES_PATH_LANE_STRIDE); + + // Compute actual address based on offsets above + uint32_t address = qsOff + pathOff + pathLaneOff + ARIES_MAC_RX_POLARITY; + + rc = ariesReadByteData(link->device->i2cDriver, address, dataByte); + CHECK_SUCCESS(rc); + *polarity = dataByte[0] & 0x1; + + // Check inversion value for rx pins on package + int inversion; + if (pinSet == 0) + { + inversion = link->device->pins[lane].pinSet1.rxPackageInversion; + } + else + { + inversion = link->device->pins[lane].pinSet2.rxPackageInversion; + } + if ((inversion == 1) && (*polarity == 1)) + { + *polarity = 0; + } + else if ((inversion == 1) && (*polarity == 0)) + { + *polarity = 1; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Get current RX att code + * + * @param[in] link Link struct created by user + * @param[in] side pma side - a (0) or b (1) + * @param[in] absLane Absolute lane number + * @param[in/out] code rx att code + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetRxAttCode(AriesLinkType* link, int side, int absLane, + int* code) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int pmaNum = ariesGetPmaNumber(absLane); + int pmaLane = ariesGetPmaLane(absLane); + + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_LANE_DIG_RX_ADPTCTL_ATT_STATUS, dataWord); + CHECK_SUCCESS(rc); + + // You need bits 7:0 + *code = dataWord[0] >> 5; + return ARIES_SUCCESS; +} + +/** + * @brief Get current RX VGA Code + * + * @param[in] link Link struct created by user + * @param[in] side pma side - a (0) or b (1) + * @param[in] absLane Absolute lane number + * @param[in/out] boostCode RX boost code + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetRxCtleBoostCode(AriesLinkType* link, int side, + int absLane, int* boostCode) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int pmaNum = ariesGetPmaNumber(absLane); + int pmaLane = ariesGetPmaLane(absLane); + + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_LANE_DIG_RX_ADPTCTL_CTLE_STATUS, dataWord); + CHECK_SUCCESS(rc); + + // Need bits 9:0 + *boostCode = (((dataWord[1] & 0x03) << 8) + dataWord[0]) >> 5; + return ARIES_SUCCESS; +} + +/** + * @brief Get current RX VGA Code + * + * @param[in] link Link struct created by user + * @param[in] side pma side - a (0) or b (1) + * @param[in] absLane Absolute lane number + * @param[in/out] vgaCode RX VGA code + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetRxVgaCode(AriesLinkType* link, int side, int absLane, + int* vgaCode) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int pmaNum = ariesGetPmaNumber(absLane); + int pmaLane = ariesGetPmaLane(absLane); + + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_LANE_DIG_RX_ADPTCTL_VGA_STATUS, dataWord); + CHECK_SUCCESS(rc); + + // Need bits 9:0 + *vgaCode = (((dataWord[1] & 0x03) << 8) + dataWord[0]) >> 6; + return ARIES_SUCCESS; +} + +/** + * @brief Get current RX Boost Value (in dB) + * + * @param[in] boostCode boostCode + * @param[in] attValDb att val (in db) + * @param[in] vgaCode vga code + * @return float - rx Boost value + */ +float ariesGetRxBoostValueDb(int boostCode, float attValDb, int vgaCode) +{ + float AfeHfGain; + float AfeLfGain; + float boostValDb; + + float attVal = 1.5 + attValDb; + float vgaVal = 0.9 * vgaCode; + float tmpVal1; + float tmpVal2; + float boostVal; + + if (vgaCode <= 6) + { + tmpVal1 = 0.9 * (6 - vgaCode); + if (boostCode <= 10) + { + tmpVal2 = 0.65 * boostCode; + } + else + { + tmpVal2 = 0.65 * 10; + } + // Find max between 2 values + boostVal = tmpVal1; + if (tmpVal1 < tmpVal2) + { + boostVal = tmpVal2; + } + } + else + { + if (boostCode <= 10) + { + tmpVal2 = 0.65 * boostCode; + } + else + { + tmpVal2 = 0.65 * 10; + } + boostVal = tmpVal2; + } + AfeHfGain = attVal + vgaVal + boostVal; + + if (boostCode <= 10) + { + boostVal = 0; + } + else + { + boostVal = -0.65 * (boostCode - 10); + } + AfeLfGain = attValDb + vgaVal + boostVal; + + boostValDb = AfeHfGain - AfeLfGain; + return boostValDb; +} + +/** + * @brief Get the current RX CTLE POLE code + * + * @param[in] link Link struct created by user + * @param[in] side PMA side + * @param[in] absLane Absolute lane number + * @param[in/out] poleCode POLE code captured + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetRxCtlePoleCode(AriesLinkType* link, int side, + int absLane, int* poleCode) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int pmaNum = ariesGetPmaNumber(absLane); + int pmaLane = ariesGetPmaLane(absLane); + + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_LANE_DIG_RX_ADPTCTL_CTLE_STATUS, dataWord); + CHECK_SUCCESS(rc); + + // Get bits 11:10 + *poleCode = (dataWord[1] & 0x0c) >> 2; + return ARIES_SUCCESS; +} + +/** + * @brief Get the current RX Adapt IQ Value + * + * @param[in] link Link struct created by user + * @param[in] side PMA side + * @param[in] absLane Absolute lane number + * @param[in/out] iqValue Adapt IQ value captured + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetRxAdaptIq(AriesLinkType* link, int side, int absLane, + int* iqValue) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int pmaNum = ariesGetPmaNumber(absLane); + int pmaLane = ariesGetPmaLane(absLane); + + float curSpeed; + rc = ariesGetLinkCurrentSpeed(link, absLane, side, &curSpeed); + CHECK_SUCCESS(rc); + + if (curSpeed == 8.0) + { + // Check the RX_ADPT_IQ_VLD bit == 1 + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADAPT_DONE, dataWord); + CHECK_SUCCESS(rc); + if ((dataWord[0] & 0x2) == 0x2) + { + // Read the Adapt IQ value + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADPT_IQ, dataWord); + CHECK_SUCCESS(rc); + } + else + { + return ARIES_FAILURE; + } + } + else if (curSpeed == 16.0) + { + // Check the RX_ADPT_IQ_VLD bit[1] is set + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADAPT_DONE_B1, dataWord); + CHECK_SUCCESS(rc); + if ((dataWord[0] & 0x2) == 0x2) + { + // Read the Adapt IQ value + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADPT_IQ_B1, dataWord); + CHECK_SUCCESS(rc); + } + else + { + return ARIES_FAILURE; + } + } + else if (curSpeed == 32.0) + { + // Check the RX_ADPT_IQ_VLD bit == 1 + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADAPT_DONE_B2, dataWord); + CHECK_SUCCESS(rc); + if ((dataWord[0] & 0x2) == 0x2) + { + // Read the Adapt IQ value + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADPT_IQ_B2, dataWord); + CHECK_SUCCESS(rc); + } + else + { + return ARIES_FAILURE; + } + } + else + { + dataWord[0] = 0; + dataWord[1] = 0; + } + + // Get bits 6:0 + *iqValue = dataWord[0] & 0x7f; + return ARIES_SUCCESS; +} + +/** + * @brief Get the current RX Adapt IQ Value + * + * @param[in] link Link struct created by user + * @param[in] side PMA side + * @param[in] absLane Absolute lane number + * @param[in] bank Bank number + * @param[in/out] iqValue Adapt IQ value captured + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetRxAdaptIqBank(AriesLinkType* link, int side, int absLane, + int bank, int* iqValue) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int pmaNum = ariesGetPmaNumber(absLane); + int pmaLane = ariesGetPmaLane(absLane); + + if (bank == 0) + { + // Read the Adapt IQ value + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADPT_IQ, dataWord); + CHECK_SUCCESS(rc); + } + else if (bank == 1) + { + // Read the Adapt IQ value + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADPT_IQ_B1, dataWord); + CHECK_SUCCESS(rc); + } + else if (bank == 2) + { + // Read the Adapt IQ value + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADPT_IQ_B2, dataWord); + CHECK_SUCCESS(rc); + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + // Get bits 6:0 + *iqValue = dataWord[0] & 0x7f; + return ARIES_SUCCESS; +} + +/** + * @brief Get the current RX Adapt IQ Value + * + * @param[in] link Link struct created by user + * @param[in] side PMA side + * @param[in] absLane Absolute lane number + * @param[in] bank Bank number + * @param[in/out] doneValue Adapt done value captured + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetRxAdaptDoneBank(AriesLinkType* link, int side, + int absLane, int bank, int* doneValue) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int pmaNum = ariesGetPmaNumber(absLane); + int pmaLane = ariesGetPmaLane(absLane); + + if (bank == 0) + { + // Check the RX_ADPT_IQ_VLD bit == 1 + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADAPT_DONE, dataWord); + CHECK_SUCCESS(rc); + } + else if (bank == 1) + { + // Check the RX_ADPT_IQ_VLD bit == 1 + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADAPT_DONE_B1, dataWord); + CHECK_SUCCESS(rc); + } + else if (bank == 2) + { + // Check the RX_ADPT_IQ_VLD bit == 1 + rc = ariesReadWordPmaLaneMainMicroIndirect( + link->device->i2cDriver, side, pmaNum, pmaLane, + ARIES_PMA_RAWLANEAON_DIG_RX_ADAPT_DONE_B2, dataWord); + CHECK_SUCCESS(rc); + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + // Get bits 6:0 + *doneValue = (dataWord[1] << 8) | dataWord[0]; + return ARIES_SUCCESS; +} + +/** + * @brief Get the current RX DFE code + * + * @param[in] link Link struct created by user + * @param[in] side PMA side + * @param[in] absLane Absolute lane number + * @param[in] tapNum DFE tap code + * @param[in/out] dfeCode dfe code captured + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetRxDfeCode(AriesLinkType* link, int side, int absLane, + int tapNum, int* dfeCode) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int pmaNum = ariesGetPmaNumber(absLane); + int pmaLane = ariesGetPmaLane(absLane); + int address; + int tapVal; + + switch (tapNum) + { + case 1: + // Get PMA Tap address from a util function + address = ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP1_STATUS; + rc = ariesReadWordPmaLaneMainMicroIndirect(link->device->i2cDriver, + side, pmaNum, pmaLane, + address, dataWord); + CHECK_SUCCESS(rc); + // Get Bits 13:0 + tapVal = (((dataWord[1] & 0x3f) << 8) + dataWord[0]) >> 5; + *dfeCode = tapVal; + if (tapVal >= 256) + { + // dfe_tap1_code is stored in two's complement format + *dfeCode = tapVal - 512; + } + break; + + case 2: + // Get PMA Tap address from a util function + address = ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP2_STATUS; + rc = ariesReadWordPmaLaneMainMicroIndirect(link->device->i2cDriver, + side, pmaNum, pmaLane, + address, dataWord); + CHECK_SUCCESS(rc); + // Get bits 12:0 + tapVal = (((dataWord[1] & 0x1f) << 8) + dataWord[0]) >> 5; + *dfeCode = tapVal - 128; + break; + + case 3: + // Get PMA Tap address from a util function + address = ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP3_STATUS; + rc = ariesReadWordPmaLaneMainMicroIndirect(link->device->i2cDriver, + side, pmaNum, pmaLane, + address, dataWord); + CHECK_SUCCESS(rc); + // Get bits 11:0 + tapVal = (((dataWord[1] & 0x0f) << 8) + dataWord[0]) >> 5; + *dfeCode = tapVal - 64; + break; + + case 4: + // Get PMA Tap address from a util function + address = ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP4_STATUS; + rc = ariesReadWordPmaLaneMainMicroIndirect(link->device->i2cDriver, + side, pmaNum, pmaLane, + address, dataWord); + CHECK_SUCCESS(rc); + // Get bits 11:0 + tapVal = (((dataWord[1] & 0x0f) << 8) + dataWord[0]) >> 5; + *dfeCode = tapVal - 64; + break; + + case 5: + // Get PMA Tap address from a util function + address = ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP5_STATUS; + rc = ariesReadWordPmaLaneMainMicroIndirect(link->device->i2cDriver, + side, pmaNum, pmaLane, + address, dataWord); + CHECK_SUCCESS(rc); + // Get bits 11:0 + tapVal = (((dataWord[1] & 0x0f) << 8) + dataWord[0]) >> 5; + *dfeCode = tapVal - 64; + break; + + case 6: + // Get PMA Tap address from a util function + address = ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP6_STATUS; + rc = ariesReadWordPmaLaneMainMicroIndirect(link->device->i2cDriver, + side, pmaNum, pmaLane, + address, dataWord); + CHECK_SUCCESS(rc); + // Get bits 11:0 + tapVal = (((dataWord[1] & 0x0f) << 8) + dataWord[0]) >> 5; + *dfeCode = tapVal - 64; + break; + + case 7: + // Get PMA Tap address from a util function + address = ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP7_STATUS; + rc = ariesReadWordPmaLaneMainMicroIndirect(link->device->i2cDriver, + side, pmaNum, pmaLane, + address, dataWord); + CHECK_SUCCESS(rc); + // Get bits 11:0 + tapVal = (((dataWord[1] & 0x0f) << 8) + dataWord[0]) >> 5; + *dfeCode = tapVal - 64; + break; + + case 8: + // Get PMA Tap address from a util function + address = ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP8_STATUS; + rc = ariesReadWordPmaLaneMainMicroIndirect(link->device->i2cDriver, + side, pmaNum, pmaLane, + address, dataWord); + CHECK_SUCCESS(rc); + // Get bits 11:0 + tapVal = (((dataWord[1] & 0x0f) << 8) + dataWord[0]) >> 5; + *dfeCode = tapVal - 64; + break; + + default: + ASTERA_ERROR("Invalid DFE Tag"); + return ARIES_INVALID_ARGUMENT; + break; + } + return ARIES_SUCCESS; +} + +/** + * @brief Get the last speed at which EQ was run (e.g. 3 for Gen-3) + * + * @param[in] link Link struct created by user + * @param[in] lane Absolute lane number + * @param[in] direction PMA side + * @param[in/out] speed last captures speed + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLastEqSpeed(AriesLinkType* link, int lane, int direction, + int* speed) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int pathID = ((lane / 2) * 2) + direction; + + // Reading gp_ctrl_sts_struct.last_eq_pcie_gen + int address = link->device->pm_gp_ctrl_sts_struct_addr + + ARIES_CTRL_STS_STRUCT_LAST_EQ_PCIE_GEN; + + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, pathID, + address, dataByte); + CHECK_SUCCESS(rc); + + *speed = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief Get the deskew status string for the given lane + * + * @param[in] link Link struct created by user + * @param[in] lane Absolute lane number + * @param[in] direction PMA side + * @param[in/out] val deskew status for lane + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetDeskewStatus(AriesLinkType* link, int lane, + int direction, int* status) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + // Based on the lane info, determine QS and Path info + int qs; + int qsPath; + int qsPathLane; + ariesGetQSPathInfo(lane, direction, &qs, &qsPath, &qsPathLane); + + // Compute reg address + // Calculate QS, Path, Path lane relative offsets first, + // and then compute actual address + int qsOff = ARIES_QS_0_CSR_OFFSET + (qs * ARIES_QS_STRIDE); + int pathOff = ARIES_PATH_WRAPPER_0_CSR_OFFSET + + (qsPath * ARIES_PATH_WRP_STRIDE); + int pathLaneOff = ARIES_PATH_LANE_0_CSR_OFFSET + + (qsPathLane * ARIES_PATH_LANE_STRIDE); + + uint32_t address = qsOff + pathOff + pathLaneOff + ARIES_DESKEW_STATUS; + + rc = ariesReadByteData(link->device->i2cDriver, address, dataByte); + CHECK_SUCCESS(rc); + *status = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief Get the number of clocks of deskew applied for the given lane + * + * @param[in] link Link struct created by user + * @param[in] lane Absolute lane number + * @param[in] direction PMA side + * @param[in/out] val deskew clocks for lane + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetDeskewClks(AriesLinkType* link, int lane, int direction, + int* val) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + // Based on the lane info, determine QS and Path info + int qs; + int qsPath; + int qsPathLane; + ariesGetQSPathInfo(lane, direction, &qs, &qsPath, &qsPathLane); + + // Compute reg address + // Calculate QS, Path, Path lane relative offsets first, + // and then compute actual address + int qsOff = ARIES_QS_0_CSR_OFFSET + (qs * ARIES_QS_STRIDE); + int pathOff = ARIES_PATH_WRAPPER_0_CSR_OFFSET + + (qsPath * ARIES_PATH_WRP_STRIDE); + int pathLaneOff = ARIES_PATH_LANE_0_CSR_OFFSET + + (qsPathLane * ARIES_PATH_LANE_STRIDE); + + uint32_t address = qsOff + pathOff + pathLaneOff + ARIES_DSK_CC_DELTA; + + rc = ariesReadByteData(link->device->i2cDriver, address, dataByte); + CHECK_SUCCESS(rc); + *val = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief For the last round of Equalization, get the final pre-cursor request + * the link partner made. If the last request was a Perest request, this value + * will be 0. + * + * @param[in] link Link struct created by user + * @param[in] lane Absolute lane number + * @param[in] direction PMA side + * @param[in/out] val Pre-cursor request value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLastEqReqPre(AriesLinkType* link, int lane, + int direction, int* val) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int pathID = ((lane / 2) * 2) + direction; + int pathLane = lane % 2; + int offset; + + if (pathLane == 0) + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRE_LN0; + } + else + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRE_LN1; + } + + // Reading gp_ctrl_sts_struct.last_eq_final_req_pre_lnX + int address = link->device->pm_gp_ctrl_sts_struct_addr + offset; + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, pathID, + address, dataByte); + CHECK_SUCCESS(rc); + + *val = dataByte[0]; + return ARIES_SUCCESS; +} + +AriesErrorType ariesGetPathFWState(AriesLinkType* link, int lane, int direction, + int* state) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int pathID = ((lane / 2) * 2) + direction; + int offset = ARIES_CTRL_STS_STRUCT_FW_STATE; + + int address = link->device->pm_gp_ctrl_sts_struct_addr + offset; + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, pathID, + address, dataByte); + CHECK_SUCCESS(rc); + + *state = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief For the last round of Equalization, get the final cursor request + * the link partner made. If the last request was a Perest request, this value + * will be 0. + * + * @param[in] link Link struct created by user + * @param[in] lane Absolute lane number + * @param[in] direction PMA side + * @param[in/out] val cursor request value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLastEqReqCur(AriesLinkType* link, int lane, + int direction, int* val) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int pathID = ((lane / 2) * 2) + direction; + int pathLane = lane % 2; + int offset; + + if (pathLane == 0) + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_CUR_LN0; + } + else + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_CUR_LN1; + } + + // Reading gp_ctrl_sts_struct.last_eq_final_req_cur_lnX + int address = link->device->pm_gp_ctrl_sts_struct_addr + offset; + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, pathID, + address, dataByte); + CHECK_SUCCESS(rc); + + *val = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief For the last round of Equalization, get the final post-cursor request + * the link partner made. If the last request was a Perest request, this value + * will be 0. + * + * @param[in] link Link struct created by user + * @param[in] lane Absolute lane number + * @param[in] direction PMA side + * @param[in/out] val Post-cursor request value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLastEqReqPst(AriesLinkType* link, int lane, + int direction, int* val) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int pathID = ((lane / 2) * 2) + direction; + int pathLane = lane % 2; + int offset; + + if (pathLane == 0) + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PST_LN0; + } + else + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PST_LN1; + } + + // Reading gp_ctrl_sts_struct.last_eq_final_req_pst_lnX + int address = link->device->pm_gp_ctrl_sts_struct_addr + offset; + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, pathID, + address, dataByte); + CHECK_SUCCESS(rc); + + *val = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief For the last round of Equalization, get the final preset request + * the link partner made. A value of 0xf will indicate the final request was a + * coefficient request. + * + * @param[in] link Link struct created by user + * @param[in] lane Absolute lane number + * @param[in] direction PMA side + * @param[in/out] val Preset request value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLastEqReqPreset(AriesLinkType* link, int lane, + int direction, int* val) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int pathID = ((lane / 2) * 2) + direction; + int pathLane = lane % 2; + int offset; + + if (pathLane == 0) + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRESET_LN0; + } + else + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRESET_LN1; + } + + // Reading gp_ctrl_sts_struct.last_eq_final_req_preset_lnX + int address = link->device->pm_gp_ctrl_sts_struct_addr + offset; + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, pathID, + address, dataByte); + CHECK_SUCCESS(rc); + + *val = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief For the last round of Equalization, get the Preset value for the + * specified request. + * + * @param[in] link Link struct created by user + * @param[in] lane Absolute lane number + * @param[in] reqNum Request num + * @param[in/out] val Preset value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLastEqPresetReq(AriesLinkType* link, int lane, + int direction, int reqNum, int* val) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int pathID = ((lane / 2) * 2) + direction; + int pathLane = lane % 2; + int offset; + + if (pathLane == 0) + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_PRESET_REQS_LN0; + } + else + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_PRESET_REQS_LN1; + } + + // Reading gp_ctrl_sts_struct.last_eq_final_req_preset_ln + int address = link->device->pm_gp_ctrl_sts_struct_addr + offset + reqNum; + rc = ariesReadBlockDataPathMicroIndirect(link->device->i2cDriver, pathID, + address, 1, dataByte); + CHECK_SUCCESS(rc); + + *val = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief For the last round of Equalization, get the FOM value for the + * specified request. + * + * @param[in] link Link struct created by user + * @param[in] lane Absolute lane number + * @param[in] reqNum Request num + * @param[in/out] val FOM value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLastEqPresetReqFOM(AriesLinkType* link, int lane, + int direction, int reqNum, int* val) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int pathID = ((lane / 2) * 2) + direction; + int pathLane = lane % 2; + int offset; + + if (pathLane == 0) + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_FOMS_LN0; + } + else + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_FOMS_LN1; + } + + // Reading gp_ctrl_sts_struct.last_eq_final_req_preset_ln + int address = link->device->pm_gp_ctrl_sts_struct_addr + offset + reqNum; + rc = ariesReadBlockDataPathMicroIndirect(link->device->i2cDriver, pathID, + address, 1, dataByte); + CHECK_SUCCESS(rc); + + *val = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief For the last round of Equalization, get the number of + * reset requests issued. + * + * @param[in] link Link struct created by user + * @param[in] lane Absolute lane number + * @param[in] reqNum Request num + * @param[in/out] val Number of reset requests + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLastEqNumPresetReq(AriesLinkType* link, int lane, + int direction, int* val) +{ + AriesErrorType rc; + int pathID = ((lane / 2) * 2) + direction; + int pathLane = lane % 2; + uint8_t dataByte[1] = {0}; + int offset; + + if (pathLane == 0) + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_NUM_PRESET_REQS_LN0; + } + else + { + offset = ARIES_CTRL_STS_STRUCT_LAST_EQ_NUM_PRESET_REQS_LN1; + } + + // Reading gp_ctrl_sts_struct.last_eq_final_req_preset_ln + int address = link->device->pm_gp_ctrl_sts_struct_addr + offset; + rc = ariesReadBlockDataPathMicroIndirect(link->device->i2cDriver, pathID, + address, 1, dataByte); + CHECK_SUCCESS(rc); + + *val = dataByte[0]; + return ARIES_SUCCESS; +} + +/** + * @brief Get the format ID at offset in the print buffer + * + * @param[in] link Link struct created by user + * @param[in] loggerType Logger type (main or which path) + * @param[in] offset Print buffer offset + * @param[in/out] fmtID Format ID from logger + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLoggerFmtID(AriesLinkType* link, + AriesLTSSMLoggerEnumType loggerType, + int offset, int* fmtID) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int address; + + // Main Micro + if (loggerType == ARIES_LTSSM_LINK_LOGGER) + { + address = link->device->mm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_PRINT_BUFFER_OFFSET + offset; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, + address, dataByte); + CHECK_SUCCESS(rc); + *fmtID = dataByte[0]; + } + else + { + address = link->device->pm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_PRINT_BUFFER_OFFSET + offset; + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, + loggerType, address, dataByte); + CHECK_SUCCESS(rc); + *fmtID = dataByte[0]; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Get the write pointer location in the LTSSM logger + * + * @param[in] link Link struct created by user + * @param[in] loggerType Logger type (main or which path) + * @param[in/out] writeOffset Current write offset + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLoggerWriteOffset(AriesLinkType* link, + AriesLTSSMLoggerEnumType loggerType, + int* writeOffset) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int address; + int data0; + int data1; + + // Main Micro + if (loggerType == ARIES_LTSSM_LINK_LOGGER) + { + address = link->device->mm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_WR_PTR_OFFSET; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, + address, dataByte); + CHECK_SUCCESS(rc); + data0 = dataByte[0]; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, + (address + 1), dataByte); + CHECK_SUCCESS(rc); + data1 = dataByte[0]; + + *writeOffset = (data1 << 8) | data0; + } + else + { + address = link->device->pm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_WR_PTR_OFFSET; + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, + loggerType, address, dataByte); + CHECK_SUCCESS(rc); + data0 = dataByte[0]; + rc = ariesReadByteDataPathMicroIndirect( + link->device->i2cDriver, loggerType, (address + 1), dataByte); + CHECK_SUCCESS(rc); + data1 = dataByte[0]; + + *writeOffset = (data1 << 8) | data0; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Check if one batch mode in the LTSSM logger is enabled or not + * + * @param[in] link Link struct created by user + * @param[in] loggerType Logger type (main or which path) + * @param[in/out] oneBatchModeEn One batch mode enabled or not + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetLoggerOneBatchModeEn(AriesLinkType* link, + AriesLTSSMLoggerEnumType loggerType, + int* oneBatchModeEn) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int address; + + if (loggerType == ARIES_LTSSM_LINK_LOGGER) + { + address = link->device->mm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_ONE_BATCH_MODE_EN_OFFSET; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, + address, dataByte); + CHECK_SUCCESS(rc); + *oneBatchModeEn = dataByte[0]; + } + else + { + address = link->device->pm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_ONE_BATCH_MODE_EN_OFFSET; + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, + loggerType, address, dataByte); + CHECK_SUCCESS(rc); + *oneBatchModeEn = dataByte[0]; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Check if one batch write in the LTSSM logger is enabled or not + * + * @param[in] link Link struct created by user + * @param[in] loggerType Logger type (main or which path) + * @param[in/out] oneBatchWrEn One batch write enabled or not + * @return AriesErrorType - Aries error code + * + */ +AriesErrorType ariesGetLoggerOneBatchWrEn(AriesLinkType* link, + AriesLTSSMLoggerEnumType loggerType, + int* oneBatchWrEn) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int address; + + if (loggerType == ARIES_LTSSM_LINK_LOGGER) + { + address = link->device->mm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_ONE_BATCH_WR_EN_OFFSET; + rc = ariesReadByteDataMainMicroIndirect(link->device->i2cDriver, + address, dataByte); + CHECK_SUCCESS(rc); + *oneBatchWrEn = dataByte[0]; + } + else + { + address = link->device->pm_print_info_struct_addr + + ARIES_PRINT_INFO_STRUCT_ONE_BATCH_WR_EN_OFFSET; + rc = ariesReadByteDataPathMicroIndirect(link->device->i2cDriver, + loggerType, address, dataByte); + CHECK_SUCCESS(rc); + *oneBatchWrEn = dataByte[0]; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Calculate CRC-8 byte from polynomial + * + * @param[in] msg Polynomial to calculate PEC + * @param[in] length Length of polynomial + * @return uint8_t - crc-8 byte + */ +uint8_t ariesGetPecByte(uint8_t* polynomial, uint8_t length) +{ + uint8_t crc; + int byteIndex; + int bitIndex; + + // Shift polynomial by 1 so that it is 8 bit + // Take this extra bit into account later + uint8_t poly = ARIES_CRC8_POLYNOMIAL >> 1; + crc = polynomial[0]; + + for (byteIndex = 1; byteIndex < length; byteIndex++) + { + uint8_t nextByte = polynomial[byteIndex]; + for (bitIndex = 7; bitIndex >= 0; bitIndex--) + { + // Check if MSB in CRC is 1 + if (crc & 0x80) + { + // Perform subtraction of first 8 bits + crc = (crc ^ poly) << 1; + // Add final bit of mod2 subtraction and place in pos 0 of CRC + crc = crc + (((nextByte >> bitIndex) & 1) ^ 1); + } + else + { + // Shift out 0 + crc = crc << 1; + // Shift in next bit + crc = crc + ((nextByte >> bitIndex) & 1); + } + } + } + return crc; +} + +/** + * @brief Capture the min FoM value seen for a given lane. + * + * @param[in] device Device struct containing i2c driver + * @param[in] side PMA side + * @param[in] pathID Path ID + * @param[in] lane lane inside path + * @param[in] regOffset address offset for FoM reg + * @param[in,out] data 2-byte data returned, containing FoM value + * @return AriesErrorType - Aries error code + */ +AriesErrorType ariesGetMinFoMVal(AriesDeviceType* device, int side, int pathID, + int lane, int regOffset, uint8_t* data) +{ + AriesErrorType rc; + AriesErrorType lc; + uint8_t dataByte[1] = {0}; + uint8_t dataWord[2] = {0}; + + int address = regOffset; + if (lane < 4) + { + address = regOffset + (lane * ARIES_PMA_LANE_STRIDE); + } + + lc = ariesLock(device->i2cDriver); + CHECK_SUCCESS(lc); + + // Set up reg address + dataWord[0] = address & 0xff; + dataWord[1] = (address >> 8) & 0xff; + rc = ariesWriteBlockData(device->i2cDriver, ARIES_MM_ASSIST_REG_ADDR_OFFSET, + 2, dataWord); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Path ID is represented in upper 4 bits of this byte + dataByte[0] = pathID << 4; + rc = ariesWriteByteData(device->i2cDriver, ARIES_MM_ASSIST_PATH_ID_OFFSET, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Set up PMA side + dataByte[0] = ARIES_MM_RD_PID_IND_PMA0 + side; + rc = ariesWriteByteData(device->i2cDriver, ARIES_MM_ASSIST_CMD_OFFSET, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Get data (LSB and MSB) + rc = ariesReadByteData(device->i2cDriver, ARIES_MM_ASSIST_DATA_OFFSET, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + data[0] = dataByte[0]; + rc = ariesReadByteData(device->i2cDriver, ARIES_MM_ASSIST_STS_OFFSET, + dataByte); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + data[1] = dataByte[0]; + + lc = ariesUnlock(device->i2cDriver); + CHECK_SUCCESS(lc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesGetPinMap(AriesDeviceType* device) +{ + // All pins are same size + size_t pinSize = sizeof(device->pins[0].pinSet1.rxPin); + if (device->partNumber == ARIES_PTX16) + { + device->pins[0].lane = 0; + strncpy(device->pins[0].pinSet1.rxPin, "B_PER0", pinSize); + strncpy(device->pins[0].pinSet1.txPin, "A_PET0", pinSize); + device->pins[0].pinSet1.rxPackageInversion = 1; + device->pins[0].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[0].pinSet2.rxPin, "A_PER0", pinSize); + strncpy(device->pins[0].pinSet2.txPin, "B_PET0", pinSize); + device->pins[0].pinSet2.rxPackageInversion = 1; + device->pins[0].pinSet2.txPackageInsersion = 1; + + device->pins[1].lane = 1; + strncpy(device->pins[1].pinSet1.rxPin, "B_PER1", pinSize); + strncpy(device->pins[1].pinSet1.txPin, "A_PET1", pinSize); + device->pins[1].pinSet1.rxPackageInversion = 1; + device->pins[1].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[1].pinSet2.rxPin, "A_PER1", pinSize); + strncpy(device->pins[1].pinSet2.txPin, "B_PET1", pinSize); + device->pins[1].pinSet2.rxPackageInversion = 0; + device->pins[1].pinSet2.txPackageInsersion = 0; + + device->pins[2].lane = 2; + strncpy(device->pins[2].pinSet1.rxPin, "B_PER2", pinSize); + strncpy(device->pins[2].pinSet1.txPin, "A_PET2", pinSize); + device->pins[2].pinSet1.rxPackageInversion = 0; + device->pins[2].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[2].pinSet2.rxPin, "A_PER2", pinSize); + strncpy(device->pins[2].pinSet2.txPin, "B_PET2", pinSize); + device->pins[2].pinSet2.rxPackageInversion = 1; + device->pins[2].pinSet2.txPackageInsersion = 0; + + device->pins[3].lane = 3; + strncpy(device->pins[3].pinSet1.rxPin, "B_PER3", pinSize); + strncpy(device->pins[3].pinSet1.txPin, "A_PET3", pinSize); + device->pins[3].pinSet1.rxPackageInversion = 0; + device->pins[3].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[3].pinSet2.rxPin, "A_PER3", pinSize); + strncpy(device->pins[3].pinSet2.txPin, "B_PET3", pinSize); + device->pins[3].pinSet2.rxPackageInversion = 1; + device->pins[3].pinSet2.txPackageInsersion = 1; + + device->pins[4].lane = 4; + strncpy(device->pins[4].pinSet1.rxPin, "B_PER4", pinSize); + strncpy(device->pins[4].pinSet1.txPin, "A_PET4", pinSize); + device->pins[4].pinSet1.rxPackageInversion = 1; + device->pins[4].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[4].pinSet2.rxPin, "A_PER4", pinSize); + strncpy(device->pins[4].pinSet2.txPin, "B_PET4", pinSize); + device->pins[4].pinSet2.rxPackageInversion = 0; + device->pins[4].pinSet2.txPackageInsersion = 1; + + device->pins[5].lane = 5; + strncpy(device->pins[5].pinSet1.rxPin, "B_PER5", pinSize); + strncpy(device->pins[5].pinSet1.txPin, "A_PET5", pinSize); + device->pins[5].pinSet1.rxPackageInversion = 1; + device->pins[5].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[5].pinSet2.rxPin, "A_PER5", pinSize); + strncpy(device->pins[5].pinSet2.txPin, "B_PET5", pinSize); + device->pins[5].pinSet2.rxPackageInversion = 0; + device->pins[5].pinSet2.txPackageInsersion = 0; + + device->pins[6].lane = 6; + strncpy(device->pins[6].pinSet1.rxPin, "B_PER6", pinSize); + strncpy(device->pins[6].pinSet1.txPin, "A_PET6", pinSize); + device->pins[6].pinSet1.rxPackageInversion = 0; + device->pins[6].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[6].pinSet2.rxPin, "A_PER6", pinSize); + strncpy(device->pins[6].pinSet2.txPin, "B_PET6", pinSize); + device->pins[6].pinSet2.rxPackageInversion = 1; + device->pins[6].pinSet2.txPackageInsersion = 1; + + device->pins[7].lane = 7; + strncpy(device->pins[7].pinSet1.rxPin, "B_PER7", pinSize); + strncpy(device->pins[7].pinSet1.txPin, "A_PET7", pinSize); + device->pins[7].pinSet1.rxPackageInversion = 0; + device->pins[7].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[7].pinSet2.rxPin, "A_PER7", pinSize); + strncpy(device->pins[7].pinSet2.txPin, "B_PET7", pinSize); + device->pins[7].pinSet2.rxPackageInversion = 1; + device->pins[7].pinSet2.txPackageInsersion = 1; + + device->pins[8].lane = 8; + strncpy(device->pins[8].pinSet1.rxPin, "B_PER8", pinSize); + strncpy(device->pins[8].pinSet1.txPin, "A_PET8", pinSize); + device->pins[8].pinSet1.rxPackageInversion = 1; + device->pins[8].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[8].pinSet2.rxPin, "A_PER8", pinSize); + strncpy(device->pins[8].pinSet2.txPin, "B_PET8", pinSize); + device->pins[8].pinSet2.rxPackageInversion = 1; + device->pins[8].pinSet2.txPackageInsersion = 0; + + device->pins[9].lane = 9; + strncpy(device->pins[9].pinSet1.rxPin, "B_PER9", pinSize); + strncpy(device->pins[9].pinSet1.txPin, "A_PET9", pinSize); + device->pins[9].pinSet1.rxPackageInversion = 1; + device->pins[9].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[9].pinSet2.rxPin, "A_PER9", pinSize); + strncpy(device->pins[9].pinSet2.txPin, "B_PET9", pinSize); + device->pins[9].pinSet2.rxPackageInversion = 1; + device->pins[9].pinSet2.txPackageInsersion = 0; + + device->pins[10].lane = 10; + strncpy(device->pins[10].pinSet1.rxPin, "B_PER10", pinSize); + strncpy(device->pins[10].pinSet1.txPin, "A_PET10", pinSize); + device->pins[10].pinSet1.rxPackageInversion = 0; + device->pins[10].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[10].pinSet2.rxPin, "A_PER10", pinSize); + strncpy(device->pins[10].pinSet2.txPin, "B_PET10", pinSize); + device->pins[10].pinSet2.rxPackageInversion = 0; + device->pins[10].pinSet2.txPackageInsersion = 0; + + device->pins[11].lane = 11; + strncpy(device->pins[11].pinSet1.rxPin, "B_PER11", pinSize); + strncpy(device->pins[11].pinSet1.txPin, "A_PET11", pinSize); + device->pins[11].pinSet1.rxPackageInversion = 0; + device->pins[11].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[11].pinSet2.rxPin, "A_PER11", pinSize); + strncpy(device->pins[11].pinSet2.txPin, "B_PET11", pinSize); + device->pins[11].pinSet2.rxPackageInversion = 0; + device->pins[11].pinSet2.txPackageInsersion = 1; + + device->pins[12].lane = 12; + strncpy(device->pins[12].pinSet1.rxPin, "B_PER12", pinSize); + strncpy(device->pins[12].pinSet1.txPin, "A_PET12", pinSize); + device->pins[12].pinSet1.rxPackageInversion = 1; + device->pins[12].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[12].pinSet2.rxPin, "A_PER12", pinSize); + strncpy(device->pins[12].pinSet2.txPin, "B_PET12", pinSize); + device->pins[12].pinSet2.rxPackageInversion = 1; + device->pins[12].pinSet2.txPackageInsersion = 1; + + device->pins[13].lane = 13; + strncpy(device->pins[13].pinSet1.rxPin, "B_PER13", pinSize); + strncpy(device->pins[13].pinSet1.txPin, "A_PET13", pinSize); + device->pins[13].pinSet1.rxPackageInversion = 1; + device->pins[13].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[13].pinSet2.rxPin, "A_PER13", pinSize); + strncpy(device->pins[13].pinSet2.txPin, "B_PET13", pinSize); + device->pins[13].pinSet2.rxPackageInversion = 1; + device->pins[13].pinSet2.txPackageInsersion = 1; + + device->pins[14].lane = 14; + strncpy(device->pins[14].pinSet1.rxPin, "B_PER14", pinSize); + strncpy(device->pins[14].pinSet1.txPin, "A_PET14", pinSize); + device->pins[14].pinSet1.rxPackageInversion = 0; + device->pins[14].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[14].pinSet2.rxPin, "A_PER14", pinSize); + strncpy(device->pins[14].pinSet2.txPin, "B_PET14", pinSize); + device->pins[14].pinSet2.rxPackageInversion = 0; + device->pins[14].pinSet2.txPackageInsersion = 0; + + device->pins[15].lane = 15; + strncpy(device->pins[15].pinSet1.rxPin, "B_PER15", pinSize); + strncpy(device->pins[15].pinSet1.txPin, "A_PET15", pinSize); + device->pins[15].pinSet1.rxPackageInversion = 0; + device->pins[15].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[15].pinSet2.rxPin, "A_PER15", pinSize); + strncpy(device->pins[15].pinSet2.txPin, "B_PET15", pinSize); + device->pins[15].pinSet2.rxPackageInversion = 1; + device->pins[15].pinSet2.txPackageInsersion = 0; + } + else if (device->partNumber == ARIES_PTX08) + { + device->pins[0].lane = 0; + strncpy(device->pins[0].pinSet1.rxPin, "", pinSize); + strncpy(device->pins[0].pinSet1.txPin, "", pinSize); + device->pins[0].pinSet1.rxPackageInversion = 1; + device->pins[0].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[0].pinSet2.rxPin, "", pinSize); + strncpy(device->pins[0].pinSet2.txPin, "", pinSize); + device->pins[0].pinSet2.rxPackageInversion = 1; + device->pins[0].pinSet2.txPackageInsersion = 1; + + device->pins[1].lane = 1; + strncpy(device->pins[1].pinSet1.rxPin, "", pinSize); + strncpy(device->pins[1].pinSet1.txPin, "", pinSize); + device->pins[1].pinSet1.rxPackageInversion = 1; + device->pins[1].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[1].pinSet2.rxPin, "", pinSize); + strncpy(device->pins[1].pinSet2.txPin, "", pinSize); + device->pins[1].pinSet2.rxPackageInversion = 0; + device->pins[1].pinSet2.txPackageInsersion = 0; + + device->pins[2].lane = 2; + strncpy(device->pins[2].pinSet1.rxPin, "", pinSize); + strncpy(device->pins[2].pinSet1.txPin, "", pinSize); + device->pins[2].pinSet1.rxPackageInversion = 0; + device->pins[2].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[2].pinSet2.rxPin, "", pinSize); + strncpy(device->pins[2].pinSet2.txPin, "", pinSize); + device->pins[2].pinSet2.rxPackageInversion = 1; + device->pins[2].pinSet2.txPackageInsersion = 0; + + device->pins[3].lane = 3; + strncpy(device->pins[3].pinSet1.rxPin, "", pinSize); + strncpy(device->pins[3].pinSet1.txPin, "", pinSize); + device->pins[3].pinSet1.rxPackageInversion = 0; + device->pins[3].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[3].pinSet2.rxPin, "", pinSize); + strncpy(device->pins[3].pinSet2.txPin, "", pinSize); + device->pins[3].pinSet2.rxPackageInversion = 1; + device->pins[3].pinSet2.txPackageInsersion = 1; + + device->pins[4].lane = 4; + strncpy(device->pins[4].pinSet1.rxPin, "A_PER0", pinSize); + strncpy(device->pins[4].pinSet1.txPin, "B_PET0", pinSize); + device->pins[4].pinSet1.rxPackageInversion = 1; + device->pins[4].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[4].pinSet2.rxPin, "B_PER0", pinSize); + strncpy(device->pins[4].pinSet2.txPin, "A_PET0", pinSize); + device->pins[4].pinSet2.rxPackageInversion = 0; + device->pins[4].pinSet2.txPackageInsersion = 1; + + device->pins[5].lane = 5; + strncpy(device->pins[5].pinSet1.rxPin, "A_PER1", pinSize); + strncpy(device->pins[5].pinSet1.txPin, "B_PET1", pinSize); + device->pins[5].pinSet1.rxPackageInversion = 1; + device->pins[5].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[5].pinSet2.rxPin, "B_PER1", pinSize); + strncpy(device->pins[5].pinSet2.txPin, "A_PET1", pinSize); + device->pins[5].pinSet2.rxPackageInversion = 0; + device->pins[5].pinSet2.txPackageInsersion = 0; + + device->pins[6].lane = 6; + strncpy(device->pins[6].pinSet1.rxPin, "A_PER2", pinSize); + strncpy(device->pins[6].pinSet1.txPin, "B_PET2", pinSize); + device->pins[6].pinSet1.rxPackageInversion = 0; + device->pins[6].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[6].pinSet2.rxPin, "B_PER2", pinSize); + strncpy(device->pins[6].pinSet2.txPin, "A_PET2", pinSize); + device->pins[6].pinSet2.rxPackageInversion = 1; + device->pins[6].pinSet2.txPackageInsersion = 1; + + device->pins[7].lane = 7; + strncpy(device->pins[7].pinSet1.rxPin, "A_PER3", pinSize); + strncpy(device->pins[7].pinSet1.txPin, "B_PET3", pinSize); + device->pins[7].pinSet1.rxPackageInversion = 0; + device->pins[7].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[7].pinSet2.rxPin, "B_PER3", pinSize); + strncpy(device->pins[7].pinSet2.txPin, "A_PET3", pinSize); + device->pins[7].pinSet2.rxPackageInversion = 1; + device->pins[7].pinSet2.txPackageInsersion = 1; + + device->pins[8].lane = 8; + strncpy(device->pins[8].pinSet1.rxPin, "A_PER4", pinSize); + strncpy(device->pins[8].pinSet1.txPin, "B_PET4", pinSize); + device->pins[8].pinSet1.rxPackageInversion = 1; + device->pins[8].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[8].pinSet2.rxPin, "B_PER4", pinSize); + strncpy(device->pins[8].pinSet2.txPin, "A_PET4", pinSize); + device->pins[8].pinSet2.rxPackageInversion = 1; + device->pins[8].pinSet2.txPackageInsersion = 0; + + device->pins[9].lane = 9; + strncpy(device->pins[9].pinSet1.rxPin, "A_PER5", pinSize); + strncpy(device->pins[9].pinSet1.txPin, "B_PET5", pinSize); + device->pins[9].pinSet1.rxPackageInversion = 1; + device->pins[9].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[9].pinSet2.rxPin, "B_PER5", pinSize); + strncpy(device->pins[9].pinSet2.txPin, "A_PET5", pinSize); + device->pins[9].pinSet2.rxPackageInversion = 1; + device->pins[9].pinSet2.txPackageInsersion = 0; + + device->pins[10].lane = 10; + strncpy(device->pins[10].pinSet1.rxPin, "A_PER6", pinSize); + strncpy(device->pins[10].pinSet1.txPin, "B_PET6", pinSize); + device->pins[10].pinSet1.rxPackageInversion = 0; + device->pins[10].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[10].pinSet2.rxPin, "B_PER6", pinSize); + strncpy(device->pins[10].pinSet2.txPin, "A_PET6", pinSize); + device->pins[10].pinSet2.rxPackageInversion = 0; + device->pins[10].pinSet2.txPackageInsersion = 0; + + device->pins[11].lane = 11; + strncpy(device->pins[11].pinSet1.rxPin, "A_PER7", pinSize); + strncpy(device->pins[11].pinSet1.txPin, "B_PET7", pinSize); + device->pins[11].pinSet1.rxPackageInversion = 0; + device->pins[11].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[11].pinSet2.rxPin, "B_PER7", pinSize); + strncpy(device->pins[11].pinSet2.txPin, "A_PET7", pinSize); + device->pins[11].pinSet2.rxPackageInversion = 0; + device->pins[11].pinSet2.txPackageInsersion = 1; + + device->pins[12].lane = 12; + strncpy(device->pins[12].pinSet1.rxPin, "", pinSize); + strncpy(device->pins[12].pinSet1.txPin, "", pinSize); + device->pins[12].pinSet1.rxPackageInversion = 1; + device->pins[12].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[12].pinSet2.rxPin, "", pinSize); + strncpy(device->pins[12].pinSet2.txPin, "", pinSize); + device->pins[12].pinSet2.rxPackageInversion = 1; + device->pins[12].pinSet2.txPackageInsersion = 1; + + device->pins[13].lane = 13; + strncpy(device->pins[13].pinSet1.rxPin, "", pinSize); + strncpy(device->pins[13].pinSet1.txPin, "", pinSize); + device->pins[13].pinSet1.rxPackageInversion = 1; + device->pins[13].pinSet1.txPackageInsersion = 0; + strncpy(device->pins[13].pinSet2.rxPin, "", pinSize); + strncpy(device->pins[13].pinSet2.txPin, "", pinSize); + device->pins[13].pinSet2.rxPackageInversion = 1; + device->pins[13].pinSet2.txPackageInsersion = 1; + + device->pins[14].lane = 14; + strncpy(device->pins[14].pinSet1.rxPin, "", pinSize); + strncpy(device->pins[14].pinSet1.txPin, "", pinSize); + device->pins[14].pinSet1.rxPackageInversion = 0; + device->pins[14].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[14].pinSet2.rxPin, "", pinSize); + strncpy(device->pins[14].pinSet2.txPin, "", pinSize); + device->pins[14].pinSet2.rxPackageInversion = 0; + device->pins[14].pinSet2.txPackageInsersion = 0; + + device->pins[15].lane = 15; + strncpy(device->pins[15].pinSet1.rxPin, "", pinSize); + strncpy(device->pins[15].pinSet1.txPin, "", pinSize); + device->pins[15].pinSet1.rxPackageInversion = 0; + device->pins[15].pinSet1.txPackageInsersion = 1; + strncpy(device->pins[15].pinSet2.rxPin, "", pinSize); + strncpy(device->pins[15].pinSet2.txPin, "", pinSize); + device->pins[15].pinSet2.rxPackageInversion = 1; + device->pins[15].pinSet2.txPackageInsersion = 0; + } + else + { + return ARIES_INVALID_ARGUMENT; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Read numBytes bytes of data starting at startAddr + */ +AriesErrorType ariesEepromReadBlockData(AriesDeviceType* device, + uint8_t* values, int startAddr, + uint8_t numBytes) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int addrMSB; + int addr; + int currentPage = 0; + bool firstRead = true; + int valIndex = 0; + + if ((numBytes <= 0) || (startAddr < 0) || (startAddr >= 262144) || + ((startAddr + numBytes - 1) >= 262144)) + { + return ARIES_INVALID_ARGUMENT; + } + + // Perform legacy mode reads here + for (addr = startAddr; addr < (startAddr + numBytes); addr++) + { + addrMSB = floor(addr / 65536); + // Set page and address on the bus with first read + // Or whenever there is a page update + if (firstRead || (addrMSB != currentPage)) + { + // Set updated page address + rc = ariesI2CMasterSetPage(device->i2cDriver, addrMSB); + CHECK_SUCCESS(rc); + currentPage = addrMSB; + + // Send EEPROM address 0 after page update + dataByte[0] = (addr >> 8) & 0xff; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 2); + CHECK_SUCCESS(rc); + dataByte[0] = addr & 0xff; + rc = ariesI2CMasterSendByte(device->i2cDriver, dataByte, 1); + CHECK_SUCCESS(rc); + firstRead = false; + } + + // Perform byte read and store in values array + rc = ariesI2CMasterReceiveByte(device->i2cDriver, dataByte); + CHECK_SUCCESS(rc); + + values[valIndex] = dataByte[0]; + valIndex++; + } + + return ARIES_SUCCESS; +} + +/** + * @brief Read numBytes bytes from the EEPROM starting at startAddr and + * calculate a running checksum (e.g. add the bytes as you read them): + * uint8_t checksum = (checksum + new_byte) % 256 + */ +AriesErrorType ariesEepromCalcChecksum(AriesDeviceType* device, int startAddr, + int numBytes, uint8_t* checksum) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t runningChecksum = 0; + + if ((numBytes <= 0) || (startAddr < 0) || (startAddr >= 262144) || + ((startAddr + numBytes - 1) >= 262144)) + { + return ARIES_INVALID_ARGUMENT; + } + + // Calculate expected checksum + int addr; + for (addr = startAddr; addr < (startAddr + numBytes); addr++) + { + rc = ariesEepromReadBlockData(device, dataByte, addr, 1); + CHECK_SUCCESS(rc); + runningChecksum = (runningChecksum + dataByte[0]) & 0xff; + } + *checksum = runningChecksum; + return ARIES_SUCCESS; +} + +/** + * @brief Sort Array in ascending order + * + * @param[in] arr Array to be sorted + * @param[in] size Num elements in array + * @return None + */ +void ariesSortArray(uint16_t* arr, int size) +{ + int i, j, temp; + for (i = 0; i < (size - 1); i++) + { + for (j = 0; j < (size - i - 1); j++) + { + if (arr[j] > arr[j + 1]) + { + temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } +} + +/** + * @brief Get median of all elements in the array + * + * @param[in] arr Array to be sorted + * @param[in] size Num elements in array + * @return None + */ +uint16_t ariesGetMedian(uint16_t* arr, int size) +{ + ariesSortArray(arr, size); + // Capture middle element + int ind = ((size + 1) / 2) - 1; + return arr[ind]; +} + +/** + * @brief This loads an binary file into the mem[] array + */ +AriesErrorType ariesLoadBinFile(const char* filename, uint8_t* mem) +{ + FILE* fin; + int numBytesRead; + + // Check if file is valid + if (strlen(filename) == 0) + { + ASTERA_ERROR("Can't load a file without the filename"); + return ARIES_INVALID_ARGUMENT; + } + fin = fopen(filename, "rb"); + if (fin == NULL) + { + ASTERA_ERROR("Can't open file '%s' for reading", filename); + return ARIES_FAILURE; + } + + // Read in bytes from file + numBytesRead = fread(mem, 1, ARIES_EEPROM_MAX_NUM_BYTES, fin); + + if (numBytesRead != ARIES_EEPROM_MAX_NUM_BYTES) + { + ASTERA_ERROR( + "We did not read out the expected ARIES_EEPROM_MAX_NUM_BYTES: %d bytes from file '%s' was %d", + ARIES_EEPROM_MAX_NUM_BYTES, filename, numBytesRead); + fclose(fin); + return ARIES_FAILURE; + } + else if (ferror(fin)) + { + ASTERA_ERROR("There was some error in reading from file '%s'", + filename); + fclose(fin); + return ARIES_FAILURE; + } + + // Close file + fclose(fin); + + return ARIES_SUCCESS; +} + +/** + * @brief This loads an intel hex file into the mem[] array + */ +AriesErrorType ariesLoadIhxFile(const char* filename, uint8_t* mem) +{ + AriesErrorType rc; + char line[1000]; + FILE* fin; + int addr; + int n; + int status; + uint8_t bytes[256] = {0}; + int byte; + int indx = 0; + int total = 0; + int lineno = 1; + int minAddr = 0xffff; + int maxAddr = 0; + + // Check if file is valid + if (strlen(filename) == 0) + { + ASTERA_ERROR("Can't load a file without the filename"); + return ARIES_INVALID_ARGUMENT; + } + fin = fopen(filename, "r"); + if (fin == NULL) + { + ASTERA_ERROR("Can't open file '%s' for reading", filename); + return ARIES_INVALID_ARGUMENT; + } + + while (!feof(fin) && !ferror(fin)) + { + line[0] = '\0'; + if (fgets(line, 1000, fin) == NULL) + { + ASTERA_ERROR("Error reading data from file"); + fclose(fin); + return ARIES_FAILURE; + } + if (line[strlen(line) - 1] == '\n') + { + line[strlen(line) - 1] = '\0'; + } + if (line[strlen(line) - 1] == '\r') + { + line[strlen(line) - 1] = '\0'; + } + + rc = ariesParseIhxLine(line, bytes, &addr, &n, &status); + if (rc != ARIES_SUCCESS) + { + ASTERA_ERROR("Error: '%s', line: %d", filename, lineno); + } + else + { + if (status == 0) + { /* data */ + for (byte = 0; byte <= (n - 1); byte++) + { + mem[indx] = bytes[byte] & 0xff; + total++; + if (addr < minAddr) + minAddr = addr; + if (addr > maxAddr) + maxAddr = addr; + addr++; + indx++; + } + } + if (status == 1) + { /* end of file */ + break; + } + } + lineno++; + } + fclose(fin); + return ARIES_SUCCESS; +} + +/** + * @brief This is used by loadFile to get each line of intex hex + */ +AriesErrorType ariesParseIhxLine(char* line, uint8_t* bytes, int* addr, + int* num, int* status) +{ + int sum; + int cksum; + int len; + char* ptr; + int val; + + *num = 0; + // First char + if (line[0] != ':') + { + return ARIES_FAILURE; + } + + if (strlen(line) < 11) + { + return ARIES_FAILURE; + } + ptr = line + 1; + + if (!sscanf(ptr, "%02x", (unsigned int*)&len)) + { + return ARIES_FAILURE; + } + ptr += 2; + + if (strlen(line) < (size_t)(11 + (len * 2))) + { + return ARIES_FAILURE; + } + if (!sscanf(ptr, "%04x", (unsigned int*)addr)) + { + return ARIES_FAILURE; + } + ptr += 4; + + if (!sscanf(ptr, "%02x", (unsigned int*)status)) + { + return ARIES_FAILURE; + } + ptr += 2; + sum = (len & 0xff) + ((*addr >> 8) & 0xff) + (*addr & 0xff) + + (*status & 0xff); + + while (*num != len) + { + int ret = sscanf(ptr, "%02x", (unsigned int*)&val); + bytes[*num] = (uint8_t)val; + if (!ret) + { + return ARIES_FAILURE; + } + ptr += 2; + sum += bytes[*num] & 0xff; + (*num)++; + if (*num >= 256) + { + return ARIES_FAILURE; + } + } + if (!sscanf(ptr, "%02x", (unsigned int*)&cksum)) + { + return ARIES_FAILURE; + } + if (((sum & 0xff) + (cksum & 0xff)) & 0xff) + { + return ARIES_FAILURE; /* checksum error */ + } + return ARIES_SUCCESS; +} + +AriesErrorType ariesI2cMasterSoftReset(AriesI2CDriverType* i2cDriver) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t i2cInitCtrl; + + // Enable Bit bang mode + dataByte[0] = 3; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_BB_OUTPUT_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + + rc = ariesReadByteData(i2cDriver, ARIES_I2C_MST_INIT_CTRL_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + i2cInitCtrl = dataByte[0]; + + i2cInitCtrl = + ARIES_MAIN_MICRO_EXT_CSR_I2C_MST_INIT_CTRL_BIT_BANG_MODE_EN_MODIFY( + i2cInitCtrl, 1); + dataByte[0] = i2cInitCtrl; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_INIT_CTRL_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + + // Start Sequence + // SDA = 1, SCL = 1 + dataByte[0] = 3; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_BB_OUTPUT_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + // SDA = 0, SCL = 1 + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_BB_OUTPUT_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + // SDA = 0, SCL = 0 + dataByte[0] = 0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_BB_OUTPUT_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + // SDA = 1, SCL = 0 + dataByte[0] = 2; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_BB_OUTPUT_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + + int i = 0; + for (i = 0; i < 9; i++) + { + // SDA = 1, SCL = 1 + dataByte[0] = 3; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_BB_OUTPUT_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + + // SDA = 1 SCL = 0 + dataByte[0] = 2; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_BB_OUTPUT_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + } + + // Stop Sequence + // SDA = 0, SCL = 0 + dataByte[0] = 0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_BB_OUTPUT_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + // SDA = 0, SCL = 1 + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_BB_OUTPUT_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + // SDA = 1, SCL = 1 + dataByte[0] = 3; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_BB_OUTPUT_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + + // Disable BB mode + i2cInitCtrl = + ARIES_MAIN_MICRO_EXT_CSR_I2C_MST_INIT_CTRL_BIT_BANG_MODE_EN_MODIFY( + i2cInitCtrl, 0); + dataByte[0] = i2cInitCtrl; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_INIT_CTRL_ADDRESS, + dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesGetEEPROMFirstBlock(AriesI2CDriverType* i2cDriver, + int* blockStartAddr) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + // Set Page = 0 + rc = ariesI2CMasterSetPage(i2cDriver, 0); + CHECK_SUCCESS(rc); + + // Set address = 0 + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(i2cDriver, dataByte, 2); + CHECK_SUCCESS(rc); + dataByte[0] = 0; + rc = ariesI2CMasterSendByte(i2cDriver, dataByte, 1); + CHECK_SUCCESS(rc); + + // The first block SHOULD start at address zero, but check up to + // maxAddrToCheck bytes to find it. + int addr = 0; + int maxAddrToCheck = 50; + + uint8_t byte0; + uint8_t byte1; + uint8_t byte2; + uint8_t byte3; + + while (addr < maxAddrToCheck) + { + rc = ariesI2CMasterReceiveByte(i2cDriver, dataByte); + CHECK_SUCCESS(rc); + byte0 = dataByte[0]; + if (byte0 == 0xa5) + { + rc = ariesI2CMasterReceiveByte(i2cDriver, dataByte); + CHECK_SUCCESS(rc); + byte1 = dataByte[0]; + if (byte1 == 0x5a) + { + rc = ariesI2CMasterReceiveByte(i2cDriver, dataByte); + CHECK_SUCCESS(rc); + byte2 = dataByte[0]; + if (byte2 == 0xa5) + { + rc = ariesI2CMasterReceiveByte(i2cDriver, dataByte); + CHECK_SUCCESS(rc); + byte3 = dataByte[0]; + if (byte3 == 0x5a) + { + *blockStartAddr = addr; + break; + } + } + } + } + addr++; + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesGetEEPROMBlockType(AriesI2CDriverType* i2cDriver, + int blockStartAddr, uint8_t* blockType) +{ + AriesErrorType rc; + int addr; + addr = blockStartAddr + 4; + + rc = ariesEEPROMGetRandomByte(i2cDriver, addr, blockType); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesGetEEPROMBlockCrcByte(AriesI2CDriverType* i2cDriver, + int blockStartAddr, int blockLength, + uint8_t* crcByte) +{ + AriesErrorType rc; + int addr; + addr = blockStartAddr + blockLength + 11; + + rc = ariesEEPROMGetRandomByte(i2cDriver, addr, crcByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesEEPROMGetRandomByte(AriesI2CDriverType* i2cDriver, int addr, + uint8_t* value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + int addrMSB = floor(addr / 65536); + + rc = ariesI2CMasterSetPage(i2cDriver, addrMSB); + CHECK_SUCCESS(rc); + + // Send address + // Write command + dataByte[0] = 0x10; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_IC_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + // Prepare Flag Byte + dataByte[0] = 0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA1_ADDR, dataByte); + CHECK_SUCCESS(rc); + + uint8_t addr15To8; + uint8_t addr7To0; + addr15To8 = (addr >> 8) & 0xff; + addr7To0 = addr & 0xff; + dataByte[0] = addr15To8; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = addr7To0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + + dataByte[0] = 3; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_DATA1_ADDR, dataByte); + CHECK_SUCCESS(rc); + + dataByte[0] = 0x1; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + usleep(ARIES_I2C_MASTER_CMD_RST); + dataByte[0] = 0x0; + rc = ariesWriteByteData(i2cDriver, ARIES_I2C_MST_CMD_ADDR, dataByte); + CHECK_SUCCESS(rc); + rc = ariesReadByteData(i2cDriver, ARIES_I2C_MST_DATA0_ADDR, dataByte); + CHECK_SUCCESS(rc); + *value = dataByte[0]; + + return ARIES_SUCCESS; +} + +AriesErrorType ariesEEPROMGetBlockLength(AriesI2CDriverType* i2cDriver, + int blockStartAddr, int* blockLength) +{ + AriesErrorType rc; + int addrMSB = floor(blockStartAddr / 65536); + + rc = ariesI2CMasterSetPage(i2cDriver, addrMSB); + CHECK_SUCCESS(rc); + + int addrBlockLengthLSB = blockStartAddr + 5; + uint8_t blockLengthLSB; + rc = ariesEEPROMGetRandomByte(i2cDriver, addrBlockLengthLSB, + &blockLengthLSB); + CHECK_SUCCESS(rc); + + int addrBlockLengthMSB = blockStartAddr + 6; + uint8_t blockLengthMSB; + rc = ariesEEPROMGetRandomByte(i2cDriver, addrBlockLengthMSB, + &blockLengthMSB); + CHECK_SUCCESS(rc); + + *blockLength = (blockLengthMSB << 8) + blockLengthLSB; + return ARIES_SUCCESS; +} + +void ariesGetCrcBytesImage(uint8_t* image, uint8_t* crcBytes, + uint8_t* numCrcBytes) +{ + int numBlocks = 0; + + // Get First Block + int addr = 0; + int blockStartAddr = 0; + int maxAddrToCheck = 50; + uint8_t byte0; + uint8_t byte1; + uint8_t byte2; + uint8_t byte3; + while (addr < maxAddrToCheck) + { + byte0 = image[addr]; + if (byte0 == 0xa5) + { + addr++; + byte1 = image[addr]; + if (byte1 == 0x5a) + { + addr++; + byte2 = image[addr]; + if (byte2 == 0xa5) + { + addr++; + byte3 = image[addr]; + if (byte3 == 0x5a) + { + blockStartAddr = addr - 3; + break; + } + } + } + } + addr++; + } + + while (numBlocks < ARIES_EEPROM_MAX_NUM_CRC_BLOCKS) + { + // Get Block Type + uint8_t blockType = image[(blockStartAddr + 4)]; + if (blockType != 0xff) + { + // Get Block Length + uint8_t blockLengthLSB = image[(blockStartAddr + 5)]; + uint8_t blockLengthMSB = image[(blockStartAddr + 6)]; + uint16_t blockLength = (blockLengthMSB << 8) + blockLengthLSB; + + uint8_t crcByte = image[(blockStartAddr + blockLength + 11)]; + crcBytes[numBlocks] = crcByte; + + blockStartAddr += blockLength + 13; + numBlocks++; + } + else + { + // Last Page + break; + } + } + + *numCrcBytes = numBlocks; +} + +AriesErrorType ariesPipeRxAdapt(AriesDeviceType* device, int side, int lane) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = lane / 4; + int qsLane = lane % 4; + + // Clear Rx Req Override Enable + rc = ariesPMAPCSRxReqBlock(device, side, lane, false); + CHECK_SUCCESS(rc); + + // Set PIPE Rx EQ In Progress + rc = ariesPipeRxEqInProgressSet(device, side, lane, true); + CHECK_SUCCESS(rc); + + // Clear PhyStatus + rc = ariesPipePhyStatusClear(device, side, lane); + CHECK_SUCCESS(rc); + + // Set PIPE Rx EQ Eval + rc = ariesPipeRxEqEval(device, side, lane, true); + CHECK_SUCCESS(rc); + + // Wait for PhyStatus == 1 + rc = ariesPipePhyStatusToggle(device, side, lane); + CHECK_SUCCESS(rc); + + // Clear PIPE Rx EQ Eval + rc = ariesPipeRxEqEval(device, side, lane, false); + CHECK_SUCCESS(rc); + + // Set Tx Data Enable + rc = ariesPMARxDataEnSet(device, side, lane, true); + CHECK_SUCCESS(rc); + + int i; + for (i = 0; i < 20; i++) + { + rc = ariesPMAPCSRxReqBlock(device, side, lane, true); + CHECK_SUCCESS(rc); + + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_RX_ASIC_OUT_0, dataWord); + CHECK_SUCCESS(rc); + uint8_t rxvalid = (dataWord[0] >> 1) & 1; // VALID is bit 1 + + if (rxvalid) + { + break; + } + else + { + rc = ariesPMAPCSRxReqBlock(device, side, lane, false); + CHECK_SUCCESS(rc); + } + } + // Check for no-adapt case + if (i == 20) + { + ASTERA_ERROR( + "Side:%d, Lane:%2d, RxValid=0! Confirm link partner transmitter is enabled at the correct data rate", + side, lane); + } + + rc = ariesPMAPCSRxReqBlock(device, side, lane, true); + CHECK_SUCCESS(rc); + + rc = ariesPipeRxEqInProgressSet(device, side, lane, false); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeFomGet(AriesDeviceType* device, int side, int lane, + int* fom) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + + rc = ariesReadRetimerRegister(device->i2cDriver, side, lane, + ARIES_RET_PTH_LN_PHY_MAC_FOMFEEDBACK_ADDR, 1, + dataByte); + CHECK_SUCCESS(rc); + fom[0] = dataByte[0]; + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeRxStandbySet(AriesDeviceType* device, int side, + int lane, bool value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + if (value) + { + dataByte[0] = 3; // en=1, val=1 + } + else + { + dataByte[0] = 2; // en=1, val=0 + } + rc = ariesWriteRetimerRegister(device->i2cDriver, side, lane, + ARIES_RET_PTH_LN_MAC_PHY_RXSTANDBY_ADDR, 1, + dataByte); + CHECK_SUCCESS(rc); + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeTxElecIdleSet(AriesDeviceType* device, int side, + int lane, bool value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t adjSide = 0; + uint8_t lane_base = lane & 0xfe; // for access to ret_pth_gbl register, use + // a ret_pth_ln offset of 0 + + // txdatavalid and txelecidle for pid N (N even) is captured in path N+1 + adjSide = side ? 0 : 1; + + // The MAC must always have TxDataValid asserted when TxElecIdle transitions + // to either asserted or deasserted + dataByte[0] = 0xf; + rc = ariesWriteRetimerRegister(device->i2cDriver, adjSide, lane_base, + ARIES_RET_PTH_GBL_MAC_PHY_TXDATAVALID_ADDR, + 1, dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = (3 << 2) | (value << 1) | value; + rc = ariesWriteRetimerRegister(device->i2cDriver, adjSide, lane_base, + ARIES_RET_PTH_GBL_MAC_PHY_TXELECIDLE_ADDR, 1, + dataByte); + CHECK_SUCCESS(rc); + dataByte[0] = 0x0; + rc = ariesWriteRetimerRegister(device->i2cDriver, adjSide, lane_base, + ARIES_RET_PTH_GBL_MAC_PHY_TXDATAVALID_ADDR, + 1, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeRxEqEval(AriesDeviceType* device, int side, int lane, + bool value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + if (value == true) + { + dataByte[0] = 3; // en=1, val=1 + } + else + { + dataByte[0] = 2; // en=1, val=0 + } + rc = ariesWriteRetimerRegister(device->i2cDriver, side, lane, + ARIES_RET_PTH_LN_MAC_PHY_RXEQEVAL_ADDR, 1, + dataByte); + CHECK_SUCCESS(rc); + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeRxEqInProgressSet(AriesDeviceType* device, int side, + int lane, bool value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + // self.csr.__dict__[f'qs_{qs}'].__dict__[f'pth_wrap_{pth_wrap}'] + // .__dict__[f'ret_pth_ln{ret_ln}'].mac_phy_rxeqinprogress = 3 + if (value == true) + { + dataByte[0] = 3; // en=1, val=1 + } + else + { + dataByte[0] = 2; // en=1, val=0 + } + + rc = ariesWriteRetimerRegister(device->i2cDriver, side, lane, + ARIES_RET_PTH_LN_MAC_PHY_RXEQINPROGRESS, 1, + dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipePhyStatusClear(AriesDeviceType* device, int side, + int lane) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t adjSide = 0; + + // PhyStatus for pid N (N even) is captured in path N+1 + adjSide = side ? 0 : 1; + + // Write 1 to bits 2:1 to clear these flags + dataByte[0] = 0x6; + rc = ariesWriteRetimerRegister(device->i2cDriver, adjSide, lane, + ARIES_RET_PTH_LN_PHY_MAC_PHYSTATUS_ADDR, 1, + dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipePhyStatusGet(AriesDeviceType* device, int side, + int lane, bool* value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t adjSide = 0; + + // PhyStatus for pid N (N even) is captured in path N+1 + adjSide = side ? 0 : 1; + + rc = ariesReadRetimerRegister(device->i2cDriver, adjSide, lane, + ARIES_RET_PTH_LN_PHY_MAC_PHYSTATUS_ADDR, 1, + dataByte); + CHECK_SUCCESS(rc); + + if ((dataByte[0] & 0x6) == 0) + { + *value = 0; + } + else + { + *value = 1; + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipePhyStatusToggle(AriesDeviceType* device, int side, + int lane) +{ + AriesErrorType rc; + uint8_t count = 0; + bool phyStatus = 0; + + while (count < 100) + { + rc = ariesPipePhyStatusGet(device, side, lane, &phyStatus); + CHECK_SUCCESS(rc); + if (phyStatus == 1) + { + break; + } + count++; + } + + if (phyStatus == 0) + { + ASTERA_ERROR("Side:%d, Lane:%2d, PhyStatus=0", side, lane); + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipePowerdownSet(AriesDeviceType* device, int side, + int lane, int value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t curPowerState; + uint8_t lane_base = lane & 0xfe; // for access to ret_pth_gbl register, use + // a ret_pth_ln offset of 0 + // Put paths into new Pstate if not already + rc = ariesReadRetimerRegister(device->i2cDriver, side, lane_base, + ARIES_RET_PTH_GBL_MAC_PHY_POWERDOWN_ADDR, 1, + dataByte); + CHECK_SUCCESS(rc); + curPowerState = dataByte[0] & 0xf; + if (curPowerState != value) + { + // If we are transitioning P0 -> P1 + if (curPowerState == 0 && value == 2) + { + // Put Transmitters into electrical idle + rc = ariesPipeTxElecIdleSet(device, side, lane, true); + CHECK_SUCCESS(rc); + } + // Clear PhyStatus + rc = ariesPipePhyStatusClear(device, side, lane); + CHECK_SUCCESS(rc); + // Set Powerdown + dataByte[0] = (1 << 4) | (value & 0xf); // bit 4 is en + rc = ariesWriteRetimerRegister(device->i2cDriver, side, lane_base, + ARIES_RET_PTH_GBL_MAC_PHY_POWERDOWN_ADDR, + 1, dataByte); + CHECK_SUCCESS(rc); + // Wait for PhyStatus == 1 + rc = ariesPipePhyStatusToggle(device, side, lane); + CHECK_SUCCESS(rc); + // If we are transitioning P1 -> P0 + if (curPowerState == 2 && value == 0) + { + // Take Transmitters out of electrical idle + rc = ariesPipeTxElecIdleSet(device, side, lane, false); + CHECK_SUCCESS(rc); + } + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipePowerdownCheck(AriesDeviceType* device, int side, + int lane, int value) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = lane / 4; + int qsLane = lane % 4; + + // Read the txX_pstate register + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_TX_ASIC_IN_0, dataWord); + CHECK_SUCCESS(rc); + uint8_t tx_pstate = (dataWord[0] >> 6) & 0x3; // PSTATE is bits 7:6 + uint8_t tx_pstate_expect = 0; + if (value == 0) // P0 + { + tx_pstate_expect = 0; + } + else if (value == 2) // P1 + { + tx_pstate_expect = 2; + } + else + { + ASTERA_ERROR("Side:%d, Lane:%2d, unsupported POWERDOWN value %d", side, + lane, value); + } + // check if value matches expectation + if (tx_pstate != tx_pstate_expect) + { + ASTERA_ERROR( + "Side:%d, Lane:%2d, txX_pstate (%d) does not match expected value (%d)", + side, lane, tx_pstate, tx_pstate_expect); + } + + // Read the rxX_pstate register + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_RX_ASIC_IN_0, dataWord); + CHECK_SUCCESS(rc); + uint8_t rx_pstate = (dataWord[0] >> 5) & 0x3; // PSTATE is bits 6:5 + uint8_t rx_pstate_expect = 0; + if (value == 0) // P0 + { + rx_pstate_expect = 0; + } + else if (value == 2) // P1 + { + rx_pstate_expect = 2; + } + else + { + ASTERA_ERROR("Side:%d, Lane:%2d, unsupported POWERDOWN value %d", side, + lane, value); + } + // check if value matches expectation + if (rx_pstate != rx_pstate_expect) + { + ASTERA_ERROR( + "Side:%d, Lane:%2d, rxX_pstate (%d) does not match expected value (%d)", + side, lane, rx_pstate, rx_pstate_expect); + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeRateChange(AriesDeviceType* device, int side, int lane, + int rate) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + uint8_t pipeRate; + uint8_t curPipeRate; + if ((rate > 5) || (rate < 1)) + { + ASTERA_ERROR("rate argument must be 1, 2, ..., or 5"); + return ARIES_INVALID_ARGUMENT; + } + pipeRate = rate - 1; + // Check if rate needs to be updated + rc = ariesReadRetimerRegister( + device->i2cDriver, side, lane, + ARIES_RET_PTH_GBL_MAC_PHY_RATE_AND_PCLK_RATE_ADDR, 1, dataByte); + CHECK_SUCCESS(rc); + // PIPE rate is 0, 1, .., 4 for Gen1, Gen2, ..., Gen5 + curPipeRate = dataByte[0] & 0x7; + if (curPipeRate != pipeRate) + { + // Put receveiers into standby (both receivers in the path) + rc = ariesPipeRxStandbySet(device, side, lane, true); + CHECK_SUCCESS(rc); + rc = ariesPipeRxStandbySet(device, side, lane + 1, true); + CHECK_SUCCESS(rc); + // Put Transmitters into electrical idle + rc = ariesPipeTxElecIdleSet(device, side, lane, true); + CHECK_SUCCESS(rc); + // Clear PhyStatus + rc = ariesPipePhyStatusClear(device, side, lane); + CHECK_SUCCESS(rc); + // Change rate on the PIPE interface + // Bit 7 and bit 3 are override enable for pclk_rate and rate, + // respectively + dataByte[0] = 0x88 | (pipeRate << 4) | (pipeRate & 0xf); + rc = ariesWriteRetimerRegister( + device->i2cDriver, side, lane, + ARIES_RET_PTH_GBL_MAC_PHY_RATE_AND_PCLK_RATE_ADDR, 1, dataByte); + CHECK_SUCCESS(rc); + // Wait for PhyStatus == 1 + rc = ariesPipePhyStatusToggle(device, side, lane); + CHECK_SUCCESS(rc); + // // Take Receivers out of standby (Now done in test_mode_rx_config) + // rc = ariesPipeRxStandbySet(device, side, lane_base, false); + // CHECK_SUCCESS(rc); + // rc = ariesPipeRxStandbySet(device, side, lane_base + 1, false); + // CHECK_SUCCESS(rc); + // // Take Transmitters out of electrical idle + // rc = ariesPipeTxElecIdleSet(device, side, lane, false); + // CHECK_SUCCESS(rc); + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeRateCheck(AriesDeviceType* device, int side, int lane, + int rate) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = lane / 4; + int qsLane = lane % 4; + if ((rate > 5) || (rate < 1)) + { + ASTERA_ERROR("rate argument must be 1, 2, ..., or 5"); + return ARIES_INVALID_ARGUMENT; + } + + // Read the txX_rate register + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_TX_ASIC_IN_0, dataWord); + CHECK_SUCCESS(rc); + uint8_t tx_rate = dataWord[1] & 0x7; // RATE is bits 10:8 + uint8_t tx_rate_expect = 0; + if (rate == 1) // Gen1 + { + tx_rate_expect = 3; + } + else if (rate == 2) // Gen2 + { + tx_rate_expect = 2; + } + else if (rate == 3) // Gen3 + { + tx_rate_expect = 2; + } + else if (rate == 4) // Gen4 + { + tx_rate_expect = 1; + } + else if (rate == 5) // Gen5 + { + tx_rate_expect = 0; + } + // check if value matches expectation + if (tx_rate != tx_rate_expect) + { + ASTERA_ERROR( + "Side:%d, Lane:%2d, txX_rate (%d) does not match expected value (%d) for Gen%d", + side, lane, tx_rate, tx_rate_expect, rate); + } + + // Read the rxX_rate register + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_RX_ASIC_IN_0, dataWord); + CHECK_SUCCESS(rc); + uint8_t rx_rate = (dataWord[0] & 0x80) >> 7; + rx_rate |= (dataWord[1] & 0x3) << 1; // RATE is bits 9:7 + uint8_t rx_rate_expect = 0; + if (rate == 1) // Gen1 + { + rx_rate_expect = 3; + } + else if (rate == 2) // Gen2 + { + rx_rate_expect = 2; + } + else if (rate == 3) // Gen3 + { + rx_rate_expect = 1; + } + else if (rate == 4) // Gen4 + { + rx_rate_expect = 0; + } + else if (rate == 5) // Gen5 + { + rx_rate_expect = 0; + } + // check if value matches expectation + if (rx_rate != rx_rate_expect) + { + if (rate != 3) // Suppress this check for Gen3 + { + ASTERA_ERROR( + "Side:%d, Lane:%2d, rxX_rate (%d) does not match expected value (%d) for Gen%d", + side, lane, rx_rate, rx_rate_expect, rate); + } + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeDeepmhasisSet(AriesDeviceType* device, int side, + int lane, int de, int preset, int pre, + int main, int pst) +{ + AriesErrorType rc; + uint8_t dataByte3[3] = {0}; + uint32_t deemph; + if (de != ARIES_PIPE_DEEMPHASIS_DE_NONE) + { + deemph = de; + // ASTERA_INFO("Side:%d, Lane:%2d, Setting de-emphasis value to 0x%x + // for Gen1/2", side, lane, deemph); + } + else if (preset != ARIES_PIPE_DEEMPHASIS_PRESET_NONE) + { + switch (preset) + { + case 4: + deemph = (48 << 6) | (0 << 0) | (0 << 12); + break; + case 1: + deemph = (40 << 6) | (0 << 0) | (8 << 12); + break; + case 0: + deemph = (36 << 6) | (0 << 0) | (12 << 12); + break; + case 9: + deemph = (40 << 6) | (8 << 0) | (0 << 12); + break; + case 8: + deemph = (36 << 6) | (6 << 0) | (6 << 12); + break; + case 7: + deemph = (34 << 6) | (5 << 0) | (9 << 12); + break; + case 5: + deemph = (44 << 6) | (4 << 0) | (0 << 12); + break; + case 6: + deemph = (42 << 6) | (6 << 0) | (0 << 12); + break; + case 3: + deemph = (42 << 6) | (0 << 0) | (6 << 12); + break; + case 2: + deemph = (38 << 6) | (0 << 0) | (10 << 12); + break; + case 10: + deemph = (32 << 6) | (0 << 0) | (16 << 12); + break; + default: + deemph = (44 << 6) | (4 << 0) | (0 << 12); + } + // ASTERA_INFO("Side:%d, Lane:%2d, Setting de-emphasis value to 0x%x + // (Preset %d) for Gen3/4/5", side, lane, deemph, preset); + } + else + { + deemph = (main << 6) | (pre << 0) | (pst << 12); + // ASTERA_INFO("Side:%d, Lane:%2d, Setting de-emphasis value to 0x%x + // (custom)", side, lane, deemph); + } + // self.csr.__dict__['qs_' + str(qs)].__dict__['pth_wrap_' + + // str(pth_wrap)].__dict__['ret_pth_ln'+str(ret_ln)].mac_phy_txdeemph = ((1 + // << 18) | deemph) # en is bit 18 + dataByte3[2] = (1 << 2) | ((deemph >> 16) & 0xff); + dataByte3[1] = ((deemph >> 8) & 0xff); + dataByte3[0] = (deemph & 0xff); + rc = ariesWriteRetimerRegister(device->i2cDriver, side, lane, + ARIES_RET_PTH_LN_MAC_PHY_TXDEEMPH_ADDR, 3, + dataByte3); + CHECK_SUCCESS(rc); + + // Read back + // rc = ariesReadRetimerRegister(device->i2cDriver, side, lane, + // ARIES_RET_PTH_LN_MAC_PHY_TXDEEMPH_OB_ADDR, 3, dataByte3); + // CHECK_SUCCESS(rc); + // uint32_t value = (dataByte3[2]<<16) | (dataByte3[1]<<8) | (dataByte3[0]); + // ASTERA_INFO("Side:%d, Lane:%2d, de-emphasis read-back: 0x%x", side, + // lane, value); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeRxPolaritySet(AriesDeviceType* device, int side, + int lane, int value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + // self.csr.__dict__['qs_' + str(qs)].__dict__['pth_wrap_' + + // str(pth_wrap)].__dict__['ret_pth_ln' + str(ret_ln)].mac_phy_rxpolarity = + // (1 << 1) | polarity # en is bit 1 + dataByte[0] = (1 << 1) | (value & 1); // bit 1 is enable + rc = ariesWriteRetimerRegister(device->i2cDriver, side, lane, + ARIES_RET_PTH_LN_MAC_PHY_RXPOLARITY_ADDR, 1, + dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeRxTermSet(AriesDeviceType* device, int side, int lane, + bool value) +{ + AriesErrorType rc; + uint8_t dataByte[1] = {0}; + // self.csr.__dict__['qs_' + str(qs)].__dict__['pth_wrap_' + + // str(pth_wrap)].__dict__['ret_pth_ln' + str(ret_ln)].pcs_rx_termination = + // rx_termination + dataByte[0] = value ? 1 : 0; + rc = ariesWriteRetimerRegister(device->i2cDriver, side, lane, + ARIES_RET_PTH_LN_PCS_RX_TERMINATION_ADDR, 1, + dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPipeBlkAlgnCtrlSet(AriesDeviceType* device, int side, + int lane, bool value, bool enable) +{ + AriesErrorType rc; + uint8_t lane_base = lane & 0xfe; // for access to ret_pth_gbl register, use + // a ret_pth_ln offset of 0 + uint8_t dataByte[1] = {0}; + // self.csr.__dict__['qs_' + str(qs)].__dict__['pth_wrap_' + + // str(pth_wrap)].ret_pth_gbl.mac_phy_blockaligncontrol = (1 << 1) | + // blockaligncontrol # en is bit 1 + dataByte[0] = (enable << 1) | value; // bit 1 is enable + rc = ariesWriteRetimerRegister( + device->i2cDriver, side, lane_base, + ARIES_RET_PTH_GBL_MAC_PHY_BLOCKALIGNCONTROL_ADDR, 1, dataByte); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPMABertPatChkSts(AriesDeviceType* device, int side, + int lane, int* ecount) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = 0; + int qsLane = 0; + int ecountVal = 0; + + qs = lane / 4; + qsLane = lane % 4; + // Double-read required + rc = ariesReadWordPmaLaneMainMicroIndirect(device->i2cDriver, side, qs, + qsLane, 0x108d, dataWord); + CHECK_SUCCESS(rc); + rc = ariesReadWordPmaLaneMainMicroIndirect(device->i2cDriver, side, qs, + qsLane, 0x108d, dataWord); + CHECK_SUCCESS(rc); + + ecountVal = (dataWord[1] << 8) | dataWord[0]; + if (ecountVal >= 32768) + { + ecountVal = ecountVal >> 1; + if (ecountVal == (32768 - 1)) + { + ASTERA_INFO("Side:%d, Lane:%2d, Error Count saturated", side, lane); + } + ecountVal = ecountVal * 128; + } + ecount[0] = ecountVal; + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPMABertPatChkToggleSync(AriesDeviceType* device, int side, + int lane) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = 0; + int qsLane = 0; + + qs = lane / 4; + qsLane = lane % 4; + // bert_pat_chk_toggle_sync(ipid, lane=pma_ln) + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, ARIES_PMA_LANE_DIG_RX_LBERT_CTL, + dataWord); + CHECK_SUCCESS(rc); + // ipid.__dict__['LANE'+lane_str+'_DIG_RX_LBERT_CTL'].SYNC = 1 + dataWord[0] |= (1 << 4); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, ARIES_PMA_LANE_DIG_RX_LBERT_CTL, + dataWord); + CHECK_SUCCESS(rc); + // ipid.__dict__['LANE'+lane_str+'_DIG_RX_LBERT_CTL'].SYNC = 0 + dataWord[0] &= ~(1 << 4); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, ARIES_PMA_LANE_DIG_RX_LBERT_CTL, + dataWord); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPMABertPatChkDetectCorrectPolarity(AriesDeviceType* device, + int side, int lane) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = 0; + int qsLane = 0; + int ecount[1]; + int invert_ovrd_en = 0; + int invert = 0; + int cur_polarity = 0; + int new_polarity = 0; + + qs = lane / 4; + qsLane = lane % 4; + + // Read the error counter + rc = ariesPMABertPatChkSts(device, side, lane, ecount); + CHECK_SUCCESS(rc); + + if (ecount[0] == 4194176) + { + ASTERA_INFO("Side:%d, Lane:%2d, Invert polarity", side, lane); + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_RX_OVRD_IN_0, dataWord); + CHECK_SUCCESS(rc); + invert_ovrd_en = (dataWord[0] >> 3) & 0x1; + invert = (dataWord[0] >> 2) & 0x1; + if (invert_ovrd_en) + { + cur_polarity = invert; + } + else + { + cur_polarity = 0; + } + new_polarity = 1 - cur_polarity; + // Set new polarity + rc = ariesPMARxInvertSet(device, side, lane, new_polarity, 1); + CHECK_SUCCESS(rc); + // CLear error counter + rc = ariesPMABertPatChkToggleSync(device, side, lane); + CHECK_SUCCESS(rc); + rc = ariesPMABertPatChkToggleSync(device, side, lane); + CHECK_SUCCESS(rc); + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPMARxInvertSet(AriesDeviceType* device, int side, int lane, + bool invert, bool override) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = 0; + int qsLane = 0; + + qs = lane / 4; + qsLane = lane % 4; + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_RX_OVRD_IN_0, dataWord); + CHECK_SUCCESS(rc); + if (invert) + { + dataWord[0] |= (1 << 2); + } + else + { + dataWord[0] &= ~(1 << 2); + } + if (override) + { + dataWord[0] |= (1 << 3); + } + else + { + dataWord[0] &= ~(1 << 3); + } + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_RX_OVRD_IN_0, dataWord); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPMABertPatChkConfig(AriesDeviceType* device, int side, + int lane, AriesPRBSPatternType mode) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0, 0}; + int qs = 0; + int qsLane = 0; + + qs = lane / 4; + qsLane = lane % 4; + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, ARIES_PMA_LANE_DIG_RX_LBERT_CTL, + dataWord); + CHECK_SUCCESS(rc); + + dataWord[0] &= ~(0xF); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, ARIES_PMA_LANE_DIG_RX_LBERT_CTL, + dataWord); + CHECK_SUCCESS(rc); + + dataWord[0] |= (mode & 0xF); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, ARIES_PMA_LANE_DIG_RX_LBERT_CTL, + dataWord); + CHECK_SUCCESS(rc); + + int i = 0; + for (i = 0; i < 100; i++) + { + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_RX_LBERT_CTL, dataWord); + CHECK_SUCCESS(rc); + if ((dataWord[0] & 0xF) != mode) + { + ASTERA_WARN( + "Unable to set pattern check mode to %d on lane %d, retrying", + mode, lane); + CHECK_SUCCESS(rc); + dataWord[0] &= ~(0xF); + dataWord[0] |= (mode & 0xF); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_RX_LBERT_CTL, dataWord); + CHECK_SUCCESS(rc); + } + else + { + break; + } + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPMABertPatGenConfig(AriesDeviceType* device, int side, + int lane, AriesPRBSPatternType mode) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = 0; + int qsLane = 0; + + qs = lane / 4; + qsLane = lane % 4; + // ASTERA_INFO("Side:%d, Lane:%2d, PRBS generator mode: %d", side, lane, + // mode); + + // As per datasheet Table 11-340: when changing modes, you must change to + // disabled (0) first + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, ARIES_PMA_LANE_DIG_TX_LBERT_CTL, + dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~(0xF); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, ARIES_PMA_LANE_DIG_TX_LBERT_CTL, + dataWord); + CHECK_SUCCESS(rc); + dataWord[0] |= (mode & 0xF); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, ARIES_PMA_LANE_DIG_TX_LBERT_CTL, + dataWord); + CHECK_SUCCESS(rc); + int i = 0; + for (i = 0; i < 20; i++) + { + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_TX_LBERT_CTL, dataWord); + CHECK_SUCCESS(rc); + if ((dataWord[0] & 0xF) != (mode & 0xF)) + { + ASTERA_WARN( + "Unable to set pattern check mode to %d on lane %d, retrying", + mode & 0xF, lane); + dataWord[0] &= ~(0xF); + dataWord[0] |= (mode & 0xF); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_TX_LBERT_CTL, dataWord); + CHECK_SUCCESS(rc); + } + } + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPMARxDataEnSet(AriesDeviceType* device, int side, int lane, + bool value) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = 0; + int qsLane = 0; + + qs = lane / 4; + qsLane = lane % 4; + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_RX_OVRD_IN_0, dataWord); + CHECK_SUCCESS(rc); + if (value == false) + { + dataWord[0] &= ~(1 << 4); + dataWord[0] &= ~(1 << 5); + } + else + { + dataWord[0] |= (1 << 4); + dataWord[0] |= (1 << 5); + } + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_RX_OVRD_IN_0, dataWord); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPMATxDataEnSet(AriesDeviceType* device, int side, int lane, + bool enable) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = 0; + int qsLane = 0; + + qs = lane / 4; + qsLane = lane % 4; + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_TX_OVRD_IN_0, dataWord); + CHECK_SUCCESS(rc); + if (enable == false) + { + dataWord[0] &= ~(1 << 6); // DATA_EN_OVRD_VAL = 0 + dataWord[0] &= ~(1 << 7); // DATA_EN_OVRD_EN = 0 + } + else + { + dataWord[0] |= (1 << 6); // DATA_EN_OVRD_VAL = 1 + dataWord[0] |= (1 << 7); // DATA_EN_OVRD_EN = 1 + } + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_DIG_ASIC_TX_OVRD_IN_0, dataWord); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPMAPCSRxRecalBankOvrdSet(AriesDeviceType* device, int side, + int lane, bool enable) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = lane / 4; + int qsLane = lane % 4; + + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_RECAL_BANK_OVRD, dataWord); + CHECK_SUCCESS(rc); + if (!enable) // Gen-5 + { + dataWord[0] |= (1 << 1); + dataWord[0] &= ~(1 << 0); + } + else // Gen-3/4 + { + dataWord[0] |= (1 << 1); + dataWord[0] |= (1 << 0); + } + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_RECAL_BANK_OVRD, dataWord); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} +AriesErrorType ariesPMAPCSRxReqBlock(AriesDeviceType* device, int side, + int lane, bool enable) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = 0; + int qsLane = 0; + + qs = lane / 4; + qsLane = lane % 4; + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_ATE_OVRD_IN, dataWord); + CHECK_SUCCESS(rc); + if (enable) + { + dataWord[0] |= (1 << 5); + } + else + { + dataWord[0] &= ~(1 << 5); + } + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_RAWLANE_DIG_PCS_XF_ATE_OVRD_IN, dataWord); + CHECK_SUCCESS(rc); + + return ARIES_SUCCESS; +} + +AriesErrorType ariesPMAVregVrefSet(AriesDeviceType* device, int side, int lane, + int rate) +{ + AriesErrorType rc; + uint8_t dataWord[2] = {0}; + int qs = 0; + int qsLane = 0; + + qs = lane / 4; + qsLane = lane % 4; + + // for A0 devices changing between G1-2 to G3-5 + if (device->revNumber == 1) + { + if (rate <= 2) + { + // Vreg + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_ANA_RX_VREG_CTRL2, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] |= (1 << 7); + dataWord[0] &= ~(1 << 6); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_ANA_RX_VREG_CTRL2, dataWord); + CHECK_SUCCESS(rc); + + // Vref + // There is one control bit per PMA instance + if ((qsLane % 4) == 0) + { + rc = ariesReadWordPmaMainMicroIndirect( + device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_SWITCH_MISC_MEAS, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~(1 << 5); + dataWord[0] &= ~(1 << 4); + rc = ariesWriteWordPmaMainMicroIndirect( + device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_SWITCH_MISC_MEAS, dataWord); + CHECK_SUCCESS(rc); + } + } + else + { + // Vreg + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_ANA_RX_VREG_CTRL2, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~(1 << 7); + dataWord[0] |= (1 << 6); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_ANA_RX_VREG_CTRL2, dataWord); + CHECK_SUCCESS(rc); + + // Vref + // There is one control bit per PMA instance + if ((qsLane % 4) == 0) + { + rc = ariesReadWordPmaMainMicroIndirect( + device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_SWITCH_MISC_MEAS, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] |= (1 << 5); + dataWord[0] &= ~(1 << 4); + rc = ariesWriteWordPmaMainMicroIndirect( + device->i2cDriver, side, qs, + ARIES_PMA_SUP_ANA_SWITCH_MISC_MEAS, dataWord); + CHECK_SUCCESS(rc); + } + } + } + // for B0 devices changing between G1-3 to G4-5 + else + { + if (rate <= 3) + { + // Vreg + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_ANA_RX_VREG_CTRL2, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~(1 << 5); + dataWord[0] &= ~(1 << 4); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_ANA_RX_VREG_CTRL2, dataWord); + CHECK_SUCCESS(rc) + } + else + { + // Vreg + rc = ariesReadWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_ANA_RX_VREG_CTRL2, dataWord); + CHECK_SUCCESS(rc); + dataWord[0] &= ~(1 << 5); + dataWord[0] |= (1 << 4); + rc = ariesWriteWordPmaLaneMainMicroIndirect( + device->i2cDriver, side, qs, qsLane, + ARIES_PMA_LANE_ANA_RX_VREG_CTRL2, dataWord); + CHECK_SUCCESS(rc) + } + } + + return ARIES_SUCCESS; +} + +/** + * @brief Read multiple data bytes from Aries over I2C. Purposely force an error + * in the transaction by injecting an intervening I2C transaction in the middle + * between the Write and Read. + */ +AriesErrorType ariesReadBlockDataForceError(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values) +{ + AriesErrorType rc; + AriesErrorType lc; + int readBytes; + + uint8_t writeBuf[3] = {0}; // Max of (2):Intel and (3):Astera + uint8_t readBuf[16] = {0}; // Max of (2+4):Intel and (16):Astera + + uint8_t wrCmdCode; + uint8_t rdCmdCode; + uint8_t pecEn; + uint8_t rsvd; + uint8_t funcCode; + uint8_t start; + uint8_t end; + + uint8_t addr15To8; // Get bits 15:8 + uint8_t addr7To0; // Get bits 7:0 + + uint8_t tryIndex; + uint8_t readTryCount = 3; + + uint8_t currBytes = 0; + uint8_t remainingBytes = numBytes; + if (i2cDriver->i2cFormat == ARIES_I2C_FORMAT_INTEL) + { + // Addresses in this format can be 16 bit only + if (address > 0xffff) + { + ASTERA_ERROR("Address cannot be more than 16 bits in Intel format"); + return ARIES_INVALID_ARGUMENT; + } + + // If byte count is greater than 4, perform multiple iterations + while (remainingBytes > 0) + { + if (remainingBytes > 4) + { + currBytes = 4; + remainingBytes -= 4; + } + else + { + currBytes = remainingBytes; + remainingBytes = 0; + } + + pecEn = 0; + rsvd = 0; + funcCode = 0; + start = 1; + end = 0; + + if (i2cDriver->pecEnable == ARIES_I2C_PEC_ENABLE) + { + pecEn = 1; + } + + ASTERA_TRACE("Reading from address: 0x%08x", address); + + // Construct command code + wrCmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 0); + + addr15To8 = (address & 0xff00) >> 8; + addr7To0 = address & 0xff; + + // Construct data buffer + writeBuf[0] = addr7To0; + writeBuf[1] = addr15To8; + + ASTERA_TRACE("Write:"); + ASTERA_TRACE(" cmdCode = 0x%02x", wrCmdCode); + ASTERA_TRACE(" byteCount = 0x02"); + ASTERA_TRACE(" writeBuf[0] = 0x%02x", writeBuf[0]); + ASTERA_TRACE(" writeBuf[1] = 0x%02x", writeBuf[1]); + + // Set up I2C lock, to keep write and read atomic + // Do not unblock here on return of error since this error code + // would mean that the block call did not happen + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // Perform read operation + // Try upto 3 times before issuing a block read failure + for (tryIndex = 0; tryIndex < readTryCount; tryIndex++) + { + rc = asteraI2CWriteBlockData(i2cDriver->handle, wrCmdCode, 2, + writeBuf); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Inject an intervening transaction + rc = asteraI2CWriteBlockData(i2cDriver->handle, 0, 0, writeBuf); + if (rc != ARIES_SUCCESS) + { + ASTERA_WARN("Received expected non-zero return code " + "during invalid transaction"); + } + + start = 0; + end = 1; + funcCode = 0; + + rdCmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 0); + + ASTERA_TRACE("Read:"); + ASTERA_TRACE(" cmdCode = 0x%02x", rdCmdCode); + + readBytes = asteraI2CReadBlockData(i2cDriver->handle, rdCmdCode, + currBytes, readBuf); + if (readBytes != currBytes) + { + ASTERA_TRACE("Expected %d bytes but got %d", currBytes, + readBytes); + ASTERA_TRACE("Perform read again..."); + + if (tryIndex == (readTryCount - 1)) + { + ASTERA_ERROR("Incorrect num. bytes returned by read"); + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_I2C_BLOCK_READ_FAILURE; + } + } + else + { + break; + } + } + + // Unlock previous lock set before write + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + // Print the values + ASTERA_TRACE("Values read:"); + int pos; + for (pos = 0; pos < currBytes; pos++) + { + values[pos] = readBuf[2 + pos]; + ASTERA_TRACE(" readBuf[%d] = 0x%02x", pos, values[pos]); + } + // Increment iteration count + values += currBytes; + address += currBytes; + } + } + else if (i2cDriver->i2cFormat == ARIES_I2C_FORMAT_ASTERA) + { + // If byte count is greater than 16, perform multiple iterations + while (remainingBytes > 0) + { + if (remainingBytes > 16) + { + currBytes = 16; + remainingBytes -= 16; + } + else + { + currBytes = remainingBytes; + remainingBytes = 0; + } + + pecEn = 0; + rsvd = 0; + funcCode = 2; + start = 1; + end = 0; + + if (i2cDriver->pecEnable == ARIES_I2C_PEC_ENABLE) + { + pecEn = 1; + } + + ASTERA_TRACE("Reading from address: 0x%08x", address); + + // Construct command code + wrCmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 0); + + // Construct Config & Offset Upper byte + uint8_t config; + uint8_t addr16; + addr16 = (address & 0x10000) >> 16; // Get 16bit of addr + addr15To8 = (address & 0xff00) >> 8; // Get bits 15:8 + addr7To0 = address & 0xff; // Get bits 7:0 + + uint8_t cfg_type = 0; + uint8_t bdcst = 0; + uint8_t burstLen = currBytes - 1; + + config = (cfg_type << 6) + (bdcst << 4) + (burstLen << 1) + + (addr16 << 0); + + // Construct data buffer + writeBuf[0] = config; + writeBuf[1] = addr15To8; + writeBuf[2] = addr7To0; + + ASTERA_TRACE("Write:"); + ASTERA_TRACE(" cmdCode = 0x%02x", wrCmdCode); + ASTERA_TRACE(" byteCount = 0x03"); + ASTERA_TRACE(" writeBuf[0] = 0x%02x", writeBuf[0]); + ASTERA_TRACE(" writeBuf[1] = 0x%02x", writeBuf[1]); + ASTERA_TRACE(" writeBuf[2] = 0x%02x", writeBuf[2]); + + // Set up I2C lock, to keep write and read atomic + // Do not unblock here on return of error since this error code + // would mean that the block call did not happen + lc = ariesLock(i2cDriver); + CHECK_SUCCESS(lc); + + // Perform read operation + // Try upto 3 times before issuing a block read failure + for (tryIndex = 0; tryIndex < readTryCount; tryIndex++) + { + // First write address you wish to read from + rc = asteraI2CWriteBlockData(i2cDriver->handle, wrCmdCode, 3, + writeBuf); + if (rc != ARIES_SUCCESS) + { + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return rc; + } + + // Inject an intervening transaction + rc = asteraI2CWriteBlockData(i2cDriver->handle, 0, 0, writeBuf); + if (rc != ARIES_SUCCESS) + { + ASTERA_WARN("Received expected non-zero return code " + "during invalid transaction"); + } + + funcCode = 2; + start = 0; + end = 1; + rdCmdCode = (pecEn << 7) + (rsvd << 5) + (funcCode << 2) + + (start << 1) + (end << 0); + + ASTERA_TRACE("Read:"); + ASTERA_TRACE(" cmdCode = 0x%02x", rdCmdCode); + + // Then issue read + // First byte returned is length + readBytes = asteraI2CReadBlockData(i2cDriver->handle, rdCmdCode, + currBytes, readBuf); +#ifdef SMBUS_BLOCK_READ_UNSUPPORTED + readBytes -= 1; + int i; + for (i = 0; i < readBytes; i++) + { + readBuf[i] = readBuf[i + 1]; + } +#endif + if (readBytes != currBytes) + { + ASTERA_TRACE("Expected %d bytes but got %d", currBytes, + readBytes); + ASTERA_TRACE("Perform read again..."); + + if (tryIndex == (readTryCount - 1)) + { + ASTERA_ERROR("Incorrect num. bytes returned by read"); + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + return ARIES_I2C_BLOCK_READ_FAILURE; + } + } + else + { + break; + } + } + + // Unlock previous lock set before write + lc = ariesUnlock(i2cDriver); + CHECK_SUCCESS(lc); + + // Print the values + ASTERA_TRACE("Values read:"); + int pos; + for (pos = 0; pos < currBytes; pos++) + { + values[pos] = readBuf[pos]; + ASTERA_TRACE(" readBuf[%d] = 0x%02x", pos, values[pos]); + } + values += currBytes; + address += currBytes; + } + } + else if (i2cDriver->i2cFormat == ARIES_I2C_FORMAT_CMBND) + { + return ariesWriteReadBlockData(i2cDriver, address, numBytes, values); + } + else + { + return ARIES_INVALID_ARGUMENT; + } + return ARIES_SUCCESS; +} + +/** + * @brief Convert the PMA ADC code to degrees Celsius + * + * @param[in] device Aries Device struct + * @param[in] adcCode Temperature ACD code from PMA + * @param[in] calCode PMA calibration code + * @return temperature_C Temperature in Degrees Celsius + */ +float ariesTsenseADCToDegrees(AriesDeviceType* device, int adcCode, + uint8_t calCode) +{ + float temperature_C; + + temperature_C = device->tempCalCodeRefTempC + + ((adcCode - (calCode + 250)) * -0.32); + + return temperature_C; +} + +#ifdef __cplusplus +} +#endif diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_misc.h b/common/recipes-lib/retimer-v2.16.2/files/aries_misc.h new file mode 100755 index 000000000000..700fb033b9bc --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_misc.h @@ -0,0 +1,399 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_misc.h + * @brief Definition of helper functions used by aries SDK. + */ +#pragma once +#ifndef ASTERA_ARIES_SDK_MISC_H_ +#define ASTERA_ARIES_SDK_MISC_H_ + +#include "aries_globals.h" +#include "aries_error.h" +#include "aries_api_types.h" +#include "astera_log.h" +#include "aries_api.h" +#include "aries_i2c.h" + +#ifdef ARIES_MPW +#include "aries_mpw_reg_defines.h" +#else +#include "aries_a0_reg_defines.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARIES_MAIN_MICRO_EXT_CSR_I2C_MST_INIT_CTRL_BIT_BANG_MODE_EN_GET(x) \ + (((x)&0x04) >> 2) +#define ARIES_MAIN_MICRO_EXT_CSR_I2C_MST_INIT_CTRL_BIT_BANG_MODE_EN_SET(x) \ + (((x) << 2) & 0x04) +#define ARIES_MAIN_MICRO_EXT_CSR_I2C_MST_INIT_CTRL_BIT_BANG_MODE_EN_MODIFY(r, \ + x) \ + ((((x) << 2) & 0x04) | ((r)&0xfb)) + +/* Bifurcation modes lookup */ +extern AriesBifurcationParamsType bifurcationModes[36]; + +AriesErrorType ariesReadFwVersion(AriesI2CDriverType* i2cDriver, int offset, + uint8_t* dataVal); + +int ariesFirmwareIsAtLeast(AriesDeviceType* device, uint8_t major, + uint8_t minor, uint16_t build); + +AriesErrorType ariesI2CMasterSetPage(AriesI2CDriverType* i2cDriver, int page); + +AriesErrorType ariesI2CMasterSendByteBlockData(AriesI2CDriverType* i2cDriver, + int address, int numBytes, + uint8_t* value); + +AriesErrorType ariesI2CMasterWriteCtrlReg(AriesI2CDriverType* i2cDriver, + uint32_t address, + uint8_t lengthDataBytes, + uint8_t* values); + +AriesErrorType ariesI2CMasterInit(AriesI2CDriverType* i2cDriver); + +AriesErrorType ariesI2CMasterSendByte(AriesI2CDriverType* i2cDriver, + uint8_t* value, int flag); + +int ariesGetEEPROMImageEnd(uint8_t* data); + +AriesErrorType ariesI2CMasterMultiBlockWrite(AriesDeviceType* device, + uint16_t address, int numBytes, + uint8_t* values); + +AriesErrorType ariesI2CMasterRewriteAndVerifyByte(AriesI2CDriverType* i2cDriver, + int address, uint8_t* value); + +AriesErrorType ariesI2CMasterSendAddress(AriesI2CDriverType* i2cDriver, + int address); + +AriesErrorType ariesI2CMasterReceiveByteBlock(AriesDeviceType* device, + uint8_t* value); + +AriesErrorType ariesI2CMasterGetChecksum(AriesDeviceType* device, + uint16_t blockEnd, uint32_t* checksum); + +AriesErrorType ariesI2CMasterSetFrequency(AriesI2CDriverType* i2cDriver, + int frequencyHz); + +AriesErrorType ariesI2CMasterReceiveByte(AriesI2CDriverType* i2cDriver, + uint8_t* value); + +AriesErrorType + ariesI2CMasterReceiveContinuousByte(AriesI2CDriverType* i2cDriver, + uint8_t* value); + +AriesErrorType ariesGetTempCalibrationCodes(AriesDeviceType* device); + +AriesErrorType ariesReadPmaTempMax(AriesDeviceType* device); + +AriesErrorType ariesReadPmaAvgTemp(AriesDeviceType* device); + +AriesErrorType ariesReadPmaAvgTempDirect(AriesDeviceType* device); + +AriesErrorType ariesEnableThermalShutdown(AriesDeviceType* device); + +AriesErrorType ariesDisableThermalShutdown(AriesDeviceType* device); + +AriesErrorType ariesReadPmaTemp(AriesDeviceType* device, int side, int qs, + float* temperature_C); + +AriesErrorType ariesGetPortOrientation(AriesDeviceType* device, + int* orientation); + +int ariesGetPmaNumber(int absLane); + +int ariesGetPmaLane(int absLane); + +int ariesGetPathID(int lane, int direction); + +int ariesGetPathLaneID(int lane); + +void ariesGetQSPathInfo(int lane, int direction, int* qs, int* qsPath, + int* qsPathLane); + +int ariesGetStartLane(AriesLinkType* link); + +AriesErrorType ariesDumpPMARegs(AriesDeviceType* device, uint16_t* pma_addr, + int len, char* filename); + +AriesErrorType ariesGetLinkRxTerm(AriesLinkType* link, int side, int lane, + int* term); + +AriesErrorType ariesGetLinkId(AriesBifurcationType bifMode, int lane, + int* linkNum); + +AriesErrorType ariesGetLinkCurrentSpeed(AriesLinkType* link, int lane, + int direction, float* speed); + +AriesErrorType ariesGetLaneNum(AriesLinkType* link, int lane, int* laneNum); + +AriesErrorType ariesGetLogicalLaneNum(AriesLinkType* link, int lane, + int direction, int* laneNum); + +AriesErrorType ariesGetTxPre(AriesLinkType* link, int lane, int direction, + int* txPre); + +AriesErrorType ariesGetTxCur(AriesLinkType* link, int lane, int direction, + int* txCur); + +AriesErrorType ariesGetTxPst(AriesLinkType* link, int lane, int direction, + int* txPst); + +AriesErrorType ariesGetRxPolarityCode(AriesLinkType* link, int lane, + int direction, int pinSet, int* polarity); + +AriesErrorType ariesGetRxAttCode(AriesLinkType* link, int side, int absLane, + int* code); + +AriesErrorType ariesGetRxCtleBoostCode(AriesLinkType* link, int side, + int absLane, int* boostCode); + +AriesErrorType ariesGetRxVgaCode(AriesLinkType* link, int side, int absLane, + int* vgaCode); + +float ariesGetRxBoostValueDb(int boostCode, float attValDb, int vgaCode); + +AriesErrorType ariesGetRxCtlePoleCode(AriesLinkType* link, int side, + int absLane, int* poleCode); + +AriesErrorType ariesGetRxAdaptIq(AriesLinkType* link, int side, int absLane, + int* iqValue); + +AriesErrorType ariesGetRxAdaptIqBank(AriesLinkType* link, int side, int absLane, + int bank, int* iqValue); + +AriesErrorType ariesGetRxAdaptDoneBank(AriesLinkType* link, int side, + int absLane, int bank, int* doneValue); + +AriesErrorType ariesGetRxDfeCode(AriesLinkType* link, int side, int absLane, + int tapNum, int* dfeCode); + +AriesErrorType ariesGetLastEqSpeed(AriesLinkType* link, int lane, int direction, + int* speed); + +AriesErrorType ariesGetDeskewStatus(AriesLinkType* link, int lane, + int direction, int* status); + +AriesErrorType ariesGetDeskewClks(AriesLinkType* link, int lane, int direction, + int* val); + +AriesErrorType ariesGetLastEqReqPre(AriesLinkType* link, int lane, + int direction, int* val); + +AriesErrorType ariesGetLastEqReqCur(AriesLinkType* link, int lane, + int direction, int* val); + +AriesErrorType ariesGetLastEqReqPst(AriesLinkType* link, int lane, + int direction, int* val); + +AriesErrorType ariesGetLastEqReqPreset(AriesLinkType* link, int lane, + int direction, int* val); + +AriesErrorType ariesGetLastEqPresetReq(AriesLinkType* link, int lane, + int direction, int reqNum, int* val); + +AriesErrorType ariesGetLastEqPresetReq(AriesLinkType* link, int lane, + int direction, int reqNum, int* val); + +AriesErrorType ariesGetLastEqPresetReqFOM(AriesLinkType* link, int lane, + int direction, int reqNum, int* val); + +AriesErrorType ariesGetLastEqNumPresetReq(AriesLinkType* link, int lane, + int direction, int* val); + +AriesErrorType ariesGetLoggerFmtID(AriesLinkType* link, + AriesLTSSMLoggerEnumType loggerType, + int offset, int* fmtID); + +AriesErrorType ariesGetLoggerWriteOffset(AriesLinkType* link, + AriesLTSSMLoggerEnumType loggerType, + int* writeOffset); + +AriesErrorType ariesGetLoggerOneBatchModeEn(AriesLinkType* link, + AriesLTSSMLoggerEnumType loggerType, + int* oneBatchModeEn); + +AriesErrorType ariesGetLoggerOneBatchWrEn(AriesLinkType* link, + AriesLTSSMLoggerEnumType loggerType, + int* oneBatchWrEn); + +uint8_t ariesGetPecByte(uint8_t* polynomial, uint8_t length); + +AriesErrorType ariesGetMinFoMVal(AriesDeviceType* device, int side, int pathID, + int lane, int regOffset, uint8_t* data); + +AriesErrorType ariesGetPinMap(AriesDeviceType* device); + +// Read numBytes bytes of data starting at startAddr +AriesErrorType ariesEepromReadBlockData(AriesDeviceType* device, + uint8_t* values, int startAddr, + uint8_t numBytes); + +// Read numBytes bytes from the EEPROM starting at startAddr and +// calculate a running checksum (e.g. add the bytes as you read them): +// uint8_t checksum = (checksum + new_byte) % 256 +AriesErrorType ariesEepromCalcChecksum(AriesDeviceType* device, int startAddr, + int numBytes, uint8_t* checksum); + +void ariesSortArray(uint16_t* arr, int size); + +uint16_t ariesGetMedian(uint16_t* arr, int size); + +AriesErrorType ariesLoadBinFile(const char* filename, uint8_t* mem); + +AriesErrorType ariesLoadIhxFile(const char* filename, uint8_t* mem); + +AriesErrorType ariesParseIhxLine(char* line, uint8_t* bytes, int* addr, + int* num, int* code); + +AriesErrorType ariesI2cMasterSoftReset(AriesI2CDriverType* i2cDriver); + +void ariesGetCrcBytesImage(uint8_t* image, uint8_t* crcBytes, + uint8_t* numCrcBytes); + +AriesErrorType ariesEEPROMGetBlockLength(AriesI2CDriverType* i2cDriver, + int blockStartAddr, int* blockLength); + +AriesErrorType ariesEEPROMGetRandomByte(AriesI2CDriverType* i2cDriver, int addr, + uint8_t* value); + +AriesErrorType ariesGetEEPROMBlockCrcByte(AriesI2CDriverType* i2cDriver, + int blockStartAddr, int blockLength, + uint8_t* crcByte); + +AriesErrorType ariesGetEEPROMBlockType(AriesI2CDriverType* i2cDriver, + int blockStartAddr, uint8_t* blockType); + +AriesErrorType ariesGetEEPROMFirstBlock(AriesI2CDriverType* i2cDriver, + int* blockStartAddr); + +AriesErrorType ariesGetPathFWState(AriesLinkType* link, int lane, int direction, + int* state); + +AriesErrorType ariesGetPathHWState(AriesLinkType* link, int lane, int direction, + int* state); + +AriesErrorType ariesSetPortOrientation(AriesDeviceType* device, + uint8_t orientation); + +AriesErrorType ariesPipeRxAdapt(AriesDeviceType* device, int side, int lane); + +AriesErrorType ariesPipeFomGet(AriesDeviceType* device, int side, int lane, + int* fom); + +AriesErrorType ariesPipeRxStandbySet(AriesDeviceType* device, int side, + int lane, bool value); + +AriesErrorType ariesPipeTxElecIdleSet(AriesDeviceType* device, int side, + int lane, bool value); + +AriesErrorType ariesPipeRxEqEval(AriesDeviceType* device, int side, int lane, + bool value); + +AriesErrorType ariesPipeRxEqInProgressSet(AriesDeviceType* device, int side, + int lane, bool value); + +AriesErrorType ariesPipePhyStatusClear(AriesDeviceType* device, int side, + int lane); + +AriesErrorType ariesPipePhyStatusGet(AriesDeviceType* device, int side, + int lane, bool* value); + +AriesErrorType ariesPipePhyStatusToggle(AriesDeviceType* device, int side, + int lane); + +AriesErrorType ariesPipePowerdownSet(AriesDeviceType* device, int side, + int lane, int value); + +AriesErrorType ariesPipePowerdownCheck(AriesDeviceType* device, int side, + int lane, int value); + +AriesErrorType ariesPipeRateChange(AriesDeviceType* device, int side, int lane, + int rate); + +AriesErrorType ariesPipeRateCheck(AriesDeviceType* device, int side, int lane, + int rate); + +AriesErrorType ariesPipeDeepmhasisSet(AriesDeviceType* device, int side, + int lane, int de, int preset, int pre, + int main, int pst); + +AriesErrorType ariesPipeRxPolaritySet(AriesDeviceType* device, int side, + int lane, int value); + +AriesErrorType ariesPipeRxTermSet(AriesDeviceType* device, int side, int lane, + bool value); + +AriesErrorType ariesPipeBlkAlgnCtrlSet(AriesDeviceType* device, int side, + int lane, bool value, bool enable); + +AriesErrorType ariesPMABertPatChkSts(AriesDeviceType* device, int side, + int lane, int* ecount); + +AriesErrorType ariesPMABertPatChkToggleSync(AriesDeviceType* device, int side, + int lane); + +AriesErrorType ariesPMABertPatChkDetectCorrectPolarity(AriesDeviceType* device, + int side, int lane); + +AriesErrorType ariesPMARxInvertSet(AriesDeviceType* device, int side, int lane, + bool invert, bool override); + +AriesErrorType ariesPMABertPatChkConfig(AriesDeviceType* device, int side, + int lane, AriesPRBSPatternType mode); + +AriesErrorType ariesPMABertPatGenConfig(AriesDeviceType* device, int side, + int lane, AriesPRBSPatternType mode); + +AriesErrorType ariesPMARxDataEnSet(AriesDeviceType* device, int side, int lane, + bool value); + +AriesErrorType ariesPMATxDataEnSet(AriesDeviceType* device, int side, int lane, + bool value); + +AriesErrorType ariesPMAPCSRxRecalBankOvrdSet(AriesDeviceType* device, int side, + int lane, bool enable); + +AriesErrorType ariesPMAPCSRxReqBlock(AriesDeviceType* device, int side, + int lane, bool enable); + +AriesErrorType ariesPMAVregVrefSet(AriesDeviceType* device, int side, int lane, + int rate); + +AriesErrorType ariesReadBlockDataForceError(AriesI2CDriverType* i2cDriver, + uint32_t address, uint8_t numBytes, + uint8_t* values); + +float ariesTsenseADCToDegrees(AriesDeviceType* device, int adcCode, + uint8_t calCode); + +#ifdef __cplusplus +} +#endif + +#endif /* ASTERA_ARIES_SDK_MISC_H_ */ diff --git a/common/recipes-lib/retimer-v2.16.2/files/aries_mpw_reg_defines.h b/common/recipes-lib/retimer-v2.16.2/files/aries_mpw_reg_defines.h new file mode 100755 index 000000000000..4bd3b93a19e5 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/aries_mpw_reg_defines.h @@ -0,0 +1,319 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file aries_mpw_reg_defines.h + * @brief Definition of register offsets used in Aries MPW + */ +#pragma once +#ifndef ASTERA_ARIES_MPW_REG_DEFINES_H +#define ASTERA_ARIES_MPW_REG_DEFINES_H + +/////////////////////////////////////////////////////////////// +////////////////////////// Main SRAM ////////////////////////// +/////////////////////////////////////////////////////////////// + +/** AL SRAM DMEM offset (MPW) */ +#define AL_MAIN_SRAM_DMEM_OFFSET 60 * 1024 + +/** AL Path SRAM DMEM offset (MPW) */ +#define AL_PATH_SRAM_DMEM_OFFSET (45 * 1024) + +/** SRAM read command */ +#define AL_TG_RD_LOC_IND_SRAM 0x16 + +/** SRAM write command */ +#define AL_TG_WR_LOC_IND_SRAM 0x17 + +/////////////////////////////////////////////////////////// +////////////////////////// Micros ///////////////////////// +/////////////////////////////////////////////////////////// + +/** Offset for main micro FW info */ +#define ARIES_MAIN_MICRO_FW_INFO (64 * 1024 - 128) + +/** Offset for path micro FW info */ +#define ARIES_PATH_MICRO_FW_INFO_ADDRESS (48 * 1024 - 256) + +/** Link Struct Base address (Main Micro)*/ +#define ARIES_LINK_0_MM_BASE_ADDR 0xF660 + +/////////////////////////////////////////////////////////// +/////////////////// Path Micro Members //////////////////// +/////////////////////////////////////////////////////////// + +/** FW Info (Major) offset location in struct */ +#define ARIES_MM_FW_VERSION_MAJOR 0 + +/** FW Info (Minor) offset location in struct */ +#define ARIES_MM_FW_VERSION_MINOR 1 + +/** FW Info (Build no.) offset location in struct */ +#define ARIES_MM_FW_VERSION_BUILD 2 + +/** AL print info struct address (Main Micro) */ +#define ARIES_MM_AL_PRINT_INFO_STRUCT_ADDR 4 + +/** Aries Link Struct Addr offset */ +#define ARIES_MM_LINK_STRUCT_ADDR_OFFSET 10 + +/**AL print info struct address (Path Micro) */ +#define ARIES_PM_AL_PRINT_INFO_STRUCT_ADDR 4 + +/** GP Ctrl status struct address (Main Micro) */ +#define ARIES_MM_GP_CTRL_STS_STRUCT_ADDR 6 + +/** GP Ctrl status struct address (Path Micro) */ +#define ARIES_PM_GP_CTRL_STS_STRUCT_ADDR 6 + +/** Offset to enable LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_PRINT_EN_OFFSET 0 + +/** Offset to enable one batch mode inside LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_ONE_BATCH_MODE_EN_OFFSET 2 + +/** Offset to enable one batch write inside LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_ONE_BATCH_WR_EN_OFFSET 3 + +/** Offset to get write_offset inside LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_WR_PTR_OFFSET 5 + +/** Offset to get print_class_en inside LTSSM logger for Path Micro */ +#define ARIES_PM_PRINT_INFO_STRUCT_PRINT_CLASS_EN_OFFSET 7 + +/** Offset to get print_class_en inside LTSSM logger for Main Micro */ +#define ARIES_MM_PRINT_INFO_STRUCT_PRINT_CLASS_EN_OFFSET 15 + +/** Offset to get print buffer inside LTSSM logger */ +#define ARIES_PRINT_INFO_STRUCT_PRINT_BUFFER_OFFSET 23 + +/** Offset to get FW state inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_FW_STATE 0 + +/** Offset to get last speed inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_PCIE_GEN 2 + +/** Offset to get last preset reqs for lane 0 inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_PRESET_REQS_LN0 5 + +/** Offset to get last preset reqs for lane 1 inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_PRESET_REQS_LN1 15 + +/** Offset to get last FOMs for lane 0 inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FOMS_LN0 25 + +/** Offset to get last FOMs for lane 1 inside GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FOMS_LN1 35 + +/** Offset to get preset val for lane 0 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRESET_LN0 45 + +/** Offset to get preset val for lane 1 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRESET_LN1 46 + +/** Offset to get pre cursor val for lane 0 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRE_LN0 47 + +/** Offset to get pre cursor val for lane 1 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PRE_LN1 48 + +/** Offset to get cursor val for lane 0 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_CUR_LN0 49 + +/** Offset to get cursor val for lane 1 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_CUR_LN1 50 + +/** Offset to get post cursor val for lane 0 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PST_LN0 51 + +/** Offset to get post cursor val for lane 1 in GP_CTRL_STS_STRUCT */ +#define ARIES_CTRL_STS_STRUCT_LAST_EQ_FINAL_REQ_PST_LN1 52 + +/** Offset to link width member in link struct*/ +#define ARIES_LINK_STRUCT_WIDTH_OFFSET 1 + +/** Offset to link rate member in link struct*/ +#define ARIES_LINK_STRUCT_RATE_OFFSET 6 + +/** Offset to link state member in link struct*/ +#define ARIES_LINK_STRUCT_STATE_OFFSET 10 + +/** Offset seperating 2 links in Aries Link struct */ +#define ARIES_LINK_STRUCT_LINK_SEP_OFFSET 126 + +/////////////////////////////////////////////////////////// +////////////////////// PMA registers ////////////////////// +/////////////////////////////////////////////////////////// + +/** PMA Slice 0 Cmd register address*/ +#define ARIES_PMA_QS0_CMD_ADDRESS 0x4400 + +/** PMA Slice 0 Address_1 register address*/ +#define ARIES_PMA_QS0_ADDR_1_ADDRESS 0x4401 + +/** PMA Slice 0 Address_0 register address*/ +#define ARIES_PMA_QS0_ADDR_0_ADDRESS 0x4402 + +/** PMA Slice 0 Data_0 register address*/ +#define ARIES_PMA_QS0_DATA_0_ADDRESS 0x4403 + +/** PMA Slice 0 Data_1 register address*/ +#define ARIES_PMA_QS0_DATA_1_ADDRESS 0x4404 + +/** Reg offset for PMA reg LANE_DIG_ASIC_RX_OVRD_IN_3 */ +#define ARIES_PMA_LANE_DIG_ASIC_RX_OVRD_IN_3 0x101a + +/** Reg offset for PMA reg LANE0_DIG_ASIC_RX_ASIC_IN_1 */ +#define ARIES_PMA_LANE_DIG_ASIC_RX_ASIC_IN_1 0x1029 + +/** Reg offset for PMA register LANE0_DIG_RX_ADPTCTL_ATT_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_ATT_STATUS 0x10ab + +/** Reg offset for PMA register LANE0_DIG_RX_ADPTCTL_VGA_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_VGA_STATUS 0x10ac + +/** Reg offset for PMA register LANE0_DIG_RX_ADPTCTL_CTLE_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_CTLE_STATUS 0x10ad + +/** Reg offset for PMA register DIG_RX_ADPTCTL_DFE_TAP1_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP1_STATUS 0x10ae + +/** Reg offset for PMA register DIG_RX_ADPTCTL_DFE_TAP2_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP2_STATUS 0x10af + +/** Reg offset for PMA register DIG_RX_ADPTCTL_DFE_TAP3_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP3_STATUS 0x10b0 + +/** Reg offset for PMA register DIG_RX_ADPTCTL_DFE_TAP4_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP4_STATUS 0x10b1 + +/** Reg offset for PMA register DIG_RX_ADPTCTL_DFE_TAP5_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP5_STATUS 0x10b2 + +/** Reg offset for PMA register DIG_RX_ADPTCTL_DFE_TAP6_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP6_STATUS 0x10ce + +/** Reg offset for PMA register DIG_RX_ADPTCTL_DFE_TAP7_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP7_STATUS 0x10cf + +/** Reg offset for PMA register DIG_RX_ADPTCTL_DFE_TAP8_STATUS */ +#define ARIES_PMA_LANE_DIG_RX_ADPTCTL_DFE_TAP8_STATUS 0x10d0 + +/** Reg offset for PMA register DIG_PCS_XF_RX_OVRD_IN_1 at Lane 0*/ +#define ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_1 0x2006 + +/** Reg offset for PMA register DIG_PCS_XF_RX_OVRD_IN_7 at Lane 0*/ +#define ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_7 0x200c + +/** Reg offset for PMA register DIG_PCS_XF_RX_OVRD_IN_9 at Lane 0*/ +#define ARIES_PMA_RAWLANE_DIG_PCS_XF_RX_OVRD_IN_9 0x203a + +/////////////////////////////////////////////////////////// +////////////////////// Common offsets ///////////////////// +/////////////////////////////////////////////////////////// + +/** Address offset between between lanes */ +#define ARIES_PMA_LANE_STRIDE 0x200 + +/** Address offset between quad slices */ +#define ARIES_QS_STRIDE 0x4000 + +/** Address offset between between path wrappers */ +#define ARIES_PATH_WRP_STRIDE 0x1000 + +/** Address offset between between path lanes */ +#define ARIES_PATH_LANE_STRIDE 0x400 + +/////////////////////////////////////////////////////////// +/////////////////// Hierarchical offsets ////////////////// +/////////////////////////////////////////////////////////// + +////////////////////// AL Top CSRs /////////////////////// + +/** AL Misc CSR Offset */ +#define ARIES_MISC_CSR_OFFSET 0x0 + +/** AL Main Micro CSR Offset */ +#define ARIES_MAIN_MICRO_CSR_OFFSET 0xc00 + +/** AL Quad Slice 0 Offset */ +#define ARIES_QS_0_CSR_OFFSET 0x4000 + +/** AL Quad Slice 1 Offset */ +#define ARIES_QS_1_CSR_OFFSET 0x8000 + +/** AL Quad Slice 2 Offset */ +#define ARIES_QS_2_CSR_OFFSET 0xc000 + +/** AL Quad Slice 3 Offset */ +#define ARIES_QS_3_CSR_OFFSET 0x10000 + +/////////////////// AL Quad Slice CSRs //////////////////// + +/** AL Path Wrapper 0 CSR Offset */ +#define ARIES_PATH_WRAPPER_0_CSR_OFFSET 0x0 + +/** AL Path Wrapper 1 CSR Offset */ +#define ARIES_PATH_WRAPPER_1_CSR_OFFSET 0x1000 + +/** AL Path Wrapper 2 CSR Offset */ +#define ARIES_PATH_WRAPPER_2_CSR_OFFSET 0x2000 + +/** AL Path Wrapper 3 CSR Offset */ +#define ARIES_PATH_WRAPPER_3_CSR_OFFSET 0x3000 + +/////////////////// AL Path Wrapper CSRs //////////////////// + +/** Path Micro CSR Offset */ +#define ARIES_PATH_MICRO_CSR_OFFSET 0x0 + +/** Pac CSR Offset */ +#define ARIES_PAC_CSR_OFFSET 0x400 + +/** Path Global CSR Offset */ +#define ARIES_PATH_GLOBAL_CSR_OFFSET 0x600 + +/** AL Path Lane 0 CSR offset */ +#define ARIES_PATH_LANE_0_CSR_OFFSET 0x800 + +/** AL Path Lane 1 CSR offset */ +#define ARIES_PATH_LANE_1_CSR_OFFSET 0xc00 + +/////////////////// AL Path Global CSRs //////////////////// + +/** Reg offset for MAC-to-PHY rate and pclk_rate overrides */ +#define ARIES_GBL_CSR_MAC_PHY_RATE_AND_PCLK_RATE 0xe + +/////////////////// AL Path Lane X CSRs //////////////////// + +/** Captured Lane number reg offset */ +#define ARIES_LN_CAPT_NUM 0x6 + +/** MAC-to-PHY Tx equalization setting override */ +#define ARIES_MAC_PHY_TXDEEMPH 0x120 + +/** MAC-to-PHY Rx polarity override */ +#define ARIES_MAC_RX_POLARITY 0x125 + +/** Deskew delta in pclk cycles */ +#define ARIES_DSK_CC_DELTA 0x154 + +/** Deskew status */ +#define ARIES_DESKEW_STATUS 0x156 + +/** MAC-to-PHY Tx deemph idle observe */ +#define ARIES_MAC_PHY_TXDEEMPH_OB 0x175 + +#endif diff --git a/common/recipes-lib/retimer-v2.16.2/files/astera_log.c b/common/recipes-lib/retimer-v2.16.2/files/astera_log.c new file mode 100755 index 000000000000..396b30079d42 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/astera_log.c @@ -0,0 +1,177 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file astera_log.h + * @brief Logging module for Aries. + */ + +#include "astera_log.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static struct +{ + void* udata; + logLockFn lock; + FILE* fp; + void (*ptr)(); + int level; + int quiet; + bool traceEn; +} AsteraLogger; + +static const char* logLevelNames[] = {"TRACE", "DEBUG", "INFO", + "WARN", "ERROR", "FATAL"}; + +#ifdef LOG_USE_COLOR +static const char* logLevelColors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", + "\x1b[33m", "\x1b[31m", "\x1b[35m"}; +#endif + +static void lock(void) +{ + if (AsteraLogger.lock) + { + AsteraLogger.lock(AsteraLogger.udata, 1); + } +} + +static void unlock(void) +{ + if (AsteraLogger.lock) + { + AsteraLogger.lock(AsteraLogger.udata, 0); + } +} + +void asteraLogSetUdata(void* udata) +{ + AsteraLogger.udata = udata; +} + +void asteraLogSetLock(logLockFn fn) +{ + AsteraLogger.lock = fn; +} + +void asteraLogSetFp(FILE* fp) +{ + AsteraLogger.fp = fp; +} + +void asteraLogSetCallback(void (*ptr)()) +{ + AsteraLogger.ptr = ptr; +} + +void asteraLogSetLevel(int level) +{ + AsteraLogger.level = level; + if (level == 0) + { + AsteraLogger.traceEn = true; + } +} + +void asteraLogSetQuiet(int enable) +{ + AsteraLogger.quiet = enable ? 1 : 0; +} + +void asteraLogMsg(int level, const char* file, int line, const char* fmt, ...) +{ + if (level == 0 && !(AsteraLogger.traceEn)) + { + return; + } + else if (level < AsteraLogger.level) + { + return; + } + + /* Acquire lock */ + lock(); + + /* Get current time */ + time_t t = time(NULL); + struct tm* lt = localtime(&t); + if (lt == NULL) + { + return; + } + + /* Log to stderr */ + if (!AsteraLogger.quiet) + { + va_list args; + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; +#ifdef LOG_USE_COLOR + fprintf(stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, + logLevelColors[level], logLevelNames[level], file, line); +#else + fprintf(stderr, "%s %-5s %s:%d: ", buf, logLevelNames[level], file, + line); +#endif + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + fflush(stderr); + } + + /* Log to file */ + if (AsteraLogger.fp) + { + va_list args; + char buf[32]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; + fprintf(AsteraLogger.fp, "%s %-5s %s:%d: ", buf, logLevelNames[level], + file, line); + va_start(args, fmt); + vfprintf(AsteraLogger.fp, fmt, args); + va_end(args); + fprintf(AsteraLogger.fp, "\n"); + fflush(AsteraLogger.fp); + } + + /* Call funtion callback */ + if (AsteraLogger.ptr) + { + va_list args; + char buf[32]; + char string[256]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; + snprintf(string, 256, "%s %-5s %s:%d: ", buf, logLevelNames[level], file, + line); + AsteraLogger.ptr(string); + va_start(args, fmt); + vsprintf(string, fmt, args); + AsteraLogger.ptr(string); + va_end(args); + sprintf(string, "\n"); + AsteraLogger.ptr(string); + } + + /* Release lock */ + unlock(); +} + +#ifdef __cplusplus +} +#endif diff --git a/common/recipes-lib/retimer-v2.16.2/files/astera_log.h b/common/recipes-lib/retimer-v2.16.2/files/astera_log.h new file mode 100755 index 000000000000..d59a6f3d33b2 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/astera_log.h @@ -0,0 +1,75 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file astera_log.h + * @brief Logging module for Aries. + */ +#pragma once +#ifndef ASTERA_LOG_H_ +#define ASTERA_LOG_H_ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LOG_VERSION "0.2.1" + +typedef void (*logLockFn)(void* udata, int lock); + +enum +{ + ASTERA_LOG_LEVEL_TRACE, + ASTERA_LOG_LEVEL_DEBUG, + ASTERA_LOG_LEVEL_INFO, + ASTERA_LOG_LEVEL_WARN, + ASTERA_LOG_LEVEL_ERROR, + ASTERA_LOG_LEVEL_FATAL +}; + +#define ASTERA_TRACE(...) \ + asteraLogMsg(ASTERA_LOG_LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define ASTERA_DEBUG(...) \ + asteraLogMsg(ASTERA_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define ASTERA_INFO(...) \ + asteraLogMsg(ASTERA_LOG_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define ASTERA_WARN(...) \ + asteraLogMsg(ASTERA_LOG_LEVEL_WARN, __FILE__, __LINE__, __VA_ARGS__) +#define ASTERA_ERROR(...) \ + asteraLogMsg(ASTERA_LOG_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define ASTERA_FATAL(...) \ + asteraLogMsg(ASTERA_LOG_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__) + +void asteraLogSetUdata(void* udata); +void asteraLogSetLock(logLockFn fn); +void asteraLogSetFp(FILE* fp); +void asteraLogSetCallback(void (*ptr)()); +void asteraLogSetLevel(int level); +void asteraLogSetQuiet(int enable); + +void asteraLogMsg(int level, const char* file, int line, const char* fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* ASTERA_LOG_H_ */ diff --git a/common/recipes-lib/retimer-v2.16.2/files/meson.build b/common/recipes-lib/retimer-v2.16.2/files/meson.build new file mode 100755 index 000000000000..a1f51e94d59d --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/meson.build @@ -0,0 +1,60 @@ +project('libretimer', 'c', + version: '0.1', + license: 'GPL2', + default_options: ['werror=false', 'warning_level=3'], + meson_version: '>=0.40') + +headers = [ + 'aries_a0_reg_defines.h', + 'aries_api.h', + 'aries_api_types.h', + 'aries_error.h', + 'aries_globals.h', + 'aries_i2c.h', + 'aries_link.h', + 'aries_margin.h', + 'aries_misc.h', + 'aries_mpw_reg_defines.h', + 'astera_log.h', + 'platform.h', +] + +sources = [ + 'aries_api.c', + 'aries_i2c.c', + 'aries_link.c', + 'aries_margin.c', + 'aries_misc.c', + 'astera_log.c', + 'platform.c', +] + +# Import the plat-specific subdirectory for platform +# headers/sources/dependencies. +subdir('plat') + + +install_headers( + headers, + subdir: 'openbmc') + +srcs = files( + sources, +) + +rt_deps = [ + dependency('libobmc-i2c'), +] + +# Retimer library. +rt_lib = shared_library('retimer', srcs, + dependencies: rt_deps, + version: meson.project_version(), + install: true) + +# pkgconfig for Retimer library. +pkg = import('pkgconfig') +pkg.generate(libraries: [rt_lib], + name: meson.project_name(), + version: meson.project_version(), + description: 'Aries retimer Library v2.16.2') diff --git a/common/recipes-lib/retimer-v2.16.2/files/plat/meson.build b/common/recipes-lib/retimer-v2.16.2/files/plat/meson.build new file mode 100644 index 000000000000..6b9b750debf4 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/plat/meson.build @@ -0,0 +1,2 @@ +# Op-op. Platform is expected to override this +# if it needs additional libs/sources/headers. diff --git a/common/recipes-lib/retimer-v2.16.2/files/platform.c b/common/recipes-lib/retimer-v2.16.2/files/platform.c new file mode 100755 index 000000000000..3aa50f80db5d --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/platform.c @@ -0,0 +1,157 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file platform.c + * @brief Implementation of platform specific functions for Linux + */ + +#include "platform.h" + +int asteraI2COpenConnection(int i2cBus, int slaveAddress) +{ + int handle; + int quiet = 0; + char filename[20]; + int size = sizeof(filename); + + snprintf(filename, size, "/dev/i2c/%d", i2cBus); + filename[size - 1] = '\0'; + handle = open(filename, O_RDWR); + + if (handle < 0 && (errno == ENOENT || errno == ENOTDIR)) + { + sprintf(filename, "/dev/i2c-%d", i2cBus); + handle = open(filename, O_RDWR); + } + + if (handle < 0 && !quiet) + { + if (errno == ENOENT) + { + fprintf(stderr, + "Error: Could not open file '/dev/i2c-%d' or '/dev/i2c/%d':" + " %s\n", + i2cBus, i2cBus, strerror(ENOENT)); + } + else + { + fprintf(stderr, "Error: Could not open file '%s': %s\n", filename, + strerror(errno)); + if (errno == EACCES) + { + fprintf(stderr, "Run as root?\n"); + } + } + } + asteraI2CSetSlaveAddress(handle, slaveAddress, 0); + return handle; +} + +int asteraI2CWriteBlockData(int handle, uint8_t cmdCode, uint8_t numBytes, + uint8_t* buf) +{ + return i2c_smbus_write_block_data(handle, cmdCode, numBytes, buf); +} + +int asteraI2CReadBlockData(int handle, uint8_t cmdCode, uint8_t numBytes, + uint8_t* buf) +{ +#ifdef SMBUS_BLOCK_READ_UNSUPPORTED + int rc = i2c_smbus_read_i2c_block_data(handle, cmdCode, numBytes, buf); +#else + (void)numBytes; // Unused when performing SMBus block read transaction + int rc = i2c_smbus_read_block_data(handle, cmdCode, buf); +#endif + return rc; +} + +int asteraI2CWriteReadBlockData(int handle, uint8_t cmdCode, uint8_t numBytes, + uint8_t* buf) +{ + return i2c_smbus_block_process_call(handle, cmdCode, numBytes, buf); +} + +int asteraI2CBlock(int handle) +{ + if (flock(handle, LOCK_EX) < 0) + { + fprintf(stderr, "Unable to acquire lock on retimer handle: %s\n", + strerror(errno)); + return -errno; + } + return 0; // Equivalent to ARIES_SUCCESS +} + +int asteraI2CUnblock(int handle) +{ + if (flock(handle, LOCK_UN) < 0) + { + fprintf(stderr, "Unable to release lock on retimer handle: %s\n", + strerror(errno)); + return -errno; + } + return 0; // Equivalent to ARIES_SUCCESS +} + +/** + * @brief Set I2C slave address + * + * @param[in] handle I2C handle + * @param[in] address Slave address + * @param[in] force Override user provied slave address with default + * I2C_SLAVE address + * @return int Zero if success, else a negative value + */ +int asteraI2CSetSlaveAddress(int handle, int address, int force) +{ + /* With force, let the user read from/write to the registers + even when a driver is also running */ + if (ioctl(handle, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) + { + fprintf(stderr, "Error: Could not set address to 0x%02x: %s\n", address, + strerror(errno)); + return -errno; + } + return 0; // Equivalent to ARIES_SUCCESS +} + +/** + * @brief Set I2C SMBus PEC flag + * + * @param[in] handle I2C handle + * @param[in] pec SMBus PEC (packet error checking) flag + * @return int Zero if success, else a negative value + */ +int asteraI2CSetPEC(int handle, int pec) +{ + if (ioctl(handle, I2C_PEC, pec) < 0) + { + fprintf(stderr, "Error: Could not set PEC to %d: %s\n", pec, + strerror(errno)); + return -errno; + } + return 0; // Equivalent to ARIES_SUCCESS +} + +/** + * @brief Close I2C connection + * + * @param[in] handle I2C handle + */ +void asteraI2CCloseConnection(int handle) +{ + close(handle); +} diff --git a/common/recipes-lib/retimer-v2.16.2/files/platform.h b/common/recipes-lib/retimer-v2.16.2/files/platform.h new file mode 100755 index 000000000000..ae780bf5d68c --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/files/platform.h @@ -0,0 +1,44 @@ +/* + * Copyright 2020 Astera Labs, 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. You may obtain a copy + * of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.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. + */ + +/** + * @file platform.h + * @brief Definition of platform specific functions for Linux + */ + +#ifndef ASTERA_ARIES_SDK_PLATFORM_H_ +#define ASTERA_ARIES_SDK_PLATFORM_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +// #include +#include + +int asteraI2CSetSlaveAddress(int handle, int address, int force); + +int asteraI2CSetPEC(int handle, int pec); + +void asteraI2CCloseConnection(int handle); + +#endif /* ASTERA_ARIES_SDK_PLATFORM_H_ */ diff --git a/common/recipes-lib/retimer-v2.16.2/libretimer_2.16.2.bb b/common/recipes-lib/retimer-v2.16.2/libretimer_2.16.2.bb new file mode 100755 index 000000000000..a2528fa84fd1 --- /dev/null +++ b/common/recipes-lib/retimer-v2.16.2/libretimer_2.16.2.bb @@ -0,0 +1,43 @@ +# Copyright 2018-present Facebook. All Rights Reserved. +SUMMARY = "Aries Retimer Library" +DESCRIPTION = "library for Aries Retimer v2.16.2" +SECTION = "base" +PR = "r1" +DEFAULT_PREFERENCE = "-1" +LICENSE = "GPL-2.0-or-later" +LIC_FILES_CHKSUM = "file://LICENSE;beginline=5;endline=17;md5=b96cf6516c0263b26b55c5bbf3806237" + +LOCAL_URI = "\ + file://aries_api.c \ + file://aries_i2c.c \ + file://aries_link.c \ + file://aries_margin.c \ + file://aries_misc.c \ + file://astera_log.c \ + file://aries_a0_reg_defines.h \ + file://aries_api.h \ + file://aries_api_types.h \ + file://aries_error.h \ + file://aries_globals.h \ + file://aries_i2c.h \ + file://aries_link.h \ + file://aries_margin.h \ + file://aries_misc.h \ + file://aries_mpw_reg_defines.h \ + file://astera_log.h \ + file://LICENSE \ + file://meson.build \ + file://plat/meson.build \ + file://platform.c \ + file://platform.h \ + " + +inherit meson pkgconfig + +DEPENDS += " \ + libobmc-i2c \ + " + +RDEPENDS:${PN} += " \ + libobmc-i2c \ + "