mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-18 06:15:12 +00:00
93ef6ee5c2
The value is read from the register TXGBE_RX_GEN_CTL3, and it should be written back to TXGBE_RX_GEN_CTL3 when it changes some fields. Cc: stable@vger.kernel.org Fixes: f629acc6f210 ("net: pcs: xpcs: support to switch mode for Wangxun NICs") Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com> Reported-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> Link: https://patch.msgid.link/20240924022857.865422-1-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
210 lines
7.2 KiB
C
210 lines
7.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
|
|
|
|
#include <linux/pcs/pcs-xpcs.h>
|
|
#include <linux/mdio.h>
|
|
#include "pcs-xpcs.h"
|
|
|
|
/* VR_XS_PMA_MMD */
|
|
#define TXGBE_PMA_MMD 0x8020
|
|
#define TXGBE_TX_GENCTL1 0x11
|
|
#define TXGBE_TX_GENCTL1_VBOOST_LVL GENMASK(10, 8)
|
|
#define TXGBE_TX_GENCTL1_VBOOST_EN0 BIT(4)
|
|
#define TXGBE_TX_GEN_CTL2 0x12
|
|
#define TXGBE_TX_GEN_CTL2_TX0_WIDTH(v) FIELD_PREP(GENMASK(9, 8), v)
|
|
#define TXGBE_TX_RATE_CTL 0x14
|
|
#define TXGBE_TX_RATE_CTL_TX0_RATE(v) FIELD_PREP(GENMASK(2, 0), v)
|
|
#define TXGBE_RX_GEN_CTL2 0x32
|
|
#define TXGBE_RX_GEN_CTL2_RX0_WIDTH(v) FIELD_PREP(GENMASK(9, 8), v)
|
|
#define TXGBE_RX_GEN_CTL3 0x33
|
|
#define TXGBE_RX_GEN_CTL3_LOS_TRSHLD0 GENMASK(2, 0)
|
|
#define TXGBE_RX_RATE_CTL 0x34
|
|
#define TXGBE_RX_RATE_CTL_RX0_RATE(v) FIELD_PREP(GENMASK(1, 0), v)
|
|
#define TXGBE_RX_EQ_ATTN_CTL 0x37
|
|
#define TXGBE_RX_EQ_ATTN_LVL0 GENMASK(2, 0)
|
|
#define TXGBE_RX_EQ_CTL0 0x38
|
|
#define TXGBE_RX_EQ_CTL0_VGA1_GAIN(v) FIELD_PREP(GENMASK(15, 12), v)
|
|
#define TXGBE_RX_EQ_CTL0_VGA2_GAIN(v) FIELD_PREP(GENMASK(11, 8), v)
|
|
#define TXGBE_RX_EQ_CTL0_CTLE_POLE(v) FIELD_PREP(GENMASK(7, 5), v)
|
|
#define TXGBE_RX_EQ_CTL0_CTLE_BOOST(v) FIELD_PREP(GENMASK(4, 0), v)
|
|
#define TXGBE_RX_EQ_CTL4 0x3C
|
|
#define TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0 BIT(4)
|
|
#define TXGBE_RX_EQ_CTL4_CONT_ADAPT0 BIT(0)
|
|
#define TXGBE_AFE_DFE_ENABLE 0x3D
|
|
#define TXGBE_DFE_EN_0 BIT(4)
|
|
#define TXGBE_AFE_EN_0 BIT(0)
|
|
#define TXGBE_DFE_TAP_CTL0 0x3E
|
|
#define TXGBE_MPLLA_CTL0 0x51
|
|
#define TXGBE_MPLLA_CTL2 0x53
|
|
#define TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN BIT(10)
|
|
#define TXGBE_MPLLA_CTL2_DIV10_CLK_EN BIT(9)
|
|
#define TXGBE_MPLLA_CTL3 0x57
|
|
#define TXGBE_MISC_CTL0 0x70
|
|
#define TXGBE_MISC_CTL0_PLL BIT(15)
|
|
#define TXGBE_MISC_CTL0_CR_PARA_SEL BIT(14)
|
|
#define TXGBE_MISC_CTL0_RX_VREF(v) FIELD_PREP(GENMASK(12, 8), v)
|
|
#define TXGBE_VCO_CAL_LD0 0x72
|
|
#define TXGBE_VCO_CAL_REF0 0x76
|
|
|
|
static int txgbe_read_pma(struct dw_xpcs *xpcs, int reg)
|
|
{
|
|
return xpcs_read(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg);
|
|
}
|
|
|
|
static int txgbe_write_pma(struct dw_xpcs *xpcs, int reg, u16 val)
|
|
{
|
|
return xpcs_write(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val);
|
|
}
|
|
|
|
static void txgbe_pma_config_10gbaser(struct dw_xpcs *xpcs)
|
|
{
|
|
int val;
|
|
|
|
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x21);
|
|
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0);
|
|
val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
|
|
val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
|
|
txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
|
|
txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
|
|
TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
|
|
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x549);
|
|
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x29);
|
|
txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, 0);
|
|
txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, 0);
|
|
txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(3));
|
|
txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(3));
|
|
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN |
|
|
TXGBE_MPLLA_CTL2_DIV10_CLK_EN);
|
|
|
|
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_CTLE_POLE(2) |
|
|
TXGBE_RX_EQ_CTL0_CTLE_BOOST(5));
|
|
val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
|
|
val &= ~TXGBE_RX_EQ_ATTN_LVL0;
|
|
txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
|
|
txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0xBE);
|
|
val = txgbe_read_pma(xpcs, TXGBE_AFE_DFE_ENABLE);
|
|
val &= ~(TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0);
|
|
txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, val);
|
|
val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_CTL4);
|
|
val &= ~TXGBE_RX_EQ_CTL4_CONT_ADAPT0;
|
|
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, val);
|
|
}
|
|
|
|
static void txgbe_pma_config_1g(struct dw_xpcs *xpcs)
|
|
{
|
|
int val;
|
|
|
|
val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
|
|
val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
|
|
val &= ~TXGBE_TX_GENCTL1_VBOOST_EN0;
|
|
txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
|
|
txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
|
|
TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
|
|
|
|
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_VGA1_GAIN(7) |
|
|
TXGBE_RX_EQ_CTL0_VGA2_GAIN(7) | TXGBE_RX_EQ_CTL0_CTLE_BOOST(6));
|
|
val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
|
|
val &= ~TXGBE_RX_EQ_ATTN_LVL0;
|
|
txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
|
|
txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0);
|
|
val = txgbe_read_pma(xpcs, TXGBE_RX_GEN_CTL3);
|
|
val = u16_replace_bits(val, 0x4, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0);
|
|
txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL3, val);
|
|
|
|
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x20);
|
|
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0x46);
|
|
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x540);
|
|
txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x2A);
|
|
txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, 0);
|
|
txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0);
|
|
txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, TXGBE_TX_RATE_CTL_TX0_RATE(3));
|
|
txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, TXGBE_RX_RATE_CTL_RX0_RATE(3));
|
|
txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(1));
|
|
txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(1));
|
|
txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV10_CLK_EN);
|
|
}
|
|
|
|
static int txgbe_pcs_poll_power_up(struct dw_xpcs *xpcs)
|
|
{
|
|
int val, ret;
|
|
|
|
/* Wait xpcs power-up good */
|
|
ret = read_poll_timeout(xpcs_read_vpcs, val,
|
|
(val & DW_PSEQ_ST) == DW_PSEQ_ST_GOOD,
|
|
10000, 1000000, false,
|
|
xpcs, DW_VR_XS_PCS_DIG_STS);
|
|
if (ret < 0)
|
|
dev_err(&xpcs->mdiodev->dev, "xpcs power-up timeout\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int txgbe_pma_init_done(struct dw_xpcs *xpcs)
|
|
{
|
|
int val, ret;
|
|
|
|
xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_VR_RST | DW_EN_VSMMD1);
|
|
|
|
/* wait pma initialization done */
|
|
ret = read_poll_timeout(xpcs_read_vpcs, val, !(val & DW_VR_RST),
|
|
100000, 10000000, false,
|
|
xpcs, DW_VR_XS_PCS_DIG_CTRL1);
|
|
if (ret < 0)
|
|
dev_err(&xpcs->mdiodev->dev, "xpcs pma initialization timeout\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool txgbe_xpcs_mode_quirk(struct dw_xpcs *xpcs)
|
|
{
|
|
int ret;
|
|
|
|
/* When txgbe do LAN reset, PCS will change to default 10GBASE-R mode */
|
|
ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_CTRL2);
|
|
ret &= MDIO_PCS_CTRL2_TYPE;
|
|
if ((ret == MDIO_PCS_CTRL2_10GBR &&
|
|
xpcs->interface != PHY_INTERFACE_MODE_10GBASER) ||
|
|
xpcs->interface == PHY_INTERFACE_MODE_SGMII)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface)
|
|
{
|
|
int val, ret;
|
|
|
|
switch (interface) {
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
if (xpcs->interface == interface && !txgbe_xpcs_mode_quirk(xpcs))
|
|
return 0;
|
|
|
|
xpcs->interface = interface;
|
|
|
|
ret = txgbe_pcs_poll_power_up(xpcs);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (interface == PHY_INTERFACE_MODE_10GBASER) {
|
|
xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBR);
|
|
val = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1);
|
|
val |= MDIO_CTRL1_SPEED10G;
|
|
xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, val);
|
|
txgbe_pma_config_10gbaser(xpcs);
|
|
} else {
|
|
xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBX);
|
|
xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, 0);
|
|
xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL1, 0);
|
|
txgbe_pma_config_1g(xpcs);
|
|
}
|
|
|
|
return txgbe_pma_init_done(xpcs);
|
|
}
|