diff options
Diffstat (limited to 'drivers/net/phy')
| -rw-r--r-- | drivers/net/phy/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/net/phy/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/phy/at803x.c | 18 | ||||
| -rw-r--r-- | drivers/net/phy/dp83822.c | 8 | ||||
| -rw-r--r-- | drivers/net/phy/intel-xway.c | 76 | ||||
| -rw-r--r-- | drivers/net/phy/marvell.c | 144 | ||||
| -rw-r--r-- | drivers/net/phy/marvell10g.c | 97 | ||||
| -rw-r--r-- | drivers/net/phy/mediatek-ge.c | 4 | ||||
| -rw-r--r-- | drivers/net/phy/micrel.c | 12 | ||||
| -rw-r--r-- | drivers/net/phy/mscc/mscc_ptp.c | 8 | ||||
| -rw-r--r-- | drivers/net/phy/mxl-gpy.c | 727 | ||||
| -rw-r--r-- | drivers/net/phy/nxp-tja11xx.c | 13 | ||||
| -rw-r--r-- | drivers/net/phy/phy.c | 4 | ||||
| -rw-r--r-- | drivers/net/phy/phy_device.c | 27 | ||||
| -rw-r--r-- | drivers/net/phy/phylink.c | 21 | ||||
| -rw-r--r-- | drivers/net/phy/xilinx_gmii2rgmii.c | 46 | 
16 files changed, 1150 insertions, 64 deletions
| diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index c56f703ae998..902495afcb38 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -207,6 +207,12 @@ config MARVELL_88X2222_PHY  	  Support for the Marvell 88X2222 Dual-port Multi-speed Ethernet  	  Transceiver. +config MAXLINEAR_GPHY +	tristate "Maxlinear Ethernet PHYs" +	help +	  Support for the Maxlinear GPY115, GPY211, GPY212, GPY215, +	  GPY241, GPY245 PHYs. +  config MEDIATEK_GE_PHY  	tristate "MediaTek Gigabit Ethernet PHYs"  	help @@ -230,6 +236,7 @@ config MICROCHIP_T1_PHY  config MICROSEMI_PHY  	tristate "Microsemi PHYs"  	depends on MACSEC || MACSEC=n +	depends on PTP_1588_CLOCK_OPTIONAL || !NETWORK_PHY_TIMESTAMPING  	select CRYPTO_LIB_AES if MACSEC  	help  	  Currently supports VSC8514, VSC8530, VSC8531, VSC8540 and VSC8541 PHYs @@ -247,6 +254,7 @@ config NATIONAL_PHY  config NXP_C45_TJA11XX_PHY  	tristate "NXP C45 TJA11XX PHYs" +	depends on PTP_1588_CLOCK_OPTIONAL  	help  	  Enable support for NXP C45 TJA11XX PHYs.  	  Currently supports only the TJA1103 PHY. diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 172bb193ae6a..b2728d00fc9a 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_LXT_PHY)		+= lxt.o  obj-$(CONFIG_MARVELL_10G_PHY)	+= marvell10g.o  obj-$(CONFIG_MARVELL_PHY)	+= marvell.o  obj-$(CONFIG_MARVELL_88X2222_PHY)	+= marvell-88x2222.o +obj-$(CONFIG_MAXLINEAR_GPHY)	+= mxl-gpy.o  obj-$(CONFIG_MEDIATEK_GE_PHY)	+= mediatek-ge.o  obj-$(CONFIG_MESON_GXL_PHY)	+= meson-gxl.o  obj-$(CONFIG_MICREL_KS8995MA)	+= spi_ks8995.o diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 5d62b85a4024..bdac087058b2 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -532,12 +532,6 @@ static int at8031_register_regulators(struct phy_device *phydev)  	return 0;  } -static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id) -{ -	return (phydev->phy_id & phydev->drv->phy_id_mask) -		== (phy_id & phydev->drv->phy_id_mask); -} -  static int at803x_parse_dt(struct phy_device *phydev)  {  	struct device_node *node = phydev->mdio.dev.of_node; @@ -602,8 +596,8 @@ static int at803x_parse_dt(struct phy_device *phydev)  		 *   to the AR8030 so there might be a good chance it works on  		 *   the AR8030 too.  		 */ -		if (at803x_match_phy_id(phydev, ATH8030_PHY_ID) || -		    at803x_match_phy_id(phydev, ATH8035_PHY_ID)) { +		if (phydev->drv->phy_id == ATH8030_PHY_ID || +		    phydev->drv->phy_id == ATH8035_PHY_ID) {  			priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;  			priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;  		} @@ -631,7 +625,7 @@ static int at803x_parse_dt(struct phy_device *phydev)  	/* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping  	 * options.  	 */ -	if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) { +	if (phydev->drv->phy_id == ATH8031_PHY_ID) {  		if (of_property_read_bool(node, "qca,keep-pll-enabled"))  			priv->flags |= AT803X_KEEP_PLL_ENABLED; @@ -676,7 +670,7 @@ static int at803x_probe(struct phy_device *phydev)  	 * Switch to the copper page, as otherwise we read  	 * the PHY capabilities from the fiber side.  	 */ -	if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) { +	if (phydev->drv->phy_id == ATH8031_PHY_ID) {  		phy_lock_mdio_bus(phydev);  		ret = at803x_write_page(phydev, AT803X_PAGE_COPPER);  		phy_unlock_mdio_bus(phydev); @@ -709,7 +703,7 @@ static int at803x_get_features(struct phy_device *phydev)  	if (err)  		return err; -	if (!at803x_match_phy_id(phydev, ATH8031_PHY_ID)) +	if (phydev->drv->phy_id != ATH8031_PHY_ID)  		return 0;  	/* AR8031/AR8033 have different status registers @@ -820,7 +814,7 @@ static int at803x_config_init(struct phy_device *phydev)  	if (ret < 0)  		return ret; -	if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) { +	if (phydev->drv->phy_id == ATH8031_PHY_ID) {  		ret = at8031_pll_config(phydev);  		if (ret < 0)  			return ret; diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c index f7a2ec150e54..211b5476a6f5 100644 --- a/drivers/net/phy/dp83822.c +++ b/drivers/net/phy/dp83822.c @@ -326,11 +326,9 @@ static irqreturn_t dp83822_handle_interrupt(struct phy_device *phydev)  static int dp8382x_disable_wol(struct phy_device *phydev)  { -	int value = DP83822_WOL_EN | DP83822_WOL_MAGIC_EN | -		    DP83822_WOL_SECURE_ON; - -	return phy_clear_bits_mmd(phydev, DP83822_DEVADDR, -				  MII_DP83822_WOL_CFG, value); +	return phy_clear_bits_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, +				  DP83822_WOL_EN | DP83822_WOL_MAGIC_EN | +				  DP83822_WOL_SECURE_ON);  }  static int dp83822_read_status(struct phy_device *phydev) diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c index d453ec016168..3c032868ef04 100644 --- a/drivers/net/phy/intel-xway.c +++ b/drivers/net/phy/intel-xway.c @@ -8,11 +8,16 @@  #include <linux/module.h>  #include <linux/phy.h>  #include <linux/of.h> +#include <linux/bitfield.h> +#define XWAY_MDIO_MIICTRL		0x17	/* mii control */  #define XWAY_MDIO_IMASK			0x19	/* interrupt mask */  #define XWAY_MDIO_ISTAT			0x1A	/* interrupt status */  #define XWAY_MDIO_LED			0x1B	/* led control */ +#define XWAY_MDIO_MIICTRL_RXSKEW_MASK	GENMASK(14, 12) +#define XWAY_MDIO_MIICTRL_TXSKEW_MASK	GENMASK(10, 8) +  /* bit 15:12 are reserved */  #define XWAY_MDIO_LED_LED3_EN		BIT(11)	/* Enable the integrated function of LED3 */  #define XWAY_MDIO_LED_LED2_EN		BIT(10)	/* Enable the integrated function of LED2 */ @@ -157,6 +162,73 @@  #define PHY_ID_PHY11G_VR9_1_2		0xD565A409  #define PHY_ID_PHY22F_VR9_1_2		0xD565A419 +static const int xway_internal_delay[] = {0, 500, 1000, 1500, 2000, 2500, +					 3000, 3500}; + +static int xway_gphy_rgmii_init(struct phy_device *phydev) +{ +	struct device *dev = &phydev->mdio.dev; +	unsigned int delay_size = ARRAY_SIZE(xway_internal_delay); +	s32 int_delay; +	int val = 0; + +	if (!phy_interface_is_rgmii(phydev)) +		return 0; + +	/* Existing behavior was to use default pin strapping delay in rgmii +	 * mode, but rgmii should have meant no delay.  Warn existing users, +	 * but do not change anything at the moment. +	 */ +	if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { +		u16 txskew, rxskew; + +		val = phy_read(phydev, XWAY_MDIO_MIICTRL); +		if (val < 0) +			return val; + +		txskew = FIELD_GET(XWAY_MDIO_MIICTRL_TXSKEW_MASK, val); +		rxskew = FIELD_GET(XWAY_MDIO_MIICTRL_RXSKEW_MASK, val); + +		if (txskew > 0 || rxskew > 0) +			phydev_warn(phydev, +				    "PHY has delays (e.g. via pin strapping), but phy-mode = 'rgmii'\n" +				    "Should be 'rgmii-id' to use internal delays txskew:%d ps rxskew:%d ps\n", +				    xway_internal_delay[txskew], +				    xway_internal_delay[rxskew]); +		return 0; +	} + +	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || +	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { +		int_delay = phy_get_internal_delay(phydev, dev, +						   xway_internal_delay, +						   delay_size, true); + +		/* if rx-internal-delay-ps is missing, use default of 2.0 ns */ +		if (int_delay < 0) +			int_delay = 4; /* 2000 ps */ + +		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_RXSKEW_MASK, int_delay); +	} + +	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || +	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { +		int_delay = phy_get_internal_delay(phydev, dev, +						   xway_internal_delay, +						   delay_size, false); + +		/* if tx-internal-delay-ps is missing, use default of 2.0 ns */ +		if (int_delay < 0) +			int_delay = 4; /* 2000 ps */ + +		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_TXSKEW_MASK, int_delay); +	} + +	return phy_modify(phydev, XWAY_MDIO_MIICTRL, +			  XWAY_MDIO_MIICTRL_RXSKEW_MASK | +			  XWAY_MDIO_MIICTRL_TXSKEW_MASK, val); +} +  static int xway_gphy_config_init(struct phy_device *phydev)  {  	int err; @@ -204,6 +276,10 @@ static int xway_gphy_config_init(struct phy_device *phydev)  	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh);  	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl); +	err = xway_gphy_rgmii_init(phydev); +	if (err) +		return err; +  	return 0;  } diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 3de93c9f2744..4fcfca4e1702 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -32,6 +32,7 @@  #include <linux/marvell_phy.h>  #include <linux/bitfield.h>  #include <linux/of.h> +#include <linux/sfp.h>  #include <linux/io.h>  #include <asm/irq.h> @@ -46,6 +47,7 @@  #define MII_MARVELL_MISC_TEST_PAGE	0x06  #define MII_MARVELL_VCT7_PAGE		0x07  #define MII_MARVELL_WOL_PAGE		0x11 +#define MII_MARVELL_MODE_PAGE		0x12  #define MII_M1011_IEVENT		0x13  #define MII_M1011_IEVENT_CLEAR		0x0000 @@ -155,6 +157,7 @@  #define MII_88E1318S_PHY_WOL_CTRL				0x10  #define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS		BIT(12) +#define MII_88E1318S_PHY_WOL_CTRL_LINK_UP_ENABLE		BIT(13)  #define MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE	BIT(14)  #define MII_PHY_LED_CTRL	        16 @@ -176,7 +179,14 @@  #define MII_88E1510_GEN_CTRL_REG_1		0x14  #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK	0x7 +#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII	0x0	/* RGMII to copper */  #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII	0x1	/* SGMII to copper */ +/* RGMII to 1000BASE-X */ +#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_1000X	0x2 +/* RGMII to 100BASE-FX */ +#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_100FX	0x3 +/* RGMII to SGMII */ +#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_SGMII	0x4  #define MII_88E1510_GEN_CTRL_REG_1_RESET	0x8000	/* Soft reset */  #define MII_VCT5_TX_RX_MDI0_COUPLING	0x10 @@ -1746,13 +1756,19 @@ static void m88e1318_get_wol(struct phy_device *phydev,  {  	int ret; -	wol->supported = WAKE_MAGIC; +	wol->supported = WAKE_MAGIC | WAKE_PHY;  	wol->wolopts = 0;  	ret = phy_read_paged(phydev, MII_MARVELL_WOL_PAGE,  			     MII_88E1318S_PHY_WOL_CTRL); -	if (ret >= 0 && ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE) +	if (ret < 0) +		return; + +	if (ret & MII_88E1318S_PHY_WOL_CTRL_MAGIC_PACKET_MATCH_ENABLE)  		wol->wolopts |= WAKE_MAGIC; + +	if (ret & MII_88E1318S_PHY_WOL_CTRL_LINK_UP_ENABLE) +		wol->wolopts |= WAKE_PHY;  }  static int m88e1318_set_wol(struct phy_device *phydev, @@ -1764,7 +1780,7 @@ static int m88e1318_set_wol(struct phy_device *phydev,  	if (oldpage < 0)  		goto error; -	if (wol->wolopts & WAKE_MAGIC) { +	if (wol->wolopts & (WAKE_MAGIC | WAKE_PHY)) {  		/* Explicitly switch to page 0x00, just to be sure */  		err = marvell_write_page(phydev, MII_MARVELL_COPPER_PAGE);  		if (err < 0) @@ -1796,7 +1812,9 @@ static int m88e1318_set_wol(struct phy_device *phydev,  				   MII_88E1318S_PHY_LED_TCR_INT_ACTIVE_LOW);  		if (err < 0)  			goto error; +	} +	if (wol->wolopts & WAKE_MAGIC) {  		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE);  		if (err < 0)  			goto error; @@ -1837,6 +1855,30 @@ static int m88e1318_set_wol(struct phy_device *phydev,  			goto error;  	} +	if (wol->wolopts & WAKE_PHY) { +		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE); +		if (err < 0) +			goto error; + +		/* Clear WOL status and enable link up event */ +		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, 0, +				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS | +				   MII_88E1318S_PHY_WOL_CTRL_LINK_UP_ENABLE); +		if (err < 0) +			goto error; +	} else { +		err = marvell_write_page(phydev, MII_MARVELL_WOL_PAGE); +		if (err < 0) +			goto error; + +		/* Clear WOL status and disable link up event */ +		err = __phy_modify(phydev, MII_88E1318S_PHY_WOL_CTRL, +				   MII_88E1318S_PHY_WOL_CTRL_LINK_UP_ENABLE, +				   MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS); +		if (err < 0) +			goto error; +	} +  error:  	return phy_restore_page(phydev, oldpage, err);  } @@ -2701,6 +2743,100 @@ static int marvell_probe(struct phy_device *phydev)  	return marvell_hwmon_probe(phydev);  } +static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +{ +	struct phy_device *phydev = upstream; +	phy_interface_t interface; +	struct device *dev; +	int oldpage; +	int ret = 0; +	u16 mode; + +	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, }; + +	dev = &phydev->mdio.dev; + +	sfp_parse_support(phydev->sfp_bus, id, supported); +	interface = sfp_select_interface(phydev->sfp_bus, supported); + +	dev_info(dev, "%s SFP module inserted\n", phy_modes(interface)); + +	switch (interface) { +	case PHY_INTERFACE_MODE_1000BASEX: +		mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_1000X; + +		break; +	case PHY_INTERFACE_MODE_100BASEX: +		mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_100FX; + +		break; +	case PHY_INTERFACE_MODE_SGMII: +		mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_SGMII; + +		break; +	default: +		dev_err(dev, "Incompatible SFP module inserted\n"); + +		return -EINVAL; +	} + +	oldpage = phy_select_page(phydev, MII_MARVELL_MODE_PAGE); +	if (oldpage < 0) +		goto error; + +	ret = __phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, +			   MII_88E1510_GEN_CTRL_REG_1_MODE_MASK, mode); +	if (ret < 0) +		goto error; + +	ret = __phy_set_bits(phydev, MII_88E1510_GEN_CTRL_REG_1, +			     MII_88E1510_GEN_CTRL_REG_1_RESET); + +error: +	return phy_restore_page(phydev, oldpage, ret); +} + +static void m88e1510_sfp_remove(void *upstream) +{ +	struct phy_device *phydev = upstream; +	int oldpage; +	int ret = 0; + +	oldpage = phy_select_page(phydev, MII_MARVELL_MODE_PAGE); +	if (oldpage < 0) +		goto error; + +	ret = __phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, +			   MII_88E1510_GEN_CTRL_REG_1_MODE_MASK, +			   MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII); +	if (ret < 0) +		goto error; + +	ret = __phy_set_bits(phydev, MII_88E1510_GEN_CTRL_REG_1, +			     MII_88E1510_GEN_CTRL_REG_1_RESET); + +error: +	phy_restore_page(phydev, oldpage, ret); +} + +static const struct sfp_upstream_ops m88e1510_sfp_ops = { +	.module_insert = m88e1510_sfp_insert, +	.module_remove = m88e1510_sfp_remove, +	.attach = phy_sfp_attach, +	.detach = phy_sfp_detach, +}; + +static int m88e1510_probe(struct phy_device *phydev) +{ +	int err; + +	err = marvell_probe(phydev); +	if (err) +		return err; + +	return phy_sfp_probe(phydev, &m88e1510_sfp_ops); +} +  static struct phy_driver marvell_drivers[] = {  	{  		.phy_id = MARVELL_PHY_ID_88E1101, @@ -2927,7 +3063,7 @@ static struct phy_driver marvell_drivers[] = {  		.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),  		.features = PHY_GBIT_FIBRE_FEATURES,  		.flags = PHY_POLL_CABLE_TEST, -		.probe = marvell_probe, +		.probe = m88e1510_probe,  		.config_init = m88e1510_config_init,  		.config_aneg = m88e1510_config_aneg,  		.read_status = marvell_read_status, diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 53a433442803..bd310e8d5e43 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -28,6 +28,7 @@  #include <linux/marvell_phy.h>  #include <linux/phy.h>  #include <linux/sfp.h> +#include <linux/netdevice.h>  #define MV_PHY_ALASKA_NBT_QUIRK_MASK	0xfffffffe  #define MV_PHY_ALASKA_NBT_QUIRK_REV	(MARVELL_PHY_ID_88X3310 | 0xa) @@ -104,6 +105,16 @@ enum {  	MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN	= 0x5,  	MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH	= 0x6,  	MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII			= 0x7, +	MV_V2_PORT_INTR_STS     = 0xf040, +	MV_V2_PORT_INTR_MASK    = 0xf043, +	MV_V2_PORT_INTR_STS_WOL_EN      = BIT(8), +	MV_V2_MAGIC_PKT_WORD0   = 0xf06b, +	MV_V2_MAGIC_PKT_WORD1   = 0xf06c, +	MV_V2_MAGIC_PKT_WORD2   = 0xf06d, +	/* Wake on LAN registers */ +	MV_V2_WOL_CTRL          = 0xf06e, +	MV_V2_WOL_CTRL_CLEAR_STS        = BIT(15), +	MV_V2_WOL_CTRL_MAGIC_PKT_EN     = BIT(0),  	/* Temperature control/read registers (88X3310 only) */  	MV_V2_TEMP_CTRL		= 0xf08a,  	MV_V2_TEMP_CTRL_MASK	= 0xc000, @@ -987,11 +998,19 @@ static int mv3310_get_number_of_ports(struct phy_device *phydev)  static int mv3310_match_phy_device(struct phy_device *phydev)  { +	if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & +	     MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +		return 0; +  	return mv3310_get_number_of_ports(phydev) == 1;  }  static int mv3340_match_phy_device(struct phy_device *phydev)  { +	if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & +	     MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +		return 0; +  	return mv3310_get_number_of_ports(phydev) == 4;  } @@ -1020,6 +1039,80 @@ static int mv2111_match_phy_device(struct phy_device *phydev)  	return mv211x_match_phy_device(phydev, false);  } +static void mv3110_get_wol(struct phy_device *phydev, +			   struct ethtool_wolinfo *wol) +{ +	int ret; + +	wol->supported = WAKE_MAGIC; +	wol->wolopts = 0; + +	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_WOL_CTRL); +	if (ret < 0) +		return; + +	if (ret & MV_V2_WOL_CTRL_MAGIC_PKT_EN) +		wol->wolopts |= WAKE_MAGIC; +} + +static int mv3110_set_wol(struct phy_device *phydev, +			  struct ethtool_wolinfo *wol) +{ +	int ret; + +	if (wol->wolopts & WAKE_MAGIC) { +		/* Enable the WOL interrupt */ +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, +				       MV_V2_PORT_INTR_MASK, +				       MV_V2_PORT_INTR_STS_WOL_EN); +		if (ret < 0) +			return ret; + +		/* Store the device address for the magic packet */ +		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, +				    MV_V2_MAGIC_PKT_WORD2, +				    ((phydev->attached_dev->dev_addr[5] << 8) | +				    phydev->attached_dev->dev_addr[4])); +		if (ret < 0) +			return ret; + +		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, +				    MV_V2_MAGIC_PKT_WORD1, +				    ((phydev->attached_dev->dev_addr[3] << 8) | +				    phydev->attached_dev->dev_addr[2])); +		if (ret < 0) +			return ret; + +		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, +				    MV_V2_MAGIC_PKT_WORD0, +				    ((phydev->attached_dev->dev_addr[1] << 8) | +				    phydev->attached_dev->dev_addr[0])); +		if (ret < 0) +			return ret; + +		/* Clear WOL status and enable magic packet matching */ +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, +				       MV_V2_WOL_CTRL, +				       MV_V2_WOL_CTRL_MAGIC_PKT_EN | +				       MV_V2_WOL_CTRL_CLEAR_STS); +		if (ret < 0) +			return ret; +	} else { +		/* Disable magic packet matching & reset WOL status bit */ +		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, +				     MV_V2_WOL_CTRL, +				     MV_V2_WOL_CTRL_MAGIC_PKT_EN, +				     MV_V2_WOL_CTRL_CLEAR_STS); +		if (ret < 0) +			return ret; +	} + +	/* Reset the clear WOL status bit as it does not self-clear */ +	return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, +				  MV_V2_WOL_CTRL, +				  MV_V2_WOL_CTRL_CLEAR_STS); +} +  static struct phy_driver mv3310_drivers[] = {  	{  		.phy_id		= MARVELL_PHY_ID_88X3310, @@ -1039,6 +1132,8 @@ static struct phy_driver mv3310_drivers[] = {  		.set_tunable	= mv3310_set_tunable,  		.remove		= mv3310_remove,  		.set_loopback	= genphy_c45_loopback, +		.get_wol	= mv3110_get_wol, +		.set_wol	= mv3110_set_wol,  	},  	{  		.phy_id		= MARVELL_PHY_ID_88X3310, @@ -1076,6 +1171,8 @@ static struct phy_driver mv3310_drivers[] = {  		.set_tunable	= mv3310_set_tunable,  		.remove		= mv3310_remove,  		.set_loopback	= genphy_c45_loopback, +		.get_wol	= mv3110_get_wol, +		.set_wol	= mv3110_set_wol,  	},  	{  		.phy_id		= MARVELL_PHY_ID_88E2110, diff --git a/drivers/net/phy/mediatek-ge.c b/drivers/net/phy/mediatek-ge.c index 11ff335d6228..b7a5ae20edd5 100644 --- a/drivers/net/phy/mediatek-ge.c +++ b/drivers/net/phy/mediatek-ge.c @@ -81,6 +81,8 @@ static struct phy_driver mtk_gephy_driver[] = {  		 */  		.config_intr	= genphy_no_config_intr,  		.handle_interrupt = genphy_handle_interrupt_no_ack, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume,  		.read_page	= mtk_gephy_read_page,  		.write_page	= mtk_gephy_write_page,  	}, @@ -93,6 +95,8 @@ static struct phy_driver mtk_gephy_driver[] = {  		 */  		.config_intr	= genphy_no_config_intr,  		.handle_interrupt = genphy_handle_interrupt_no_ack, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume,  		.read_page	= mtk_gephy_read_page,  		.write_page	= mtk_gephy_write_page,  	}, diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 4d53886f7d51..5c928f827173 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -401,11 +401,11 @@ static int ksz8041_config_aneg(struct phy_device *phydev)  }  static int ksz8051_ksz8795_match_phy_device(struct phy_device *phydev, -					    const u32 ksz_phy_id) +					    const bool ksz_8051)  {  	int ret; -	if ((phydev->phy_id & MICREL_PHY_ID_MASK) != ksz_phy_id) +	if ((phydev->phy_id & MICREL_PHY_ID_MASK) != PHY_ID_KSZ8051)  		return 0;  	ret = phy_read(phydev, MII_BMSR); @@ -418,7 +418,7 @@ static int ksz8051_ksz8795_match_phy_device(struct phy_device *phydev,  	 * the switch does not.  	 */  	ret &= BMSR_ERCAP; -	if (ksz_phy_id == PHY_ID_KSZ8051) +	if (ksz_8051)  		return ret;  	else  		return !ret; @@ -426,7 +426,7 @@ static int ksz8051_ksz8795_match_phy_device(struct phy_device *phydev,  static int ksz8051_match_phy_device(struct phy_device *phydev)  { -	return ksz8051_ksz8795_match_phy_device(phydev, PHY_ID_KSZ8051); +	return ksz8051_ksz8795_match_phy_device(phydev, true);  }  static int ksz8081_config_init(struct phy_device *phydev) @@ -535,7 +535,7 @@ static int ksz8061_config_init(struct phy_device *phydev)  static int ksz8795_match_phy_device(struct phy_device *phydev)  { -	return ksz8051_ksz8795_match_phy_device(phydev, PHY_ID_KSZ87XX); +	return ksz8051_ksz8795_match_phy_device(phydev, false);  }  static int ksz9021_load_values_from_of(struct phy_device *phydev, @@ -1760,8 +1760,6 @@ static struct phy_driver ksphy_driver[] = {  	.name		= "Micrel KSZ87XX Switch",  	/* PHY_BASIC_FEATURES */  	.config_init	= kszphy_config_init, -	.config_aneg	= ksz8873mll_config_aneg, -	.read_status	= ksz8873mll_read_status,  	.match_phy_device = ksz8795_match_phy_device,  	.suspend	= genphy_suspend,  	.resume		= genphy_resume, diff --git a/drivers/net/phy/mscc/mscc_ptp.c b/drivers/net/phy/mscc/mscc_ptp.c index 924ed5b034a4..edb951695b13 100644 --- a/drivers/net/phy/mscc/mscc_ptp.c +++ b/drivers/net/phy/mscc/mscc_ptp.c @@ -506,7 +506,7 @@ static int vsc85xx_ptp_cmp_init(struct phy_device *phydev, enum ts_blk blk)  {  	struct vsc8531_private *vsc8531 = phydev->priv;  	bool base = phydev->mdio.addr == vsc8531->ts_base_addr; -	u8 msgs[] = { +	static const u8 msgs[] = {  		PTP_MSGTYPE_SYNC,  		PTP_MSGTYPE_DELAY_REQ  	}; @@ -847,7 +847,7 @@ static int vsc85xx_ts_ptp_action_flow(struct phy_device *phydev, enum ts_blk blk  static int vsc85xx_ptp_conf(struct phy_device *phydev, enum ts_blk blk,  			    bool one_step, bool enable)  { -	u8 msgs[] = { +	static const u8 msgs[] = {  		PTP_MSGTYPE_SYNC,  		PTP_MSGTYPE_DELAY_REQ  	}; @@ -1268,8 +1268,8 @@ static void vsc8584_set_input_clk_configured(struct phy_device *phydev)  static int __vsc8584_init_ptp(struct phy_device *phydev)  {  	struct vsc8531_private *vsc8531 = phydev->priv; -	u32 ltc_seq_e[] = { 0, 400000, 0, 0, 0 }; -	u8  ltc_seq_a[] = { 8, 6, 5, 4, 2 }; +	static const u32 ltc_seq_e[] = { 0, 400000, 0, 0, 0 }; +	static const u8  ltc_seq_a[] = { 8, 6, 5, 4, 2 };  	u32 val;  	if (!vsc8584_is_1588_input_clk_configured(phydev)) { diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c new file mode 100644 index 000000000000..2d5d5081c3b6 --- /dev/null +++ b/drivers/net/phy/mxl-gpy.c @@ -0,0 +1,727 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2021 Maxlinear Corporation + * Copyright (C) 2020 Intel Corporation + * + * Drivers for Maxlinear Ethernet GPY + * + */ + +#include <linux/module.h> +#include <linux/bitfield.h> +#include <linux/phy.h> +#include <linux/netdevice.h> + +/* PHY ID */ +#define PHY_ID_GPYx15B_MASK	0xFFFFFFFC +#define PHY_ID_GPY21xB_MASK	0xFFFFFFF9 +#define PHY_ID_GPY2xx		0x67C9DC00 +#define PHY_ID_GPY115B		0x67C9DF00 +#define PHY_ID_GPY115C		0x67C9DF10 +#define PHY_ID_GPY211B		0x67C9DE08 +#define PHY_ID_GPY211C		0x67C9DE10 +#define PHY_ID_GPY212B		0x67C9DE09 +#define PHY_ID_GPY212C		0x67C9DE20 +#define PHY_ID_GPY215B		0x67C9DF04 +#define PHY_ID_GPY215C		0x67C9DF20 +#define PHY_ID_GPY241B		0x67C9DE40 +#define PHY_ID_GPY241BM		0x67C9DE80 +#define PHY_ID_GPY245B		0x67C9DEC0 + +#define PHY_MIISTAT		0x18	/* MII state */ +#define PHY_IMASK		0x19	/* interrupt mask */ +#define PHY_ISTAT		0x1A	/* interrupt status */ +#define PHY_FWV			0x1E	/* firmware version */ + +#define PHY_MIISTAT_SPD_MASK	GENMASK(2, 0) +#define PHY_MIISTAT_DPX		BIT(3) +#define PHY_MIISTAT_LS		BIT(10) + +#define PHY_MIISTAT_SPD_10	0 +#define PHY_MIISTAT_SPD_100	1 +#define PHY_MIISTAT_SPD_1000	2 +#define PHY_MIISTAT_SPD_2500	4 + +#define PHY_IMASK_WOL		BIT(15)	/* Wake-on-LAN */ +#define PHY_IMASK_ANC		BIT(10)	/* Auto-Neg complete */ +#define PHY_IMASK_ADSC		BIT(5)	/* Link auto-downspeed detect */ +#define PHY_IMASK_DXMC		BIT(2)	/* Duplex mode change */ +#define PHY_IMASK_LSPC		BIT(1)	/* Link speed change */ +#define PHY_IMASK_LSTC		BIT(0)	/* Link state change */ +#define PHY_IMASK_MASK		(PHY_IMASK_LSTC | \ +				 PHY_IMASK_LSPC | \ +				 PHY_IMASK_DXMC | \ +				 PHY_IMASK_ADSC | \ +				 PHY_IMASK_ANC) + +#define PHY_FWV_REL_MASK	BIT(15) +#define PHY_FWV_TYPE_MASK	GENMASK(11, 8) +#define PHY_FWV_MINOR_MASK	GENMASK(7, 0) + +/* SGMII */ +#define VSPEC1_SGMII_CTRL	0x08 +#define VSPEC1_SGMII_CTRL_ANEN	BIT(12)		/* Aneg enable */ +#define VSPEC1_SGMII_CTRL_ANRS	BIT(9)		/* Restart Aneg */ +#define VSPEC1_SGMII_ANEN_ANRS	(VSPEC1_SGMII_CTRL_ANEN | \ +				 VSPEC1_SGMII_CTRL_ANRS) + +/* WoL */ +#define VPSPEC2_WOL_CTL		0x0E06 +#define VPSPEC2_WOL_AD01	0x0E08 +#define VPSPEC2_WOL_AD23	0x0E09 +#define VPSPEC2_WOL_AD45	0x0E0A +#define WOL_EN			BIT(0) + +static const struct { +	int type; +	int minor; +} ver_need_sgmii_reaneg[] = { +	{7, 0x6D}, +	{8, 0x6D}, +	{9, 0x73}, +}; + +static int gpy_config_init(struct phy_device *phydev) +{ +	int ret; + +	/* Mask all interrupts */ +	ret = phy_write(phydev, PHY_IMASK, 0); +	if (ret) +		return ret; + +	/* Clear all pending interrupts */ +	ret = phy_read(phydev, PHY_ISTAT); +	return ret < 0 ? ret : 0; +} + +static int gpy_probe(struct phy_device *phydev) +{ +	int ret; + +	if (!phydev->is_c45) { +		ret = phy_get_c45_ids(phydev); +		if (ret < 0) +			return ret; +	} + +	/* Show GPY PHY FW version in dmesg */ +	ret = phy_read(phydev, PHY_FWV); +	if (ret < 0) +		return ret; + +	phydev_info(phydev, "Firmware Version: 0x%04X (%s)\n", ret, +		    (ret & PHY_FWV_REL_MASK) ? "release" : "test"); + +	return 0; +} + +static bool gpy_sgmii_need_reaneg(struct phy_device *phydev) +{ +	int fw_ver, fw_type, fw_minor; +	size_t i; + +	fw_ver = phy_read(phydev, PHY_FWV); +	if (fw_ver < 0) +		return true; + +	fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver); +	fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver); + +	for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) { +		if (fw_type != ver_need_sgmii_reaneg[i].type) +			continue; +		if (fw_minor < ver_need_sgmii_reaneg[i].minor) +			return true; +		break; +	} + +	return false; +} + +static bool gpy_2500basex_chk(struct phy_device *phydev) +{ +	int ret; + +	ret = phy_read(phydev, PHY_MIISTAT); +	if (ret < 0) { +		phydev_err(phydev, "Error: MDIO register access failed: %d\n", +			   ret); +		return false; +	} + +	if (!(ret & PHY_MIISTAT_LS) || +	    FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500) +		return false; + +	phydev->speed = SPEED_2500; +	phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +	phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, +		       VSPEC1_SGMII_CTRL_ANEN, 0); +	return true; +} + +static bool gpy_sgmii_aneg_en(struct phy_device *phydev) +{ +	int ret; + +	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL); +	if (ret < 0) { +		phydev_err(phydev, "Error: MMD register access failed: %d\n", +			   ret); +		return true; +	} + +	return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false; +} + +static int gpy_config_aneg(struct phy_device *phydev) +{ +	bool changed = false; +	u32 adv; +	int ret; + +	if (phydev->autoneg == AUTONEG_DISABLE) { +		/* Configure half duplex with genphy_setup_forced, +		 * because genphy_c45_pma_setup_forced does not support. +		 */ +		return phydev->duplex != DUPLEX_FULL +			? genphy_setup_forced(phydev) +			: genphy_c45_pma_setup_forced(phydev); +	} + +	ret = genphy_c45_an_config_aneg(phydev); +	if (ret < 0) +		return ret; +	if (ret > 0) +		changed = true; + +	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); +	ret = phy_modify_changed(phydev, MII_CTRL1000, +				 ADVERTISE_1000FULL | ADVERTISE_1000HALF, +				 adv); +	if (ret < 0) +		return ret; +	if (ret > 0) +		changed = true; + +	ret = genphy_c45_check_and_restart_aneg(phydev, changed); +	if (ret < 0) +		return ret; + +	if (phydev->interface == PHY_INTERFACE_MODE_USXGMII || +	    phydev->interface == PHY_INTERFACE_MODE_INTERNAL) +		return 0; + +	/* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is +	 * disabled. +	 */ +	if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) || +	    !gpy_sgmii_aneg_en(phydev)) +		return 0; + +	/* There is a design constraint in GPY2xx device where SGMII AN is +	 * only triggered when there is change of speed. If, PHY link +	 * partner`s speed is still same even after PHY TPI is down and up +	 * again, SGMII AN is not triggered and hence no new in-band message +	 * from GPY to MAC side SGMII. +	 * This could cause an issue during power up, when PHY is up prior to +	 * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII +	 * wouldn`t receive new in-band message from GPY with correct link +	 * status, speed and duplex info. +	 * +	 * 1) If PHY is already up and TPI link status is still down (such as +	 *    hard reboot), TPI link status is polled for 4 seconds before +	 *    retriggerring SGMII AN. +	 * 2) If PHY is already up and TPI link status is also up (such as soft +	 *    reboot), polling of TPI link status is not needed and SGMII AN is +	 *    immediately retriggered. +	 * 3) Other conditions such as PHY is down, speed change etc, skip +	 *    retriggering SGMII AN. Note: in case of speed change, GPY FW will +	 *    initiate SGMII AN. +	 */ + +	if (phydev->state != PHY_UP) +		return 0; + +	ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS, +				    20000, 4000000, false); +	if (ret == -ETIMEDOUT) +		return 0; +	else if (ret < 0) +		return ret; + +	/* Trigger SGMII AN. */ +	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, +			      VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS); +} + +static void gpy_update_interface(struct phy_device *phydev) +{ +	int ret; + +	/* Interface mode is fixed for USXGMII and integrated PHY */ +	if (phydev->interface == PHY_INTERFACE_MODE_USXGMII || +	    phydev->interface == PHY_INTERFACE_MODE_INTERNAL) +		return; + +	/* Automatically switch SERDES interface between SGMII and 2500-BaseX +	 * according to speed. Disable ANEG in 2500-BaseX mode. +	 */ +	switch (phydev->speed) { +	case SPEED_2500: +		phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, +				     VSPEC1_SGMII_CTRL_ANEN, 0); +		if (ret < 0) +			phydev_err(phydev, +				   "Error: Disable of SGMII ANEG failed: %d\n", +				   ret); +		break; +	case SPEED_1000: +	case SPEED_100: +	case SPEED_10: +		phydev->interface = PHY_INTERFACE_MODE_SGMII; +		if (gpy_sgmii_aneg_en(phydev)) +			break; +		/* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed +		 * if ANEG is disabled (in 2500-BaseX mode). +		 */ +		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, +				     VSPEC1_SGMII_ANEN_ANRS, +				     VSPEC1_SGMII_ANEN_ANRS); +		if (ret < 0) +			phydev_err(phydev, +				   "Error: Enable of SGMII ANEG failed: %d\n", +				   ret); +		break; +	} +} + +static int gpy_read_status(struct phy_device *phydev) +{ +	int ret; + +	ret = genphy_update_link(phydev); +	if (ret) +		return ret; + +	phydev->speed = SPEED_UNKNOWN; +	phydev->duplex = DUPLEX_UNKNOWN; +	phydev->pause = 0; +	phydev->asym_pause = 0; + +	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { +		ret = genphy_c45_read_lpa(phydev); +		if (ret < 0) +			return ret; + +		/* Read the link partner's 1G advertisement */ +		ret = phy_read(phydev, MII_STAT1000); +		if (ret < 0) +			return ret; +		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret); +	} else if (phydev->autoneg == AUTONEG_DISABLE) { +		linkmode_zero(phydev->lp_advertising); +	} + +	ret = phy_read(phydev, PHY_MIISTAT); +	if (ret < 0) +		return ret; + +	phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0; +	phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF; +	switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) { +	case PHY_MIISTAT_SPD_10: +		phydev->speed = SPEED_10; +		break; +	case PHY_MIISTAT_SPD_100: +		phydev->speed = SPEED_100; +		break; +	case PHY_MIISTAT_SPD_1000: +		phydev->speed = SPEED_1000; +		break; +	case PHY_MIISTAT_SPD_2500: +		phydev->speed = SPEED_2500; +		break; +	} + +	if (phydev->link) +		gpy_update_interface(phydev); + +	return 0; +} + +static int gpy_config_intr(struct phy_device *phydev) +{ +	u16 mask = 0; + +	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) +		mask = PHY_IMASK_MASK; + +	return phy_write(phydev, PHY_IMASK, mask); +} + +static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev) +{ +	int reg; + +	reg = phy_read(phydev, PHY_ISTAT); +	if (reg < 0) { +		phy_error(phydev); +		return IRQ_NONE; +	} + +	if (!(reg & PHY_IMASK_MASK)) +		return IRQ_NONE; + +	phy_trigger_machine(phydev); + +	return IRQ_HANDLED; +} + +static int gpy_set_wol(struct phy_device *phydev, +		       struct ethtool_wolinfo *wol) +{ +	struct net_device *attach_dev = phydev->attached_dev; +	int ret; + +	if (wol->wolopts & WAKE_MAGIC) { +		/* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5 +		 * VPSPEC2_WOL_AD45 = Byte0:Byte1 +		 * VPSPEC2_WOL_AD23 = Byte2:Byte3 +		 * VPSPEC2_WOL_AD01 = Byte4:Byte5 +		 */ +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, +				       VPSPEC2_WOL_AD45, +				       ((attach_dev->dev_addr[0] << 8) | +				       attach_dev->dev_addr[1])); +		if (ret < 0) +			return ret; + +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, +				       VPSPEC2_WOL_AD23, +				       ((attach_dev->dev_addr[2] << 8) | +				       attach_dev->dev_addr[3])); +		if (ret < 0) +			return ret; + +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, +				       VPSPEC2_WOL_AD01, +				       ((attach_dev->dev_addr[4] << 8) | +				       attach_dev->dev_addr[5])); +		if (ret < 0) +			return ret; + +		/* Enable the WOL interrupt */ +		ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL); +		if (ret < 0) +			return ret; + +		/* Enable magic packet matching */ +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, +				       VPSPEC2_WOL_CTL, +				       WOL_EN); +		if (ret < 0) +			return ret; + +		/* Clear the interrupt status register. +		 * Only WoL is enabled so clear all. +		 */ +		ret = phy_read(phydev, PHY_ISTAT); +		if (ret < 0) +			return ret; +	} else { +		/* Disable magic packet matching */ +		ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, +					 VPSPEC2_WOL_CTL, +					 WOL_EN); +		if (ret < 0) +			return ret; +	} + +	if (wol->wolopts & WAKE_PHY) { +		/* Enable the link state change interrupt */ +		ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC); +		if (ret < 0) +			return ret; + +		/* Clear the interrupt status register */ +		ret = phy_read(phydev, PHY_ISTAT); +		if (ret < 0) +			return ret; + +		if (ret & (PHY_IMASK_MASK & ~PHY_IMASK_LSTC)) +			phy_trigger_machine(phydev); + +		return 0; +	} + +	/* Disable the link state change interrupt */ +	return phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC); +} + +static void gpy_get_wol(struct phy_device *phydev, +			struct ethtool_wolinfo *wol) +{ +	int ret; + +	wol->supported = WAKE_MAGIC | WAKE_PHY; +	wol->wolopts = 0; + +	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, VPSPEC2_WOL_CTL); +	if (ret & WOL_EN) +		wol->wolopts |= WAKE_MAGIC; + +	ret = phy_read(phydev, PHY_IMASK); +	if (ret & PHY_IMASK_LSTC) +		wol->wolopts |= WAKE_PHY; +} + +static int gpy_loopback(struct phy_device *phydev, bool enable) +{ +	int ret; + +	ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, +			 enable ? BMCR_LOOPBACK : 0); +	if (!ret) { +		/* It takes some time for PHY device to switch +		 * into/out-of loopback mode. +		 */ +		msleep(100); +	} + +	return ret; +} + +static struct phy_driver gpy_drivers[] = { +	{ +		PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx), +		.name		= "Maxlinear Ethernet GPY2xx", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		.phy_id		= PHY_ID_GPY115B, +		.phy_id_mask	= PHY_ID_GPYx15B_MASK, +		.name		= "Maxlinear Ethernet GPY115B", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		PHY_ID_MATCH_MODEL(PHY_ID_GPY115C), +		.name		= "Maxlinear Ethernet GPY115C", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		.phy_id		= PHY_ID_GPY211B, +		.phy_id_mask	= PHY_ID_GPY21xB_MASK, +		.name		= "Maxlinear Ethernet GPY211B", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		PHY_ID_MATCH_MODEL(PHY_ID_GPY211C), +		.name		= "Maxlinear Ethernet GPY211C", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		.phy_id		= PHY_ID_GPY212B, +		.phy_id_mask	= PHY_ID_GPY21xB_MASK, +		.name		= "Maxlinear Ethernet GPY212B", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		PHY_ID_MATCH_MODEL(PHY_ID_GPY212C), +		.name		= "Maxlinear Ethernet GPY212C", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		.phy_id		= PHY_ID_GPY215B, +		.phy_id_mask	= PHY_ID_GPYx15B_MASK, +		.name		= "Maxlinear Ethernet GPY215B", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		PHY_ID_MATCH_MODEL(PHY_ID_GPY215C), +		.name		= "Maxlinear Ethernet GPY215C", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		PHY_ID_MATCH_MODEL(PHY_ID_GPY241B), +		.name		= "Maxlinear Ethernet GPY241B", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM), +		.name		= "Maxlinear Ethernet GPY241BM", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +	{ +		PHY_ID_MATCH_MODEL(PHY_ID_GPY245B), +		.name		= "Maxlinear Ethernet GPY245B", +		.get_features	= genphy_c45_pma_read_abilities, +		.config_init	= gpy_config_init, +		.probe		= gpy_probe, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= gpy_config_aneg, +		.aneg_done	= genphy_c45_aneg_done, +		.read_status	= gpy_read_status, +		.config_intr	= gpy_config_intr, +		.handle_interrupt = gpy_handle_interrupt, +		.set_wol	= gpy_set_wol, +		.get_wol	= gpy_get_wol, +		.set_loopback	= gpy_loopback, +	}, +}; +module_phy_driver(gpy_drivers); + +static struct mdio_device_id __maybe_unused gpy_tbl[] = { +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)}, +	{PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK}, +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)}, +	{PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK}, +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)}, +	{PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK}, +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)}, +	{PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK}, +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)}, +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)}, +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)}, +	{PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)}, +	{ } +}; +MODULE_DEVICE_TABLE(mdio, gpy_tbl); + +MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver"); +MODULE_AUTHOR("Xu Liang"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/nxp-tja11xx.c b/drivers/net/phy/nxp-tja11xx.c index afd7afa1f498..9944cc501806 100644 --- a/drivers/net/phy/nxp-tja11xx.c +++ b/drivers/net/phy/nxp-tja11xx.c @@ -47,12 +47,14 @@  #define MII_INTSRC_LINK_FAIL		BIT(10)  #define MII_INTSRC_LINK_UP		BIT(9)  #define MII_INTSRC_MASK			(MII_INTSRC_LINK_FAIL | MII_INTSRC_LINK_UP) -#define MII_INTSRC_TEMP_ERR		BIT(1)  #define MII_INTSRC_UV_ERR		BIT(3) +#define MII_INTSRC_TEMP_ERR		BIT(1)  #define MII_INTEN			22  #define MII_INTEN_LINK_FAIL		BIT(10)  #define MII_INTEN_LINK_UP		BIT(9) +#define MII_INTEN_UV_ERR		BIT(3) +#define MII_INTEN_TEMP_ERR		BIT(1)  #define MII_COMMSTAT			23  #define MII_COMMSTAT_LINK_UP		BIT(15) @@ -607,7 +609,8 @@ static int tja11xx_config_intr(struct phy_device *phydev)  		if (err)  			return err; -		value = MII_INTEN_LINK_FAIL | MII_INTEN_LINK_UP; +		value = MII_INTEN_LINK_FAIL | MII_INTEN_LINK_UP | +			MII_INTEN_UV_ERR | MII_INTEN_TEMP_ERR;  		err = phy_write(phydev, MII_INTEN, value);  	} else {  		err = phy_write(phydev, MII_INTEN, value); @@ -622,6 +625,7 @@ static int tja11xx_config_intr(struct phy_device *phydev)  static irqreturn_t tja11xx_handle_interrupt(struct phy_device *phydev)  { +	struct device *dev = &phydev->mdio.dev;  	int irq_status;  	irq_status = phy_read(phydev, MII_INTSRC); @@ -630,6 +634,11 @@ static irqreturn_t tja11xx_handle_interrupt(struct phy_device *phydev)  		return IRQ_NONE;  	} +	if (irq_status & MII_INTSRC_TEMP_ERR) +		dev_warn(dev, "Overtemperature error detected (temp > 155C°).\n"); +	if (irq_status & MII_INTSRC_UV_ERR) +		dev_warn(dev, "Undervoltage error detected.\n"); +  	if (!(irq_status & MII_INTSRC_MASK))  		return IRQ_NONE; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 8eeb26d8aeb7..f124a8a58bd4 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -426,7 +426,7 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)  EXPORT_SYMBOL(phy_mii_ioctl);  /** - * phy_do_ioctl - generic ndo_do_ioctl implementation + * phy_do_ioctl - generic ndo_eth_ioctl implementation   * @dev: the net_device struct   * @ifr: &struct ifreq for socket ioctl's   * @cmd: ioctl cmd to execute @@ -441,7 +441,7 @@ int phy_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)  EXPORT_SYMBOL(phy_do_ioctl);  /** - * phy_do_ioctl_running - generic ndo_do_ioctl implementation but test first + * phy_do_ioctl_running - generic ndo_eth_ioctl implementation but test first   *   * @dev: the net_device struct   * @ifr: &struct ifreq for socket ioctl's diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 5d5f9a9ee768..9e2891d8e8dd 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -233,11 +233,9 @@ static DEFINE_MUTEX(phy_fixup_lock);  static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)  { -	struct device_driver *drv = phydev->mdio.dev.driver; -	struct phy_driver *phydrv = to_phy_driver(drv);  	struct net_device *netdev = phydev->attached_dev; -	if (!drv || !phydrv->suspend) +	if (!phydev->drv->suspend)  		return false;  	/* PHY not attached? May suspend if the PHY has not already been @@ -969,6 +967,20 @@ void phy_device_remove(struct phy_device *phydev)  EXPORT_SYMBOL(phy_device_remove);  /** + * phy_get_c45_ids - Read 802.3-c45 IDs for phy device. + * @phydev: phy_device structure to read 802.3-c45 IDs + * + * Returns zero on success, %-EIO on bus access error, or %-ENODEV if + * the "devices in package" is invalid. + */ +int phy_get_c45_ids(struct phy_device *phydev) +{ +	return get_phy_c45_ids(phydev->mdio.bus, phydev->mdio.addr, +			       &phydev->c45_ids); +} +EXPORT_SYMBOL(phy_get_c45_ids); + +/**   * phy_find_first - finds the first PHY device on the bus   * @bus: the target MII bus   */ @@ -1807,11 +1819,10 @@ EXPORT_SYMBOL(phy_resume);  int phy_loopback(struct phy_device *phydev, bool enable)  { -	struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);  	int ret = 0; -	if (!phydrv) -		return -ENODEV; +	if (!phydev->drv) +		return -EIO;  	mutex_lock(&phydev->lock); @@ -1825,8 +1836,8 @@ int phy_loopback(struct phy_device *phydev, bool enable)  		goto out;  	} -	if (phydrv->set_loopback) -		ret = phydrv->set_loopback(phydev, enable); +	if (phydev->drv->set_loopback) +		ret = phydev->drv->set_loopback(phydev, enable);  	else  		ret = genphy_loopback(phydev, enable); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index eb29ef53d971..2cdf9f989dec 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -942,10 +942,11 @@ static void phylink_phy_change(struct phy_device *phydev, bool up)  	phylink_run_resolve(pl); -	phylink_dbg(pl, "phy link %s %s/%s/%s\n", up ? "up" : "down", +	phylink_dbg(pl, "phy link %s %s/%s/%s/%s\n", up ? "up" : "down",  		    phy_modes(phydev->interface),  		    phy_speed_to_str(phydev->speed), -		    phy_duplex_to_str(phydev->duplex)); +		    phy_duplex_to_str(phydev->duplex), +		    phylink_pause_to_str(pl->phy_state.pause));  }  static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, @@ -1457,15 +1458,11 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,  		return phy_ethtool_ksettings_set(pl->phydev, kset);  	} -	linkmode_copy(support, pl->supported);  	config = pl->link_config; -	config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE; -	/* Mask out unsupported advertisements, and force the autoneg bit */ +	/* Mask out unsupported advertisements */  	linkmode_and(config.advertising, kset->link_modes.advertising, -		     support); -	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising, -			 config.an_enabled); +		     pl->supported);  	/* FIXME: should we reject autoneg if phy/mac does not support it? */  	switch (kset->base.autoneg) { @@ -1474,7 +1471,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,  		 * duplex.  		 */  		s = phy_lookup_setting(kset->base.speed, kset->base.duplex, -				       support, false); +				       pl->supported, false);  		if (!s)  			return -EINVAL; @@ -1515,6 +1512,12 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,  	/* We have ruled out the case with a PHY attached, and the  	 * fixed-link cases.  All that is left are in-band links.  	 */ +	config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE; +	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising, +			 config.an_enabled); + +	/* Validate without changing the current supported mask. */ +	linkmode_copy(support, pl->supported);  	if (phylink_validate(pl, support, &config))  		return -EINVAL; diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c index 151c2a3f0b3a..8dcb49ed1f3d 100644 --- a/drivers/net/phy/xilinx_gmii2rgmii.c +++ b/drivers/net/phy/xilinx_gmii2rgmii.c @@ -27,12 +27,28 @@ struct gmii2rgmii {  	struct mdio_device *mdio;  }; -static int xgmiitorgmii_read_status(struct phy_device *phydev) +static void xgmiitorgmii_configure(struct gmii2rgmii *priv, int speed)  { -	struct gmii2rgmii *priv = mdiodev_get_drvdata(&phydev->mdio);  	struct mii_bus *bus = priv->mdio->bus;  	int addr = priv->mdio->addr; -	u16 val = 0; +	u16 val; + +	val = mdiobus_read(bus, addr, XILINX_GMII2RGMII_REG); +	val &= ~XILINX_GMII2RGMII_SPEED_MASK; + +	if (speed == SPEED_1000) +		val |= BMCR_SPEED1000; +	else if (speed == SPEED_100) +		val |= BMCR_SPEED100; +	else +		val |= BMCR_SPEED10; + +	mdiobus_write(bus, addr, XILINX_GMII2RGMII_REG, val); +} + +static int xgmiitorgmii_read_status(struct phy_device *phydev) +{ +	struct gmii2rgmii *priv = mdiodev_get_drvdata(&phydev->mdio);  	int err;  	if (priv->phy_drv->read_status) @@ -42,17 +58,24 @@ static int xgmiitorgmii_read_status(struct phy_device *phydev)  	if (err < 0)  		return err; -	val = mdiobus_read(bus, addr, XILINX_GMII2RGMII_REG); -	val &= ~XILINX_GMII2RGMII_SPEED_MASK; +	xgmiitorgmii_configure(priv, phydev->speed); -	if (phydev->speed == SPEED_1000) -		val |= BMCR_SPEED1000; -	else if (phydev->speed == SPEED_100) -		val |= BMCR_SPEED100; +	return 0; +} + +static int xgmiitorgmii_set_loopback(struct phy_device *phydev, bool enable) +{ +	struct gmii2rgmii *priv = mdiodev_get_drvdata(&phydev->mdio); +	int err; + +	if (priv->phy_drv->set_loopback) +		err = priv->phy_drv->set_loopback(phydev, enable);  	else -		val |= BMCR_SPEED10; +		err = genphy_loopback(phydev, enable); +	if (err < 0) +		return err; -	mdiobus_write(bus, addr, XILINX_GMII2RGMII_REG, val); +	xgmiitorgmii_configure(priv, phydev->speed);  	return 0;  } @@ -90,6 +113,7 @@ static int xgmiitorgmii_probe(struct mdio_device *mdiodev)  	memcpy(&priv->conv_phy_drv, priv->phy_dev->drv,  	       sizeof(struct phy_driver));  	priv->conv_phy_drv.read_status = xgmiitorgmii_read_status; +	priv->conv_phy_drv.set_loopback = xgmiitorgmii_set_loopback;  	mdiodev_set_drvdata(&priv->phy_dev->mdio, priv);  	priv->phy_dev->drv = &priv->conv_phy_drv; |