diff options
Diffstat (limited to 'target/linux/layerscape/patches-4.14/709-mdio-phy-support-layerscape.patch')
-rw-r--r-- | target/linux/layerscape/patches-4.14/709-mdio-phy-support-layerscape.patch | 3456 |
1 files changed, 2895 insertions, 561 deletions
diff --git a/target/linux/layerscape/patches-4.14/709-mdio-phy-support-layerscape.patch b/target/linux/layerscape/patches-4.14/709-mdio-phy-support-layerscape.patch index eeb4baaba7..b544114f3e 100644 --- a/target/linux/layerscape/patches-4.14/709-mdio-phy-support-layerscape.patch +++ b/target/linux/layerscape/patches-4.14/709-mdio-phy-support-layerscape.patch @@ -1,28 +1,63 @@ -From 8eb578a8c1eb55715a40f02790e43aba4a528c38 Mon Sep 17 00:00:00 2001 +From 83fe1ecb8ac6e0544ae74bf5a63806dcac768201 Mon Sep 17 00:00:00 2001 From: Biwen Li <biwen.li@nxp.com> -Date: Tue, 30 Oct 2018 18:26:51 +0800 -Subject: [PATCH 15/40] mdio-phy: support layerscae +Date: Wed, 17 Apr 2019 18:58:45 +0800 +Subject: [PATCH] mdio-phy: support layerscape + This is an integrated patch of mdio-phy for layerscape Signed-off-by: Bhaskar Upadhaya <Bhaskar.Upadhaya@nxp.com> +Signed-off-by: Biwen Li <biwen.li@nxp.com> Signed-off-by: Camelia Groza <camelia.groza@nxp.com> Signed-off-by: Constantin Tudor <constantin.tudor@nxp.com> Signed-off-by: costi <constantin.tudor@freescale.com> +Signed-off-by: Florin Chiculita <florinlaurentiu.chiculita@nxp.com> +Signed-off-by: Florinel Iordache <florinel.iordache@nxp.com> +Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com> +Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com> Signed-off-by: Madalin Bucur <madalin.bucur@freescale.com> +Signed-off-by: Pankaj Bansal <pankaj.bansal@nxp.com> Signed-off-by: Shaohui Xie <Shaohui.Xie@freescale.com> -Signed-off-by: Biwen Li <biwen.li@nxp.com> +Signed-off-by: Valentin Catalin Neacsu <valentin-catalin.neacsu@nxp.com> +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> --- - drivers/net/phy/Kconfig | 6 + - drivers/net/phy/Makefile | 1 + - drivers/net/phy/fsl_backplane.c | 1358 +++++++++++++++++++++++++++++++ - drivers/net/phy/swphy.c | 1 + - include/linux/phy.h | 3 + - 5 files changed, 1369 insertions(+) + drivers/net/phy/Kconfig | 33 + + drivers/net/phy/Makefile | 5 + + drivers/net/phy/aquantia.c | 328 +++- + drivers/net/phy/at803x.c | 21 + + drivers/net/phy/fsl_backplane.c | 1780 ++++++++++++++++++++ + drivers/net/phy/fsl_backplane.h | 41 + + drivers/net/phy/fsl_backplane_serdes_10g.c | 281 +++ + drivers/net/phy/fsl_backplane_serdes_28g.c | 336 ++++ + drivers/net/phy/inphi.c | 594 +++++++ + drivers/net/phy/mdio-mux-multiplexer.c | 122 ++ + drivers/net/phy/swphy.c | 1 + + include/linux/phy.h | 3 + + 12 files changed, 3526 insertions(+), 19 deletions(-) create mode 100644 drivers/net/phy/fsl_backplane.c + create mode 100644 drivers/net/phy/fsl_backplane.h + create mode 100644 drivers/net/phy/fsl_backplane_serdes_10g.c + create mode 100644 drivers/net/phy/fsl_backplane_serdes_28g.c + create mode 100644 drivers/net/phy/inphi.c + create mode 100644 drivers/net/phy/mdio-mux-multiplexer.c --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -90,6 +90,12 @@ config MDIO_BUS_MUX_MMIOREG +@@ -87,9 +87,27 @@ config MDIO_BUS_MUX_MMIOREG + + Currently, only 8/16/32 bits registers are supported. + ++config MDIO_BUS_MUX_MULTIPLEXER ++ tristate "MDIO bus multiplexer using kernel multiplexer subsystem" ++ depends on OF ++ select MULTIPLEXER ++ select MDIO_BUS_MUX ++ help ++ This module provides a driver for MDIO bus multiplexer ++ that is controlled via the kernel multiplexer subsystem. The ++ bus multiplexer connects one of several child MDIO busses to ++ a parent bus. Child bus selection is under the control of ++ the kernel multiplexer subsystem. ++ config MDIO_CAVIUM tristate @@ -35,23 +70,515 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> config MDIO_GPIO tristate "GPIO lib-based bitbanged MDIO buses" depends on MDIO_BITBANG && GPIOLIB +@@ -303,6 +321,16 @@ config AT803X_PHY + ---help--- + Currently supports the AT8030 and AT8035 model + ++config AT803X_PHY_SMART_EEE ++ depends on AT803X_PHY ++ default n ++ tristate "SmartEEE feature for AT803X PHYs" ++ ---help--- ++ Enables the Atheros SmartEEE feature (not IEEE 802.3az). When 2 PHYs ++ which support this feature are connected back-to-back, they may ++ negotiate a low-power sleep mode autonomously, without the Ethernet ++ controller's knowledge. May cause packet loss. ++ + config BCM63XX_PHY + tristate "Broadcom 63xx SOCs internal PHY" + depends on BCM63XX +@@ -385,6 +413,11 @@ config ICPLUS_PHY + ---help--- + Currently supports the IP175C and IP1001 PHYs. + ++config INPHI_PHY ++ tristate "Inphi CDR 10G/25G Ethernet PHY" ++ ---help--- ++ Currently supports the IN112525_S03 part @ 25G ++ + config INTEL_XWAY_PHY + tristate "Intel XWAY PHYs" + ---help--- --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile -@@ -45,6 +45,7 @@ obj-$(CONFIG_MDIO_BUS_MUX_BCM_IPROC) += +@@ -44,7 +44,11 @@ obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o + obj-$(CONFIG_MDIO_BUS_MUX_BCM_IPROC) += mdio-mux-bcm-iproc.o obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o ++obj-$(CONFIG_MDIO_BUS_MUX_MULTIPLEXER) += mdio-mux-multiplexer.o obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o +obj-$(CONFIG_MDIO_FSL_BACKPLANE) += fsl_backplane.o ++obj-$(CONFIG_MDIO_FSL_BACKPLANE) += fsl_backplane_serdes_10g.o ++obj-$(CONFIG_MDIO_FSL_BACKPLANE) += fsl_backplane_serdes_28g.o obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o obj-$(CONFIG_MDIO_I2C) += mdio-i2c.o +@@ -75,6 +79,7 @@ obj-$(CONFIG_DP83848_PHY) += dp83848.o + obj-$(CONFIG_DP83867_PHY) += dp83867.o + obj-$(CONFIG_FIXED_PHY) += fixed_phy.o + obj-$(CONFIG_ICPLUS_PHY) += icplus.o ++obj-$(CONFIG_INPHI_PHY) += inphi.o + obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o + obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o + obj-$(CONFIG_LXT_PHY) += lxt.o +--- a/drivers/net/phy/aquantia.c ++++ b/drivers/net/phy/aquantia.c +@@ -4,6 +4,7 @@ + * Author: Shaohui Xie <Shaohui.Xie@freescale.com> + * + * Copyright 2015 Freescale Semiconductor, Inc. ++ * Copyright 2018 NXP + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any +@@ -27,15 +28,200 @@ + + #define PHY_AQUANTIA_FEATURES (SUPPORTED_10000baseT_Full | \ + SUPPORTED_1000baseT_Full | \ ++ SUPPORTED_2500baseX_Full | \ + SUPPORTED_100baseT_Full | \ ++ SUPPORTED_Pause | \ ++ SUPPORTED_Asym_Pause | \ + PHY_DEFAULT_FEATURES) + ++#define MDIO_PMA_CTRL1_AQ_SPEED10 0 ++#define MDIO_PMA_CTRL1_AQ_SPEED2500 0x2058 ++#define MDIO_PMA_CTRL1_AQ_SPEED5000 0x205c ++#define MDIO_PMA_CTRL2_AQ_2500BT 0x30 ++#define MDIO_PMA_CTRL2_AQ_5000BT 0x31 ++#define MDIO_PMA_CTRL2_AQ_TYPE_MASK 0x3F ++ ++#define MDIO_AN_VENDOR_PROV_CTRL 0xc400 ++#define MDIO_AN_RECV_LP_STATUS 0xe820 ++ ++#define MDIO_AN_LPA_PAUSE 0x20 ++#define MDIO_AN_LPA_ASYM_PAUSE 0x10 ++#define MDIO_AN_ADV_PAUSE 0x20 ++#define MDIO_AN_ADV_ASYM_PAUSE 0x10 ++ ++static int aquantia_write_reg(struct phy_device *phydev, int devad, ++ u32 regnum, u16 val) ++{ ++ u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); ++ ++ return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, addr, val); ++} ++ ++static int aquantia_read_reg(struct phy_device *phydev, int devad, u32 regnum) ++{ ++ u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); ++ ++ return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); ++} ++ ++static int aquantia_pma_setup_forced(struct phy_device *phydev) ++{ ++ int ctrl1, ctrl2, ret; ++ ++ /* Half duplex is not supported */ ++ if (phydev->duplex != DUPLEX_FULL) ++ return -EINVAL; ++ ++ ctrl1 = aquantia_read_reg(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1); ++ if (ctrl1 < 0) ++ return ctrl1; ++ ++ ctrl2 = aquantia_read_reg(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2); ++ if (ctrl2 < 0) ++ return ctrl2; ++ ++ ctrl1 &= ~MDIO_CTRL1_SPEEDSEL; ++ ctrl2 &= ~(MDIO_PMA_CTRL2_AQ_TYPE_MASK); ++ ++ switch (phydev->speed) { ++ case SPEED_10: ++ ctrl2 |= MDIO_PMA_CTRL2_10BT; ++ break; ++ case SPEED_100: ++ ctrl1 |= MDIO_PMA_CTRL1_SPEED100; ++ ctrl2 |= MDIO_PMA_CTRL2_100BTX; ++ break; ++ case SPEED_1000: ++ ctrl1 |= MDIO_PMA_CTRL1_SPEED1000; ++ /* Assume 1000base-T */ ++ ctrl2 |= MDIO_PMA_CTRL2_1000BT; ++ break; ++ case SPEED_10000: ++ ctrl1 |= MDIO_CTRL1_SPEED10G; ++ /* Assume 10Gbase-T */ ++ ctrl2 |= MDIO_PMA_CTRL2_10GBT; ++ break; ++ case SPEED_2500: ++ ctrl1 |= MDIO_PMA_CTRL1_AQ_SPEED2500; ++ ctrl2 |= MDIO_PMA_CTRL2_AQ_2500BT; ++ break; ++ case SPEED_5000: ++ ctrl1 |= MDIO_PMA_CTRL1_AQ_SPEED5000; ++ ctrl2 |= MDIO_PMA_CTRL2_AQ_5000BT; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = aquantia_write_reg(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, ctrl1); ++ if (ret < 0) ++ return ret; ++ ++ return aquantia_write_reg(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2, ctrl2); ++} ++ ++static int aquantia_aneg(struct phy_device *phydev, bool control) ++{ ++ int reg = aquantia_read_reg(phydev, MDIO_MMD_AN, MDIO_CTRL1); ++ ++ if (reg < 0) ++ return reg; ++ ++ if (control) ++ reg |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART; ++ else ++ reg &= ~(MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART); ++ ++ return aquantia_write_reg(phydev, MDIO_MMD_AN, MDIO_CTRL1, reg); ++} ++ ++static int aquantia_config_advert(struct phy_device *phydev) ++{ ++ u32 advertise; ++ int oldadv, adv, oldadv1, adv1; ++ int err, changed = 0; ++ ++ /* Only allow advertising what this PHY supports */ ++ phydev->advertising &= phydev->supported; ++ advertise = phydev->advertising; ++ ++ /* Setup standard advertisement */ ++ oldadv = aquantia_read_reg(phydev, MDIO_MMD_AN, ++ MDIO_AN_10GBT_CTRL); ++ if (oldadv < 0) ++ return oldadv; ++ ++ /* Aquantia vendor specific advertisments */ ++ oldadv1 = aquantia_read_reg(phydev, MDIO_MMD_AN, ++ MDIO_AN_VENDOR_PROV_CTRL); ++ if (oldadv1 < 0) ++ return oldadv1; ++ ++ adv = 0; ++ adv1 = 0; ++ ++ /*100BaseT_full is supported by default*/ ++ ++ if (advertise & ADVERTISED_1000baseT_Full) ++ adv1 |= 0x8000; ++ if (advertise & ADVERTISED_10000baseT_Full) ++ adv |= 0x1000; ++ if (advertise & ADVERTISED_2500baseX_Full) ++ adv1 |= 0x400; ++ ++ if (adv != oldadv) { ++ err = aquantia_write_reg(phydev, MDIO_MMD_AN, ++ MDIO_AN_10GBT_CTRL, adv); ++ if (err < 0) ++ return err; ++ changed = 1; ++ } ++ if (adv1 != oldadv1) { ++ err = aquantia_write_reg(phydev, MDIO_MMD_AN, ++ MDIO_AN_VENDOR_PROV_CTRL, adv1); ++ if (err < 0) ++ return err; ++ changed = 1; ++ } ++ ++ /* advertise flow control */ ++ oldadv = aquantia_read_reg(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE); ++ if (oldadv < 0) ++ return oldadv; ++ ++ adv = oldadv & ~(MDIO_AN_ADV_PAUSE | MDIO_AN_ADV_ASYM_PAUSE); ++ if (advertise & ADVERTISED_Pause) ++ adv |= MDIO_AN_ADV_PAUSE; ++ if (advertise & ADVERTISED_Asym_Pause) ++ adv |= MDIO_AN_ADV_ASYM_PAUSE; ++ ++ if (adv != oldadv) { ++ err = aquantia_write_reg(phydev, MDIO_MMD_AN, ++ MDIO_AN_ADVERTISE, adv); ++ if (err < 0) ++ return err; ++ changed = 1; ++ } ++ ++ return changed; ++} ++ + static int aquantia_config_aneg(struct phy_device *phydev) + { ++ int ret = 0; ++ + phydev->supported = PHY_AQUANTIA_FEATURES; +- phydev->advertising = phydev->supported; ++ if (phydev->autoneg == AUTONEG_DISABLE) { ++ aquantia_pma_setup_forced(phydev); ++ return aquantia_aneg(phydev, false); ++ } + +- return 0; ++ ret = aquantia_config_advert(phydev); ++ if (ret > 0) ++ /* restart autoneg */ ++ return aquantia_aneg(phydev, true); ++ ++ return ret; + } + + static int aquantia_aneg_done(struct phy_device *phydev) +@@ -51,25 +237,26 @@ static int aquantia_config_intr(struct p + int err; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 1); ++ err = aquantia_write_reg(phydev, MDIO_MMD_AN, 0xd401, 1); + if (err < 0) + return err; + +- err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 1); ++ err = aquantia_write_reg(phydev, MDIO_MMD_VEND1, 0xff00, 1); + if (err < 0) + return err; + +- err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0x1001); ++ err = aquantia_write_reg(phydev, MDIO_MMD_VEND1, ++ 0xff01, 0x1001); + } else { +- err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 0); ++ err = aquantia_write_reg(phydev, MDIO_MMD_AN, 0xd401, 0); + if (err < 0) + return err; + +- err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 0); ++ err = aquantia_write_reg(phydev, MDIO_MMD_VEND1, 0xff00, 0); + if (err < 0) + return err; + +- err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0); ++ err = aquantia_write_reg(phydev, MDIO_MMD_VEND1, 0xff01, 0); + } + + return err; +@@ -79,42 +266,145 @@ static int aquantia_ack_interrupt(struct + { + int reg; + +- reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xcc01); ++ reg = aquantia_read_reg(phydev, MDIO_MMD_AN, 0xcc01); + return (reg < 0) ? reg : 0; + } + ++static int aquantia_read_advert(struct phy_device *phydev) ++{ ++ int adv, adv1; ++ ++ /* Setup standard advertisement */ ++ adv = aquantia_read_reg(phydev, MDIO_MMD_AN, ++ MDIO_AN_10GBT_CTRL); ++ ++ /* Aquantia vendor specific advertisments */ ++ adv1 = aquantia_read_reg(phydev, MDIO_MMD_AN, ++ MDIO_AN_VENDOR_PROV_CTRL); ++ ++ /*100BaseT_full is supported by default*/ ++ phydev->advertising |= ADVERTISED_100baseT_Full; ++ ++ if (adv & 0x1000) ++ phydev->advertising |= ADVERTISED_10000baseT_Full; ++ else ++ phydev->advertising &= ~ADVERTISED_10000baseT_Full; ++ if (adv1 & 0x8000) ++ phydev->advertising |= ADVERTISED_1000baseT_Full; ++ else ++ phydev->advertising &= ~ADVERTISED_1000baseT_Full; ++ if (adv1 & 0x400) ++ phydev->advertising |= ADVERTISED_2500baseX_Full; ++ else ++ phydev->advertising &= ~ADVERTISED_2500baseX_Full; ++ ++ /* flow control advertisement */ ++ adv = aquantia_read_reg(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE); ++ if (adv & MDIO_AN_ADV_PAUSE) ++ phydev->advertising |= ADVERTISED_Pause; ++ else ++ phydev->advertising &= ~ADVERTISED_Pause; ++ if (adv & MDIO_AN_ADV_ASYM_PAUSE) ++ phydev->advertising |= ADVERTISED_Asym_Pause; ++ else ++ phydev->advertising &= ~ADVERTISED_Asym_Pause; ++ ++ return 0; ++} ++ ++static int aquantia_read_lp_advert(struct phy_device *phydev) ++{ ++ int adv, adv1; ++ ++ /* Read standard link partner advertisement */ ++ adv = aquantia_read_reg(phydev, MDIO_MMD_AN, ++ MDIO_STAT1); ++ ++ if (adv & 0x1) ++ phydev->lp_advertising |= ADVERTISED_Autoneg | ++ ADVERTISED_100baseT_Full; ++ else ++ phydev->lp_advertising &= ~(ADVERTISED_Autoneg | ++ ADVERTISED_100baseT_Full); ++ ++ /* Read standard link partner advertisement */ ++ adv = aquantia_read_reg(phydev, MDIO_MMD_AN, ++ MDIO_AN_10GBT_STAT); ++ ++ /* Aquantia link partner advertisments */ ++ adv1 = aquantia_read_reg(phydev, MDIO_MMD_AN, ++ MDIO_AN_RECV_LP_STATUS); ++ ++ if (adv & 0x800) ++ phydev->lp_advertising |= ADVERTISED_10000baseT_Full; ++ else ++ phydev->lp_advertising &= ~ADVERTISED_10000baseT_Full; ++ if (adv1 & 0x8000) ++ phydev->lp_advertising |= ADVERTISED_1000baseT_Full; ++ else ++ phydev->lp_advertising &= ~ADVERTISED_1000baseT_Full; ++ if (adv1 & 0x400) ++ phydev->lp_advertising |= ADVERTISED_2500baseX_Full; ++ else ++ phydev->lp_advertising &= ~ADVERTISED_2500baseX_Full; ++ ++ return 0; ++} ++ + static int aquantia_read_status(struct phy_device *phydev) + { + int reg; + +- reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); +- reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); ++ /* Read the link status twice; the bit is latching low */ ++ reg = aquantia_read_reg(phydev, MDIO_MMD_AN, MDIO_STAT1); ++ reg = aquantia_read_reg(phydev, MDIO_MMD_AN, MDIO_STAT1); ++ + if (reg & MDIO_STAT1_LSTATUS) + phydev->link = 1; + else + phydev->link = 0; + +- reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800); + mdelay(10); +- reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800); ++ reg = aquantia_read_reg(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1); ++ ++ if ((reg & MDIO_CTRL1_SPEEDSELEXT) == MDIO_CTRL1_SPEEDSELEXT) ++ reg &= MDIO_CTRL1_SPEEDSEL; ++ else ++ reg &= MDIO_CTRL1_SPEEDSELEXT; + + switch (reg) { +- case 0x9: ++ case MDIO_PMA_CTRL1_AQ_SPEED5000: ++ phydev->speed = SPEED_5000; ++ break; ++ case MDIO_PMA_CTRL1_AQ_SPEED2500: + phydev->speed = SPEED_2500; + break; +- case 0x5: +- phydev->speed = SPEED_1000; ++ case MDIO_PMA_CTRL1_AQ_SPEED10: ++ phydev->speed = SPEED_10; + break; +- case 0x3: ++ case MDIO_PMA_CTRL1_SPEED100: + phydev->speed = SPEED_100; + break; +- case 0x7: +- default: ++ case MDIO_PMA_CTRL1_SPEED1000: ++ phydev->speed = SPEED_1000; ++ break; ++ case MDIO_CTRL1_SPEED10G: + phydev->speed = SPEED_10000; + break; ++ default: ++ phydev->speed = SPEED_UNKNOWN; ++ break; + } ++ + phydev->duplex = DUPLEX_FULL; + ++ reg = aquantia_read_reg(phydev, MDIO_MMD_AN, MDIO_AN_LPA); ++ phydev->pause = reg & MDIO_AN_LPA_PAUSE ? 1 : 0; ++ phydev->asym_pause = reg & MDIO_AN_LPA_ASYM_PAUSE ? 1 : 0; ++ ++ aquantia_read_advert(phydev); ++ aquantia_read_lp_advert(phydev); ++ + return 0; + } + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -68,6 +68,8 @@ + #define AT803X_DEBUG_REG_5 0x05 + #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) + ++#define AT803X_LPI_EN BIT(8) ++ + #define ATH8030_PHY_ID 0x004dd076 + #define ATH8031_PHY_ID 0x004dd074 + #define ATH8032_PHY_ID 0x004dd023 +@@ -290,6 +292,19 @@ static void at803x_disable_smarteee(stru + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); + } + ++static void at803x_enable_smart_eee(struct phy_device *phydev, int on) ++{ ++ int value; ++ ++ /* 5.1.11 Smart_eee control3 */ ++ value = phy_read_mmd(phydev, MDIO_MMD_PCS, 0x805D); ++ if (on) ++ value |= AT803X_LPI_EN; ++ else ++ value &= ~AT803X_LPI_EN; ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x805D, value); ++} ++ + static int at803x_config_init(struct phy_device *phydev) + { + struct at803x_platform_data *pdata; +@@ -320,6 +335,12 @@ static int at803x_config_init(struct phy + if (ret < 0) + return ret; + ++#ifdef CONFIG_AT803X_PHY_SMART_EEE ++ at803x_enable_smart_eee(phydev, 1); ++#else ++ at803x_enable_smart_eee(phydev, 0); ++#endif ++ + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { + ret = at803x_enable_rx_delay(phydev); --- /dev/null +++ b/drivers/net/phy/fsl_backplane.c -@@ -0,0 +1,1358 @@ -+/* Freescale backplane driver. +@@ -0,0 +1,1780 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DPAA backplane driver. + * Author: Shaohui Xie <Shaohui.Xie@freescale.com> ++ * Florinel Iordache <florinel.iordache@nxp.com> + * + * Copyright 2015 Freescale Semiconductor, Inc. ++ * Copyright 2018 NXP + * + * Licensed under the GPL-2 or later. + */ @@ -70,126 +597,130 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/workqueue.h> ++#include <linux/netdevice.h> + -+/* XFI PCS Device Identifier */ -+#define FSL_PCS_PHY_ID 0x0083e400 ++#include "fsl_backplane.h" + -+/* Freescale KR PMD registers */ -+#define FSL_KR_PMD_CTRL 0x96 -+#define FSL_KR_PMD_STATUS 0x97 -+#define FSL_KR_LP_CU 0x98 -+#define FSL_KR_LP_STATUS 0x99 -+#define FSL_KR_LD_CU 0x9a -+#define FSL_KR_LD_STATUS 0x9b + -+/* Freescale KR PMD defines */ -+#define PMD_RESET 0x1 -+#define PMD_STATUS_SUP_STAT 0x4 -+#define PMD_STATUS_FRAME_LOCK 0x2 -+#define TRAIN_EN 0x3 -+#define TRAIN_DISABLE 0x1 -+#define RX_STAT 0x1 ++/* PCS Device Identifier */ ++#define PCS_PHY_DEVICE_ID 0x0083e400 ++#define PCS_PHY_DEVICE_ID_MASK 0xffffffff + -+#define FSL_KR_RX_LINK_STAT_MASK 0x1000 -+#define FSL_XFI_PCS_10GR_SR1 0x20 ++/* 10G Long cables setup: 1 m to 2 m cables */ ++#define RATIO_PREQ_10G 0x3 ++#define RATIO_PST1Q_10G 0xd ++#define RATIO_EQ_10G 0x20 + -+/* Freescale KX PCS mode register */ -+#define FSL_PCS_IF_MODE 0x8014 ++/* 10G Short cables setup: up to 30 cm cable */ ++//#define RATIO_PREQ_10G 0x3 ++//#define RATIO_PST1Q_10G 0xa ++//#define RATIO_EQ_10G 0x29 + -+/* Freescale KX PCS mode register init value */ -+#define IF_MODE_INIT 0x8 ++/* 40G Long cables setup: 1 m to 2 m cables */ ++#define RATIO_PREQ_40G 0x2 ++#define RATIO_PST1Q_40G 0xd ++#define RATIO_EQ_40G 0x20 + -+/* Freescale KX/KR AN registers */ -+#define FSL_AN_AD1 0x11 -+#define FSL_AN_BP_STAT 0x30 ++/* 40G Short cables setup: up to 30 cm cable */ ++//#define RATIO_PREQ_40G 0x1 ++//#define RATIO_PST1Q_40G 0x3 ++//#define RATIO_EQ_40G 0x29 + -+/* Freescale KX/KR AN registers defines */ -+#define AN_CTRL_INIT 0x1200 -+#define KX_AN_AD1_INIT 0x25 -+#define KR_AN_AD1_INIT 0x85 -+#define AN_LNK_UP_MASK 0x4 -+#define KR_AN_MASK 0x8 -+#define TRAIN_FAIL 0x8 ++/* LX2 2x40G default RCW setup */ ++//#define RATIO_PREQ_40G 0x0 ++//#define RATIO_PST1Q_40G 0x3 ++//#define RATIO_EQ_40G 0x30 + -+/* C(-1) */ -+#define BIN_M1 0 -+/* C(1) */ -+#define BIN_LONG 1 -+#define BIN_M1_SEL 6 -+#define BIN_Long_SEL 7 -+#define CDR_SEL_MASK 0x00070000 -+#define BIN_SNAPSHOT_NUM 5 -+#define BIN_M1_THRESHOLD 3 -+#define BIN_LONG_THRESHOLD 2 -+ -+#define PRE_COE_SHIFT 22 -+#define POST_COE_SHIFT 16 -+#define ZERO_COE_SHIFT 8 -+ -+#define PRE_COE_MAX 0x0 -+#define PRE_COE_MIN 0x8 ++/* Max/Min coefficient values */ ++#define PRE_COE_MAX 0x0 ++#define PRE_COE_MIN 0x8 +#define POST_COE_MAX 0x0 +#define POST_COE_MIN 0x10 +#define ZERO_COE_MAX 0x30 +#define ZERO_COE_MIN 0x0 + -+#define TECR0_INIT 0x24200000 -+#define RATIO_PREQ 0x3 -+#define RATIO_PST1Q 0xd -+#define RATIO_EQ 0x20 ++/* KR PMD defines */ ++#define PMD_RESET 0x1 ++#define PMD_STATUS_SUP_STAT 0x4 ++#define PMD_STATUS_FRAME_LOCK 0x2 ++#define TRAIN_EN 0x3 ++#define TRAIN_DISABLE 0x1 ++#define RX_STAT 0x1 + -+#define GCR0_RESET_MASK 0x600000 -+#define GCR1_SNP_START_MASK 0x00000040 -+#define GCR1_CTL_SNP_START_MASK 0x00002000 -+#define GCR1_REIDL_TH_MASK 0x00700000 -+#define GCR1_REIDL_EX_SEL_MASK 0x000c0000 -+#define GCR1_REIDL_ET_MAS_MASK 0x00004000 -+#define TECR0_AMP_RED_MASK 0x0000003f ++/* PCS Link up */ ++#define XFI_PCS_SR1 0x20 ++#define KR_RX_LINK_STAT_MASK 0x1000 + -+#define RECR1_CTL_SNP_DONE_MASK 0x00000002 -+#define RECR1_SNP_DONE_MASK 0x00000004 -+#define TCSR1_SNP_DATA_MASK 0x0000ffc0 -+#define TCSR1_SNP_DATA_SHIFT 6 -+#define TCSR1_EQ_SNPBIN_SIGN_MASK 0x100 ++/* KX PCS mode register */ ++#define KX_PCS_IF_MODE 0x8014 + -+#define RECR1_GAINK2_MASK 0x0f000000 -+#define RECR1_GAINK2_SHIFT 24 -+#define RECR1_GAINK3_MASK 0x000f0000 -+#define RECR1_GAINK3_SHIFT 16 -+#define RECR1_OFFSET_MASK 0x00003f80 -+#define RECR1_OFFSET_SHIFT 7 -+#define RECR1_BLW_MASK 0x00000f80 -+#define RECR1_BLW_SHIFT 7 -+#define EYE_CTRL_SHIFT 12 -+#define BASE_WAND_SHIFT 10 ++/* KX PCS mode register init value */ ++#define KX_IF_MODE_INIT 0x8 ++ ++/* KX/KR AN registers */ ++#define AN_CTRL_INIT 0x1200 ++#define KX_AN_AD1_INIT 0x25 ++#define KR_AN_AD1_INIT_10G 0x85 ++#define KR_AN_AD1_INIT_40G 0x105 ++#define AN_LNK_UP_MASK 0x4 ++#define KR_AN_MASK_10G 0x8 ++#define KR_AN_MASK_40G 0x20 ++#define TRAIN_FAIL 0x8 ++#define KR_AN_40G_MDIO_OFFSET 4 + ++/* XGKR Timeouts */ +#define XGKR_TIMEOUT 1050 ++#define XGKR_DENY_RT_INTERVAL 3000 ++#define XGKR_AN_WAIT_ITERATIONS 5 + -+#define INCREMENT 1 -+#define DECREMENT 2 ++/* XGKR Increment/Decrement Requests */ ++#define INCREMENT 1 ++#define DECREMENT 2 +#define TIMEOUT_LONG 3 -+#define TIMEOUT_M1 3 ++#define TIMEOUT_M1 3 + ++/* XGKR Masks */ +#define RX_READY_MASK 0x8000 -+#define PRESET_MASK 0x2000 -+#define INIT_MASK 0x1000 -+#define COP1_MASK 0x30 -+#define COP1_SHIFT 4 -+#define COZ_MASK 0xc -+#define COZ_SHIFT 2 -+#define COM1_MASK 0x3 -+#define COM1_SHIFT 0 ++#define PRESET_MASK 0x2000 ++#define INIT_MASK 0x1000 ++#define COP1_MASK 0x30 ++#define COP1_SHIFT 4 ++#define COZ_MASK 0xc ++#define COZ_SHIFT 2 ++#define COM1_MASK 0x3 ++#define COM1_SHIFT 0 +#define REQUEST_MASK 0x3f +#define LD_ALL_MASK (PRESET_MASK | INIT_MASK | \ + COP1_MASK | COZ_MASK | COM1_MASK) + ++/* Lanes definitions */ ++#define MASTER_LANE 0 ++#define SINGLE_LANE 0 ++#define MAX_PHY_LANES_NO 4 ++ ++/* Invalid value */ ++#define VAL_INVALID 0xff ++ ++/* New XGKR Training Algorithm */ +#define NEW_ALGORITHM_TRAIN_TX ++ +#ifdef NEW_ALGORITHM_TRAIN_TX -+#define FORCE_INC_COP1_NUMBER 0 -+#define FORCE_INC_COM1_NUMBER 1 ++#define FORCE_INC_COP1_NUMBER 0 ++#define FORCE_INC_COM1_NUMBER 1 +#endif + -+#define VAL_INVALID 0xff ++/* Link_Training_Registers offsets */ ++static int lt_MDIO_MMD = 0; ++static u32 lt_KR_PMD_CTRL = 0; ++static u32 lt_KR_PMD_STATUS = 0; ++static u32 lt_KR_LP_CU = 0; ++static u32 lt_KR_LP_STATUS = 0; ++static u32 lt_KR_LD_CU = 0; ++static u32 lt_KR_LD_STATUS = 0; ++ ++/* KX/KR AN registers offsets */ ++static u32 g_an_AD1 = 0; ++static u32 g_an_BP_STAT = 0; + +static const u32 preq_table[] = {0x0, 0x1, 0x3, 0x5, + 0x7, 0x9, 0xb, 0xc, VAL_INVALID}; @@ -199,9 +730,16 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> +enum backplane_mode { + PHY_BACKPLANE_1000BASE_KX, + PHY_BACKPLANE_10GBASE_KR, ++ PHY_BACKPLANE_40GBASE_KR, + PHY_BACKPLANE_INVAL +}; + ++enum serdes_type { ++ SERDES_10G, ++ SERDES_28G, ++ SERDES_INVAL ++}; ++ +enum coe_filed { + COE_COP1, + COE_COZ, @@ -221,25 +759,6 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + TRAINED, +}; + -+struct per_lane_ctrl_status { -+ __be32 gcr0; /* 0x.000 - General Control Register 0 */ -+ __be32 gcr1; /* 0x.004 - General Control Register 1 */ -+ __be32 gcr2; /* 0x.008 - General Control Register 2 */ -+ __be32 resv1; /* 0x.00C - Reserved */ -+ __be32 recr0; /* 0x.010 - Receive Equalization Control Register 0 */ -+ __be32 recr1; /* 0x.014 - Receive Equalization Control Register 1 */ -+ __be32 tecr0; /* 0x.018 - Transmit Equalization Control Register 0 */ -+ __be32 resv2; /* 0x.01C - Reserved */ -+ __be32 tlcr0; /* 0x.020 - TTL Control Register 0 */ -+ __be32 tlcr1; /* 0x.024 - TTL Control Register 1 */ -+ __be32 tlcr2; /* 0x.028 - TTL Control Register 2 */ -+ __be32 tlcr3; /* 0x.02C - TTL Control Register 3 */ -+ __be32 tcsr0; /* 0x.030 - Test Control/Status Register 0 */ -+ __be32 tcsr1; /* 0x.034 - Test Control/Status Register 1 */ -+ __be32 tcsr2; /* 0x.038 - Test Control/Status Register 2 */ -+ __be32 tcsr3; /* 0x.03C - Test Control/Status Register 3 */ -+}; -+ +struct tx_condition { + bool bin_m1_late_early; + bool bin_long_late_early; @@ -255,19 +774,152 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> +#endif +}; + -+struct fsl_xgkr_inst { -+ void *reg_base; ++struct xgkr_params { ++ void *reg_base; /* lane memory map: registers base address */ ++ int idx; /* lane relative index inside a multi-lane PHY */ + struct phy_device *phydev; ++ struct serdes_access *srds; + struct tx_condition tx_c; + struct delayed_work xgkr_wk; + enum train_state state; ++ int an_wait_count; ++ unsigned long rt_time; + u32 ld_update; + u32 ld_status; + u32 ratio_preq; + u32 ratio_pst1q; + u32 adpt_eq; ++ u32 tuned_ratio_preq; ++ u32 tuned_ratio_pst1q; ++ u32 tuned_adpt_eq; ++}; ++ ++struct xgkr_phy_data { ++ int bp_mode; ++ u32 phy_lanes; ++ struct mutex phy_lock; ++ bool aneg_done; ++ struct xgkr_params xgkr[MAX_PHY_LANES_NO]; +}; + ++static void setup_an_lt_ls(void) ++{ ++ /* KR PMD registers */ ++ lt_MDIO_MMD = MDIO_MMD_PMAPMD; ++ lt_KR_PMD_CTRL = 0x96; ++ lt_KR_PMD_STATUS = 0x97; ++ lt_KR_LP_CU = 0x98; ++ lt_KR_LP_STATUS = 0x99; ++ lt_KR_LD_CU = 0x9a; ++ lt_KR_LD_STATUS = 0x9b; ++ ++ /* KX/KR AN registers */ ++ g_an_AD1 = 0x11; ++ g_an_BP_STAT = 0x30; ++} ++ ++static void setup_an_lt_lx(void) ++{ ++ /* Auto-Negotiation and Link Training Core Registers page 1: 256 = 0x100 */ ++ lt_MDIO_MMD = MDIO_MMD_AN; ++ lt_KR_PMD_CTRL = 0x100; ++ lt_KR_PMD_STATUS = 0x101; ++ lt_KR_LP_CU = 0x102; ++ lt_KR_LP_STATUS = 0x103; ++ lt_KR_LD_CU = 0x104; ++ lt_KR_LD_STATUS = 0x105; ++ ++ /* KX/KR AN registers */ ++ g_an_AD1 = 0x03; ++ g_an_BP_STAT = 0x0F; ++} ++ ++static u32 le_ioread32(u32 *reg) ++{ ++ return ioread32(reg); ++} ++ ++static void le_iowrite32(u32 value, u32 *reg) ++{ ++ iowrite32(value, reg); ++} ++ ++static u32 be_ioread32(u32 *reg) ++{ ++ return ioread32be(reg); ++} ++ ++static void be_iowrite32(u32 value, u32 *reg) ++{ ++ iowrite32be(value, reg); ++} ++ ++/** ++ * xgkr_phy_write_mmd - Wrapper function for phy_write_mmd ++ * for writing a register on an MMD on a given PHY. ++ * ++ * Same rules as for phy_write_mmd(); ++ */ ++static int xgkr_phy_write_mmd(struct xgkr_params *xgkr, int devad, u32 regnum, u16 val) ++{ ++ struct phy_device *phydev = xgkr->phydev; ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ int mdio_addr = phydev->mdio.addr; ++ int err; ++ ++ mutex_lock(&xgkr_inst->phy_lock); ++ ++ if (xgkr_inst->bp_mode == PHY_BACKPLANE_40GBASE_KR && devad == MDIO_MMD_AN) { ++ //40G AN: prepare mdio address for writing phydev AN registers for 40G on respective lane ++ phydev->mdio.addr = KR_AN_40G_MDIO_OFFSET + xgkr->idx; ++ } ++ ++ err = phy_write_mmd(phydev, devad, regnum, val); ++ if (err) ++ dev_err(&phydev->mdio.dev, "Writing PHY (%p) MMD = 0x%02x register = 0x%02x failed with error code: 0x%08x \n", phydev, devad, regnum, err); ++ ++ if (xgkr_inst->bp_mode == PHY_BACKPLANE_40GBASE_KR && devad == MDIO_MMD_AN) { ++ //40G AN: restore mdio address ++ phydev->mdio.addr = mdio_addr; ++ } ++ ++ mutex_unlock(&xgkr_inst->phy_lock); ++ ++ return err; ++} ++ ++/** ++ * xgkr_phy_read_mmd - Wrapper function for phy_read_mmd ++ * for reading a register from an MMD on a given PHY. ++ * ++ * Same rules as for phy_read_mmd(); ++ */ ++static int xgkr_phy_read_mmd(struct xgkr_params *xgkr, int devad, u32 regnum) ++{ ++ struct phy_device *phydev = xgkr->phydev; ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ int mdio_addr = phydev->mdio.addr; ++ int ret; ++ ++ mutex_lock(&xgkr_inst->phy_lock); ++ ++ if (xgkr_inst->bp_mode == PHY_BACKPLANE_40GBASE_KR && devad == MDIO_MMD_AN) { ++ //40G AN: prepare mdio address for reading phydev AN registers for 40G on respective lane ++ phydev->mdio.addr = KR_AN_40G_MDIO_OFFSET + xgkr->idx; ++ } ++ ++ ret = phy_read_mmd(phydev, devad, regnum); ++ ++ if (xgkr_inst->bp_mode == PHY_BACKPLANE_40GBASE_KR && devad == MDIO_MMD_AN) { ++ //40G AN: restore mdio address ++ phydev->mdio.addr = mdio_addr; ++ } ++ ++ mutex_unlock(&xgkr_inst->phy_lock); ++ ++ return ret; ++} ++ +static void tx_condition_init(struct tx_condition *tx_c) +{ + tx_c->bin_m1_late_early = true; @@ -284,88 +936,59 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> +#endif +} + -+void tune_tecr0(struct fsl_xgkr_inst *inst) ++void tune_tecr(struct xgkr_params *xgkr) +{ -+ struct per_lane_ctrl_status *reg_base = inst->reg_base; -+ u32 val; -+ -+ val = TECR0_INIT | -+ inst->adpt_eq << ZERO_COE_SHIFT | -+ inst->ratio_preq << PRE_COE_SHIFT | -+ inst->ratio_pst1q << POST_COE_SHIFT; ++ struct phy_device *phydev = xgkr->phydev; ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ bool reset = false; ++ ++ if (xgkr_inst->bp_mode == PHY_BACKPLANE_40GBASE_KR) { ++ /* Reset only the Master Lane */ ++ reset = (xgkr->idx == MASTER_LANE); ++ } else { ++ reset = true; ++ } ++ ++ xgkr->srds->tune_tecr(xgkr->reg_base, xgkr->ratio_preq, xgkr->ratio_pst1q, xgkr->adpt_eq, reset); + -+ /* reset the lane */ -+ iowrite32(ioread32(®_base->gcr0) & ~GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+ iowrite32(val, ®_base->tecr0); -+ udelay(1); -+ /* unreset the lane */ -+ iowrite32(ioread32(®_base->gcr0) | GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); ++ xgkr->tuned_ratio_preq = xgkr->ratio_preq; ++ xgkr->tuned_ratio_pst1q = xgkr->ratio_pst1q; ++ xgkr->tuned_adpt_eq = xgkr->adpt_eq; +} + -+static void start_lt(struct phy_device *phydev) ++static void start_lt(struct xgkr_params *xgkr) +{ -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_PMD_CTRL, TRAIN_EN); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, lt_KR_PMD_CTRL, TRAIN_EN); +} + -+static void stop_lt(struct phy_device *phydev) ++static void stop_lt(struct xgkr_params *xgkr) +{ -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_PMD_CTRL, TRAIN_DISABLE); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, lt_KR_PMD_CTRL, TRAIN_DISABLE); +} + -+static void reset_gcr0(struct fsl_xgkr_inst *inst) ++static void reset_lt(struct xgkr_params *xgkr) +{ -+ struct per_lane_ctrl_status *reg_base = inst->reg_base; -+ -+ iowrite32(ioread32(®_base->gcr0) & ~GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+ iowrite32(ioread32(®_base->gcr0) | GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, MDIO_CTRL1, PMD_RESET); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, lt_KR_PMD_CTRL, TRAIN_DISABLE); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, lt_KR_LD_CU, 0); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, lt_KR_LD_STATUS, 0); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, lt_KR_PMD_STATUS, 0); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, lt_KR_LP_CU, 0); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, lt_KR_LP_STATUS, 0); ++ +} + -+void lane_set_1gkx(void *reg) ++static void ld_coe_status(struct xgkr_params *xgkr) +{ -+ struct per_lane_ctrl_status *reg_base = reg; -+ u32 val; -+ -+ /* reset the lane */ -+ iowrite32(ioread32(®_base->gcr0) & ~GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); -+ -+ /* set gcr1 for 1GKX */ -+ val = ioread32(®_base->gcr1); -+ val &= ~(GCR1_REIDL_TH_MASK | GCR1_REIDL_EX_SEL_MASK | -+ GCR1_REIDL_ET_MAS_MASK); -+ iowrite32(val, ®_base->gcr1); -+ udelay(1); -+ -+ /* set tecr0 for 1GKX */ -+ val = ioread32(®_base->tecr0); -+ val &= ~TECR0_AMP_RED_MASK; -+ iowrite32(val, ®_base->tecr0); -+ udelay(1); -+ -+ /* unreset the lane */ -+ iowrite32(ioread32(®_base->gcr0) | GCR0_RESET_MASK, -+ ®_base->gcr0); -+ udelay(1); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, ++ lt_KR_LD_STATUS, xgkr->ld_status); +} + -+static void reset_lt(struct phy_device *phydev) ++static void ld_coe_update(struct xgkr_params *xgkr) +{ -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, PMD_RESET); -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_PMD_CTRL, TRAIN_DISABLE); -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LD_CU, 0); -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LD_STATUS, 0); -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_PMD_STATUS, 0); -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LP_CU, 0); -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LP_STATUS, 0); ++ dev_dbg(&xgkr->phydev->mdio.dev, "sending request: %x\n", xgkr->ld_update); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, ++ lt_KR_LD_CU, xgkr->ld_update); +} + +static void start_xgkr_state_machine(struct delayed_work *work) @@ -374,191 +997,110 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + msecs_to_jiffies(XGKR_TIMEOUT)); +} + -+static void start_xgkr_an(struct phy_device *phydev) ++static void start_xgkr_an(struct xgkr_params *xgkr) +{ -+ struct fsl_xgkr_inst *inst; ++ struct phy_device *phydev = xgkr->phydev; ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ int i; ++ int err; + -+ reset_lt(phydev); -+ phy_write_mmd(phydev, MDIO_MMD_AN, FSL_AN_AD1, KR_AN_AD1_INIT); -+ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, AN_CTRL_INIT); ++ switch (xgkr_inst->bp_mode) ++ { ++ case PHY_BACKPLANE_1000BASE_KX: ++ dev_err(&phydev->mdio.dev, "Wrong call path for 1000Base-KX \n"); ++ break; + -+ inst = phydev->priv; ++ case PHY_BACKPLANE_10GBASE_KR: ++ err = xgkr_phy_write_mmd(xgkr, MDIO_MMD_AN, g_an_AD1, KR_AN_AD1_INIT_10G); ++ if (err) ++ dev_err(&phydev->mdio.dev, "Setting AN register 0x%02x failed with error code: 0x%08x \n", g_an_AD1, err); ++ udelay(1); ++ err = xgkr_phy_write_mmd(xgkr, MDIO_MMD_AN, MDIO_CTRL1, AN_CTRL_INIT); ++ if (err) ++ dev_err(&phydev->mdio.dev, "Setting AN register 0x%02x failed with error code: 0x%08x \n", MDIO_CTRL1, err); ++ break; + -+ /* start state machine*/ -+ start_xgkr_state_machine(&inst->xgkr_wk); ++ case PHY_BACKPLANE_40GBASE_KR: ++ if (xgkr->idx == MASTER_LANE) { ++ for (i = 0; i < xgkr_inst->phy_lanes; i++) { ++ err = xgkr_phy_write_mmd(&xgkr_inst->xgkr[i], MDIO_MMD_AN, g_an_AD1, KR_AN_AD1_INIT_40G); ++ if (err) ++ dev_err(&phydev->mdio.dev, "Setting AN register 0x%02x on lane %d failed with error code: 0x%08x \n", g_an_AD1, xgkr_inst->xgkr[i].idx, err); ++ } ++ udelay(1); ++ err = xgkr_phy_write_mmd(xgkr, MDIO_MMD_AN, MDIO_CTRL1, AN_CTRL_INIT); ++ if (err) ++ dev_err(&phydev->mdio.dev, "Setting AN register 0x%02x on Master Lane failed with error code: 0x%08x \n", MDIO_CTRL1, err); ++ } ++ break; ++ } +} + +static void start_1gkx_an(struct phy_device *phydev) +{ -+ phy_write_mmd(phydev, MDIO_MMD_PCS, FSL_PCS_IF_MODE, IF_MODE_INIT); -+ phy_write_mmd(phydev, MDIO_MMD_AN, FSL_AN_AD1, KX_AN_AD1_INIT); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, KX_PCS_IF_MODE, KX_IF_MODE_INIT); ++ phy_write_mmd(phydev, MDIO_MMD_AN, g_an_AD1, KX_AN_AD1_INIT); + phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, AN_CTRL_INIT); +} + -+static void ld_coe_status(struct fsl_xgkr_inst *inst) ++static void reset_tecr(struct xgkr_params *xgkr) +{ -+ phy_write_mmd(inst->phydev, MDIO_MMD_PMAPMD, -+ FSL_KR_LD_STATUS, inst->ld_status); -+} ++ struct phy_device *phydev = xgkr->phydev; ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; + -+static void ld_coe_update(struct fsl_xgkr_inst *inst) -+{ -+ dev_dbg(&inst->phydev->mdio.dev, "sending request: %x\n", inst->ld_update); -+ phy_write_mmd(inst->phydev, MDIO_MMD_PMAPMD, -+ FSL_KR_LD_CU, inst->ld_update); -+} ++ switch (xgkr_inst->bp_mode) ++ { ++ case PHY_BACKPLANE_1000BASE_KX: ++ dev_err(&phydev->mdio.dev, "Wrong call path for 1000Base-KX \n"); ++ break; + -+static void init_inst(struct fsl_xgkr_inst *inst, int reset) -+{ -+ if (reset) { -+ inst->ratio_preq = RATIO_PREQ; -+ inst->ratio_pst1q = RATIO_PST1Q; -+ inst->adpt_eq = RATIO_EQ; -+ tune_tecr0(inst); ++ case PHY_BACKPLANE_10GBASE_KR: ++ xgkr->ratio_preq = RATIO_PREQ_10G; ++ xgkr->ratio_pst1q = RATIO_PST1Q_10G; ++ xgkr->adpt_eq = RATIO_EQ_10G; ++ break; ++ ++ case PHY_BACKPLANE_40GBASE_KR: ++ xgkr->ratio_preq = RATIO_PREQ_40G; ++ xgkr->ratio_pst1q = RATIO_PST1Q_40G; ++ xgkr->adpt_eq = RATIO_EQ_40G; ++ break; + } + -+ tx_condition_init(&inst->tx_c); -+ inst->state = DETECTING_LP; -+ inst->ld_status &= RX_READY_MASK; -+ ld_coe_status(inst); -+ inst->ld_update = 0; -+ inst->ld_status &= ~RX_READY_MASK; -+ ld_coe_status(inst); ++ tune_tecr(xgkr); +} + -+#ifdef NEW_ALGORITHM_TRAIN_TX -+static int get_median_gaink2(u32 *reg) ++static void init_xgkr(struct xgkr_params *xgkr, int reset) +{ -+ int gaink2_snap_shot[BIN_SNAPSHOT_NUM]; -+ u32 rx_eq_snp; -+ struct per_lane_ctrl_status *reg_base; -+ int timeout; -+ int i, j, tmp, pos; -+ -+ reg_base = (struct per_lane_ctrl_status *)reg; -+ -+ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) { -+ /* wait RECR1_CTL_SNP_DONE_MASK has cleared */ -+ timeout = 100; -+ while (ioread32(®_base->recr1) & -+ RECR1_CTL_SNP_DONE_MASK) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* start snap shot */ -+ iowrite32((ioread32(®_base->gcr1) | -+ GCR1_CTL_SNP_START_MASK), -+ ®_base->gcr1); -+ -+ /* wait for SNP done */ -+ timeout = 100; -+ while (!(ioread32(®_base->recr1) & -+ RECR1_CTL_SNP_DONE_MASK)) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* read and save the snap shot */ -+ rx_eq_snp = ioread32(®_base->recr1); -+ gaink2_snap_shot[i] = (rx_eq_snp & RECR1_GAINK2_MASK) >> -+ RECR1_GAINK2_SHIFT; ++ if (reset) ++ reset_tecr(xgkr); + -+ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */ -+ iowrite32((ioread32(®_base->gcr1) & -+ ~GCR1_CTL_SNP_START_MASK), -+ ®_base->gcr1); -+ } -+ -+ /* get median of the 5 snap shot */ -+ for (i = 0; i < BIN_SNAPSHOT_NUM - 1; i++) { -+ tmp = gaink2_snap_shot[i]; -+ pos = i; -+ for (j = i + 1; j < BIN_SNAPSHOT_NUM; j++) { -+ if (gaink2_snap_shot[j] < tmp) { -+ tmp = gaink2_snap_shot[j]; -+ pos = j; -+ } -+ } ++ tx_condition_init(&xgkr->tx_c); ++ xgkr->state = DETECTING_LP; + -+ gaink2_snap_shot[pos] = gaink2_snap_shot[i]; -+ gaink2_snap_shot[i] = tmp; -+ } ++ xgkr->ld_status &= RX_READY_MASK; ++ ld_coe_status(xgkr); ++ xgkr->ld_update = 0; ++ xgkr->ld_status &= ~RX_READY_MASK; ++ ld_coe_status(xgkr); + -+ return gaink2_snap_shot[2]; +} -+#endif + -+static bool is_bin_early(int bin_sel, void *reg) ++static void initialize(struct xgkr_params *xgkr) +{ -+ bool early = false; -+ int bin_snap_shot[BIN_SNAPSHOT_NUM]; -+ int i, negative_count = 0; -+ struct per_lane_ctrl_status *reg_base = reg; -+ int timeout; -+ -+ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) { -+ /* wait RECR1_SNP_DONE_MASK has cleared */ -+ timeout = 100; -+ while ((ioread32(®_base->recr1) & RECR1_SNP_DONE_MASK)) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* set TCSR1[CDR_SEL] to BinM1/BinLong */ -+ if (bin_sel == BIN_M1) { -+ iowrite32((ioread32(®_base->tcsr1) & -+ ~CDR_SEL_MASK) | BIN_M1_SEL, -+ ®_base->tcsr1); -+ } else { -+ iowrite32((ioread32(®_base->tcsr1) & -+ ~CDR_SEL_MASK) | BIN_Long_SEL, -+ ®_base->tcsr1); -+ } -+ -+ /* start snap shot */ -+ iowrite32(ioread32(®_base->gcr1) | GCR1_SNP_START_MASK, -+ ®_base->gcr1); -+ -+ /* wait for SNP done */ -+ timeout = 100; -+ while (!(ioread32(®_base->recr1) & RECR1_SNP_DONE_MASK)) { -+ udelay(1); -+ timeout--; -+ if (timeout == 0) -+ break; -+ } -+ -+ /* read and save the snap shot */ -+ bin_snap_shot[i] = (ioread32(®_base->tcsr1) & -+ TCSR1_SNP_DATA_MASK) >> TCSR1_SNP_DATA_SHIFT; -+ if (bin_snap_shot[i] & TCSR1_EQ_SNPBIN_SIGN_MASK) -+ negative_count++; -+ -+ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */ -+ iowrite32(ioread32(®_base->gcr1) & ~GCR1_SNP_START_MASK, -+ ®_base->gcr1); -+ } -+ -+ if (((bin_sel == BIN_M1) && (negative_count > BIN_M1_THRESHOLD)) || -+ ((bin_sel == BIN_LONG && (negative_count > BIN_LONG_THRESHOLD)))) { -+ early = true; -+ } ++ reset_tecr(xgkr); + -+ return early; ++ xgkr->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); ++ xgkr->ld_status |= COE_UPDATED << COP1_SHIFT | ++ COE_UPDATED << COZ_SHIFT | ++ COE_UPDATED << COM1_SHIFT; ++ ld_coe_status(xgkr); +} + -+static void train_tx(struct fsl_xgkr_inst *inst) ++static void train_remote_tx(struct xgkr_params *xgkr) +{ -+ struct phy_device *phydev = inst->phydev; -+ struct tx_condition *tx_c = &inst->tx_c; ++ struct tx_condition *tx_c = &xgkr->tx_c; + bool bin_m1_early, bin_long_early; + u32 lp_status, old_ld_update; + u32 status_cop1, status_coz, status_com1; @@ -571,11 +1113,13 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> +recheck: + if (tx_c->bin_long_stop && tx_c->bin_m1_stop) { + tx_c->tx_complete = true; -+ inst->ld_status |= RX_READY_MASK; -+ ld_coe_status(inst); ++ xgkr->ld_status |= RX_READY_MASK; ++ ld_coe_status(xgkr); ++ + /* tell LP we are ready */ -+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, -+ FSL_KR_PMD_STATUS, RX_STAT); ++ xgkr_phy_write_mmd(xgkr, lt_MDIO_MMD, ++ lt_KR_PMD_STATUS, RX_STAT); ++ + return; + } + @@ -583,13 +1127,14 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + * we can clear up the appropriate update request so that the + * subsequent code may easily issue new update requests if needed. + */ -+ lp_status = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LP_STATUS) & ++ lp_status = xgkr_phy_read_mmd(xgkr, lt_MDIO_MMD, lt_KR_LP_STATUS) & + REQUEST_MASK; ++ + status_cop1 = (lp_status & COP1_MASK) >> COP1_SHIFT; + status_coz = (lp_status & COZ_MASK) >> COZ_SHIFT; + status_com1 = (lp_status & COM1_MASK) >> COM1_SHIFT; + -+ old_ld_update = inst->ld_update; ++ old_ld_update = xgkr->ld_update; + req_cop1 = (old_ld_update & COP1_MASK) >> COP1_SHIFT; + req_coz = (old_ld_update & COZ_MASK) >> COZ_SHIFT; + req_com1 = (old_ld_update & COM1_MASK) >> COM1_SHIFT; @@ -603,7 +1148,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + if ((status_cop1 == COE_UPDATED || status_cop1 == COE_MAX) && + (status_coz == COE_UPDATED || status_coz == COE_MAX) && + (status_com1 == COE_UPDATED || status_com1 == COE_MAX)) { -+ inst->ld_update &= ~PRESET_MASK; ++ xgkr->ld_update &= ~PRESET_MASK; + } + } + @@ -614,7 +1159,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + if (status_cop1 != COE_NOTUPDATED && + status_coz != COE_NOTUPDATED && + status_com1 != COE_NOTUPDATED) { -+ inst->ld_update &= ~INIT_MASK; ++ xgkr->ld_update &= ~INIT_MASK; + } + } + @@ -624,7 +1169,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + */ + if (!tx_c->sent_init) { + if (!lp_status && !(old_ld_update & (LD_ALL_MASK))) { -+ inst->ld_update = INIT_MASK; ++ xgkr->ld_update = INIT_MASK; + tx_c->sent_init = true; + } + } @@ -636,7 +1181,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + */ + if (status_cop1 != COE_NOTUPDATED) { + if (req_cop1) { -+ inst->ld_update &= ~COP1_MASK; ++ xgkr->ld_update &= ~COP1_MASK; +#ifdef NEW_ALGORITHM_TRAIN_TX + if (tx_c->post_inc) { + if (req_cop1 == INCREMENT && @@ -648,19 +1193,19 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + tx_c->post_inc -= 1; + } + -+ ld_coe_update(inst); ++ ld_coe_update(xgkr); + goto recheck; + } +#endif + if ((req_cop1 == DECREMENT && status_cop1 == COE_MIN) || + (req_cop1 == INCREMENT && status_cop1 == COE_MAX)) { -+ dev_dbg(&inst->phydev->mdio.dev, "COP1 hit limit %s", ++ dev_dbg(&xgkr->phydev->mdio.dev, "COP1 hit limit %s", + (status_cop1 == COE_MIN) ? + "DEC MIN" : "INC MAX"); + tx_c->long_min_max_cnt++; + if (tx_c->long_min_max_cnt >= TIMEOUT_LONG) { + tx_c->bin_long_stop = true; -+ ld_coe_update(inst); ++ ld_coe_update(xgkr); + goto recheck; + } + } @@ -669,12 +1214,12 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + + if (status_coz != COE_NOTUPDATED) { + if (req_coz) -+ inst->ld_update &= ~COZ_MASK; ++ xgkr->ld_update &= ~COZ_MASK; + } + + if (status_com1 != COE_NOTUPDATED) { + if (req_com1) { -+ inst->ld_update &= ~COM1_MASK; ++ xgkr->ld_update &= ~COM1_MASK; +#ifdef NEW_ALGORITHM_TRAIN_TX + if (tx_c->pre_inc) { + if (req_com1 == INCREMENT && @@ -683,28 +1228,28 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + else + tx_c->pre_inc -= 1; + -+ ld_coe_update(inst); ++ ld_coe_update(xgkr); + goto recheck; + } +#endif + /* Stop If we have reached the limit for a parameter. */ + if ((req_com1 == DECREMENT && status_com1 == COE_MIN) || + (req_com1 == INCREMENT && status_com1 == COE_MAX)) { -+ dev_dbg(&inst->phydev->mdio.dev, "COM1 hit limit %s", ++ dev_dbg(&xgkr->phydev->mdio.dev, "COM1 hit limit %s", + (status_com1 == COE_MIN) ? + "DEC MIN" : "INC MAX"); + tx_c->m1_min_max_cnt++; + if (tx_c->m1_min_max_cnt >= TIMEOUT_M1) { + tx_c->bin_m1_stop = true; -+ ld_coe_update(inst); ++ ld_coe_update(xgkr); + goto recheck; + } + } + } + } + -+ if (old_ld_update != inst->ld_update) { -+ ld_coe_update(inst); ++ if (old_ld_update != xgkr->ld_update) { ++ ld_coe_update(xgkr); + /* Redo these status checks and updates until we have no more + * changes, to speed up the overall process. + */ @@ -721,15 +1266,15 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + return; + +#ifdef NEW_ALGORITHM_TRAIN_TX -+ if (!(inst->ld_update & (PRESET_MASK | INIT_MASK))) { ++ if (!(xgkr->ld_update & (PRESET_MASK | INIT_MASK))) { + if (tx_c->pre_inc) { -+ inst->ld_update = INCREMENT << COM1_SHIFT; -+ ld_coe_update(inst); ++ xgkr->ld_update = INCREMENT << COM1_SHIFT; ++ ld_coe_update(xgkr); + return; + } + + if (status_cop1 != COE_MAX) { -+ median_gaink2 = get_median_gaink2(inst->reg_base); ++ median_gaink2 = xgkr->srds->get_median_gaink2(xgkr->reg_base); + if (median_gaink2 == 0xf) { + tx_c->post_inc = 1; + } else { @@ -746,16 +1291,16 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + } + + if (tx_c->post_inc) { -+ inst->ld_update = INCREMENT << COP1_SHIFT; -+ ld_coe_update(inst); ++ xgkr->ld_update = INCREMENT << COP1_SHIFT; ++ ld_coe_update(xgkr); + return; + } + } +#endif + + /* snapshot and select bin */ -+ bin_m1_early = is_bin_early(BIN_M1, inst->reg_base); -+ bin_long_early = is_bin_early(BIN_LONG, inst->reg_base); ++ bin_m1_early = xgkr->srds->is_bin_early(BIN_M1, xgkr->reg_base); ++ bin_long_early = xgkr->srds->is_bin_early(BIN_LONG, xgkr->reg_base); + + if (!tx_c->bin_m1_stop && !tx_c->bin_m1_late_early && bin_m1_early) { + tx_c->bin_m1_stop = true; @@ -773,39 +1318,39 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + * pending. We also only request coefficient updates when the + * corresponding status is NOT UPDATED and nothing is pending. + */ -+ if (!(inst->ld_update & (PRESET_MASK | INIT_MASK))) { ++ if (!(xgkr->ld_update & (PRESET_MASK | INIT_MASK))) { + if (!tx_c->bin_long_stop) { + /* BinM1 correction means changing COM1 */ -+ if (!status_com1 && !(inst->ld_update & COM1_MASK)) { ++ if (!status_com1 && !(xgkr->ld_update & COM1_MASK)) { + /* Avoid BinM1Late by requesting an + * immediate decrement. + */ + if (!bin_m1_early) { + /* request decrement c(-1) */ + temp = DECREMENT << COM1_SHIFT; -+ inst->ld_update = temp; -+ ld_coe_update(inst); ++ xgkr->ld_update = temp; ++ ld_coe_update(xgkr); + tx_c->bin_m1_late_early = bin_m1_early; + return; + } + } + + /* BinLong correction means changing COP1 */ -+ if (!status_cop1 && !(inst->ld_update & COP1_MASK)) { ++ if (!status_cop1 && !(xgkr->ld_update & COP1_MASK)) { + /* Locate BinLong transition point (if any) + * while avoiding BinM1Late. + */ + if (bin_long_early) { + /* request increment c(1) */ + temp = INCREMENT << COP1_SHIFT; -+ inst->ld_update = temp; ++ xgkr->ld_update = temp; + } else { + /* request decrement c(1) */ + temp = DECREMENT << COP1_SHIFT; -+ inst->ld_update = temp; ++ xgkr->ld_update = temp; + } + -+ ld_coe_update(inst); ++ ld_coe_update(xgkr); + tx_c->bin_long_late_early = bin_long_early; + } + /* We try to finish BinLong before we do BinM1 */ @@ -814,19 +1359,19 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + + if (!tx_c->bin_m1_stop) { + /* BinM1 correction means changing COM1 */ -+ if (!status_com1 && !(inst->ld_update & COM1_MASK)) { ++ if (!status_com1 && !(xgkr->ld_update & COM1_MASK)) { + /* Locate BinM1 transition point (if any) */ + if (bin_m1_early) { + /* request increment c(-1) */ + temp = INCREMENT << COM1_SHIFT; -+ inst->ld_update = temp; ++ xgkr->ld_update = temp; + } else { + /* request decrement c(-1) */ + temp = DECREMENT << COM1_SHIFT; -+ inst->ld_update = temp; ++ xgkr->ld_update = temp; + } + -+ ld_coe_update(inst); ++ ld_coe_update(xgkr); + tx_c->bin_m1_late_early = bin_m1_early; + } + } @@ -835,20 +1380,26 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + +static int is_link_up(struct phy_device *phydev) +{ -+ int val; ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ int val = 0; ++ ++ mutex_lock(&xgkr_inst->phy_lock); + -+ phy_read_mmd(phydev, MDIO_MMD_PCS, FSL_XFI_PCS_10GR_SR1); -+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, FSL_XFI_PCS_10GR_SR1); ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, XFI_PCS_SR1); + -+ return (val & FSL_KR_RX_LINK_STAT_MASK) ? 1 : 0; ++ mutex_unlock(&xgkr_inst->phy_lock); ++ ++ return (val & KR_RX_LINK_STAT_MASK) ? 1 : 0; +} + -+static int is_link_training_fail(struct phy_device *phydev) ++static int is_link_training_fail(struct xgkr_params *xgkr) +{ ++ struct phy_device *phydev = xgkr->phydev; + int val; + int timeout = 100; + -+ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_PMD_STATUS); ++ val = xgkr_phy_read_mmd(xgkr, lt_MDIO_MMD, lt_KR_PMD_STATUS); ++ + if (!(val & TRAIN_FAIL) && (val & RX_STAT)) { + /* check LNK_STAT for sure */ + while (timeout--) { @@ -862,18 +1413,18 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + return 1; +} + -+static int check_rx(struct phy_device *phydev) ++static int check_rx(struct xgkr_params *xgkr) +{ -+ return phy_read_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LP_STATUS) & ++ return xgkr_phy_read_mmd(xgkr, lt_MDIO_MMD, lt_KR_LP_STATUS) & + RX_READY_MASK; +} + +/* Coefficient values have hardware restrictions */ -+static int is_ld_valid(struct fsl_xgkr_inst *inst) ++static int is_ld_valid(struct xgkr_params *xgkr) +{ -+ u32 ratio_pst1q = inst->ratio_pst1q; -+ u32 adpt_eq = inst->adpt_eq; -+ u32 ratio_preq = inst->ratio_preq; ++ u32 ratio_pst1q = xgkr->ratio_pst1q; ++ u32 adpt_eq = xgkr->adpt_eq; ++ u32 ratio_preq = xgkr->ratio_preq; + + if ((ratio_pst1q + adpt_eq + ratio_preq) > 48) + return 0; @@ -909,15 +1460,15 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + } +} + -+static int inc_dec(struct fsl_xgkr_inst *inst, int field, int request) ++static enum coe_update inc_dec(struct xgkr_params *xgkr, int field, int request) +{ + u32 ld_limit[3], ld_coe[3], step[3]; + -+ ld_coe[0] = inst->ratio_pst1q; -+ ld_coe[1] = inst->adpt_eq; -+ ld_coe[2] = inst->ratio_preq; ++ ld_coe[0] = xgkr->ratio_pst1q; ++ ld_coe[1] = xgkr->adpt_eq; ++ ld_coe[2] = xgkr->ratio_preq; + -+ /* Information specific to the Freescale SerDes for 10GBase-KR: ++ /* Information specific to the SerDes for 10GBase-KR: + * Incrementing C(+1) means *decrementing* RATIO_PST1Q + * Incrementing C(0) means incrementing ADPT_EQ + * Incrementing C(-1) means *decrementing* RATIO_PREQ @@ -935,7 +1486,7 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + ld_coe[field] += step[field]; + else + /* MAX */ -+ return 2; ++ return COE_MAX; + break; + case DECREMENT: + ld_limit[0] = POST_COE_MIN; @@ -945,76 +1496,83 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + ld_coe[field] -= step[field]; + else + /* MIN */ -+ return 1; ++ return COE_MIN; + break; + default: + break; + } + -+ if (is_ld_valid(inst)) { ++ if (is_ld_valid(xgkr)) { + /* accept new ld */ -+ inst->ratio_pst1q = ld_coe[0]; -+ inst->adpt_eq = ld_coe[1]; -+ inst->ratio_preq = ld_coe[2]; ++ xgkr->ratio_pst1q = ld_coe[0]; ++ xgkr->adpt_eq = ld_coe[1]; ++ xgkr->ratio_preq = ld_coe[2]; + /* only some values for preq and pst1q can be used. + * for preq: 0x0, 0x1, 0x3, 0x5, 0x7, 0x9, 0xb, 0xc. + * for pst1q: 0x0, 0x1, 0x3, 0x5, 0x7, 0x9, 0xb, 0xd, 0xf, 0x10. + */ + if (!is_value_allowed((const u32 *)&preq_table, ld_coe[2])) { -+ dev_dbg(&inst->phydev->mdio.dev, ++ dev_dbg(&xgkr->phydev->mdio.dev, + "preq skipped value: %d\n", ld_coe[2]); -+ return 0; ++ /* NOT UPDATED */ ++ return COE_NOTUPDATED; + } + + if (!is_value_allowed((const u32 *)&pst1q_table, ld_coe[0])) { -+ dev_dbg(&inst->phydev->mdio.dev, ++ dev_dbg(&xgkr->phydev->mdio.dev, + "pst1q skipped value: %d\n", ld_coe[0]); -+ return 0; ++ /* NOT UPDATED */ ++ return COE_NOTUPDATED; + } + -+ tune_tecr0(inst); ++ tune_tecr(xgkr); + } else { + if (request == DECREMENT) + /* MIN */ -+ return 1; ++ return COE_MIN; + if (request == INCREMENT) + /* MAX */ -+ return 2; ++ return COE_MAX; + } + -+ return 0; ++ /* UPDATED */ ++ return COE_UPDATED; +} + -+static void min_max_updated(struct fsl_xgkr_inst *inst, int field, int new_ld) ++static void min_max_updated(struct xgkr_params *xgkr, int field, enum coe_update cs) +{ -+ u32 ld_coe[] = {COE_UPDATED, COE_MIN, COE_MAX}; + u32 mask, val; ++ u32 ld_cs = cs; ++ ++ if (cs == COE_INV) ++ return; + + switch (field) { + case COE_COP1: + mask = COP1_MASK; -+ val = ld_coe[new_ld] << COP1_SHIFT; ++ val = ld_cs << COP1_SHIFT; + break; + case COE_COZ: + mask = COZ_MASK; -+ val = ld_coe[new_ld] << COZ_SHIFT; ++ val = ld_cs << COZ_SHIFT; + break; + case COE_COM: + mask = COM1_MASK; -+ val = ld_coe[new_ld] << COM1_SHIFT; ++ val = ld_cs << COM1_SHIFT; + break; + default: + return; + } + -+ inst->ld_status &= ~mask; -+ inst->ld_status |= val; ++ xgkr->ld_status &= ~mask; ++ xgkr->ld_status |= val; +} + -+static void check_request(struct fsl_xgkr_inst *inst, int request) ++static void check_request(struct xgkr_params *xgkr, int request) +{ + int cop1_req, coz_req, com_req; -+ int old_status, new_ld_sta; ++ int old_status; ++ enum coe_update cu; + + cop1_req = (request & COP1_MASK) >> COP1_SHIFT; + coz_req = (request & COZ_MASK) >> COZ_SHIFT; @@ -1023,65 +1581,51 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + /* IEEE802.3-2008, 72.6.10.2.5 + * Ensure we only act on INCREMENT/DECREMENT when we are in NOT UPDATED + */ -+ old_status = inst->ld_status; ++ old_status = xgkr->ld_status; + -+ if (cop1_req && !(inst->ld_status & COP1_MASK)) { -+ new_ld_sta = inc_dec(inst, COE_COP1, cop1_req); -+ min_max_updated(inst, COE_COP1, new_ld_sta); ++ if (cop1_req && !(xgkr->ld_status & COP1_MASK)) { ++ cu = inc_dec(xgkr, COE_COP1, cop1_req); ++ min_max_updated(xgkr, COE_COP1, cu); + } + -+ if (coz_req && !(inst->ld_status & COZ_MASK)) { -+ new_ld_sta = inc_dec(inst, COE_COZ, coz_req); -+ min_max_updated(inst, COE_COZ, new_ld_sta); ++ if (coz_req && !(xgkr->ld_status & COZ_MASK)) { ++ cu = inc_dec(xgkr, COE_COZ, coz_req); ++ min_max_updated(xgkr, COE_COZ, cu); + } + -+ if (com_req && !(inst->ld_status & COM1_MASK)) { -+ new_ld_sta = inc_dec(inst, COE_COM, com_req); -+ min_max_updated(inst, COE_COM, new_ld_sta); ++ if (com_req && !(xgkr->ld_status & COM1_MASK)) { ++ cu = inc_dec(xgkr, COE_COM, com_req); ++ min_max_updated(xgkr, COE_COM, cu); + } + -+ if (old_status != inst->ld_status) -+ ld_coe_status(inst); ++ if (old_status != xgkr->ld_status) ++ ld_coe_status(xgkr); +} + -+static void preset(struct fsl_xgkr_inst *inst) ++static void preset(struct xgkr_params *xgkr) +{ + /* These are all MAX values from the IEEE802.3 perspective. */ -+ inst->ratio_pst1q = POST_COE_MAX; -+ inst->adpt_eq = ZERO_COE_MAX; -+ inst->ratio_preq = PRE_COE_MAX; ++ xgkr->ratio_pst1q = POST_COE_MAX; ++ xgkr->adpt_eq = ZERO_COE_MAX; ++ xgkr->ratio_preq = PRE_COE_MAX; + -+ tune_tecr0(inst); -+ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); -+ inst->ld_status |= COE_MAX << COP1_SHIFT | ++ tune_tecr(xgkr); ++ xgkr->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); ++ xgkr->ld_status |= COE_MAX << COP1_SHIFT | + COE_MAX << COZ_SHIFT | + COE_MAX << COM1_SHIFT; -+ ld_coe_status(inst); -+} -+ -+static void initialize(struct fsl_xgkr_inst *inst) -+{ -+ inst->ratio_preq = RATIO_PREQ; -+ inst->ratio_pst1q = RATIO_PST1Q; -+ inst->adpt_eq = RATIO_EQ; -+ -+ tune_tecr0(inst); -+ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); -+ inst->ld_status |= COE_UPDATED << COP1_SHIFT | -+ COE_UPDATED << COZ_SHIFT | -+ COE_UPDATED << COM1_SHIFT; -+ ld_coe_status(inst); ++ ld_coe_status(xgkr); +} + -+static void train_rx(struct fsl_xgkr_inst *inst) ++static void train_local_tx(struct xgkr_params *xgkr) +{ -+ struct phy_device *phydev = inst->phydev; + int request, old_ld_status; + + /* get request from LP */ -+ request = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LP_CU) & ++ request = xgkr_phy_read_mmd(xgkr, lt_MDIO_MMD, lt_KR_LP_CU) & + (LD_ALL_MASK); -+ old_ld_status = inst->ld_status; ++ ++ old_ld_status = xgkr->ld_status; + + /* IEEE802.3-2008, 72.6.10.2.5 + * Ensure we always go to NOT UDPATED for status reporting in @@ -1092,69 +1636,87 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + */ + if (!(request & (PRESET_MASK | INIT_MASK))) { + if (!(request & COP1_MASK)) -+ inst->ld_status &= ~COP1_MASK; ++ xgkr->ld_status &= ~COP1_MASK; + + if (!(request & COZ_MASK)) -+ inst->ld_status &= ~COZ_MASK; ++ xgkr->ld_status &= ~COZ_MASK; + + if (!(request & COM1_MASK)) -+ inst->ld_status &= ~COM1_MASK; ++ xgkr->ld_status &= ~COM1_MASK; + -+ if (old_ld_status != inst->ld_status) -+ ld_coe_status(inst); ++ if (old_ld_status != xgkr->ld_status) ++ ld_coe_status(xgkr); + } + + /* As soon as the LP shows ready, no need to do any more updates. */ -+ if (check_rx(phydev)) { ++ if (check_rx(xgkr)) { + /* LP receiver is ready */ -+ if (inst->ld_status & (COP1_MASK | COZ_MASK | COM1_MASK)) { -+ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); -+ ld_coe_status(inst); ++ if (xgkr->ld_status & (COP1_MASK | COZ_MASK | COM1_MASK)) { ++ xgkr->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK); ++ ld_coe_status(xgkr); + } + } else { + /* IEEE802.3-2008, 72.6.10.2.3.1/2 + * only act on PRESET/INITIALIZE if all status is NOT UPDATED. + */ + if (request & (PRESET_MASK | INIT_MASK)) { -+ if (!(inst->ld_status & ++ if (!(xgkr->ld_status & + (COP1_MASK | COZ_MASK | COM1_MASK))) { + if (request & PRESET_MASK) -+ preset(inst); ++ preset(xgkr); + + if (request & INIT_MASK) -+ initialize(inst); ++ initialize(xgkr); + } + } + + /* LP Coefficient are not in HOLD */ + if (request & REQUEST_MASK) -+ check_request(inst, request & REQUEST_MASK); ++ check_request(xgkr, request & REQUEST_MASK); + } +} + -+static void xgkr_start_train(struct phy_device *phydev) ++static void xgkr_start_train(struct xgkr_params *xgkr) +{ -+ struct fsl_xgkr_inst *inst = phydev->priv; -+ struct tx_condition *tx_c = &inst->tx_c; -+ int val = 0, i; ++ struct phy_device *phydev = xgkr->phydev; ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ struct tx_condition *tx_c = &xgkr->tx_c; ++ int val = 0, i, j; + int lt_state; + unsigned long dead_line; -+ int rx_ok, tx_ok; -+ -+ init_inst(inst, 0); -+ start_lt(phydev); ++ int lp_rx_ready, tx_training_complete; ++ u32 lt_timeout = 500; ++ ++ init_xgkr(xgkr, 0); ++ ++ start_lt(xgkr); ++ ++ if (xgkr_inst->bp_mode == PHY_BACKPLANE_40GBASE_KR) { ++ lt_timeout = 2000; ++ } + + for (i = 0; i < 2;) { -+ dead_line = jiffies + msecs_to_jiffies(500); ++ ++ dead_line = jiffies + msecs_to_jiffies(lt_timeout); ++ + while (time_before(jiffies, dead_line)) { -+ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, -+ FSL_KR_PMD_STATUS); ++ ++ val = xgkr_phy_read_mmd(xgkr, lt_MDIO_MMD, ++ lt_KR_PMD_STATUS); ++ + if (val & TRAIN_FAIL) { + /* LT failed already, reset lane to avoid + * it run into hanging, then start LT again. + */ -+ reset_gcr0(inst); -+ start_lt(phydev); ++ if (xgkr_inst->bp_mode == PHY_BACKPLANE_40GBASE_KR) { ++ /* Reset only the Master Lane */ ++ if (xgkr->idx == MASTER_LANE) ++ xgkr->srds->reset_lane(xgkr->reg_base); ++ } else { ++ xgkr->srds->reset_lane(xgkr->reg_base); ++ } ++ ++ start_lt(xgkr); + } else if ((val & PMD_STATUS_SUP_STAT) && + (val & PMD_STATUS_FRAME_LOCK)) + break; @@ -1168,105 +1730,293 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + } + + /* init process */ -+ rx_ok = false; -+ tx_ok = false; ++ lp_rx_ready = false; ++ tx_training_complete = false; + /* the LT should be finished in 500ms, failed or OK. */ -+ dead_line = jiffies + msecs_to_jiffies(500); ++ dead_line = jiffies + msecs_to_jiffies(lt_timeout); + + while (time_before(jiffies, dead_line)) { + /* check if the LT is already failed */ -+ lt_state = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, -+ FSL_KR_PMD_STATUS); ++ ++ lt_state = xgkr_phy_read_mmd(xgkr, lt_MDIO_MMD, ++ lt_KR_PMD_STATUS); ++ + if (lt_state & TRAIN_FAIL) { -+ reset_gcr0(inst); ++ ++ if (xgkr_inst->bp_mode == PHY_BACKPLANE_40GBASE_KR) { ++ /* Reset only the Master Lane */ ++ if (xgkr->idx == MASTER_LANE) ++ xgkr->srds->reset_lane(xgkr->reg_base); ++ } else { ++ xgkr->srds->reset_lane(xgkr->reg_base); ++ } ++ + break; + } + -+ rx_ok = check_rx(phydev); -+ tx_ok = tx_c->tx_complete; ++ lp_rx_ready = check_rx(xgkr); ++ tx_training_complete = tx_c->tx_complete; + -+ if (rx_ok && tx_ok) ++ if (lp_rx_ready && tx_training_complete) + break; + -+ if (!rx_ok) -+ train_rx(inst); ++ if (!lp_rx_ready) ++ train_local_tx(xgkr); + -+ if (!tx_ok) -+ train_tx(inst); ++ if (!tx_training_complete) ++ train_remote_tx(xgkr); + + usleep_range(100, 500); + } + + i++; + /* check LT result */ -+ if (is_link_training_fail(phydev)) { -+ init_inst(inst, 0); ++ if (is_link_training_fail(xgkr)) { ++ init_xgkr(xgkr, 0); + continue; + } else { -+ stop_lt(phydev); -+ inst->state = TRAINED; ++ stop_lt(xgkr); ++ xgkr->state = TRAINED; ++ ++ switch (xgkr_inst->bp_mode) ++ { ++ case PHY_BACKPLANE_10GBASE_KR: ++ if (phydev->attached_dev == NULL) ++ dev_info(&phydev->mdio.dev, "10GBase-KR link trained (Tx equalization: RATIO_PREQ = 0x%x, RATIO_PST1Q = 0x%x, ADPT_EQ = 0x%x)\n", ++ xgkr->tuned_ratio_preq, xgkr->tuned_ratio_pst1q, xgkr->tuned_adpt_eq); ++ else ++ dev_info(&phydev->mdio.dev, "%s %s: 10GBase-KR link trained (Tx equalization: RATIO_PREQ = 0x%x, RATIO_PST1Q = 0x%x, ADPT_EQ = 0x%x)\n", ++ dev_driver_string(phydev->attached_dev->dev.parent), ++ dev_name(phydev->attached_dev->dev.parent), ++ xgkr->tuned_ratio_preq, xgkr->tuned_ratio_pst1q, xgkr->tuned_adpt_eq); ++ break; ++ ++ case PHY_BACKPLANE_40GBASE_KR: ++ if (xgkr->idx == xgkr_inst->phy_lanes - 1) { ++ if (phydev->attached_dev == NULL) ++ dev_info(&phydev->mdio.dev, "40GBase-KR link trained at lanes Tx equalization:\n"); ++ else ++ dev_info(&phydev->mdio.dev, "%s %s: 40GBase-KR link trained at lanes Tx equalization:\n", ++ dev_driver_string(phydev->attached_dev->dev.parent), ++ dev_name(phydev->attached_dev->dev.parent)); ++ ++ for (j = 0; j < xgkr_inst->phy_lanes; j++) { ++ if (phydev->attached_dev == NULL) ++ dev_info(&phydev->mdio.dev, "40GBase-KR Lane %d: RATIO_PREQ = 0x%x, RATIO_PST1Q = 0x%x, ADPT_EQ = 0x%x\n", ++ j, xgkr_inst->xgkr[j].tuned_ratio_preq, xgkr_inst->xgkr[j].tuned_ratio_pst1q, xgkr_inst->xgkr[j].tuned_adpt_eq); ++ else ++ dev_info(&phydev->mdio.dev, "%s %s: 40GBase-KR Lane %d: RATIO_PREQ = 0x%x, RATIO_PST1Q = 0x%x, ADPT_EQ = 0x%x\n", ++ dev_driver_string(phydev->attached_dev->dev.parent), ++ dev_name(phydev->attached_dev->dev.parent), ++ j, xgkr_inst->xgkr[j].tuned_ratio_preq, xgkr_inst->xgkr[j].tuned_ratio_pst1q, xgkr_inst->xgkr[j].tuned_adpt_eq); ++ } ++ } ++ break; ++ } ++ + break; + } + } +} + ++static void xgkr_request_restart_an(struct xgkr_params *xgkr) ++{ ++ struct phy_device *phydev = xgkr->phydev; ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ int i; ++ ++ if (time_before(jiffies, xgkr->rt_time)) ++ return; ++ ++ switch (xgkr_inst->bp_mode) ++ { ++ case PHY_BACKPLANE_1000BASE_KX: ++ dev_err(&phydev->mdio.dev, "Wrong call path for 1000Base-KX \n"); ++ break; ++ ++ case PHY_BACKPLANE_10GBASE_KR: ++ init_xgkr(xgkr, 0); ++ reset_lt(xgkr); ++ xgkr->state = DETECTING_LP; ++ start_xgkr_an(xgkr); ++ start_xgkr_state_machine(&xgkr->xgkr_wk); ++ break; ++ ++ case PHY_BACKPLANE_40GBASE_KR: ++ for (i = 0; i < xgkr_inst->phy_lanes; i++) { ++ init_xgkr(&xgkr_inst->xgkr[i], 0); ++ reset_lt(&xgkr_inst->xgkr[i]); ++ xgkr_inst->xgkr[i].state = DETECTING_LP; ++ } ++ //Start AN only for Master Lane ++ start_xgkr_an(&xgkr_inst->xgkr[MASTER_LANE]); ++ //start state machine ++ for (i = 0; i < xgkr_inst->phy_lanes; i++) { ++ start_xgkr_state_machine(&xgkr_inst->xgkr[i].xgkr_wk); ++ } ++ break; ++ } ++ ++ xgkr->rt_time = jiffies + msecs_to_jiffies(XGKR_DENY_RT_INTERVAL); ++} ++ +static void xgkr_state_machine(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); -+ struct fsl_xgkr_inst *inst = container_of(dwork, -+ struct fsl_xgkr_inst, -+ xgkr_wk); -+ struct phy_device *phydev = inst->phydev; ++ struct xgkr_params *xgkr = container_of(dwork, ++ struct xgkr_params, xgkr_wk); ++ struct phy_device *phydev = xgkr->phydev; ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; + int an_state; -+ bool needs_train = false; ++ bool start_train = false; ++ bool all_lanes_trained = false; ++ int i; + -+ mutex_lock(&phydev->lock); ++ if (!xgkr_inst->aneg_done) { ++ start_xgkr_state_machine(&xgkr->xgkr_wk); ++ return; ++ } + -+ switch (inst->state) { ++ mutex_lock(&phydev->lock); ++ ++ switch (xgkr->state) { + case DETECTING_LP: -+ phy_read_mmd(phydev, MDIO_MMD_AN, FSL_AN_BP_STAT); -+ an_state = phy_read_mmd(phydev, MDIO_MMD_AN, FSL_AN_BP_STAT); -+ if ((an_state & KR_AN_MASK)) -+ needs_train = true; ++ ++ switch (xgkr_inst->bp_mode) ++ { ++ case PHY_BACKPLANE_1000BASE_KX: ++ dev_err(&phydev->mdio.dev, "Wrong call path for 1000Base-KX \n"); ++ break; ++ ++ case PHY_BACKPLANE_10GBASE_KR: ++ an_state = xgkr_phy_read_mmd(xgkr, MDIO_MMD_AN, g_an_BP_STAT); ++ if (an_state & KR_AN_MASK_10G) { ++ //AN acquired: Train the lane ++ xgkr->an_wait_count = 0; ++ start_train = true; ++ } else { ++ //AN lost or not yet acquired ++ if (!is_link_up(phydev)) { ++ //Link is down: restart training ++ xgkr->an_wait_count = 0; ++ xgkr_request_restart_an(xgkr); ++ } else { ++ //Link is up: wait few iterations for AN to be acquired ++ if (xgkr->an_wait_count >= XGKR_AN_WAIT_ITERATIONS) { ++ xgkr->an_wait_count = 0; ++ xgkr_request_restart_an(xgkr); ++ } else { ++ xgkr->an_wait_count++; ++ } ++ } ++ } ++ break; ++ ++ case PHY_BACKPLANE_40GBASE_KR: ++ //Check AN state only on Master Lane ++ an_state = xgkr_phy_read_mmd(&xgkr_inst->xgkr[MASTER_LANE], MDIO_MMD_AN, g_an_BP_STAT); ++ if (an_state & KR_AN_MASK_40G) { ++ //AN acquired: Train all lanes in order starting with Master Lane ++ xgkr->an_wait_count = 0; ++ if (xgkr->idx == MASTER_LANE) { ++ start_train = true; ++ } ++ else if (xgkr_inst->xgkr[xgkr->idx - 1].state == TRAINED) { ++ start_train = true; ++ } ++ } else { ++ //AN lost or not yet acquired ++ if (!is_link_up(phydev)) { ++ //Link is down: restart training ++ xgkr->an_wait_count = 0; ++ xgkr_request_restart_an(xgkr); ++ } else { ++ //Link is up: wait few iterations for AN to be acquired ++ if (xgkr->an_wait_count >= XGKR_AN_WAIT_ITERATIONS) { ++ xgkr->an_wait_count = 0; ++ xgkr_request_restart_an(xgkr); ++ } else { ++ xgkr->an_wait_count++; ++ } ++ } ++ } ++ break; ++ } + break; ++ + case TRAINED: + if (!is_link_up(phydev)) { -+ dev_info(&phydev->mdio.dev, -+ "Detect hotplug, restart training\n"); -+ init_inst(inst, 1); -+ start_xgkr_an(phydev); -+ inst->state = DETECTING_LP; ++ switch (xgkr_inst->bp_mode) ++ { ++ case PHY_BACKPLANE_1000BASE_KX: ++ dev_err(&phydev->mdio.dev, "Wrong call path for 1000Base-KX \n"); ++ break; ++ ++ case PHY_BACKPLANE_10GBASE_KR: ++ dev_info(&phydev->mdio.dev, "Detect hotplug, restart training\n"); ++ xgkr_request_restart_an(xgkr); ++ break; ++ ++ case PHY_BACKPLANE_40GBASE_KR: ++ if (xgkr->idx == MASTER_LANE) { ++ //check if all lanes are trained only on Master Lane ++ all_lanes_trained = true; ++ for (i = 0; i < xgkr_inst->phy_lanes; i++) { ++ if (xgkr_inst->xgkr[i].state != TRAINED) { ++ all_lanes_trained = false; ++ break; ++ } ++ } ++ if (all_lanes_trained) { ++ dev_info(&phydev->mdio.dev, "Detect hotplug, restart training\n"); ++ xgkr_request_restart_an(xgkr); ++ } ++ } ++ break; ++ } + } + break; + } + -+ if (needs_train) -+ xgkr_start_train(phydev); ++ if (start_train) { ++ xgkr_start_train(xgkr); ++ } + + mutex_unlock(&phydev->lock); -+ queue_delayed_work(system_power_efficient_wq, &inst->xgkr_wk, -+ msecs_to_jiffies(XGKR_TIMEOUT)); ++ start_xgkr_state_machine(&xgkr->xgkr_wk); +} + +static int fsl_backplane_probe(struct phy_device *phydev) +{ -+ struct fsl_xgkr_inst *xgkr_inst; ++ struct xgkr_phy_data *xgkr_inst; + struct device_node *phy_node, *lane_node; + struct resource res_lane; ++ struct serdes_access *srds = NULL; ++ int serdes_type; ++ const char *st; + const char *bm; -+ int ret; ++ int ret, i, phy_lanes; + int bp_mode; -+ u32 lane[2]; ++ u32 lane_base_addr[MAX_PHY_LANES_NO], lane_memmap_size; + + phy_node = phydev->mdio.dev.of_node; ++ if (!phy_node) { ++ dev_err(&phydev->mdio.dev, "No associated device tree node\n"); ++ return -EINVAL; ++ } ++ + bp_mode = of_property_read_string(phy_node, "backplane-mode", &bm); + if (bp_mode < 0) -+ return 0; ++ return -EINVAL; + ++ phy_lanes = 1; + if (!strcasecmp(bm, "1000base-kx")) { + bp_mode = PHY_BACKPLANE_1000BASE_KX; + } else if (!strcasecmp(bm, "10gbase-kr")) { + bp_mode = PHY_BACKPLANE_10GBASE_KR; ++ } else if (!strcasecmp(bm, "40gbase-kr")) { ++ bp_mode = PHY_BACKPLANE_40GBASE_KR; ++ phy_lanes = 4; + } else { + dev_err(&phydev->mdio.dev, "Unknown backplane-mode\n"); + return -EINVAL; @@ -1278,6 +2028,20 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + return -EINVAL; + } + ++ ret = of_property_read_string(lane_node, "compatible", &st); ++ if (ret < 0) { ++ //assume SERDES-10G if compatible property is not specified ++ serdes_type = SERDES_10G; ++ } ++ else if (!strcasecmp(st, "fsl,serdes-10g")) { ++ serdes_type = SERDES_10G; ++ } else if (!strcasecmp(st, "fsl,serdes-28g")) { ++ serdes_type = SERDES_28G; ++ } else { ++ dev_err(&phydev->mdio.dev, "Unknown serdes-type\n"); ++ return -EINVAL; ++ } ++ + ret = of_address_to_resource(lane_node, 0, &res_lane); + if (ret) { + dev_err(&phydev->mdio.dev, "could not obtain memory map\n"); @@ -1286,25 +2050,43 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + + of_node_put(lane_node); + ret = of_property_read_u32_array(phy_node, "fsl,lane-reg", -+ (u32 *)&lane, 2); ++ (u32 *)lane_base_addr, phy_lanes); + if (ret) { + dev_err(&phydev->mdio.dev, "could not get fsl,lane-reg\n"); + return -EINVAL; + } + -+ phydev->priv = devm_ioremap_nocache(&phydev->mdio.dev, -+ res_lane.start + lane[0], -+ lane[1]); -+ if (!phydev->priv) { -+ dev_err(&phydev->mdio.dev, "ioremap_nocache failed\n"); -+ return -ENOMEM; ++ switch (serdes_type) ++ { ++ case SERDES_10G: ++ setup_an_lt_ls(); ++ srds = setup_serdes_access_10g(); ++ break; ++ ++ case SERDES_28G: ++ setup_an_lt_lx(); ++ srds = setup_serdes_access_28g(); ++ break; ++ ++ default: ++ dev_err(&phydev->mdio.dev, "Unsupported serdes-type\n"); ++ return -EINVAL; + } + -+ if (bp_mode == PHY_BACKPLANE_1000BASE_KX) { -+ phydev->speed = SPEED_1000; -+ /* configure the lane for 1000BASE-KX */ -+ lane_set_1gkx(phydev->priv); -+ return 0; ++ if (!srds) { ++ dev_err(&phydev->mdio.dev, "Unsupported serdes-type\n"); ++ return -EINVAL; ++ } ++ ++ srds->serdes_type = serdes_type; ++ srds->is_little_endian = of_property_read_bool(lane_node, "little-endian"); ++ ++ if (srds->is_little_endian) { ++ srds->ioread32 = le_ioread32; ++ srds->iowrite32 = le_iowrite32; ++ } else { ++ srds->ioread32 = be_ioread32; ++ srds->iowrite32 = be_iowrite32; + } + + xgkr_inst = devm_kzalloc(&phydev->mdio.dev, @@ -1312,13 +2094,46 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + if (!xgkr_inst) + return -ENOMEM; + -+ xgkr_inst->reg_base = phydev->priv; -+ xgkr_inst->phydev = phydev; ++ xgkr_inst->phy_lanes = phy_lanes; ++ xgkr_inst->bp_mode = bp_mode; ++ mutex_init(&xgkr_inst->phy_lock); ++ ++ lane_memmap_size = srds->get_lane_memmap_size(); ++ ++ for (i = 0; i < phy_lanes; i++) { ++ xgkr_inst->xgkr[i].idx = i; ++ xgkr_inst->xgkr[i].phydev = phydev; ++ xgkr_inst->xgkr[i].srds = srds; ++ xgkr_inst->xgkr[i].reg_base = devm_ioremap_nocache(&phydev->mdio.dev, ++ res_lane.start + lane_base_addr[i], ++ lane_memmap_size); ++ if (!xgkr_inst->xgkr[i].reg_base) { ++ dev_err(&phydev->mdio.dev, "ioremap_nocache failed\n"); ++ return -ENOMEM; ++ } ++ xgkr_inst->xgkr[i].rt_time = jiffies + msecs_to_jiffies(XGKR_DENY_RT_INTERVAL); ++ } ++ + phydev->priv = xgkr_inst; + -+ if (bp_mode == PHY_BACKPLANE_10GBASE_KR) { ++ switch (bp_mode) ++ { ++ case PHY_BACKPLANE_1000BASE_KX: ++ phydev->speed = SPEED_1000; ++ /* configure the lane for 1000BASE-KX */ ++ srds->lane_set_1gkx(xgkr_inst->xgkr[SINGLE_LANE].reg_base); ++ break; ++ ++ case PHY_BACKPLANE_10GBASE_KR: + phydev->speed = SPEED_10000; -+ INIT_DELAYED_WORK(&xgkr_inst->xgkr_wk, xgkr_state_machine); ++ INIT_DELAYED_WORK(&xgkr_inst->xgkr[SINGLE_LANE].xgkr_wk, xgkr_state_machine); ++ break; ++ ++ case PHY_BACKPLANE_40GBASE_KR: ++ phydev->speed = SPEED_40000; ++ for (i = 0; i < phy_lanes; i++) ++ INIT_DELAYED_WORK(&xgkr_inst->xgkr[i].xgkr_wk, xgkr_state_machine); ++ break; + } + + return 0; @@ -1326,17 +2141,56 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + +static int fsl_backplane_aneg_done(struct phy_device *phydev) +{ ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ ++ if (!phydev->mdio.dev.of_node) { ++ dev_err(&phydev->mdio.dev, "No associated device tree node\n"); ++ return -EINVAL; ++ } ++ ++ xgkr_inst->aneg_done = true; ++ + return 1; +} + +static int fsl_backplane_config_aneg(struct phy_device *phydev) +{ -+ if (phydev->speed == SPEED_10000) { -+ phydev->supported |= SUPPORTED_10000baseKR_Full; -+ start_xgkr_an(phydev); -+ } else if (phydev->speed == SPEED_1000) { ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ int i; ++ ++ if (!phydev->mdio.dev.of_node) { ++ dev_err(&phydev->mdio.dev, "No associated device tree node\n"); ++ return -EINVAL; ++ } ++ ++ switch (phydev->speed) ++ { ++ case SPEED_1000: + phydev->supported |= SUPPORTED_1000baseKX_Full; + start_1gkx_an(phydev); ++ break; ++ ++ case SPEED_10000: ++ phydev->supported |= SUPPORTED_10000baseKR_Full; ++ reset_lt(&xgkr_inst->xgkr[SINGLE_LANE]); ++ start_xgkr_an(&xgkr_inst->xgkr[SINGLE_LANE]); ++ /* start state machine*/ ++ start_xgkr_state_machine(&xgkr_inst->xgkr[SINGLE_LANE].xgkr_wk); ++ break; ++ ++ case SPEED_40000: ++ phydev->supported |= SUPPORTED_40000baseKR4_Full; ++ for (i = 0; i < xgkr_inst->phy_lanes; i++) { ++ reset_lt(&xgkr_inst->xgkr[i]); ++ } ++ //Start AN only for Master Lane ++ start_xgkr_an(&xgkr_inst->xgkr[MASTER_LANE]); ++ /* start state machine*/ ++ for (i = 0; i < xgkr_inst->phy_lanes; i++) { ++ start_xgkr_state_machine(&xgkr_inst->xgkr[i].xgkr_wk); ++ } ++ ++ break; + } + + phydev->advertising = phydev->supported; @@ -1347,29 +2201,48 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + +static int fsl_backplane_suspend(struct phy_device *phydev) +{ -+ if (phydev->speed == SPEED_10000) { -+ struct fsl_xgkr_inst *xgkr_inst = phydev->priv; ++ int i; + -+ cancel_delayed_work_sync(&xgkr_inst->xgkr_wk); ++ if (!phydev->mdio.dev.of_node) { ++ dev_err(&phydev->mdio.dev, "No associated device tree node\n"); ++ return -EINVAL; ++ } ++ ++ if (phydev->speed == SPEED_10000 || phydev->speed == SPEED_40000) { ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ ++ for (i = 0; i < xgkr_inst->phy_lanes; i++) ++ cancel_delayed_work_sync(&xgkr_inst->xgkr[i].xgkr_wk); + } + return 0; +} + +static int fsl_backplane_resume(struct phy_device *phydev) +{ -+ if (phydev->speed == SPEED_10000) { -+ struct fsl_xgkr_inst *xgkr_inst = phydev->priv; ++ struct xgkr_phy_data *xgkr_inst = phydev->priv; ++ int i; + -+ init_inst(xgkr_inst, 1); -+ queue_delayed_work(system_power_efficient_wq, -+ &xgkr_inst->xgkr_wk, -+ msecs_to_jiffies(XGKR_TIMEOUT)); ++ if (!phydev->mdio.dev.of_node) { ++ dev_err(&phydev->mdio.dev, "No associated device tree node\n"); ++ return -EINVAL; ++ } ++ ++ if (phydev->speed == SPEED_10000 || phydev->speed == SPEED_40000) { ++ for (i = 0; i < xgkr_inst->phy_lanes; i++) { ++ init_xgkr(&xgkr_inst->xgkr[i], 1); ++ start_xgkr_state_machine(&xgkr_inst->xgkr[i].xgkr_wk); ++ } + } + return 0; +} + +static int fsl_backplane_read_status(struct phy_device *phydev) +{ ++ if (!phydev->mdio.dev.of_node) { ++ dev_err(&phydev->mdio.dev, "No associated device tree node\n"); ++ return -EINVAL; ++ } ++ + if (is_link_up(phydev)) + phydev->link = 1; + else @@ -1378,11 +2251,82 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + return 0; +} + ++static int fsl_backplane_match_phy_device(struct phy_device *phydev) ++{ ++ struct device_node *phy_node, *lane_node; ++ const char *st; ++ int serdes_type, i, ret; ++ const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); ++ ++ if (!phydev->mdio.dev.of_node) { ++ return 0; ++ } ++ ++ // WORKAROUND: ++ // Required for LX2 devices ++ // where PHY ID cannot be verified in PCS ++ // because PCS Device Identifier Upper and Lower registers are hidden ++ // and always return 0 when they are read: ++ // 2 02 Device_ID0 RO Bits 15:0 0 ++ // val = phy_read_mmd(phydev, MDIO_MMD_PCS, 0x2); ++ // 3 03 Device_ID1 RO Bits 31:16 0 ++ // val = phy_read_mmd(phydev, MDIO_MMD_PCS, 0x3); ++ // ++ // To be removed: After the issue will be fixed on LX2 devices ++ ++ if (!phydev->is_c45) ++ return 0; ++ ++ phy_node = phydev->mdio.dev.of_node; ++ ++ lane_node = of_parse_phandle(phy_node, "fsl,lane-handle", 0); ++ if (!lane_node) { ++ dev_err(&phydev->mdio.dev, "parse fsl,lane-handle failed\n"); ++ return 0; ++ } ++ ++ ret = of_property_read_string(lane_node, "compatible", &st); ++ if (ret < 0) { ++ //assume SERDES-10G if compatible property is not specified ++ serdes_type = SERDES_10G; ++ } ++ else if (!strcasecmp(st, "fsl,serdes-10g")) { ++ serdes_type = SERDES_10G; ++ } else if (!strcasecmp(st, "fsl,serdes-28g")) { ++ serdes_type = SERDES_28G; ++ } else { ++ dev_err(&phydev->mdio.dev, "Unknown serdes-type\n"); ++ return 0; ++ } ++ ++ if (serdes_type == SERDES_10G) { ++ //On LS devices we must find the c45 device with correct PHY ID ++ //Implementation similar with the one existent in phy_device: @function: phy_bus_match ++ for (i = 1; i < num_ids; i++) { ++ if (!(phydev->c45_ids.devices_in_package & (1 << i))) ++ continue; ++ ++ if ((PCS_PHY_DEVICE_ID & PCS_PHY_DEVICE_ID_MASK) == ++ (phydev->c45_ids.device_ids[i] & PCS_PHY_DEVICE_ID_MASK)) ++ { ++ return 1; ++ } ++ } ++ return 0; ++ } ++ ++ //On LX devices we cannot verify PHY ID ++ //so we are happy only with preliminary verifications already made: mdio.dev.of_node and is_c45 ++ //because we already filtered other undesired devices: non clause 45 ++ ++ return 1; ++} ++ +static struct phy_driver fsl_backplane_driver[] = { + { -+ .phy_id = FSL_PCS_PHY_ID, ++ .phy_id = PCS_PHY_DEVICE_ID, + .name = "Freescale Backplane", -+ .phy_id_mask = 0xffffffff, ++ .phy_id_mask = PCS_PHY_DEVICE_ID_MASK, + .features = SUPPORTED_Backplane | SUPPORTED_Autoneg | + SUPPORTED_MII, + .probe = fsl_backplane_probe, @@ -1391,13 +2335,14 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> + .read_status = fsl_backplane_read_status, + .suspend = fsl_backplane_suspend, + .resume = fsl_backplane_resume, ++ .match_phy_device = fsl_backplane_match_phy_device, + }, +}; + +module_phy_driver(fsl_backplane_driver); + +static struct mdio_device_id __maybe_unused freescale_tbl[] = { -+ { FSL_PCS_PHY_ID, 0xffffffff }, ++ { PCS_PHY_DEVICE_ID, PCS_PHY_DEVICE_ID_MASK }, + { } +}; + @@ -1406,6 +2351,1395 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com> +MODULE_DESCRIPTION("Freescale Backplane driver"); +MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>"); +MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/drivers/net/phy/fsl_backplane.h +@@ -0,0 +1,41 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * DPAA backplane driver. ++ * Author: Florinel Iordache <florinel.iordache@nxp.com> ++ * ++ * Copyright 2018 NXP ++ * ++ * Licensed under the GPL-2 or later. ++ */ ++ ++#ifndef FSL_BACKPLANE_H ++#define FSL_BACKPLANE_H ++ ++/* C(-1) */ ++#define BIN_M1 0 ++/* C(1) */ ++#define BIN_LONG 1 ++ ++#define BIN_SNAPSHOT_NUM 5 ++#define BIN_M1_THRESHOLD 3 ++#define BIN_LONG_THRESHOLD 2 ++ ++struct serdes_access { ++ ++ int serdes_type; ++ bool is_little_endian; ++ u32 (*ioread32)(u32 *reg); ++ void (*iowrite32)(u32 value, u32 *reg); ++ u32 (*get_lane_memmap_size)(void); ++ void (*tune_tecr)(void *reg, u32 ratio_preq, u32 ratio_pst1q, u32 adpt_eq, bool reset); ++ void (*reset_lane)(void *reg); ++ void (*lane_set_1gkx)(void *reg); ++ int (*get_median_gaink2)(u32 *reg); ++ bool (*is_bin_early)(int bin_sel, void *reg); ++}; ++ ++struct serdes_access* setup_serdes_access_10g(void); ++struct serdes_access* setup_serdes_access_28g(void); ++ ++ ++#endif //FSL_BACKPLANE_H +--- /dev/null ++++ b/drivers/net/phy/fsl_backplane_serdes_10g.c +@@ -0,0 +1,281 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DPAA backplane driver for SerDes 10G. ++ * Author: Florinel Iordache <florinel.iordache@nxp.com> ++ * ++ * Copyright 2018 NXP ++ * ++ * Licensed under the GPL-2 or later. ++ */ ++ ++#include <linux/io.h> ++#include <linux/delay.h> ++ ++#include "fsl_backplane.h" ++ ++#define BIN_M1_SEL 6 ++#define BIN_Long_SEL 7 ++#define CDR_SEL_MASK 0x00070000 ++ ++#define PRE_COE_SHIFT 22 ++#define POST_COE_SHIFT 16 ++#define ZERO_COE_SHIFT 8 ++ ++#define TECR0_INIT 0x24200000 ++ ++#define GCR0_RESET_MASK 0x00600000 ++ ++#define GCR1_SNP_START_MASK 0x00000040 ++#define GCR1_CTL_SNP_START_MASK 0x00002000 ++ ++#define RECR1_CTL_SNP_DONE_MASK 0x00000002 ++#define RECR1_SNP_DONE_MASK 0x00000004 ++#define TCSR1_SNP_DATA_MASK 0x0000ffc0 ++#define TCSR1_SNP_DATA_SHIFT 6 ++#define TCSR1_EQ_SNPBIN_SIGN_MASK 0x100 ++ ++#define RECR1_GAINK2_MASK 0x0f000000 ++#define RECR1_GAINK2_SHIFT 24 ++ ++/* Required only for 1000BASE KX */ ++#define GCR1_REIDL_TH_MASK 0x00700000 ++#define GCR1_REIDL_EX_SEL_MASK 0x000c0000 ++#define GCR1_REIDL_ET_MAS_MASK 0x00004000 ++#define TECR0_AMP_RED_MASK 0x0000003f ++ ++struct per_lane_ctrl_status { ++ u32 gcr0; /* 0x.000 - General Control Register 0 */ ++ u32 gcr1; /* 0x.004 - General Control Register 1 */ ++ u32 gcr2; /* 0x.008 - General Control Register 2 */ ++ u32 resv1; /* 0x.00C - Reserved */ ++ u32 recr0; /* 0x.010 - Receive Equalization Control Register 0 */ ++ u32 recr1; /* 0x.014 - Receive Equalization Control Register 1 */ ++ u32 tecr0; /* 0x.018 - Transmit Equalization Control Register 0 */ ++ u32 resv2; /* 0x.01C - Reserved */ ++ u32 tlcr0; /* 0x.020 - TTL Control Register 0 */ ++ u32 tlcr1; /* 0x.024 - TTL Control Register 1 */ ++ u32 tlcr2; /* 0x.028 - TTL Control Register 2 */ ++ u32 tlcr3; /* 0x.02C - TTL Control Register 3 */ ++ u32 tcsr0; /* 0x.030 - Test Control/Status Register 0 */ ++ u32 tcsr1; /* 0x.034 - Test Control/Status Register 1 */ ++ u32 tcsr2; /* 0x.038 - Test Control/Status Register 2 */ ++ u32 tcsr3; /* 0x.03C - Test Control/Status Register 3 */ ++}; ++ ++static struct serdes_access srds; ++ ++static u32 get_lane_memmap_size(void) ++{ ++ return 0x40; ++} ++ ++static void reset_lane(void *reg) ++{ ++ struct per_lane_ctrl_status *reg_base = reg; ++ ++ /* reset the lane */ ++ srds.iowrite32(srds.ioread32(®_base->gcr0) & ~GCR0_RESET_MASK, ++ ®_base->gcr0); ++ udelay(1); ++ ++ /* unreset the lane */ ++ srds.iowrite32(srds.ioread32(®_base->gcr0) | GCR0_RESET_MASK, ++ ®_base->gcr0); ++ udelay(1); ++} ++ ++static void tune_tecr(void *reg, u32 ratio_preq, u32 ratio_pst1q, u32 adpt_eq, bool reset) ++{ ++ struct per_lane_ctrl_status *reg_base = reg; ++ u32 val; ++ ++ val = TECR0_INIT | ++ adpt_eq << ZERO_COE_SHIFT | ++ ratio_preq << PRE_COE_SHIFT | ++ ratio_pst1q << POST_COE_SHIFT; ++ ++ if (reset) { ++ /* reset the lane */ ++ srds.iowrite32(srds.ioread32(®_base->gcr0) & ~GCR0_RESET_MASK, ++ ®_base->gcr0); ++ udelay(1); ++ } ++ ++ srds.iowrite32(val, ®_base->tecr0); ++ udelay(1); ++ ++ if (reset) { ++ /* unreset the lane */ ++ srds.iowrite32(srds.ioread32(®_base->gcr0) | GCR0_RESET_MASK, ++ ®_base->gcr0); ++ udelay(1); ++ } ++} ++ ++static void lane_set_1gkx(void *reg) ++{ ++ struct per_lane_ctrl_status *reg_base = reg; ++ u32 val; ++ ++ /* reset the lane */ ++ srds.iowrite32(srds.ioread32(®_base->gcr0) & ~GCR0_RESET_MASK, ++ ®_base->gcr0); ++ udelay(1); ++ ++ /* set gcr1 for 1GKX */ ++ val = srds.ioread32(®_base->gcr1); ++ val &= ~(GCR1_REIDL_TH_MASK | GCR1_REIDL_EX_SEL_MASK | ++ GCR1_REIDL_ET_MAS_MASK); ++ srds.iowrite32(val, ®_base->gcr1); ++ udelay(1); ++ ++ /* set tecr0 for 1GKX */ ++ val = srds.ioread32(®_base->tecr0); ++ val &= ~TECR0_AMP_RED_MASK; ++ srds.iowrite32(val, ®_base->tecr0); ++ udelay(1); ++ ++ /* unreset the lane */ ++ srds.iowrite32(srds.ioread32(®_base->gcr0) | GCR0_RESET_MASK, ++ ®_base->gcr0); ++ udelay(1); ++} ++ ++static int get_median_gaink2(u32 *reg) ++{ ++ int gaink2_snap_shot[BIN_SNAPSHOT_NUM]; ++ u32 rx_eq_snp; ++ struct per_lane_ctrl_status *reg_base; ++ int timeout; ++ int i, j, tmp, pos; ++ ++ reg_base = (struct per_lane_ctrl_status *)reg; ++ ++ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) { ++ /* wait RECR1_CTL_SNP_DONE_MASK has cleared */ ++ timeout = 100; ++ while (srds.ioread32(®_base->recr1) & ++ RECR1_CTL_SNP_DONE_MASK) { ++ udelay(1); ++ timeout--; ++ if (timeout == 0) ++ break; ++ } ++ ++ /* start snap shot */ ++ srds.iowrite32((srds.ioread32(®_base->gcr1) | ++ GCR1_CTL_SNP_START_MASK), ++ ®_base->gcr1); ++ ++ /* wait for SNP done */ ++ timeout = 100; ++ while (!(srds.ioread32(®_base->recr1) & ++ RECR1_CTL_SNP_DONE_MASK)) { ++ udelay(1); ++ timeout--; ++ if (timeout == 0) ++ break; ++ } ++ ++ /* read and save the snap shot */ ++ rx_eq_snp = srds.ioread32(®_base->recr1); ++ gaink2_snap_shot[i] = (rx_eq_snp & RECR1_GAINK2_MASK) >> ++ RECR1_GAINK2_SHIFT; ++ ++ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */ ++ srds.iowrite32((srds.ioread32(®_base->gcr1) & ++ ~GCR1_CTL_SNP_START_MASK), ++ ®_base->gcr1); ++ } ++ ++ /* get median of the 5 snap shot */ ++ for (i = 0; i < BIN_SNAPSHOT_NUM - 1; i++) { ++ tmp = gaink2_snap_shot[i]; ++ pos = i; ++ for (j = i + 1; j < BIN_SNAPSHOT_NUM; j++) { ++ if (gaink2_snap_shot[j] < tmp) { ++ tmp = gaink2_snap_shot[j]; ++ pos = j; ++ } ++ } ++ ++ gaink2_snap_shot[pos] = gaink2_snap_shot[i]; ++ gaink2_snap_shot[i] = tmp; ++ } ++ ++ return gaink2_snap_shot[2]; ++} ++ ++static bool is_bin_early(int bin_sel, void *reg) ++{ ++ bool early = false; ++ int bin_snap_shot[BIN_SNAPSHOT_NUM]; ++ int i, negative_count = 0; ++ struct per_lane_ctrl_status *reg_base = reg; ++ int timeout; ++ ++ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) { ++ /* wait RECR1_SNP_DONE_MASK has cleared */ ++ timeout = 100; ++ while ((srds.ioread32(®_base->recr1) & RECR1_SNP_DONE_MASK)) { ++ udelay(1); ++ timeout--; ++ if (timeout == 0) ++ break; ++ } ++ ++ /* set TCSR1[CDR_SEL] to BinM1/BinLong */ ++ if (bin_sel == BIN_M1) { ++ srds.iowrite32((srds.ioread32(®_base->tcsr1) & ++ ~CDR_SEL_MASK) | BIN_M1_SEL, ++ ®_base->tcsr1); ++ } else { ++ srds.iowrite32((srds.ioread32(®_base->tcsr1) & ++ ~CDR_SEL_MASK) | BIN_Long_SEL, ++ ®_base->tcsr1); ++ } ++ ++ /* start snap shot */ ++ srds.iowrite32(srds.ioread32(®_base->gcr1) | GCR1_SNP_START_MASK, ++ ®_base->gcr1); ++ ++ /* wait for SNP done */ ++ timeout = 100; ++ while (!(srds.ioread32(®_base->recr1) & RECR1_SNP_DONE_MASK)) { ++ udelay(1); ++ timeout--; ++ if (timeout == 0) ++ break; ++ } ++ ++ /* read and save the snap shot */ ++ bin_snap_shot[i] = (srds.ioread32(®_base->tcsr1) & ++ TCSR1_SNP_DATA_MASK) >> TCSR1_SNP_DATA_SHIFT; ++ if (bin_snap_shot[i] & TCSR1_EQ_SNPBIN_SIGN_MASK) ++ negative_count++; ++ ++ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */ ++ srds.iowrite32(srds.ioread32(®_base->gcr1) & ~GCR1_SNP_START_MASK, ++ ®_base->gcr1); ++ } ++ ++ if (((bin_sel == BIN_M1) && (negative_count > BIN_M1_THRESHOLD)) || ++ ((bin_sel == BIN_LONG && (negative_count > BIN_LONG_THRESHOLD)))) { ++ early = true; ++ } ++ ++ return early; ++} ++ ++struct serdes_access* setup_serdes_access_10g(void) ++{ ++ srds.get_lane_memmap_size = get_lane_memmap_size; ++ srds.tune_tecr = tune_tecr; ++ srds.reset_lane = reset_lane; ++ srds.lane_set_1gkx = lane_set_1gkx; ++ srds.get_median_gaink2 = get_median_gaink2; ++ srds.is_bin_early = is_bin_early; ++ ++ return &srds; ++} ++ +--- /dev/null ++++ b/drivers/net/phy/fsl_backplane_serdes_28g.c +@@ -0,0 +1,336 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * DPAA backplane driver for SerDes 28G. ++ * Author: Florinel Iordache <florinel.iordache@nxp.com> ++ * ++ * Copyright 2018 NXP ++ * ++ * Licensed under the GPL-2 or later. ++ */ ++ ++#include <linux/io.h> ++#include <linux/delay.h> ++#include <linux/sched.h> ++ ++#include "fsl_backplane.h" ++ ++#define BIN_M1_SEL 0x0000c000 ++#define BIN_Long_SEL 0x0000d000 ++#define CDR_SEL_MASK 0x0000f000 ++ ++#define PRE_COE_SHIFT 16 ++#define POST_COE_SHIFT 8 ++#define ZERO_COE_SHIFT 24 ++ ++#define TECR0_INIT 0x20808000 ++ ++#define RESET_REQ_MASK 0x80000000 ++ ++#define RECR3_SNP_START_MASK 0x80000000 ++#define RECR3_SNP_DONE_MASK 0x40000000 ++ ++#define RECR4_SNP_DATA_MASK 0x000003ff ++#define RECR4_SNP_DATA_SHIFT 0 ++#define RECR4_EQ_SNPBIN_SIGN_MASK 0x200 ++ ++#define RECR3_GAINK2_MASK 0x1f000000 ++#define RECR3_GAINK2_SHIFT 24 ++ ++/* Required only for 1000BASE KX */ ++#define GCR1_REIDL_TH_MASK 0x00700000 ++#define GCR1_REIDL_EX_SEL_MASK 0x000c0000 ++#define GCR1_REIDL_ET_MAS_MASK 0x04000000 ++#define TECR0_AMP_RED_MASK 0x0000003f ++ ++struct per_lane_ctrl_status { ++ u32 gcr0; /* 0x.000 - General Control Register 0 */ ++ u32 resv1; /* 0x.004 - Reserved */ ++ u32 resv2; /* 0x.008 - Reserved */ ++ u32 resv3; /* 0x.00C - Reserved */ ++ u32 resv4; /* 0x.010 - Reserved */ ++ u32 resv5; /* 0x.014 - Reserved */ ++ u32 resv6; /* 0x.018 - Reserved */ ++ u32 resv7; /* 0x.01C - Reserved */ ++ u32 trstctl; /* 0x.020 - TX Reset Control Register */ ++ u32 tgcr0; /* 0x.024 - TX General Control Register 0 */ ++ u32 tgcr1; /* 0x.028 - TX General Control Register 1 */ ++ u32 tgcr2; /* 0x.02C - TX General Control Register 2 */ ++ u32 tecr0; /* 0x.030 - Transmit Equalization Control Register 0 */ ++ u32 tecr1; /* 0x.034 - Transmit Equalization Control Register 1 */ ++ u32 resv8; /* 0x.038 - Reserved */ ++ u32 resv9; /* 0x.03C - Reserved */ ++ u32 rrstctl; /* 0x.040 - RX Reset Control Register */ ++ u32 rgcr0; /* 0x.044 - RX General Control Register 0 */ ++ u32 rxgcr1; /* 0x.048 - RX General Control Register 1 */ ++ u32 resv10; /* 0x.04C - Reserved */ ++ u32 recr0; /* 0x.050 - RX Equalization Register 0 */ ++ u32 recr1; /* 0x.054 - RX Equalization Register 1 */ ++ u32 recr2; /* 0x.058 - RX Equalization Register 2 */ ++ u32 recr3; /* 0x.05C - RX Equalization Register 3 */ ++ u32 recr4; /* 0x.060 - RX Equalization Register 4 */ ++ u32 resv11; /* 0x.064 - Reserved */ ++ u32 rccr0; /* 0x.068 - RX Calibration Register 0 */ ++ u32 rccr1; /* 0x.06C - RX Calibration Register 1 */ ++ u32 rcpcr0; /* 0x.070 - RX Clock Path Register 0 */ ++ u32 rsccr0; /* 0x.074 - RX Sampler Calibration Control Register 0 */ ++ u32 rsccr1; /* 0x.078 - RX Sampler Calibration Control Register 1 */ ++ u32 resv12; /* 0x.07C - Reserved */ ++ u32 ttlcr0; /* 0x.080 - Transition Tracking Loop Register 0 */ ++ u32 ttlcr1; /* 0x.084 - Transition Tracking Loop Register 1 */ ++ u32 ttlcr2; /* 0x.088 - Transition Tracking Loop Register 2 */ ++ u32 ttlcr3; /* 0x.08C - Transition Tracking Loop Register 3 */ ++ u32 resv13; /* 0x.090 - Reserved */ ++ u32 resv14; /* 0x.094 - Reserved */ ++ u32 resv15; /* 0x.098 - Reserved */ ++ u32 resv16; /* 0x.09C - Reserved */ ++ u32 tcsr0; /* 0x.0A0 - Test Control/Status Register 0 */ ++ u32 tcsr1; /* 0x.0A4 - Test Control/Status Register 1 */ ++ u32 tcsr2; /* 0x.0A8 - Test Control/Status Register 2 */ ++ u32 tcsr3; /* 0x.0AC - Test Control/Status Register 3 */ ++ u32 tcsr4; /* 0x.0B0 - Test Control/Status Register 4 */ ++ u32 resv17; /* 0x.0B4 - Reserved */ ++ u32 resv18; /* 0x.0B8 - Reserved */ ++ u32 resv19; /* 0x.0BC - Reserved */ ++ u32 rxcb0; /* 0x.0C0 - RX Control Block Register 0 */ ++ u32 rxcb1; /* 0x.0C4 - RX Control Block Register 1 */ ++ u32 resv20; /* 0x.0C8 - Reserved */ ++ u32 resv21; /* 0x.0CC - Reserved */ ++ u32 rxss0; /* 0x.0D0 - RX Speed Switch Register 0 */ ++ u32 rxss1; /* 0x.0D4 - RX Speed Switch Register 1 */ ++ u32 rxss2; /* 0x.0D8 - RX Speed Switch Register 2 */ ++ u32 resv22; /* 0x.0DC - Reserved */ ++ u32 txcb0; /* 0x.0E0 - TX Control Block Register 0 */ ++ u32 txcb1; /* 0x.0E4 - TX Control Block Register 1 */ ++ u32 resv23; /* 0x.0E8 - Reserved */ ++ u32 resv24; /* 0x.0EC - Reserved */ ++ u32 txss0; /* 0x.0F0 - TX Speed Switch Register 0 */ ++ u32 txss1; /* 0x.0F4 - TX Speed Switch Register 1 */ ++ u32 txss2; /* 0x.0F8 - TX Speed Switch Register 2 */ ++ u32 resv25; /* 0x.0FC - Reserved */ ++}; ++ ++static struct serdes_access srds; ++ ++static u32 get_lane_memmap_size(void) ++{ ++ return 0x100; ++} ++ ++static void reset_lane(void *reg) ++{ ++ struct per_lane_ctrl_status *reg_base = reg; ++ u32 val; ++ unsigned long timeout; ++ ++ /* reset Tx lane: send reset request */ ++ srds.iowrite32(srds.ioread32(®_base->trstctl) | RESET_REQ_MASK, ++ ®_base->trstctl); ++ udelay(1); ++ timeout = 10; ++ while (timeout--) { ++ val = srds.ioread32(®_base->trstctl); ++ if (!(val & RESET_REQ_MASK)) ++ break; ++ usleep_range(5, 20); ++ } ++ ++ /* reset Rx lane: send reset request */ ++ srds.iowrite32(srds.ioread32(®_base->rrstctl) | RESET_REQ_MASK, ++ ®_base->rrstctl); ++ udelay(1); ++ timeout = 10; ++ while (timeout--) { ++ val = srds.ioread32(®_base->rrstctl); ++ if (!(val & RESET_REQ_MASK)) ++ break; ++ usleep_range(5, 20); ++ } ++ ++ /* wait for a while after reset */ ++ timeout = jiffies + 10; ++ while (time_before(jiffies, timeout)) { ++ schedule(); ++ usleep_range(5, 20); ++ } ++} ++ ++static void tune_tecr(void *reg, u32 ratio_preq, u32 ratio_pst1q, u32 adpt_eq, bool reset) ++{ ++ struct per_lane_ctrl_status *reg_base = reg; ++ u32 val; ++ ++ if (reset) { ++ /* reset lanes */ ++ reset_lane(reg); ++ } ++ ++ val = TECR0_INIT | ++ ratio_preq << PRE_COE_SHIFT | ++ ratio_pst1q << POST_COE_SHIFT; ++ srds.iowrite32(val, ®_base->tecr0); ++ ++ val = adpt_eq << ZERO_COE_SHIFT; ++ srds.iowrite32(val, ®_base->tecr1); ++ ++ udelay(1); ++} ++ ++static void lane_set_1gkx(void *reg) ++{ ++ struct per_lane_ctrl_status *reg_base = reg; ++ u32 val; ++ ++ /* reset lanes */ ++ reset_lane(reg); ++ ++ /* set gcr1 for 1GKX */ ++ val = srds.ioread32(®_base->rxgcr1); ++ val &= ~(GCR1_REIDL_TH_MASK | GCR1_REIDL_EX_SEL_MASK | ++ GCR1_REIDL_ET_MAS_MASK); ++ srds.iowrite32(val, ®_base->rxgcr1); ++ udelay(1); ++ ++ /* set tecr0 for 1GKX */ ++ val = srds.ioread32(®_base->tecr0); ++ val &= ~TECR0_AMP_RED_MASK; ++ srds.iowrite32(val, ®_base->tecr0); ++ udelay(1); ++} ++ ++static int get_median_gaink2(u32 *reg) ++{ ++ int gaink2_snap_shot[BIN_SNAPSHOT_NUM]; ++ u32 rx_eq_snp; ++ struct per_lane_ctrl_status *reg_base; ++ int timeout; ++ int i, j, tmp, pos; ++ ++ reg_base = (struct per_lane_ctrl_status *)reg; ++ ++ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) { ++ /* wait RECR3_SNP_DONE_MASK has cleared */ ++ timeout = 100; ++ while (srds.ioread32(®_base->recr3) & ++ RECR3_SNP_DONE_MASK) { ++ udelay(1); ++ timeout--; ++ if (timeout == 0) ++ break; ++ } ++ ++ /* start snap shot */ ++ srds.iowrite32((srds.ioread32(®_base->recr3) | ++ RECR3_SNP_START_MASK), ++ ®_base->recr3); ++ ++ /* wait for SNP done */ ++ timeout = 100; ++ while (!(srds.ioread32(®_base->recr3) & ++ RECR3_SNP_DONE_MASK)) { ++ udelay(1); ++ timeout--; ++ if (timeout == 0) ++ break; ++ } ++ ++ /* read and save the snap shot */ ++ rx_eq_snp = srds.ioread32(®_base->recr3); ++ gaink2_snap_shot[i] = (rx_eq_snp & RECR3_GAINK2_MASK) >> ++ RECR3_GAINK2_SHIFT; ++ ++ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */ ++ srds.iowrite32((srds.ioread32(®_base->recr3) & ++ ~RECR3_SNP_START_MASK), ++ ®_base->recr3); ++ } ++ ++ /* get median of the 5 snap shot */ ++ for (i = 0; i < BIN_SNAPSHOT_NUM - 1; i++) { ++ tmp = gaink2_snap_shot[i]; ++ pos = i; ++ for (j = i + 1; j < BIN_SNAPSHOT_NUM; j++) { ++ if (gaink2_snap_shot[j] < tmp) { ++ tmp = gaink2_snap_shot[j]; ++ pos = j; ++ } ++ } ++ ++ gaink2_snap_shot[pos] = gaink2_snap_shot[i]; ++ gaink2_snap_shot[i] = tmp; ++ } ++ ++ return gaink2_snap_shot[2]; ++} ++ ++static bool is_bin_early(int bin_sel, void *reg) ++{ ++ bool early = false; ++ int bin_snap_shot[BIN_SNAPSHOT_NUM]; ++ int i, negative_count = 0; ++ struct per_lane_ctrl_status *reg_base = reg; ++ int timeout; ++ ++ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) { ++ /* wait RECR3_SNP_DONE_MASK has cleared */ ++ timeout = 100; ++ while ((srds.ioread32(®_base->recr3) & RECR3_SNP_DONE_MASK)) { ++ udelay(1); ++ timeout--; ++ if (timeout == 0) ++ break; ++ } ++ ++ /* set TCSR1[CDR_SEL] to BinM1/BinLong */ ++ if (bin_sel == BIN_M1) { ++ srds.iowrite32((srds.ioread32(®_base->recr4) & ++ ~CDR_SEL_MASK) | BIN_M1_SEL, ++ ®_base->recr4); ++ } else { ++ srds.iowrite32((srds.ioread32(®_base->recr4) & ++ ~CDR_SEL_MASK) | BIN_Long_SEL, ++ ®_base->recr4); ++ } ++ ++ /* start snap shot */ ++ srds.iowrite32(srds.ioread32(®_base->recr3) | RECR3_SNP_START_MASK, ++ ®_base->recr3); ++ ++ /* wait for SNP done */ ++ timeout = 100; ++ while (!(srds.ioread32(®_base->recr3) & RECR3_SNP_DONE_MASK)) { ++ udelay(1); ++ timeout--; ++ if (timeout == 0) ++ break; ++ } ++ ++ /* read and save the snap shot */ ++ bin_snap_shot[i] = (srds.ioread32(®_base->recr4) & ++ RECR4_SNP_DATA_MASK) >> RECR4_SNP_DATA_SHIFT; ++ if (bin_snap_shot[i] & RECR4_EQ_SNPBIN_SIGN_MASK) ++ negative_count++; ++ ++ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */ ++ srds.iowrite32(srds.ioread32(®_base->recr3) & ~RECR3_SNP_START_MASK, ++ ®_base->recr3); ++ } ++ ++ if (((bin_sel == BIN_M1) && (negative_count > BIN_M1_THRESHOLD)) || ++ ((bin_sel == BIN_LONG && (negative_count > BIN_LONG_THRESHOLD)))) { ++ early = true; ++ } ++ ++ return early; ++} ++ ++struct serdes_access* setup_serdes_access_28g(void) ++{ ++ srds.get_lane_memmap_size = get_lane_memmap_size; ++ srds.tune_tecr = tune_tecr; ++ srds.reset_lane = reset_lane; ++ srds.lane_set_1gkx = lane_set_1gkx; ++ srds.get_median_gaink2 = get_median_gaink2; ++ srds.is_bin_early = is_bin_early; ++ ++ return &srds; ++} +--- /dev/null ++++ b/drivers/net/phy/inphi.c +@@ -0,0 +1,594 @@ ++/* ++ * Copyright 2018 NXP ++ * Copyright 2018 INPHI ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions and the following disclaimer. ++ * 2. 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 HOLDER 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. ++ * ++ * Inphi is a registered trademark of Inphi Corporation ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/phy.h> ++#include <linux/mdio.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <linux/of_irq.h> ++#include <linux/workqueue.h> ++#include <linux/i2c.h> ++#include <linux/timer.h> ++#include <linux/delay.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/cdev.h> ++#include <linux/device.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++ ++#define PHY_ID_IN112525 0x02107440 ++ ++#define INPHI_S03_DEVICE_ID_MSB 0x2 ++#define INPHI_S03_DEVICE_ID_LSB 0x3 ++ ++#define ALL_LANES 4 ++#define INPHI_POLL_DELAY 2500 ++ ++#define PHYCTRL_REG1 0x0012 ++#define PHYCTRL_REG2 0x0014 ++#define PHYCTRL_REG3 0x0120 ++#define PHYCTRL_REG4 0x0121 ++#define PHYCTRL_REG5 0x0180 ++#define PHYCTRL_REG6 0x0580 ++#define PHYCTRL_REG7 0x05C4 ++#define PHYCTRL_REG8 0x01C8 ++#define PHYCTRL_REG9 0x0521 ++ ++#define PHYSTAT_REG1 0x0021 ++#define PHYSTAT_REG2 0x0022 ++#define PHYSTAT_REG3 0x0123 ++ ++#define PHYMISC_REG1 0x0025 ++#define PHYMISC_REG2 0x002c ++#define PHYMISC_REG3 0x00b3 ++#define PHYMISC_REG4 0x0181 ++#define PHYMISC_REG5 0x019D ++#define PHYMISC_REG6 0x0198 ++#define PHYMISC_REG7 0x0199 ++#define PHYMISC_REG8 0x0581 ++#define PHYMISC_REG9 0x0598 ++#define PHYMISC_REG10 0x059c ++#define PHYMISC_REG20 0x01B0 ++#define PHYMISC_REG21 0x01BC ++#define PHYMISC_REG22 0x01C0 ++ ++#define RX_VCO_CODE_OFFSET 5 ++#define VCO_CODE 390 ++ ++int vco_codes[ALL_LANES] = { ++ VCO_CODE, ++ VCO_CODE, ++ VCO_CODE, ++ VCO_CODE ++}; ++ ++static void mykmod_work_handler(struct work_struct *w); ++ ++static struct workqueue_struct *wq; ++static DECLARE_DELAYED_WORK(mykmod_work, mykmod_work_handler); ++static unsigned long onesec; ++struct phy_device *inphi_phydev; ++ ++static int mdio_wr(u32 regnum, u16 val) ++{ ++ regnum = MII_ADDR_C45 | (MDIO_MMD_VEND1 << 16) | (regnum & 0xffff); ++ ++ return mdiobus_write(inphi_phydev->mdio.bus, inphi_phydev->mdio.addr, ++ regnum, val); ++} ++ ++static int mdio_rd(u32 regnum) ++{ ++ regnum = MII_ADDR_C45 | (MDIO_MMD_VEND1 << 16) | (regnum & 0xffff); ++ ++ return mdiobus_read(inphi_phydev->mdio.bus, inphi_phydev->mdio.addr, ++ regnum); ++} ++ ++ ++int bit_test(int value, int bit_field) ++{ ++ int result; ++ int bit_mask = (1 << bit_field); ++ ++ result = ((value & bit_mask) == bit_mask); ++ return result; ++} ++ ++int tx_pll_lock_test(int lane) ++{ ++ int i, val, locked = 1; ++ ++ if (lane == ALL_LANES) { ++ for (i = 0; i < ALL_LANES; i++) { ++ val = mdio_rd(i * 0x100 + PHYSTAT_REG3); ++ locked = locked & bit_test(val, 15); ++ } ++ } else { ++ val = mdio_rd(lane * 0x100 + PHYSTAT_REG3); ++ locked = locked & bit_test(val, 15); ++ } ++ ++ return locked; ++} ++ ++void rx_reset_assert(int lane) ++{ ++ int mask, val; ++ ++ if (lane == ALL_LANES) { ++ val = mdio_rd(PHYMISC_REG2); ++ mask = (1 << 15); ++ mdio_wr(PHYMISC_REG2, val + mask); ++ } else { ++ val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); ++ mask = (1 << 6); ++ mdio_wr(lane * 0x100 + PHYCTRL_REG8, val + mask); ++ } ++} ++ ++void rx_reset_de_assert(int lane) ++{ ++ int mask, val; ++ ++ if (lane == ALL_LANES) { ++ val = mdio_rd(PHYMISC_REG2); ++ mask = 0xffff - (1 << 15); ++ mdio_wr(PHYMISC_REG2, val & mask); ++ } else { ++ val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); ++ mask = 0xffff - (1 << 6); ++ mdio_wr(lane * 0x100 + PHYCTRL_REG8, val & mask); ++ } ++} ++ ++void rx_powerdown_assert(int lane) ++{ ++ int mask, val; ++ ++ val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); ++ mask = (1 << 5); ++ mdio_wr(lane * 0x100 + PHYCTRL_REG8, val + mask); ++} ++ ++void rx_powerdown_de_assert(int lane) ++{ ++ int mask, val; ++ ++ val = mdio_rd(lane * 0x100 + PHYCTRL_REG8); ++ mask = 0xffff - (1 << 5); ++ mdio_wr(lane * 0x100 + PHYCTRL_REG8, val & mask); ++} ++ ++void tx_pll_assert(int lane) ++{ ++ int val, recal; ++ ++ if (lane == ALL_LANES) { ++ val = mdio_rd(PHYMISC_REG2); ++ recal = (1 << 12); ++ mdio_wr(PHYMISC_REG2, val | recal); ++ } else { ++ val = mdio_rd(lane * 0x100 + PHYCTRL_REG4); ++ recal = (1 << 15); ++ mdio_wr(lane * 0x100 + PHYCTRL_REG4, val | recal); ++ } ++} ++ ++void tx_pll_de_assert(int lane) ++{ ++ int recal, val; ++ ++ if (lane == ALL_LANES) { ++ val = mdio_rd(PHYMISC_REG2); ++ recal = 0xefff; ++ mdio_wr(PHYMISC_REG2, val & recal); ++ } else { ++ val = mdio_rd(lane * 0x100 + PHYCTRL_REG4); ++ recal = 0x7fff; ++ mdio_wr(lane * 0x100 + PHYCTRL_REG4, val & recal); ++ } ++} ++ ++void tx_core_assert(int lane) ++{ ++ int recal, val, val2, core_reset; ++ ++ if (lane == 4) { ++ val = mdio_rd(PHYMISC_REG2); ++ recal = 1 << 10; ++ mdio_wr(PHYMISC_REG2, val | recal); ++ } else { ++ val2 = mdio_rd(PHYMISC_REG3); ++ core_reset = (1 << (lane + 8)); ++ mdio_wr(PHYMISC_REG3, val2 | core_reset); ++ } ++} ++ ++void lol_disable(int lane) ++{ ++ int val, mask; ++ ++ val = mdio_rd(PHYMISC_REG3); ++ mask = 1 << (lane + 4); ++ mdio_wr(PHYMISC_REG3, val | mask); ++} ++ ++void tx_core_de_assert(int lane) ++{ ++ int val, recal, val2, core_reset; ++ ++ if (lane == ALL_LANES) { ++ val = mdio_rd(PHYMISC_REG2); ++ recal = 0xffff - (1 << 10); ++ mdio_wr(PHYMISC_REG2, val & recal); ++ } else { ++ val2 = mdio_rd(PHYMISC_REG3); ++ core_reset = 0xffff - (1 << (lane + 8)); ++ mdio_wr(PHYMISC_REG3, val2 & core_reset); ++ } ++} ++ ++void tx_restart(int lane) ++{ ++ tx_core_assert(lane); ++ tx_pll_assert(lane); ++ tx_pll_de_assert(lane); ++ usleep_range(1500, 1600); ++ tx_core_de_assert(lane); ++} ++ ++void disable_lane(int lane) ++{ ++ rx_reset_assert(lane); ++ rx_powerdown_assert(lane); ++ tx_core_assert(lane); ++ lol_disable(lane); ++} ++ ++void toggle_reset(int lane) ++{ ++ int reg, val, orig; ++ ++ if (lane == ALL_LANES) { ++ mdio_wr(PHYMISC_REG2, 0x8000); ++ udelay(100); ++ mdio_wr(PHYMISC_REG2, 0x0000); ++ } else { ++ reg = lane * 0x100 + PHYCTRL_REG8; ++ val = (1 << 6); ++ orig = mdio_rd(reg); ++ mdio_wr(reg, orig + val); ++ udelay(100); ++ mdio_wr(reg, orig); ++ } ++} ++ ++int az_complete_test(int lane) ++{ ++ int success = 1, value; ++ ++ if (lane == 0 || lane == ALL_LANES) { ++ value = mdio_rd(PHYCTRL_REG5); ++ success = success & bit_test(value, 2); ++ } ++ if (lane == 1 || lane == ALL_LANES) { ++ value = mdio_rd(PHYCTRL_REG5 + 0x100); ++ success = success & bit_test(value, 2); ++ } ++ if (lane == 2 || lane == ALL_LANES) { ++ value = mdio_rd(PHYCTRL_REG5 + 0x200); ++ success = success & bit_test(value, 2); ++ } ++ if (lane == 3 || lane == ALL_LANES) { ++ value = mdio_rd(PHYCTRL_REG5 + 0x300); ++ success = success & bit_test(value, 2); ++ } ++ ++ return success; ++} ++ ++void save_az_offsets(int lane) ++{ ++ int i; ++ ++#define AZ_OFFSET_LANE_UPDATE(reg, lane) \ ++ mdio_wr((reg) + (lane) * 0x100, \ ++ (mdio_rd((reg) + (lane) * 0x100) >> 8)) ++ ++ if (lane == ALL_LANES) { ++ for (i = 0; i < ALL_LANES; i++) { ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20, i); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 1, i); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 2, i); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 3, i); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21, i); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 1, i); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 2, i); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 3, i); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG22, i); ++ } ++ } else { ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20, lane); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 1, lane); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 2, lane); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG20 + 3, lane); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21, lane); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 1, lane); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 2, lane); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG21 + 3, lane); ++ AZ_OFFSET_LANE_UPDATE(PHYMISC_REG22, lane); ++ } ++ ++ mdio_wr(PHYCTRL_REG7, 0x0001); ++} ++ ++void save_vco_codes(int lane) ++{ ++ int i; ++ ++ if (lane == ALL_LANES) { ++ for (i = 0; i < ALL_LANES; i++) { ++ vco_codes[i] = mdio_rd(PHYMISC_REG5 + i * 0x100); ++ mdio_wr(PHYMISC_REG5 + i * 0x100, ++ vco_codes[i] + RX_VCO_CODE_OFFSET); ++ } ++ } else { ++ vco_codes[lane] = mdio_rd(PHYMISC_REG5 + lane * 0x100); ++ mdio_wr(PHYMISC_REG5 + lane * 0x100, ++ vco_codes[lane] + RX_VCO_CODE_OFFSET); ++ } ++} ++ ++int inphi_lane_recovery(int lane) ++{ ++ int i, value, az_pass; ++ ++ switch (lane) { ++ case 0: ++ case 1: ++ case 2: ++ case 3: ++ rx_reset_assert(lane); ++ mdelay(20); ++ break; ++ case ALL_LANES: ++ mdio_wr(PHYMISC_REG2, 0x9C00); ++ mdelay(20); ++ do { ++ value = mdio_rd(PHYMISC_REG2); ++ udelay(10); ++ } while (!bit_test(value, 4)); ++ break; ++ default: ++ dev_err(&inphi_phydev->mdio.dev, ++ "Incorrect usage of APIs in %s driver\n", ++ inphi_phydev->drv->name); ++ break; ++ } ++ ++ if (lane == ALL_LANES) { ++ for (i = 0; i < ALL_LANES; i++) ++ mdio_wr(PHYMISC_REG7 + i * 0x100, VCO_CODE); ++ } else { ++ mdio_wr(PHYMISC_REG7 + lane * 0x100, VCO_CODE); ++ } ++ ++ if (lane == ALL_LANES) ++ for (i = 0; i < ALL_LANES; i++) ++ mdio_wr(PHYCTRL_REG5 + i * 0x100, 0x0418); ++ else ++ mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0418); ++ ++ mdio_wr(PHYCTRL_REG7, 0x0000); ++ ++ rx_reset_de_assert(lane); ++ ++ if (lane == ALL_LANES) { ++ for (i = 0; i < ALL_LANES; i++) { ++ mdio_wr(PHYCTRL_REG5 + i * 0x100, 0x0410); ++ mdio_wr(PHYCTRL_REG5 + i * 0x100, 0x0412); ++ } ++ } else { ++ mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0410); ++ mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0412); ++ } ++ ++ for (i = 0; i < 64; i++) { ++ mdelay(100); ++ az_pass = az_complete_test(lane); ++ if (az_pass) { ++ save_az_offsets(lane); ++ break; ++ } ++ } ++ ++ if (!az_pass) { ++ pr_info("in112525: AZ calibration fail @ lane=%d\n", lane); ++ return -1; ++ } ++ ++ if (lane == ALL_LANES) { ++ mdio_wr(PHYMISC_REG8, 0x0002); ++ mdio_wr(PHYMISC_REG9, 0x2028); ++ mdio_wr(PHYCTRL_REG6, 0x0010); ++ usleep_range(1000, 1200); ++ mdio_wr(PHYCTRL_REG6, 0x0110); ++ mdelay(30); ++ mdio_wr(PHYMISC_REG9, 0x3020); ++ } else { ++ mdio_wr(PHYMISC_REG4 + lane * 0x100, 0x0002); ++ mdio_wr(PHYMISC_REG6 + lane * 0x100, 0x2028); ++ mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0010); ++ usleep_range(1000, 1200); ++ mdio_wr(PHYCTRL_REG5 + lane * 0x100, 0x0110); ++ mdelay(30); ++ mdio_wr(PHYMISC_REG6 + lane * 0x100, 0x3020); ++ } ++ ++ if (lane == ALL_LANES) { ++ mdio_wr(PHYMISC_REG2, 0x1C00); ++ mdio_wr(PHYMISC_REG2, 0x0C00); ++ } else { ++ tx_restart(lane); ++ mdelay(11); ++ } ++ ++ if (lane == ALL_LANES) { ++ if (bit_test(mdio_rd(PHYMISC_REG2), 6) == 0) ++ return -1; ++ } else { ++ if (tx_pll_lock_test(lane) == 0) ++ return -1; ++ } ++ ++ save_vco_codes(lane); ++ ++ if (lane == ALL_LANES) { ++ mdio_wr(PHYMISC_REG2, 0x0400); ++ mdio_wr(PHYMISC_REG2, 0x0000); ++ value = mdio_rd(PHYCTRL_REG1); ++ value = value & 0xffbf; ++ mdio_wr(PHYCTRL_REG2, value); ++ } else { ++ tx_core_de_assert(lane); ++ } ++ ++ if (lane == ALL_LANES) { ++ mdio_wr(PHYMISC_REG1, 0x8000); ++ mdio_wr(PHYMISC_REG1, 0x0000); ++ } ++ mdio_rd(PHYMISC_REG1); ++ mdio_rd(PHYMISC_REG1); ++ usleep_range(1000, 1200); ++ mdio_rd(PHYSTAT_REG1); ++ mdio_rd(PHYSTAT_REG2); ++ ++ return 0; ++} ++ ++static void mykmod_work_handler(struct work_struct *w) ++{ ++ int all_lanes_lock, lane0_lock, lane1_lock, lane2_lock, lane3_lock; ++ ++ lane0_lock = bit_test(mdio_rd(0x123), 15); ++ lane1_lock = bit_test(mdio_rd(0x223), 15); ++ lane2_lock = bit_test(mdio_rd(0x323), 15); ++ lane3_lock = bit_test(mdio_rd(0x423), 15); ++ ++ /* check if the chip had any successful lane lock from the previous ++ * stage (e.g. u-boot) ++ */ ++ all_lanes_lock = lane0_lock | lane1_lock | lane2_lock | lane3_lock; ++ ++ if (!all_lanes_lock) { ++ /* start fresh */ ++ inphi_lane_recovery(ALL_LANES); ++ } else { ++ if (!lane0_lock) ++ inphi_lane_recovery(0); ++ if (!lane1_lock) ++ inphi_lane_recovery(1); ++ if (!lane2_lock) ++ inphi_lane_recovery(2); ++ if (!lane3_lock) ++ inphi_lane_recovery(3); ++ } ++ ++ queue_delayed_work(wq, &mykmod_work, onesec); ++} ++ ++int inphi_probe(struct phy_device *phydev) ++{ ++ int phy_id = 0, id_lsb = 0, id_msb = 0; ++ ++ /* setup the inphi_phydev ptr for mdio_rd/mdio_wr APIs */ ++ inphi_phydev = phydev; ++ ++ /* Read device id from phy registers */ ++ id_lsb = mdio_rd(INPHI_S03_DEVICE_ID_MSB); ++ if (id_lsb < 0) ++ return -ENXIO; ++ ++ phy_id = id_lsb << 16; ++ ++ id_msb = mdio_rd(INPHI_S03_DEVICE_ID_LSB); ++ if (id_msb < 0) ++ return -ENXIO; ++ ++ phy_id |= id_msb; ++ ++ /* Make sure the device tree binding matched the driver with the ++ * right device. ++ */ ++ if (phy_id != phydev->drv->phy_id) { ++ dev_err(&phydev->mdio.dev, ++ "Error matching phy with %s driver\n", ++ phydev->drv->name); ++ return -ENODEV; ++ } ++ ++ /* update the local phydev pointer, used inside all APIs */ ++ inphi_phydev = phydev; ++ onesec = msecs_to_jiffies(INPHI_POLL_DELAY); ++ ++ wq = create_singlethread_workqueue("inphi_kmod"); ++ if (wq) { ++ queue_delayed_work(wq, &mykmod_work, onesec); ++ } else { ++ dev_err(&phydev->mdio.dev, ++ "Error creating kernel workqueue for %s driver\n", ++ phydev->drv->name); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static struct phy_driver inphi_driver[] = { ++{ ++ .phy_id = PHY_ID_IN112525, ++ .phy_id_mask = 0x0ff0fff0, ++ .name = "Inphi 112525_S03", ++ .features = PHY_GBIT_FEATURES, ++ .probe = &inphi_probe, ++}, ++}; ++ ++module_phy_driver(inphi_driver); ++ ++static struct mdio_device_id __maybe_unused inphi_tbl[] = { ++ { PHY_ID_IN112525, 0x0ff0fff0}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(mdio, inphi_tbl); +--- /dev/null ++++ b/drivers/net/phy/mdio-mux-multiplexer.c +@@ -0,0 +1,122 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* MDIO bus multiplexer using kernel multiplexer subsystem ++ * ++ * Copyright 2019 NXP ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/mdio-mux.h> ++#include <linux/module.h> ++#include <linux/mux/consumer.h> ++ ++struct mdio_mux_multiplexer_state { ++ struct mux_control *muxc; ++ bool do_deselect; ++ void *mux_handle; ++}; ++ ++/** ++ * mdio_mux_multiplexer_switch_fn - This function is called by the mdio-mux ++ * layer when it thinks the mdio bus ++ * multiplexer needs to switch. ++ * @current_child: current value of the mux register. ++ * @desired_child: value of the 'reg' property of the target child MDIO node. ++ * @data: Private data used by this switch_fn passed to mdio_mux_init function ++ * via mdio_mux_init(.., .., .., .., data, ..). ++ * ++ * The first time this function is called, current_child == -1. ++ * If current_child == desired_child, then the mux is already set to the ++ * correct bus. ++ */ ++static int mdio_mux_multiplexer_switch_fn(int current_child, int desired_child, ++ void *data) ++{ ++ struct platform_device *pdev; ++ struct mdio_mux_multiplexer_state *s; ++ int ret = 0; ++ ++ pdev = (struct platform_device *)data; ++ s = platform_get_drvdata(pdev); ++ ++ if (!(current_child ^ desired_child)) ++ return 0; ++ ++ if (s->do_deselect) ++ ret = mux_control_deselect(s->muxc); ++ if (ret) { ++ dev_err(&pdev->dev, "mux_control_deselect failed in %s: %d\n", ++ __func__, ret); ++ return ret; ++ } ++ ++ ret = mux_control_select(s->muxc, desired_child); ++ if (!ret) { ++ dev_dbg(&pdev->dev, "%s %d -> %d\n", __func__, current_child, ++ desired_child); ++ s->do_deselect = true; ++ } else { ++ s->do_deselect = false; ++ } ++ ++ return ret; ++} ++ ++static int mdio_mux_multiplexer_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mdio_mux_multiplexer_state *s; ++ int ret = 0; ++ ++ s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL); ++ if (!s) ++ return -ENOMEM; ++ ++ s->muxc = devm_mux_control_get(dev, NULL); ++ if (IS_ERR(s->muxc)) { ++ ret = PTR_ERR(s->muxc); ++ if (ret != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "Failed to get mux: %d\n", ret); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, s); ++ ++ ret = mdio_mux_init(&pdev->dev, pdev->dev.of_node, ++ mdio_mux_multiplexer_switch_fn, &s->mux_handle, ++ pdev, NULL); ++ ++ return ret; ++} ++ ++static int mdio_mux_multiplexer_remove(struct platform_device *pdev) ++{ ++ struct mdio_mux_multiplexer_state *s = platform_get_drvdata(pdev); ++ ++ mdio_mux_uninit(s->mux_handle); ++ ++ if (s->do_deselect) ++ mux_control_deselect(s->muxc); ++ ++ return 0; ++} ++ ++static const struct of_device_id mdio_mux_multiplexer_match[] = { ++ { .compatible = "mdio-mux-multiplexer", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mdio_mux_multiplexer_match); ++ ++static struct platform_driver mdio_mux_multiplexer_driver = { ++ .driver = { ++ .name = "mdio-mux-multiplexer", ++ .of_match_table = mdio_mux_multiplexer_match, ++ }, ++ .probe = mdio_mux_multiplexer_probe, ++ .remove = mdio_mux_multiplexer_remove, ++}; ++ ++module_platform_driver(mdio_mux_multiplexer_driver); ++ ++MODULE_DESCRIPTION("MDIO bus multiplexer using kernel multiplexer subsystem"); ++MODULE_AUTHOR("Pankaj Bansal <pankaj.bansal@nxp.com>"); ++MODULE_LICENSE("GPL"); --- a/drivers/net/phy/swphy.c +++ b/drivers/net/phy/swphy.c @@ -77,6 +77,7 @@ static const struct swmii_regs duplex[] |