diff options
Diffstat (limited to 'drivers/net/pcs')
-rw-r--r-- | drivers/net/pcs/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/pcs/pcs-lynx.c | 2 | ||||
-rw-r--r-- | drivers/net/pcs/pcs-mtk-lynxi.c | 8 | ||||
-rw-r--r-- | drivers/net/pcs/pcs-rzn1-miic.c | 11 | ||||
-rw-r--r-- | drivers/net/pcs/pcs-xpcs-wx.c | 209 | ||||
-rw-r--r-- | drivers/net/pcs/pcs-xpcs.c | 112 | ||||
-rw-r--r-- | drivers/net/pcs/pcs-xpcs.h | 17 |
7 files changed, 345 insertions, 16 deletions
diff --git a/drivers/net/pcs/Makefile b/drivers/net/pcs/Makefile index ea662a7989b2..fb1694192ae6 100644 --- a/drivers/net/pcs/Makefile +++ b/drivers/net/pcs/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Makefile for Linux PCS drivers -pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o +pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o pcs-xpcs-wx.o obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o diff --git a/drivers/net/pcs/pcs-lynx.c b/drivers/net/pcs/pcs-lynx.c index 9021b96d4f9d..dc3962b2aa6b 100644 --- a/drivers/net/pcs/pcs-lynx.c +++ b/drivers/net/pcs/pcs-lynx.c @@ -216,7 +216,7 @@ static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, /* The PCS needs to be configured manually only * when not operating on in-band mode */ - if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) return; if (duplex == DUPLEX_HALF) diff --git a/drivers/net/pcs/pcs-mtk-lynxi.c b/drivers/net/pcs/pcs-mtk-lynxi.c index b0f3ede945d9..8501dd365279 100644 --- a/drivers/net/pcs/pcs-mtk-lynxi.c +++ b/drivers/net/pcs/pcs-mtk-lynxi.c @@ -233,11 +233,19 @@ static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, } } +static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs) +{ + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + + mpcs->interface = PHY_INTERFACE_MODE_NA; +} + static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = { .pcs_get_state = mtk_pcs_lynxi_get_state, .pcs_config = mtk_pcs_lynxi_config, .pcs_an_restart = mtk_pcs_lynxi_restart_an, .pcs_link_up = mtk_pcs_lynxi_link_up, + .pcs_disable = mtk_pcs_lynxi_disable, }; struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, diff --git a/drivers/net/pcs/pcs-rzn1-miic.c b/drivers/net/pcs/pcs-rzn1-miic.c index 323bec5e57f8..97139c07130f 100644 --- a/drivers/net/pcs/pcs-rzn1-miic.c +++ b/drivers/net/pcs/pcs-rzn1-miic.c @@ -12,6 +12,7 @@ #include <linux/of_platform.h> #include <linux/pcs-rzn1-miic.h> #include <linux/phylink.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <dt-bindings/net/pcs-rzn1-miic.h> @@ -313,15 +314,21 @@ struct phylink_pcs *miic_create(struct device *dev, struct device_node *np) pdev = of_find_device_by_node(pcs_np); of_node_put(pcs_np); - if (!pdev || !platform_get_drvdata(pdev)) + if (!pdev || !platform_get_drvdata(pdev)) { + if (pdev) + put_device(&pdev->dev); return ERR_PTR(-EPROBE_DEFER); + } miic_port = kzalloc(sizeof(*miic_port), GFP_KERNEL); - if (!miic_port) + if (!miic_port) { + put_device(&pdev->dev); return ERR_PTR(-ENOMEM); + } miic = platform_get_drvdata(pdev); device_link_add(dev, miic->dev, DL_FLAG_AUTOREMOVE_CONSUMER); + put_device(&pdev->dev); miic_port->miic = miic; miic_port->port = port - 1; diff --git a/drivers/net/pcs/pcs-xpcs-wx.c b/drivers/net/pcs/pcs-xpcs-wx.c new file mode 100644 index 000000000000..19c75886f070 --- /dev/null +++ b/drivers/net/pcs/pcs-xpcs-wx.c @@ -0,0 +1,209 @@ +// 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_EQ_ATTN_CTL, 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); +} diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 44b037646865..4dbc21f604f2 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -228,16 +228,39 @@ static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg, return xpcs_write(xpcs, dev, DW_VENDOR | reg, val); } -static int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg) +int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg) { return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg); } -static int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val) +int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val) { return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val); } +static int xpcs_dev_flag(struct dw_xpcs *xpcs) +{ + int ret, oui; + + ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID1); + if (ret < 0) + return ret; + + oui = ret; + + ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID2); + if (ret < 0) + return ret; + + ret = (ret >> 10) & 0x3F; + oui |= ret << 16; + + if (oui == DW_OUI_WX) + xpcs->dev_flag = DW_DEV_TXGBE; + + return 0; +} + static int xpcs_poll_reset(struct dw_xpcs *xpcs, int dev) { /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ @@ -660,7 +683,10 @@ EXPORT_SYMBOL_GPL(xpcs_config_eee); static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int neg_mode) { - int ret, mdio_ctrl; + int ret, mdio_ctrl, tx_conf; + + if (xpcs->dev_flag == DW_DEV_TXGBE) + xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1); /* For AN for C37 SGMII mode, the settings are :- * 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case @@ -697,9 +723,15 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, ret |= (DW_VR_MII_PCS_MODE_C37_SGMII << DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT & DW_VR_MII_PCS_MODE_MASK); - ret |= (DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII << - DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT & - DW_VR_MII_TX_CONFIG_MASK); + if (xpcs->dev_flag == DW_DEV_TXGBE) { + ret |= DW_VR_MII_AN_CTRL_8BIT; + /* Hardware requires it to be PHY side SGMII */ + tx_conf = DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII; + } else { + tx_conf = DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII; + } + ret |= tx_conf << DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT & + DW_VR_MII_TX_CONFIG_MASK; ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); if (ret < 0) return ret; @@ -713,6 +745,9 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, else ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; + if (xpcs->dev_flag == DW_DEV_TXGBE) + ret |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL; + ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); if (ret < 0) return ret; @@ -732,6 +767,9 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, int ret, mdio_ctrl, adv; bool changed = 0; + if (xpcs->dev_flag == DW_DEV_TXGBE) + xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1); + /* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must * be disabled first:- * 1) VR_MII_MMD_CTRL Bit(12)[AN_ENABLE] = 0b @@ -753,6 +791,8 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, return ret; ret &= ~DW_VR_MII_PCS_MODE_MASK; + if (!xpcs->pcs.poll) + ret |= DW_VR_MII_AN_INTR_EN; ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); if (ret < 0) return ret; @@ -818,6 +858,12 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, if (!compat) return -ENODEV; + if (xpcs->dev_flag == DW_DEV_TXGBE) { + ret = txgbe_xpcs_switch_mode(xpcs, interface); + if (ret) + return ret; + } + switch (compat->an_mode) { case DW_10GBASER: break; @@ -977,6 +1023,33 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, state->duplex = DUPLEX_FULL; else state->duplex = DUPLEX_HALF; + } else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) { + int speed, duplex; + + state->link = true; + + speed = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); + if (speed < 0) + return speed; + + speed &= SGMII_SPEED_SS13 | SGMII_SPEED_SS6; + if (speed == SGMII_SPEED_SS6) + state->speed = SPEED_1000; + else if (speed == SGMII_SPEED_SS13) + state->speed = SPEED_100; + else if (speed == 0) + state->speed = SPEED_10; + + duplex = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_ADVERTISE); + if (duplex < 0) + return duplex; + + if (duplex & DW_FULL_DUPLEX) + state->duplex = DUPLEX_FULL; + else if (duplex & DW_HALF_DUPLEX) + state->duplex = DUPLEX_HALF; + + xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0); } return 0; @@ -1000,6 +1073,17 @@ static int xpcs_get_state_c37_1000basex(struct dw_xpcs *xpcs, if (bmsr < 0) return bmsr; + /* Clear AN complete interrupt */ + if (!xpcs->pcs.poll) { + int an_intr; + + an_intr = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS); + if (an_intr & DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) { + an_intr &= ~DW_VR_MII_AN_STS_C37_ANCMPLT_INTR; + xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, an_intr); + } + } + phylink_mii_c22_pcs_decode_state(state, bmsr, lpa); } @@ -1284,16 +1368,20 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, goto out; } + ret = xpcs_dev_flag(xpcs); + if (ret) + goto out; + xpcs->pcs.ops = &xpcs_phylink_ops; xpcs->pcs.neg_mode = true; - if (compat->an_mode == DW_10GBASER) - return xpcs; - xpcs->pcs.poll = true; + if (xpcs->dev_flag != DW_DEV_TXGBE) { + xpcs->pcs.poll = true; - ret = xpcs_soft_reset(xpcs, compat); - if (ret) - goto out; + ret = xpcs_soft_reset(xpcs, compat); + if (ret) + goto out; + } return xpcs; } diff --git a/drivers/net/pcs/pcs-xpcs.h b/drivers/net/pcs/pcs-xpcs.h index 68c6b5a62088..39a90417e535 100644 --- a/drivers/net/pcs/pcs-xpcs.h +++ b/drivers/net/pcs/pcs-xpcs.h @@ -15,8 +15,14 @@ /* VR_XS_PCS */ #define DW_USXGMII_RST BIT(10) #define DW_USXGMII_EN BIT(9) +#define DW_VR_XS_PCS_DIG_CTRL1 0x0000 +#define DW_VR_RST BIT(15) +#define DW_EN_VSMMD1 BIT(13) +#define DW_CL37_BP BIT(12) #define DW_VR_XS_PCS_DIG_STS 0x0010 #define DW_RXFIFO_ERR GENMASK(6, 5) +#define DW_PSEQ_ST GENMASK(4, 2) +#define DW_PSEQ_ST_GOOD FIELD_PREP(GENMASK(4, 2), 0x4) /* SR_MII */ #define DW_USXGMII_FULL BIT(8) @@ -61,12 +67,14 @@ /* VR_MII_DIG_CTRL1 */ #define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9) +#define DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL BIT(0) /* VR_MII_DIG_CTRL2 */ #define DW_VR_MII_DIG_CTRL2_TX_POL_INV BIT(4) #define DW_VR_MII_DIG_CTRL2_RX_POL_INV BIT(0) /* VR_MII_AN_CTRL */ +#define DW_VR_MII_AN_CTRL_8BIT BIT(8) #define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3 #define DW_VR_MII_TX_CONFIG_MASK BIT(3) #define DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII 0x1 @@ -75,8 +83,10 @@ #define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1) #define DW_VR_MII_PCS_MODE_C37_1000BASEX 0x0 #define DW_VR_MII_PCS_MODE_C37_SGMII 0x2 +#define DW_VR_MII_AN_INTR_EN BIT(0) /* VR_MII_AN_INTR_STS */ +#define DW_VR_MII_AN_STS_C37_ANCMPLT_INTR BIT(0) #define DW_VR_MII_AN_STS_C37_ANSGM_FD BIT(1) #define DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT 2 #define DW_VR_MII_AN_STS_C37_ANSGM_SP GENMASK(3, 2) @@ -90,6 +100,10 @@ #define SGMII_SPEED_SS13 BIT(13) /* SGMII speed along with SS6 */ #define SGMII_SPEED_SS6 BIT(6) /* SGMII speed along with SS13 */ +/* SR MII MMD AN Advertisement defines */ +#define DW_HALF_DUPLEX BIT(6) +#define DW_FULL_DUPLEX BIT(5) + /* VR MII EEE Control 0 defines */ #define DW_VR_MII_EEE_LTX_EN BIT(0) /* LPI Tx Enable */ #define DW_VR_MII_EEE_LRX_EN BIT(1) /* LPI Rx Enable */ @@ -106,6 +120,9 @@ int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg); int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val); +int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg); +int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val); int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs); int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs); int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs); +int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface); |