diff options
Diffstat (limited to 'sys/contrib/alpine-hal/eth/al_hal_eth_kr.c')
-rw-r--r-- | sys/contrib/alpine-hal/eth/al_hal_eth_kr.c | 1030 |
1 files changed, 1030 insertions, 0 deletions
diff --git a/sys/contrib/alpine-hal/eth/al_hal_eth_kr.c b/sys/contrib/alpine-hal/eth/al_hal_eth_kr.c new file mode 100644 index 000000000000..14ef797eb00c --- /dev/null +++ b/sys/contrib/alpine-hal/eth/al_hal_eth_kr.c @@ -0,0 +1,1030 @@ +/*- +******************************************************************************* +Copyright (C) 2015 Annapurna Labs Ltd. + +This file may be licensed under the terms of the Annapurna Labs Commercial +License Agreement. + +Alternatively, this file can be distributed under the terms of the GNU General +Public License V2 as published by the Free Software Foundation and can be +found at http://www.gnu.org/licenses/gpl-2.0.html + +Alternatively, redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/** + * Ethernet + * @{ + * @file al_hal_eth_kr.c + * + * @brief KR HAL driver for main functions (auto-neg, Link Training) + * + */ + +#include "al_hal_eth_kr.h" +#include "al_hal_eth_mac_regs.h" +#include "al_hal_an_lt_wrapper_regs.h" + +enum al_eth_lt_unit_rev { + AL_ETH_LT_UNIT_REV_1 = 0, + AL_ETH_LT_UNIT_REV_2, + + AL_ETH_LT_UNIT_REV_MAX +}; + +enum al_eth_an_lt_regs_ids { + AL_ETH_KR_AN_CONTROL = 0, + AL_ETH_KR_AN_STATUS, + AL_ETH_KR_AN_ADV0, + AL_ETH_KR_AN_ADV1, + AL_ETH_KR_AN_ADV2, + AL_ETH_KR_AN_REM_ADV0, + AL_ETH_KR_AN_REM_ADV1, + AL_ETH_KR_AN_REM_ADV2, + AL_ETH_KR_PMD_CONTROL, + AL_ETH_KR_PMD_STATUS, + AL_ETH_KR_PMD_LP_COEF_UP, + AL_ETH_KR_PMD_LP_STATUS_REPORT, + AL_ETH_KR_PMD_LD_COEF_UP, + AL_ETH_KR_PMD_LD_STATUS_REPORT, + AL_ETH_KR_AN_XNP_ADV0, + AL_ETH_KR_AN_XNP_ADV1, + AL_ETH_KR_AN_XNP_ADV2, + AL_ETH_KR_AN_REM_XNP_ADV0, + AL_ETH_KR_AN_REM_XNP_ADV1, + AL_ETH_KR_AN_REM_XNP_ADV2, +}; + +static uint32_t al_eth_an_lt_regs_addr[][AL_ETH_LT_UNIT_REV_MAX] = { + [AL_ETH_KR_AN_CONTROL] = {0 , 0x0}, + [AL_ETH_KR_AN_STATUS] = {1 , 0x4}, + [AL_ETH_KR_AN_ADV0] = {16 , 0x8}, + [AL_ETH_KR_AN_ADV1] = {17 , 0xc}, + [AL_ETH_KR_AN_ADV2] = {18 , 0x10}, + [AL_ETH_KR_AN_REM_ADV0] = {19 , 0x14}, + [AL_ETH_KR_AN_REM_ADV1] = {20 , 0x18}, + [AL_ETH_KR_AN_REM_ADV2] = {21 , 0x1c}, + [AL_ETH_KR_PMD_CONTROL] = {150, 0x400}, + [AL_ETH_KR_PMD_STATUS] = {151, 0x404}, + [AL_ETH_KR_PMD_LP_COEF_UP] = {152, 0x408}, + [AL_ETH_KR_PMD_LP_STATUS_REPORT] = {153, 0x40c}, + [AL_ETH_KR_PMD_LD_COEF_UP] = {154, 0x410}, + [AL_ETH_KR_PMD_LD_STATUS_REPORT] = {155, 0x414}, + [AL_ETH_KR_AN_XNP_ADV0] = {22 , 0x24}, + [AL_ETH_KR_AN_XNP_ADV1] = {23 , 0x28}, + [AL_ETH_KR_AN_XNP_ADV2] = {24 , 0x2c}, + [AL_ETH_KR_AN_REM_XNP_ADV0] = {25 , 0x30}, + [AL_ETH_KR_AN_REM_XNP_ADV1] = {26 , 0x34}, + [AL_ETH_KR_AN_REM_XNP_ADV2] = {27 , 0x38}, +}; + + +/* + * AN(Auto Negotiation) registers + * (read / write indirect with al_eth_an_reg_read/write) + */ +#define AL_ETH_KR_AN_CONTROL_RESTART AL_BIT(9) +#define AL_ETH_KR_AN_CONTROL_ENABLE AL_BIT(12) +#define AL_ETH_KR_AN_CONTROL_NP_ENABLE AL_BIT(13) + +#define AL_ETH_KR_AN_STATUS_COMPLETED AL_BIT(5) +#define AL_ETH_KR_AN_STATUS_BASE_PAGE_RECEIVED AL_BIT(6) +#define AL_ETH_KR_AN_STATUS_CHECK_MASK 0xFF0A +#define AL_ETH_KR_AN_STATUS_CHECK_NO_ERROR 0x0008 + +/* AN advertising registers parsing */ +/* register 1 */ +#define AL_ETH_KR_AN_ADV1_SEL_FIELD_MASK 0x001f +#define AL_ETH_KR_AN_ADV1_SEL_FIELD_SHIFT 0 +#define AL_ETH_KR_AN_ADV1_ECHOED_NONCE_MASK 0x03e0 +#define AL_ETH_KR_AN_ADV1_ECHOED_NONCE_SHIFT 5 +#define AL_ETH_KR_AN_ADV1_CAPABILITY_MASK 0x1c00 +#define AL_ETH_KR_AN_ADV1_CAPABILITY_SHIFT 10 +#define AL_ETH_KR_AN_ADV1_REM_FAULT_MASK 0x2000 +#define AL_ETH_KR_AN_ADV1_REM_FAULT_SHIFT 13 +#define AL_ETH_KR_AN_ADV1_ACK_MASK 0x4000 +#define AL_ETH_KR_AN_ADV1_ACK_SHIFT 14 +#define AL_ETH_KR_AN_ADV1_NEXT_PAGE_MASK 0x8000 +#define AL_ETH_KR_AN_ADV1_NEXT_PAGE_SHIFT 15 +/* register 2 */ +#define AL_ETH_KR_AN_ADV2_TX_NONCE_MASK 0x001f +#define AL_ETH_KR_AN_ADV2_TX_NONCE_SHIFT 0 +#define AL_ETH_KR_AN_ADV2_TECH_MASK 0xffe0 +#define AL_ETH_KR_AN_ADV2_TECH_SHIFT 5 +/* register 3 */ +/* TECH field in the third register is extended to the field in the second + * register and it is currently reserved (should be always 0) */ +#define AL_ETH_KR_AN_ADV3_TECH_MASK 0x1fff +#define AL_ETH_KR_AN_ADV3_TECH_SHIFT 0 +#define AL_ETH_KR_AN_ADV3_FEC_MASK 0xc000 +#define AL_ETH_KR_AN_ADV3_FEC_SHIFT 14 + +/* Next Page Fields */ +/* register 1 */ +#define AL_ETH_KR_AN_NP_ADV1_DATA1_MASK 0x07ff +#define AL_ETH_KR_AN_NP_ADV1_DATA1_SHIFT 0 +#define AL_ETH_KR_AN_NP_ADV1_TOGGLE_MASK 0x0800 +#define AL_ETH_KR_AN_NP_ADV1_TOGGLE_SHIFT 11 +#define AL_ETH_KR_AN_NP_ADV1_ACK2_MASK 0x1000 +#define AL_ETH_KR_AN_NP_ADV1_ACK2_SHIFT 12 +#define AL_ETH_KR_AN_NP_ADV1_MSG_PAGE_MASK 0x2000 +#define AL_ETH_KR_AN_NP_ADV1_MSG_PAGE_SHIFT 13 +#define AL_ETH_KR_AN_NP_ADV1_NP_MASK 0x8000 +#define AL_ETH_KR_AN_NP_ADV1_NP_SHIFT 15 + +/* + * LT(Link Training) registers + * (read / write indirect with al_eth_pma_reg_read/write) + */ +#define AL_ETH_KR_PMD_CONTROL_RESTART 0 +#define AL_ETH_KR_PMD_CONTROL_ENABLE 1 + +#define AL_ETH_KR_PMD_STATUS_RECEIVER_COMPLETED_SHIFT 0 +#define AL_ETH_KR_PMD_STATUS_RECEIVER_FRAME_LOCK_SHIFT 1 +#define AL_ETH_KR_PMD_STATUS_RECEIVER_START_UP_PROTO_PROG_SHIFT 2 +#define AL_ETH_KR_PMD_STATUS_FAILURE_SHIFT 3 + +#define AL_ETH_KR_PMD_LP_COEF_UP_MINUS_MASK 0x0003 +#define AL_ETH_KR_PMD_LP_COEF_UP_MINUS_SHIFT 0 +#define AL_ETH_KR_PMD_LP_COEF_UP_ZERO_MASK 0x000C +#define AL_ETH_KR_PMD_LP_COEF_UP_ZERO_SHIFT 2 +#define AL_ETH_KR_PMD_LP_COEF_UP_PLUS_MASK 0x0030 +#define AL_ETH_KR_PMD_LP_COEF_UP_PLUS_SHIFT 4 +#define AL_ETH_KR_PMD_LP_COEF_UP_INITIALIZE_SHIFT 12 +#define AL_ETH_KR_PMD_LP_COEF_UP_PRESET_SHIFT 13 + +#define AL_ETH_KR_PMD_LP_STATUS_REPORT_MINUS_MASK 0x0003 +#define AL_ETH_KR_PMD_LP_STATUS_REPORT_MINUS_SHIFT 0 +#define AL_ETH_KR_PMD_LP_STATUS_REPORT_ZERO_MASK 0x000C +#define AL_ETH_KR_PMD_LP_STATUS_REPORT_ZERO_SHIFT 2 +#define AL_ETH_KR_PMD_LP_STATUS_REPORT_PLUS_MASK 0x0030 +#define AL_ETH_KR_PMD_LP_STATUS_REPORT_PLUS_SHIFT 4 +#define AL_ETH_KR_PMD_LP_STATUS_RECEIVER_READY_SHIFT 15 + +#define AL_ETH_KR_PMD_LD_COEF_UP_MINUS_MASK 0x0003 +#define AL_ETH_KR_PMD_LD_COEF_UP_MINUS_SHIFT 0 +#define AL_ETH_KR_PMD_LD_COEF_UP_ZERO_MASK 0x000C +#define AL_ETH_KR_PMD_LD_COEF_UP_ZERO_SHIFT 2 +#define AL_ETH_KR_PMD_LD_COEF_UP_PLUS_MASK 0x0030 +#define AL_ETH_KR_PMD_LD_COEF_UP_PLUS_SHIFT 4 +#define AL_ETH_KR_PMD_LD_COEF_UP_INITIALIZE_SHIFT 12 +#define AL_ETH_KR_PMD_LD_COEF_UP_PRESET_SHIFT 13 + +#define AL_ETH_KR_PMD_LD_STATUS_REPORT_MINUS_MASK 0x0003 +#define AL_ETH_KR_PMD_LD_STATUS_REPORT_MINUS_SHIFT 0 +#define AL_ETH_KR_PMD_LD_STATUS_REPORT_ZERO_MASK 0x000C +#define AL_ETH_KR_PMD_LD_STATUS_REPORT_ZERO_SHIFT 2 +#define AL_ETH_KR_PMD_LD_STATUS_REPORT_PLUS_MASK 0x0030 +#define AL_ETH_KR_PMD_LD_STATUS_REPORT_PLUS_SHIFT 4 +#define AL_ETH_KR_PMD_LD_STATUS_REPORT_RECEIVER_READY_SHIFT 15 + + +enum al_eth_an_lt_regs { + AL_ETH_AN_REGS, + AL_ETH_LT_REGS, +}; + +static uint16_t al_eth_an_lt_reg_read( + struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_regs_ids reg_id, + enum al_eth_an_lt_regs an_lt, + enum al_eth_an_lt_lane lane) +{ + uint32_t val; + uint16_t reg_addr; + + if (adapter->rev_id < AL_ETH_REV_ID_3) { + al_assert(lane == AL_ETH_AN__LT_LANE_0); + + reg_addr = al_eth_an_lt_regs_addr[reg_id][AL_ETH_LT_UNIT_REV_1]; + if (an_lt == AL_ETH_AN_REGS) { + al_reg_write32(&adapter->mac_regs_base->kr.an_addr, reg_addr); + val = al_reg_read32(&adapter->mac_regs_base->kr.an_data); + } else { + al_reg_write32(&adapter->mac_regs_base->kr.pma_addr, reg_addr); + val = al_reg_read32(&adapter->mac_regs_base->kr.pma_data); + } + } else { + struct al_an_lt_wrapper_regs *regs = NULL; + + reg_addr = al_eth_an_lt_regs_addr[reg_id][AL_ETH_LT_UNIT_REV_2]; + + switch (lane) { + case AL_ETH_AN__LT_LANE_0: + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_0_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].addr); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_0_data, + reg_addr); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_0_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].data); + val = al_reg_read32(&adapter->mac_regs_base->gen_v3.an_lt_0_data); + break; + case AL_ETH_AN__LT_LANE_1: + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_1_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].addr); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_1_data, + reg_addr); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_1_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].data); + val = al_reg_read32(&adapter->mac_regs_base->gen_v3.an_lt_1_data); + break; + case AL_ETH_AN__LT_LANE_2: + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_2_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].addr); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_2_data, + reg_addr); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_2_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].data); + val = al_reg_read32(&adapter->mac_regs_base->gen_v3.an_lt_2_data); + break; + case AL_ETH_AN__LT_LANE_3: + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_3_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].addr); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_3_data, + reg_addr); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_3_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].data); + val = al_reg_read32(&adapter->mac_regs_base->gen_v3.an_lt_3_data); + break; + default: + al_err("%s: Unknown Lane %d\n", __func__, lane); + return 0; + } + } + + + al_dbg("[%s]: %s - (%s) lane %d, reg %d, val 0x%x", adapter->name, __func__, + (an_lt == AL_ETH_AN_REGS) ? "AN" : "LT", lane, reg_addr, val); + + return (uint16_t)val; +} + +static void al_eth_an_lt_reg_write( + struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_regs_ids reg_id, + enum al_eth_an_lt_regs an_lt, + enum al_eth_an_lt_lane lane, + uint16_t val) +{ + uint16_t reg_addr; + + if (adapter->rev_id < AL_ETH_REV_ID_3) { + reg_addr = al_eth_an_lt_regs_addr[reg_id][AL_ETH_LT_UNIT_REV_1]; + if (an_lt == AL_ETH_AN_REGS) { + al_reg_write32(&adapter->mac_regs_base->kr.an_addr, reg_addr); + al_reg_write32(&adapter->mac_regs_base->kr.an_data, val); + } else { + al_reg_write32(&adapter->mac_regs_base->kr.pma_addr, reg_addr); + al_reg_write32(&adapter->mac_regs_base->kr.pma_data, val); + } + } else { + struct al_an_lt_wrapper_regs *regs = NULL; + + reg_addr = al_eth_an_lt_regs_addr[reg_id][AL_ETH_LT_UNIT_REV_2]; + + switch (lane) { + case AL_ETH_AN__LT_LANE_0: + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_0_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].addr); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_0_data, + reg_addr); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_0_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].data); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_0_data, + val); + break; + case AL_ETH_AN__LT_LANE_1: + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_1_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].addr); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_1_data, + reg_addr); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_1_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].data); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_1_data, + val); + break; + case AL_ETH_AN__LT_LANE_2: + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_2_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].addr); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_2_data, + reg_addr); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_2_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].data); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_2_data, + val); + break; + case AL_ETH_AN__LT_LANE_3: + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_3_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].addr); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_3_data, + reg_addr); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_3_addr, + (uintptr_t)®s->an_lt[adapter->curr_lt_unit].data); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_3_data, + val); + break; + default: + al_err("%s: Unknown Lane %d\n", __func__, lane); + return; + } + } + + + al_dbg("[%s]: %s - (%s) lane %d, reg %d, val 0x%x", adapter->name, __func__, + (an_lt == AL_ETH_AN_REGS) ? "AN" : "LT", lane, reg_addr, val); +} + +static void al_eth_an_lt_unit_config(struct al_hal_eth_adapter *adapter) +{ + struct al_an_lt_wrapper_regs *regs = NULL; + uint32_t cfg_lane_0 = (AN_LT_WRAPPER_GEN_CFG_BYPASS_RX | AN_LT_WRAPPER_GEN_CFG_BYPASS_TX); + uint32_t cfg_lane_1 = (AN_LT_WRAPPER_GEN_CFG_BYPASS_RX | AN_LT_WRAPPER_GEN_CFG_BYPASS_TX); + uint32_t cfg_lane_2 = (AN_LT_WRAPPER_GEN_CFG_BYPASS_RX | AN_LT_WRAPPER_GEN_CFG_BYPASS_TX); + uint32_t cfg_lane_3 = (AN_LT_WRAPPER_GEN_CFG_BYPASS_RX | AN_LT_WRAPPER_GEN_CFG_BYPASS_TX); + + switch (adapter->mac_mode) { + case AL_ETH_MAC_MODE_10GbE_Serial: + cfg_lane_0 = 0; + AL_REG_FIELD_SET(cfg_lane_0, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_SHIFT, + AL_ETH_AN_LT_UNIT_20_BIT); + AL_REG_FIELD_SET(cfg_lane_0, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_SHIFT, + AL_ETH_AN_LT_UNIT_20_BIT); + + adapter->curr_lt_unit = AL_ETH_AN_LT_UNIT_20_BIT; + + break; + case AL_ETH_MAC_MODE_KR_LL_25G: + cfg_lane_0 = 0; + AL_REG_FIELD_SET(cfg_lane_0, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_SHIFT, + AL_ETH_AN_LT_UNIT_32_BIT); + AL_REG_FIELD_SET(cfg_lane_0, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_SHIFT, + AL_ETH_AN_LT_UNIT_32_BIT); + + adapter->curr_lt_unit = AL_ETH_AN_LT_UNIT_32_BIT; + + break; + case AL_ETH_MAC_MODE_XLG_LL_40G: + cfg_lane_0 = 0; + AL_REG_FIELD_SET(cfg_lane_0, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_SHIFT, + AL_ETH_AN_LT_UNIT_16_BIT); + AL_REG_FIELD_SET(cfg_lane_0, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_SHIFT, + AL_ETH_AN_LT_UNIT_16_BIT); + + cfg_lane_1 = 0; + AL_REG_FIELD_SET(cfg_lane_1, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_SHIFT, + AL_ETH_AN_LT_UNIT_16_BIT); + AL_REG_FIELD_SET(cfg_lane_1, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_SHIFT, + AL_ETH_AN_LT_UNIT_16_BIT); + + cfg_lane_2 = 0; + AL_REG_FIELD_SET(cfg_lane_2, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_SHIFT, + AL_ETH_AN_LT_UNIT_16_BIT); + AL_REG_FIELD_SET(cfg_lane_2, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_SHIFT, + AL_ETH_AN_LT_UNIT_16_BIT); + + cfg_lane_3 = 0; + AL_REG_FIELD_SET(cfg_lane_3, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_SHIFT, + AL_ETH_AN_LT_UNIT_16_BIT); + AL_REG_FIELD_SET(cfg_lane_3, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_SHIFT, + AL_ETH_AN_LT_UNIT_16_BIT); + + adapter->curr_lt_unit = AL_ETH_AN_LT_UNIT_16_BIT; + + break; + case AL_ETH_MAC_MODE_XLG_LL_50G: + cfg_lane_0 = 0; + AL_REG_FIELD_SET(cfg_lane_0, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_SHIFT, + AL_ETH_AN_LT_UNIT_32_BIT); + AL_REG_FIELD_SET(cfg_lane_0, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_SHIFT, + AL_ETH_AN_LT_UNIT_32_BIT); + + cfg_lane_1 = 0; + AL_REG_FIELD_SET(cfg_lane_1, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_RX_SHIFT, + AL_ETH_AN_LT_UNIT_32_BIT); + AL_REG_FIELD_SET(cfg_lane_1, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_MASK, + AN_LT_WRAPPER_GEN_CFG_AN_LT_SEL_TX_SHIFT, + AL_ETH_AN_LT_UNIT_32_BIT); + + adapter->curr_lt_unit = AL_ETH_AN_LT_UNIT_32_BIT; + + break; + default: + al_err("%s: Unknown mac_mode\n", __func__); + return; + } + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_0_addr, + (uintptr_t)®s->gen.cfg); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_0_data, + cfg_lane_0); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_1_addr, + (uintptr_t)®s->gen.cfg); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_1_data, + cfg_lane_1); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_2_addr, + (uintptr_t)®s->gen.cfg); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_2_data, + cfg_lane_2); + + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_3_addr, + (uintptr_t)®s->gen.cfg); + al_reg_write32(&adapter->mac_regs_base->gen_v3.an_lt_3_data, + cfg_lane_3); +} + +void al_eth_lp_coeff_up_get( + struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane, + struct al_eth_kr_coef_up_data *lpcoeff) +{ + uint16_t reg; + + reg = al_eth_an_lt_reg_read(adapter, AL_ETH_KR_PMD_LP_COEF_UP, AL_ETH_LT_REGS, lane); + + lpcoeff->preset = + (AL_REG_BIT_GET( + reg, AL_ETH_KR_PMD_LP_COEF_UP_PRESET_SHIFT) != 0); + + lpcoeff->initialize = + (AL_REG_BIT_GET( + reg, AL_ETH_KR_PMD_LP_COEF_UP_INITIALIZE_SHIFT) != 0); + + lpcoeff->c_minus = AL_REG_FIELD_GET(reg, + AL_ETH_KR_PMD_LP_COEF_UP_MINUS_MASK, + AL_ETH_KR_PMD_LP_COEF_UP_MINUS_SHIFT); + + lpcoeff->c_zero = AL_REG_FIELD_GET(reg, + AL_ETH_KR_PMD_LP_COEF_UP_ZERO_MASK, + AL_ETH_KR_PMD_LP_COEF_UP_ZERO_SHIFT); + + lpcoeff->c_plus = AL_REG_FIELD_GET(reg, + AL_ETH_KR_PMD_LP_COEF_UP_PLUS_MASK, + AL_ETH_KR_PMD_LP_COEF_UP_PLUS_SHIFT); +} + +void al_eth_lp_status_report_get( + struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane, + struct al_eth_kr_status_report_data *status) +{ + uint16_t reg; + + reg = al_eth_an_lt_reg_read(adapter, AL_ETH_KR_PMD_LP_STATUS_REPORT, AL_ETH_LT_REGS, lane); + + status->c_minus = AL_REG_FIELD_GET(reg, + AL_ETH_KR_PMD_LP_STATUS_REPORT_MINUS_MASK, + AL_ETH_KR_PMD_LP_STATUS_REPORT_MINUS_SHIFT); + + status->c_zero = AL_REG_FIELD_GET(reg, + AL_ETH_KR_PMD_LP_STATUS_REPORT_ZERO_MASK, + AL_ETH_KR_PMD_LP_STATUS_REPORT_ZERO_SHIFT); + + status->c_plus = AL_REG_FIELD_GET(reg, + AL_ETH_KR_PMD_LP_STATUS_REPORT_PLUS_MASK, + AL_ETH_KR_PMD_LP_STATUS_REPORT_PLUS_SHIFT); + + status->receiver_ready = + (AL_REG_BIT_GET( + reg, AL_ETH_KR_PMD_LP_STATUS_RECEIVER_READY_SHIFT) != 0); + +} + +void al_eth_ld_coeff_up_set( + struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane, + struct al_eth_kr_coef_up_data *ldcoeff) +{ + uint16_t reg = 0; + + if (ldcoeff->preset) + AL_REG_BIT_SET(reg, AL_ETH_KR_PMD_LD_COEF_UP_PRESET_SHIFT); + + if (ldcoeff->initialize) + AL_REG_BIT_SET(reg, AL_ETH_KR_PMD_LD_COEF_UP_INITIALIZE_SHIFT); + + AL_REG_FIELD_SET(reg, + AL_ETH_KR_PMD_LD_COEF_UP_MINUS_MASK, + AL_ETH_KR_PMD_LD_COEF_UP_MINUS_SHIFT, + ldcoeff->c_minus); + + AL_REG_FIELD_SET(reg, + AL_ETH_KR_PMD_LD_COEF_UP_ZERO_MASK, + AL_ETH_KR_PMD_LD_COEF_UP_ZERO_SHIFT, + ldcoeff->c_zero); + + AL_REG_FIELD_SET(reg, + AL_ETH_KR_PMD_LD_COEF_UP_PLUS_MASK, + AL_ETH_KR_PMD_LD_COEF_UP_PLUS_SHIFT, + ldcoeff->c_plus); + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_PMD_LD_COEF_UP, AL_ETH_LT_REGS, lane, reg); +} + +void al_eth_ld_status_report_set( + struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane, + struct al_eth_kr_status_report_data *status) +{ + uint16_t reg = 0; + + AL_REG_FIELD_SET(reg, + AL_ETH_KR_PMD_LD_STATUS_REPORT_MINUS_MASK, + AL_ETH_KR_PMD_LD_STATUS_REPORT_MINUS_SHIFT, + status->c_minus); + + AL_REG_FIELD_SET(reg, + AL_ETH_KR_PMD_LD_STATUS_REPORT_ZERO_MASK, + AL_ETH_KR_PMD_LD_STATUS_REPORT_ZERO_SHIFT, + status->c_zero); + + AL_REG_FIELD_SET(reg, + AL_ETH_KR_PMD_LD_STATUS_REPORT_PLUS_MASK, + AL_ETH_KR_PMD_LD_STATUS_REPORT_PLUS_SHIFT, + status->c_plus); + + if (status->receiver_ready) + AL_REG_BIT_SET(reg, + AL_ETH_KR_PMD_LD_STATUS_REPORT_RECEIVER_READY_SHIFT); + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_PMD_LD_STATUS_REPORT, AL_ETH_LT_REGS, lane, reg); +} + +al_bool al_eth_kr_receiver_frame_lock_get(struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane) +{ + uint16_t reg; + + reg = al_eth_an_lt_reg_read(adapter, AL_ETH_KR_PMD_STATUS, AL_ETH_LT_REGS, lane); + + return (AL_REG_BIT_GET(reg, + AL_ETH_KR_PMD_STATUS_RECEIVER_FRAME_LOCK_SHIFT) != 0); +} + +al_bool al_eth_kr_startup_proto_prog_get(struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane) +{ + uint16_t reg; + + reg = al_eth_an_lt_reg_read(adapter, AL_ETH_KR_PMD_STATUS, AL_ETH_LT_REGS, lane); + + return (AL_REG_BIT_GET( + reg, AL_ETH_KR_PMD_STATUS_RECEIVER_START_UP_PROTO_PROG_SHIFT) != 0); +} + +al_bool al_eth_kr_training_status_fail_get(struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane) +{ + uint16_t reg; + + reg = al_eth_an_lt_reg_read(adapter, AL_ETH_KR_PMD_STATUS, AL_ETH_LT_REGS, lane); + + return (AL_REG_BIT_GET(reg, AL_ETH_KR_PMD_STATUS_FAILURE_SHIFT) != 0); +} + +void al_eth_receiver_ready_set(struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane) +{ + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_PMD_STATUS, AL_ETH_LT_REGS, lane, 1); +} + +/*************************** auto negotiation *********************************/ +static int al_eth_kr_an_validate_adv(struct al_hal_eth_adapter *adapter, + struct al_eth_an_adv *an_adv) +{ + al_assert(adapter); + + if (an_adv == NULL) + return 0; + + if (an_adv->selector_field != 1) { + al_err("[%s]: %s failed on selector_field (%d)\n", + adapter->name, __func__, an_adv->selector_field); + return -EINVAL; + } + + if (an_adv->capability & AL_BIT(2)) { + al_err("[%s]: %s failed on capability bit 2 (%d)\n", + adapter->name, __func__, an_adv->capability); + return -EINVAL; + } + + if (an_adv->remote_fault) { + al_err("[%s]: %s failed on remote_fault (%d)\n", + adapter->name, __func__, an_adv->remote_fault); + return -EINVAL; + } + + if (an_adv->acknowledge) { + al_err("[%s]: %s failed on acknowledge (%d)\n", + adapter->name, __func__, an_adv->acknowledge); + return -EINVAL; + } + + return 0; +} + +static int al_eth_kr_an_write_adv(struct al_hal_eth_adapter *adapter, + struct al_eth_an_adv *an_adv) +{ + uint16_t reg; + + if(an_adv == NULL) + return 0; + + reg = 0; + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_ADV1_SEL_FIELD_MASK, + AL_ETH_KR_AN_ADV1_SEL_FIELD_SHIFT, + an_adv->selector_field); + + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_ADV1_ECHOED_NONCE_MASK, + AL_ETH_KR_AN_ADV1_ECHOED_NONCE_SHIFT, + an_adv->echoed_nonce); + + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_ADV1_CAPABILITY_MASK, + AL_ETH_KR_AN_ADV1_CAPABILITY_SHIFT, + an_adv->capability); + + AL_REG_BIT_VAL_SET(reg, AL_ETH_KR_AN_ADV1_REM_FAULT_SHIFT, + an_adv->remote_fault); + + AL_REG_BIT_VAL_SET(reg, AL_ETH_KR_AN_ADV1_ACK_SHIFT, + an_adv->acknowledge); + + AL_REG_BIT_VAL_SET(reg, AL_ETH_KR_AN_ADV1_NEXT_PAGE_SHIFT, + an_adv->next_page); + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_AN_ADV0, AL_ETH_AN_REGS, + AL_ETH_AN__LT_LANE_0, reg); + + reg = 0; + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_ADV2_TX_NONCE_MASK, + AL_ETH_KR_AN_ADV2_TX_NONCE_SHIFT, + an_adv->transmitted_nonce); + + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_ADV2_TECH_MASK, + AL_ETH_KR_AN_ADV2_TECH_SHIFT, + an_adv->technology); + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_AN_ADV1, AL_ETH_AN_REGS, + AL_ETH_AN__LT_LANE_0, reg); + + reg = 0; + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_ADV3_TECH_MASK, + AL_ETH_KR_AN_ADV3_TECH_SHIFT, + an_adv->technology >> 11); + + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_ADV3_FEC_MASK, + AL_ETH_KR_AN_ADV3_FEC_SHIFT, + an_adv->fec_capability); + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_AN_ADV2, AL_ETH_AN_REGS, + AL_ETH_AN__LT_LANE_0, reg); + + return 0; +} + +void al_eth_kr_an_read_adv(struct al_hal_eth_adapter *adapter, + struct al_eth_an_adv *an_adv) +{ + int16_t reg; + + al_assert(an_adv != NULL); + + + reg = al_eth_an_lt_reg_read(adapter, AL_ETH_KR_AN_REM_ADV0, + AL_ETH_AN_REGS, AL_ETH_AN__LT_LANE_0); + + an_adv->selector_field = AL_REG_FIELD_GET(reg, + AL_ETH_KR_AN_ADV1_SEL_FIELD_MASK, + AL_ETH_KR_AN_ADV1_SEL_FIELD_SHIFT); + + an_adv->echoed_nonce = AL_REG_FIELD_GET(reg, + AL_ETH_KR_AN_ADV1_ECHOED_NONCE_MASK, + AL_ETH_KR_AN_ADV1_ECHOED_NONCE_SHIFT); + + an_adv->capability = AL_REG_FIELD_GET(reg, + AL_ETH_KR_AN_ADV1_CAPABILITY_MASK, + AL_ETH_KR_AN_ADV1_CAPABILITY_SHIFT); + + an_adv->remote_fault = AL_REG_BIT_GET(reg, + AL_ETH_KR_AN_ADV1_REM_FAULT_SHIFT); + + an_adv->acknowledge = AL_REG_BIT_GET(reg, + AL_ETH_KR_AN_ADV1_ACK_SHIFT); + + an_adv->next_page = AL_REG_BIT_GET(reg, + AL_ETH_KR_AN_ADV1_NEXT_PAGE_SHIFT); + + + reg = al_eth_an_lt_reg_read(adapter, AL_ETH_KR_AN_REM_ADV1, + AL_ETH_AN_REGS, AL_ETH_AN__LT_LANE_0); + + an_adv->transmitted_nonce = AL_REG_FIELD_GET(reg, + AL_ETH_KR_AN_ADV2_TX_NONCE_MASK, + AL_ETH_KR_AN_ADV2_TX_NONCE_SHIFT); + + an_adv->technology = AL_REG_FIELD_GET(reg, + AL_ETH_KR_AN_ADV2_TECH_MASK, + AL_ETH_KR_AN_ADV2_TECH_SHIFT); + + + reg = al_eth_an_lt_reg_read(adapter, AL_ETH_KR_AN_REM_ADV2, + AL_ETH_AN_REGS, AL_ETH_AN__LT_LANE_0); + + an_adv->technology |= (AL_REG_FIELD_GET(reg, + AL_ETH_KR_AN_ADV3_TECH_MASK, + AL_ETH_KR_AN_ADV3_TECH_SHIFT) << 11); + + an_adv->fec_capability = AL_REG_FIELD_GET(reg, + AL_ETH_KR_AN_ADV3_FEC_MASK, + AL_ETH_KR_AN_ADV3_FEC_SHIFT); +} + +int al_eth_kr_next_page_read(struct al_hal_eth_adapter *adapter, + struct al_eth_an_np *np) +{ + uint16_t reg; + + reg = al_eth_an_lt_reg_read(adapter, + AL_ETH_KR_AN_REM_XNP_ADV0, + AL_ETH_AN_REGS, + AL_ETH_AN__LT_LANE_0); + + np->unformatted_code_field = AL_REG_FIELD_GET(reg, AL_ETH_KR_AN_NP_ADV1_DATA1_MASK, + AL_ETH_KR_AN_NP_ADV1_DATA1_SHIFT); + + np->toggle = AL_REG_FIELD_GET(reg, AL_ETH_KR_AN_NP_ADV1_TOGGLE_MASK, + AL_ETH_KR_AN_NP_ADV1_TOGGLE_SHIFT); + + np->ack2 = AL_REG_FIELD_GET(reg, AL_ETH_KR_AN_NP_ADV1_ACK2_MASK, + AL_ETH_KR_AN_NP_ADV1_ACK2_SHIFT); + + np->msg_page = AL_REG_FIELD_GET(reg, AL_ETH_KR_AN_NP_ADV1_MSG_PAGE_MASK, + AL_ETH_KR_AN_NP_ADV1_MSG_PAGE_SHIFT); + + np->next_page = AL_REG_FIELD_GET(reg, AL_ETH_KR_AN_NP_ADV1_NP_MASK, + AL_ETH_KR_AN_NP_ADV1_NP_SHIFT); + + np->unformatted_code_field1 = al_eth_an_lt_reg_read(adapter, + AL_ETH_KR_AN_REM_XNP_ADV1, + AL_ETH_AN_REGS, + AL_ETH_AN__LT_LANE_0); + np->unformatted_code_field2 = al_eth_an_lt_reg_read(adapter, + AL_ETH_KR_AN_REM_XNP_ADV2, + AL_ETH_AN_REGS, + AL_ETH_AN__LT_LANE_0); + + return 0; +} + +int al_eth_kr_next_page_write(struct al_hal_eth_adapter *adapter, + struct al_eth_an_np *np) +{ + uint16_t reg = 0; + + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_NP_ADV1_DATA1_MASK, + AL_ETH_KR_AN_NP_ADV1_DATA1_SHIFT, + np->unformatted_code_field); + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_NP_ADV1_TOGGLE_MASK, + AL_ETH_KR_AN_NP_ADV1_TOGGLE_SHIFT, + np->toggle); + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_NP_ADV1_ACK2_MASK, + AL_ETH_KR_AN_NP_ADV1_ACK2_SHIFT, + np->ack2); + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_NP_ADV1_MSG_PAGE_MASK, + AL_ETH_KR_AN_NP_ADV1_MSG_PAGE_SHIFT, + np->msg_page); + AL_REG_FIELD_SET(reg, AL_ETH_KR_AN_NP_ADV1_NP_MASK, + AL_ETH_KR_AN_NP_ADV1_NP_SHIFT, + np->next_page); + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_AN_XNP_ADV0, AL_ETH_AN_REGS, + AL_ETH_AN__LT_LANE_0, reg); + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_AN_XNP_ADV1, AL_ETH_AN_REGS, + AL_ETH_AN__LT_LANE_0, np->unformatted_code_field1); + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_AN_XNP_ADV2, AL_ETH_AN_REGS, + AL_ETH_AN__LT_LANE_0, np->unformatted_code_field2); + + return 0; +} + +int al_eth_kr_an_init(struct al_hal_eth_adapter *adapter, + struct al_eth_an_adv *an_adv) +{ + int rc; + + if (adapter->rev_id > AL_ETH_REV_ID_2) + al_eth_an_lt_unit_config(adapter); + + rc = al_eth_kr_an_validate_adv(adapter, an_adv); + if (rc) + return rc; + + rc = al_eth_kr_an_write_adv(adapter, an_adv); + if (rc) + return rc; + + /* clear status */ + al_eth_an_lt_reg_read(adapter, AL_ETH_KR_AN_STATUS, AL_ETH_AN_REGS, AL_ETH_AN__LT_LANE_0); + + al_dbg("[%s]: autonegotiation initialized successfully", adapter->name); + return 0; +} + +int al_eth_kr_an_start(struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane, + al_bool next_page_enable, + al_bool lt_enable) +{ + uint16_t control = AL_ETH_KR_AN_CONTROL_ENABLE | AL_ETH_KR_AN_CONTROL_RESTART; + + al_dbg("Eth [%s]: enable autonegotiation. lt_en %s", + adapter->name, (lt_enable == AL_TRUE) ? "yes" : "no"); + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_PMD_CONTROL, AL_ETH_LT_REGS, + lane, AL_BIT(AL_ETH_KR_PMD_CONTROL_RESTART)); + + if (next_page_enable == AL_TRUE) + control |= AL_ETH_KR_AN_CONTROL_NP_ENABLE; + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_AN_CONTROL, AL_ETH_AN_REGS, + lane, control); + + if (lt_enable == AL_TRUE) { + al_eth_kr_lt_initialize(adapter, lane); + } + + return 0; +} + +void al_eth_kr_an_stop(struct al_hal_eth_adapter *adapter) +{ + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_AN_CONTROL, AL_ETH_AN_REGS, + AL_ETH_AN__LT_LANE_0, 0); +} + +void al_eth_kr_an_status_check(struct al_hal_eth_adapter *adapter, + al_bool *page_received, + al_bool *an_completed, + al_bool *error) +{ + uint16_t reg; + + reg = al_eth_an_lt_reg_read(adapter, AL_ETH_KR_AN_STATUS, + AL_ETH_AN_REGS, AL_ETH_AN__LT_LANE_0); + + if ((reg & AL_ETH_KR_AN_STATUS_CHECK_MASK) != + AL_ETH_KR_AN_STATUS_CHECK_NO_ERROR) { + al_err("[%s]: %s AN_STATUS (0x%x) indicated error\n", + adapter->name, __func__, reg); + + *error = AL_TRUE; + } + + if (reg & AL_ETH_KR_AN_STATUS_BASE_PAGE_RECEIVED) + *page_received = AL_TRUE; + else + *page_received = AL_FALSE; + + if (reg & AL_ETH_KR_AN_STATUS_COMPLETED) + *an_completed = AL_TRUE; + else + *an_completed = AL_FALSE; +} + + +/****************************** KR Link Training *****************************/ +void al_eth_kr_lt_restart(struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane) +{ + al_dbg("[%s]: KR LT Restart Link Training.\n", adapter->name); + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_PMD_CONTROL, AL_ETH_LT_REGS, + lane, (AL_BIT(AL_ETH_KR_PMD_CONTROL_ENABLE) | + AL_BIT(AL_ETH_KR_PMD_CONTROL_RESTART))); +} + +void al_eth_kr_lt_stop(struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane) +{ + al_dbg("[%s]: KR LT Stop Link Training.\n", adapter->name); + + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_PMD_CONTROL, AL_ETH_LT_REGS, + lane, AL_BIT(AL_ETH_KR_PMD_CONTROL_RESTART)); +} + +void al_eth_kr_lt_initialize(struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane) +{ + al_dbg("[%s]: KR LT Initialize.\n", adapter->name); + + /* Reset LT state machine */ + al_eth_kr_lt_stop(adapter, lane); + + /* clear receiver status */ + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_PMD_STATUS, AL_ETH_LT_REGS, lane, 0); + + /* Coefficient Update to all zero (no command, hold) */ + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_PMD_LD_COEF_UP, AL_ETH_LT_REGS, lane, 0); + /* Coefficient Status to all zero (not_updated) */ + al_eth_an_lt_reg_write(adapter, AL_ETH_KR_PMD_LD_STATUS_REPORT, AL_ETH_LT_REGS, lane, 0); + + /* start */ + al_eth_kr_lt_restart(adapter, lane); +} + +al_bool al_eth_kr_lt_frame_lock_wait(struct al_hal_eth_adapter *adapter, + enum al_eth_an_lt_lane lane, + uint32_t timeout) +{ + uint32_t loop; + uint16_t reg = 0; + + for (loop = 0; loop < timeout; loop++) { + reg = al_eth_an_lt_reg_read(adapter, AL_ETH_KR_PMD_STATUS, AL_ETH_LT_REGS, lane); + + if (AL_REG_BIT_GET(reg, AL_ETH_KR_PMD_STATUS_FAILURE_SHIFT)) { + al_info("[%s]: Failed on Training Failure." + " loops %d PMD STATUS 0x%04x\n", + adapter->name, loop, reg); + + return AL_FALSE; + } + if (AL_REG_BIT_GET(reg, + AL_ETH_KR_PMD_STATUS_RECEIVER_FRAME_LOCK_SHIFT)) { + al_dbg("[%s]: Frame lock received." + " loops %d PMD STATUS 0x%04x\n", + adapter->name, loop, reg); + + return AL_TRUE; + } + al_udelay(1); + } + al_info("[%s]: Failed on timeout. PMD STATUS 0x%04x\n", + adapter->name, reg); + + return AL_FALSE; +} |