diff options
Diffstat (limited to 'drivers/net/phy')
| -rw-r--r-- | drivers/net/phy/Kconfig | 35 | ||||
| -rw-r--r-- | drivers/net/phy/Makefile | 4 | ||||
| -rw-r--r-- | drivers/net/phy/aquantia.c | 15 | ||||
| -rw-r--r-- | drivers/net/phy/at803x.c | 4 | ||||
| -rw-r--r-- | drivers/net/phy/bcm-cygnus.c | 158 | ||||
| -rw-r--r-- | drivers/net/phy/bcm-phy-lib.c | 213 | ||||
| -rw-r--r-- | drivers/net/phy/bcm-phy-lib.h | 37 | ||||
| -rw-r--r-- | drivers/net/phy/bcm63xx.c | 38 | ||||
| -rw-r--r-- | drivers/net/phy/bcm7xxx.c | 136 | ||||
| -rw-r--r-- | drivers/net/phy/broadcom.c | 149 | ||||
| -rw-r--r-- | drivers/net/phy/dp83640.c | 64 | ||||
| -rw-r--r-- | drivers/net/phy/dp83848.c | 99 | ||||
| -rw-r--r-- | drivers/net/phy/marvell.c | 16 | ||||
| -rw-r--r-- | drivers/net/phy/mdio-bcm-iproc.c | 213 | ||||
| -rw-r--r-- | drivers/net/phy/mdio-gpio.c | 2 | ||||
| -rw-r--r-- | drivers/net/phy/mdio-mux-mmioreg.c | 2 | ||||
| -rw-r--r-- | drivers/net/phy/mdio-mux.c | 1 | ||||
| -rw-r--r-- | drivers/net/phy/mdio_bus.c | 55 | ||||
| -rw-r--r-- | drivers/net/phy/micrel.c | 23 | ||||
| -rw-r--r-- | drivers/net/phy/phy.c | 3 | ||||
| -rw-r--r-- | drivers/net/phy/phy_device.c | 133 | ||||
| -rw-r--r-- | drivers/net/phy/smsc.c | 19 | ||||
| -rw-r--r-- | drivers/net/phy/spi_ks8995.c | 1 | ||||
| -rw-r--r-- | drivers/net/phy/teranetics.c | 15 | ||||
| -rw-r--r-- | drivers/net/phy/vitesse.c | 16 | 
25 files changed, 1102 insertions, 349 deletions
| diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index c5ad98ace5d0..60994a83a0d6 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -69,20 +69,39 @@ config SMSC_PHY  	---help---  	  Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs +config BCM_NET_PHYLIB +	tristate +  config BROADCOM_PHY  	tristate "Drivers for Broadcom PHYs" +	select BCM_NET_PHYLIB  	---help---  	  Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464,  	  BCM5481 and BCM5482 PHYs. +config BCM_CYGNUS_PHY +	tristate "Drivers for Broadcom Cygnus SoC internal PHY" +	depends on ARCH_BCM_CYGNUS || COMPILE_TEST +	depends on MDIO_BCM_IPROC +	select BCM_NET_PHYLIB +	---help--- +	  This PHY driver is for the 1G internal PHYs of the Broadcom +	  Cygnus Family SoC. + +	  Currently supports internal PHY's used in the BCM11300, +	  BCM11320, BCM11350, BCM11360, BCM58300, BCM58302, +	  BCM58303 & BCM58305 Broadcom Cygnus SoCs. +  config BCM63XX_PHY  	tristate "Drivers for Broadcom 63xx SOCs internal PHY"  	depends on BCM63XX +	select BCM_NET_PHYLIB  	---help---  	  Currently supports the 6348 and 6358 PHYs.  config BCM7XXX_PHY  	tristate "Drivers for Broadcom 7xxx SOCs internal PHYs" +	select BCM_NET_PHYLIB  	---help---  	  Currently supports the BCM7366, BCM7439, BCM7445, and  	  40nm and 65nm generation of BCM7xxx Set Top Box SoCs. @@ -122,6 +141,11 @@ config MICREL_PHY  	---help---  	  Supports the KSZ9021, VSC8201, KS8001 PHYs. +config DP83848_PHY +	tristate "Driver for Texas Instruments DP83848 PHY" +	---help--- +	  Supports the DP83848 PHY. +  config DP83867_PHY  	tristate "Drivers for Texas Instruments DP83867 Gigabit PHY"  	---help--- @@ -168,8 +192,6 @@ config MDIO_OCTEON  	  busses. It is required by the Octeon and ThunderX ethernet device  	  drivers. -	  If in doubt, say Y. -  config MDIO_SUN4I  	tristate "Allwinner sun4i MDIO interface support"  	depends on ARCH_SUNXI @@ -225,6 +247,15 @@ config MDIO_BCM_UNIMAC  	  This hardware can be found in the Broadcom GENET Ethernet MAC  	  controllers as well as some Broadcom Ethernet switches such as the  	  Starfighter 2 switches. + +config MDIO_BCM_IPROC +	tristate "Broadcom iProc MDIO bus controller" +	depends on ARCH_BCM_IPROC || COMPILE_TEST +	depends on HAS_IOMEM && OF_MDIO +	help +	  This module provides a driver for the MDIO busses found in the +	  Broadcom iProc SoC's. +  endif # PHYLIB  config MICREL_KS8995MA diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 87f079c4b2c7..f31a4e25cf15 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -12,10 +12,12 @@ obj-$(CONFIG_QSEMI_PHY)		+= qsemi.o  obj-$(CONFIG_SMSC_PHY)		+= smsc.o  obj-$(CONFIG_TERANETICS_PHY)	+= teranetics.o  obj-$(CONFIG_VITESSE_PHY)	+= vitesse.o +obj-$(CONFIG_BCM_NET_PHYLIB)	+= bcm-phy-lib.o  obj-$(CONFIG_BROADCOM_PHY)	+= broadcom.o  obj-$(CONFIG_BCM63XX_PHY)	+= bcm63xx.o  obj-$(CONFIG_BCM7XXX_PHY)	+= bcm7xxx.o  obj-$(CONFIG_BCM87XX_PHY)	+= bcm87xx.o +obj-$(CONFIG_BCM_CYGNUS_PHY)	+= bcm-cygnus.o  obj-$(CONFIG_ICPLUS_PHY)	+= icplus.o  obj-$(CONFIG_REALTEK_PHY)	+= realtek.o  obj-$(CONFIG_LSI_ET1011C_PHY)	+= et1011c.o @@ -24,6 +26,7 @@ obj-$(CONFIG_MDIO_BITBANG)	+= mdio-bitbang.o  obj-$(CONFIG_MDIO_GPIO)		+= mdio-gpio.o  obj-$(CONFIG_NATIONAL_PHY)	+= national.o  obj-$(CONFIG_DP83640_PHY)	+= dp83640.o +obj-$(CONFIG_DP83848_PHY)	+= dp83848.o  obj-$(CONFIG_DP83867_PHY)	+= dp83867.o  obj-$(CONFIG_STE10XP)		+= ste10Xp.o  obj-$(CONFIG_MICREL_PHY)	+= micrel.o @@ -38,3 +41,4 @@ obj-$(CONFIG_MDIO_SUN4I)	+= mdio-sun4i.o  obj-$(CONFIG_MDIO_MOXART)	+= mdio-moxart.o  obj-$(CONFIG_MDIO_BCM_UNIMAC)	+= mdio-bcm-unimac.o  obj-$(CONFIG_MICROCHIP_PHY)	+= microchip.o +obj-$(CONFIG_MDIO_BCM_IPROC)	+= mdio-bcm-iproc.o diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c index d6111affbcb6..f1936b7a7af6 100644 --- a/drivers/net/phy/aquantia.c +++ b/drivers/net/phy/aquantia.c @@ -171,20 +171,7 @@ static struct phy_driver aquantia_driver[] = {  },  }; -static int __init aquantia_init(void) -{ -	return phy_drivers_register(aquantia_driver, -				    ARRAY_SIZE(aquantia_driver)); -} - -static void __exit aquantia_exit(void) -{ -	return phy_drivers_unregister(aquantia_driver, -				      ARRAY_SIZE(aquantia_driver)); -} - -module_init(aquantia_init); -module_exit(aquantia_exit); +module_phy_driver(aquantia_driver);  static struct mdio_device_id __maybe_unused aquantia_tbl[] = {  	{ PHY_ID_AQ1202, 0xfffffff0 }, diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index fabf11d32d27..2d020a3ec0b5 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -308,6 +308,8 @@ static struct phy_driver at803x_driver[] = {  	.flags			= PHY_HAS_INTERRUPT,  	.config_aneg		= genphy_config_aneg,  	.read_status		= genphy_read_status, +	.ack_interrupt		= at803x_ack_interrupt, +	.config_intr		= at803x_config_intr,  	.driver			= {  		.owner = THIS_MODULE,  	}, @@ -327,6 +329,8 @@ static struct phy_driver at803x_driver[] = {  	.flags			= PHY_HAS_INTERRUPT,  	.config_aneg		= genphy_config_aneg,  	.read_status		= genphy_read_status, +	.ack_interrupt		= at803x_ack_interrupt, +	.config_intr		= at803x_config_intr,  	.driver			= {  		.owner = THIS_MODULE,  	}, diff --git a/drivers/net/phy/bcm-cygnus.c b/drivers/net/phy/bcm-cygnus.c new file mode 100644 index 000000000000..49bbc6826883 --- /dev/null +++ b/drivers/net/phy/bcm-cygnus.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* Broadcom Cygnus SoC internal transceivers support. */ +#include "bcm-phy-lib.h" +#include <linux/brcmphy.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/phy.h> + +/* Broadcom Cygnus Phy specific registers */ +#define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0  0x91E5 /* VDAL Control register */ + +static int bcm_cygnus_afe_config(struct phy_device *phydev) +{ +	int rc; + +	/* ensure smdspclk is enabled */ +	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30); +	if (rc < 0) +		return rc; + +	/* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */ +	rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8); +	if (rc < 0) +		return rc; + +	/* AFE_HPF_TRIM_OTHERS bit11=1, short cascode enable for all modes*/ +	rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803); +	if (rc < 0) +		return rc; + +	/* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */ +	rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740); +	if (rc < 0) +		return rc; + +	/* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */ +	rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400); +	if (rc < 0) +		return rc; + +	/* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */ +	rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004); +	if (rc < 0) +		return rc; + +	/* Adjust bias current trim to overcome digital offSet */ +	rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02); +	if (rc < 0) +		return rc; + +	/* make rcal=100, since rdb default is 000 */ +	rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB1, 0x10); +	if (rc < 0) +		return rc; + +	/* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */ +	rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x10); +	if (rc < 0) +		return rc; + +	/* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */ +	rc = bcm_phy_write_exp(phydev, MII_BRCM_CORE_EXPB0, 0x00); + +	return 0; +} + +static int bcm_cygnus_config_init(struct phy_device *phydev) +{ +	int reg, rc; + +	reg = phy_read(phydev, MII_BCM54XX_ECR); +	if (reg < 0) +		return reg; + +	/* Mask interrupts globally. */ +	reg |= MII_BCM54XX_ECR_IM; +	rc = phy_write(phydev, MII_BCM54XX_ECR, reg); +	if (rc) +		return rc; + +	/* Unmask events of interest */ +	reg = ~(MII_BCM54XX_INT_DUPLEX | +		MII_BCM54XX_INT_SPEED | +		MII_BCM54XX_INT_LINK); +	rc = phy_write(phydev, MII_BCM54XX_IMR, reg); +	if (rc) +		return rc; + +	/* Apply AFE settings for the PHY */ +	rc = bcm_cygnus_afe_config(phydev); +	if (rc) +		return rc; + +	/* Advertise EEE */ +	rc = bcm_phy_enable_eee(phydev); +	if (rc) +		return rc; + +	/* Enable APD */ +	return bcm_phy_enable_apd(phydev, false); +} + +static int bcm_cygnus_resume(struct phy_device *phydev) +{ +	int rc; + +	genphy_resume(phydev); + +	/* Re-initialize the PHY to apply AFE work-arounds and +	 * configurations when coming out of suspend. +	 */ +	rc = bcm_cygnus_config_init(phydev); +	if (rc) +		return rc; + +	/* restart auto negotiation with the new settings */ +	return genphy_config_aneg(phydev); +} + +static struct phy_driver bcm_cygnus_phy_driver[] = { +{ +	.phy_id        = PHY_ID_BCM_CYGNUS, +	.phy_id_mask   = 0xfffffff0, +	.name          = "Broadcom Cygnus PHY", +	.features      = PHY_GBIT_FEATURES | +			SUPPORTED_Pause | SUPPORTED_Asym_Pause, +	.config_init   = bcm_cygnus_config_init, +	.config_aneg   = genphy_config_aneg, +	.read_status   = genphy_read_status, +	.ack_interrupt = bcm_phy_ack_intr, +	.config_intr   = bcm_phy_config_intr, +	.suspend       = genphy_suspend, +	.resume        = bcm_cygnus_resume, +} }; + +static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = { +	{ PHY_ID_BCM_CYGNUS, 0xfffffff0, }, +	{ } +}; +MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl); + +module_phy_driver(bcm_cygnus_phy_driver); + +MODULE_DESCRIPTION("Broadcom Cygnus internal PHY driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Broadcom Corporation"); diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c new file mode 100644 index 000000000000..ddb377e53633 --- /dev/null +++ b/drivers/net/phy/bcm-phy-lib.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "bcm-phy-lib.h" +#include <linux/brcmphy.h> +#include <linux/export.h> +#include <linux/mdio.h> +#include <linux/module.h> +#include <linux/phy.h> + +#define MII_BCM_CHANNEL_WIDTH     0x2000 +#define BCM_CL45VEN_EEE_ADV       0x3c + +int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) +{ +	int rc; + +	rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); +	if (rc < 0) +		return rc; + +	return phy_write(phydev, MII_BCM54XX_EXP_DATA, val); +} +EXPORT_SYMBOL_GPL(bcm_phy_write_exp); + +int bcm_phy_read_exp(struct phy_device *phydev, u16 reg) +{ +	int val; + +	val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); +	if (val < 0) +		return val; + +	val = phy_read(phydev, MII_BCM54XX_EXP_DATA); + +	/* Restore default value.  It's O.K. if this write fails. */ +	phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); + +	return val; +} +EXPORT_SYMBOL_GPL(bcm_phy_read_exp); + +int bcm_phy_write_misc(struct phy_device *phydev, +		       u16 reg, u16 chl, u16 val) +{ +	int rc; +	int tmp; + +	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, +		       MII_BCM54XX_AUXCTL_SHDWSEL_MISC); +	if (rc < 0) +		return rc; + +	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); +	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; +	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); +	if (rc < 0) +		return rc; + +	tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; +	rc = bcm_phy_write_exp(phydev, tmp, val); + +	return rc; +} +EXPORT_SYMBOL_GPL(bcm_phy_write_misc); + +int bcm_phy_read_misc(struct phy_device *phydev, +		      u16 reg, u16 chl) +{ +	int rc; +	int tmp; + +	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, +		       MII_BCM54XX_AUXCTL_SHDWSEL_MISC); +	if (rc < 0) +		return rc; + +	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); +	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; +	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); +	if (rc < 0) +		return rc; + +	tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; +	rc = bcm_phy_read_exp(phydev, tmp); + +	return rc; +} +EXPORT_SYMBOL_GPL(bcm_phy_read_misc); + +int bcm_phy_ack_intr(struct phy_device *phydev) +{ +	int reg; + +	/* Clear pending interrupts.  */ +	reg = phy_read(phydev, MII_BCM54XX_ISR); +	if (reg < 0) +		return reg; + +	return 0; +} +EXPORT_SYMBOL_GPL(bcm_phy_ack_intr); + +int bcm_phy_config_intr(struct phy_device *phydev) +{ +	int reg; + +	reg = phy_read(phydev, MII_BCM54XX_ECR); +	if (reg < 0) +		return reg; + +	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) +		reg &= ~MII_BCM54XX_ECR_IM; +	else +		reg |= MII_BCM54XX_ECR_IM; + +	return phy_write(phydev, MII_BCM54XX_ECR, reg); +} +EXPORT_SYMBOL_GPL(bcm_phy_config_intr); + +int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow) +{ +	phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); +	return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); +} +EXPORT_SYMBOL_GPL(bcm_phy_read_shadow); + +int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, +			 u16 val) +{ +	return phy_write(phydev, MII_BCM54XX_SHD, +			 MII_BCM54XX_SHD_WRITE | +			 MII_BCM54XX_SHD_VAL(shadow) | +			 MII_BCM54XX_SHD_DATA(val)); +} +EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); + +int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) +{ +	int val; + +	if (dll_pwr_down) { +		val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); +		if (val < 0) +			return val; + +		val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; +		bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); +	} + +	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); +	if (val < 0) +		return val; + +	/* Clear APD bits */ +	val &= BCM_APD_CLR_MASK; + +	if (phydev->autoneg == AUTONEG_ENABLE) +		val |= BCM54XX_SHD_APD_EN; +	else +		val |= BCM_NO_ANEG_APD_EN; + +	/* Enable energy detect single link pulse for easy wakeup */ +	val |= BCM_APD_SINGLELP_EN; + +	/* Enable Auto Power-Down (APD) for the PHY */ +	return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); +} +EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); + +int bcm_phy_enable_eee(struct phy_device *phydev) +{ +	int val; + +	/* Enable EEE at PHY level */ +	val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, +				    MDIO_MMD_AN, phydev->addr); +	if (val < 0) +		return val; + +	val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; + +	phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, +			       MDIO_MMD_AN,  phydev->addr, (u32)val); + +	/* Advertise EEE */ +	val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, +				    MDIO_MMD_AN, phydev->addr); +	if (val < 0) +		return val; + +	val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); + +	phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, +			       MDIO_MMD_AN,  phydev->addr, (u32)val); + +	return 0; +} +EXPORT_SYMBOL_GPL(bcm_phy_enable_eee); + +MODULE_DESCRIPTION("Broadcom PHY Library"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Broadcom Corporation"); diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h new file mode 100644 index 000000000000..b2091c88b44d --- /dev/null +++ b/drivers/net/phy/bcm-phy-lib.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_BCM_PHY_LIB_H +#define _LINUX_BCM_PHY_LIB_H + +#include <linux/phy.h> + +int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val); +int bcm_phy_read_exp(struct phy_device *phydev, u16 reg); + +int bcm_phy_write_misc(struct phy_device *phydev, +		       u16 reg, u16 chl, u16 value); +int bcm_phy_read_misc(struct phy_device *phydev, +		      u16 reg, u16 chl); + +int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, +			 u16 val); +int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow); + +int bcm_phy_ack_intr(struct phy_device *phydev); +int bcm_phy_config_intr(struct phy_device *phydev); + +int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down); + +int bcm_phy_enable_eee(struct phy_device *phydev); +#endif /* _LINUX_BCM_PHY_LIB_H */ diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c index 830ec31f952f..86b28052bf06 100644 --- a/drivers/net/phy/bcm63xx.c +++ b/drivers/net/phy/bcm63xx.c @@ -6,6 +6,7 @@   *	as published by the Free Software Foundation; either version   *	2 of the License, or (at your option) any later version.   */ +#include "bcm-phy-lib.h"  #include <linux/module.h>  #include <linux/phy.h> @@ -42,35 +43,6 @@ static int bcm63xx_config_init(struct phy_device *phydev)  	return phy_write(phydev, MII_BCM63XX_IR, reg);  } -static int bcm63xx_ack_interrupt(struct phy_device *phydev) -{ -	int reg; - -	/* Clear pending interrupts.  */ -	reg = phy_read(phydev, MII_BCM63XX_IR); -	if (reg < 0) -		return reg; - -	return 0; -} - -static int bcm63xx_config_intr(struct phy_device *phydev) -{ -	int reg, err; - -	reg = phy_read(phydev, MII_BCM63XX_IR); -	if (reg < 0) -		return reg; - -	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) -		reg &= ~MII_BCM63XX_IR_GMASK; -	else -		reg |= MII_BCM63XX_IR_GMASK; - -	err = phy_write(phydev, MII_BCM63XX_IR, reg); -	return err; -} -  static struct phy_driver bcm63xx_driver[] = {  {  	.phy_id		= 0x00406000, @@ -82,8 +54,8 @@ static struct phy_driver bcm63xx_driver[] = {  	.config_init	= bcm63xx_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm63xx_ack_interrupt, -	.config_intr	= bcm63xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	/* same phy as above, with just a different OUI */ @@ -95,8 +67,8 @@ static struct phy_driver bcm63xx_driver[] = {  	.config_init	= bcm63xx_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm63xx_ack_interrupt, -	.config_intr	= bcm63xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  } }; diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 6b701b3ded74..03d4809a9126 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -12,12 +12,12 @@  #include <linux/module.h>  #include <linux/phy.h>  #include <linux/delay.h> +#include "bcm-phy-lib.h"  #include <linux/bitops.h>  #include <linux/brcmphy.h>  #include <linux/mdio.h>  /* Broadcom BCM7xxx internal PHY registers */ -#define MII_BCM7XXX_CHANNEL_WIDTH	0x2000  /* 40nm only register definitions */  #define MII_BCM7XXX_100TX_AUX_CTL	0x10 @@ -25,7 +25,6 @@  #define MII_BCM7XXX_100TX_DISC		0x14  #define MII_BCM7XXX_AUX_MODE		0x1d  #define  MII_BCM7XX_64CLK_MDIO		BIT(12) -#define MII_BCM7XXX_CORE_BASE1E		0x1e  #define MII_BCM7XXX_TEST		0x1f  #define  MII_BCM7XXX_SHD_MODE_2		BIT(2) @@ -46,39 +45,13 @@  #define AFE_VDAC_OTHERS_0		MISC_ADDR(0x39, 3)  #define AFE_HPF_TRIM_OTHERS		MISC_ADDR(0x3a, 0) -#define CORE_EXPB0			0xb0 - -static void phy_write_exp(struct phy_device *phydev, -					u16 reg, u16 value) -{ -	phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg); -	phy_write(phydev, MII_BCM54XX_EXP_DATA, value); -} - -static void phy_write_misc(struct phy_device *phydev, -					u16 reg, u16 chl, u16 value) -{ -	int tmp; - -	phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); - -	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); -	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; -	phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); - -	tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg; -	phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp); - -	phy_write(phydev, MII_BCM54XX_EXP_DATA, value); -} -  static void r_rc_cal_reset(struct phy_device *phydev)  {  	/* Reset R_CAL/RC_CAL Engine */ -	phy_write_exp(phydev, 0x00b0, 0x0010); +	bcm_phy_write_exp(phydev, 0x00b0, 0x0010);  	/* Disable Reset R_AL/RC_CAL Engine */ -	phy_write_exp(phydev, 0x00b0, 0x0000); +	bcm_phy_write_exp(phydev, 0x00b0, 0x0000);  }  static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) @@ -86,38 +59,38 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)  	/* Increase VCO range to prevent unlocking problem of PLL at low  	 * temp  	 */ -	phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); +	bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);  	/* Change Ki to 011 */ -	phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); +	bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);  	/* Disable loading of TVCO buffer to bandgap, set bandgap trim  	 * to 111  	 */ -	phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); +	bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);  	/* Adjust bias current trim by -3 */ -	phy_write_misc(phydev, DSP_TAP10, 0x690b); +	bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);  	/* Switch to CORE_BASE1E */ -	phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd); +	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);  	r_rc_cal_reset(phydev);  	/* write AFE_RXCONFIG_0 */ -	phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); +	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);  	/* write AFE_RXCONFIG_1 */ -	phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); +	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);  	/* write AFE_RX_LP_COUNTER */ -	phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); +	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);  	/* write AFE_HPF_TRIM_OTHERS */ -	phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); +	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);  	/* write AFTE_TX_CONFIG */ -	phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); +	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);  	return 0;  } @@ -125,36 +98,36 @@ static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)  static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)  {  	/* AFE_RXCONFIG_0 */ -	phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15); +	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);  	/* AFE_RXCONFIG_1 */ -	phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); +	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);  	/* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */ -	phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003); +	bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);  	/* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */ -	phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); +	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);  	/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ -	phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); +	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);  	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ -	phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); +	bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);  	/* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */ -	phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020); +	bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);  	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal  	 * offset for HT=0 code  	 */ -	phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); +	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);  	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ -	phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); +	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);  	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ -	phy_write_misc(phydev, DSP_TAP10, 0x011b); +	bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);  	/* Reset R_CAL/RC_CAL engine */  	r_rc_cal_reset(phydev); @@ -165,24 +138,24 @@ static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)  static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)  {  	/* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */ -	phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); +	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);  	/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ -	phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); +	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);  	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ -	phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); +	bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);  	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal  	 * offset for HT=0 code  	 */ -	phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); +	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);  	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ -	phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010); +	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);  	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ -	phy_write_misc(phydev, DSP_TAP10, 0x011b); +	bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);  	/* Reset R_CAL/RC_CAL engine */  	r_rc_cal_reset(phydev); @@ -190,53 +163,6 @@ static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)  	return 0;  } -static int bcm7xxx_apd_enable(struct phy_device *phydev) -{ -	int val; - -	/* Enable powering down of the DLL during auto-power down */ -	val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3); -	if (val < 0) -		return val; - -	val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; -	bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val); - -	/* Enable auto-power down */ -	val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD); -	if (val < 0) -		return val; - -	val |= BCM54XX_SHD_APD_EN; -	return bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val); -} - -static int bcm7xxx_eee_enable(struct phy_device *phydev) -{ -	int val; - -	val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, -				    MDIO_MMD_AN, phydev->addr); -	if (val < 0) -		return val; - -	/* Enable general EEE feature at the PHY level */ -	val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; - -	phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, -			       MDIO_MMD_AN, phydev->addr, val); - -	/* Advertise supported modes */ -	val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, -				    MDIO_MMD_AN, phydev->addr); - -	val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); -	phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, -			       MDIO_MMD_AN, phydev->addr, val); - -	return 0; -} -  static int bcm7xxx_28nm_config_init(struct phy_device *phydev)  {  	u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags); @@ -273,11 +199,11 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)  	if (ret)  		return ret; -	ret = bcm7xxx_eee_enable(phydev); +	ret = bcm_phy_enable_eee(phydev);  	if (ret)  		return ret; -	return bcm7xxx_apd_enable(phydev); +	return bcm_phy_enable_apd(phydev, true);  }  static int bcm7xxx_28nm_resume(struct phy_device *phydev) diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 9c71295f2fef..07a6119121c3 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -14,6 +14,7 @@   *	2 of the License, or (at your option) any later version.   */ +#include "bcm-phy-lib.h"  #include <linux/module.h>  #include <linux/phy.h>  #include <linux/brcmphy.h> @@ -29,39 +30,6 @@ MODULE_DESCRIPTION("Broadcom PHY driver");  MODULE_AUTHOR("Maciej W. Rozycki");  MODULE_LICENSE("GPL"); -/* Indirect register access functions for the Expansion Registers */ -static int bcm54xx_exp_read(struct phy_device *phydev, u16 regnum) -{ -	int val; - -	val = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum); -	if (val < 0) -		return val; - -	val = phy_read(phydev, MII_BCM54XX_EXP_DATA); - -	/* Restore default value.  It's O.K. if this write fails. */ -	phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); - -	return val; -} - -static int bcm54xx_exp_write(struct phy_device *phydev, u16 regnum, u16 val) -{ -	int ret; - -	ret = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum); -	if (ret < 0) -		return ret; - -	ret = phy_write(phydev, MII_BCM54XX_EXP_DATA, val); - -	/* Restore default value.  It's O.K. if this write fails. */ -	phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); - -	return ret; -} -  static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)  {  	return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); @@ -72,28 +40,28 @@ static int bcm50610_a0_workaround(struct phy_device *phydev)  {  	int err; -	err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH0, +	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,  				MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |  				MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);  	if (err < 0)  		return err; -	err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH3, -					MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ); +	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3, +				MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);  	if (err < 0)  		return err; -	err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, +	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,  				MII_BCM54XX_EXP_EXP75_VDACCTRL);  	if (err < 0)  		return err; -	err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP96, +	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,  				MII_BCM54XX_EXP_EXP96_MYST);  	if (err < 0)  		return err; -	err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP97, +	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,  				MII_BCM54XX_EXP_EXP97_MYST);  	return err; @@ -114,7 +82,7 @@ static int bcm54xx_phydsp_config(struct phy_device *phydev)  	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||  	    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {  		/* Clear bit 9 to fix a phy interop issue. */ -		err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08, +		err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,  					MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);  		if (err < 0)  			goto error; @@ -129,12 +97,12 @@ static int bcm54xx_phydsp_config(struct phy_device *phydev)  	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {  		int val; -		val = bcm54xx_exp_read(phydev, MII_BCM54XX_EXP_EXP75); +		val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);  		if (val < 0)  			goto error;  		val |= MII_BCM54XX_EXP_EXP75_CM_OSC; -		err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, val); +		err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);  	}  error: @@ -159,7 +127,7 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)  	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M)  		return; -	val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3); +	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);  	if (val < 0)  		return; @@ -190,9 +158,9 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)  		val |= BCM54XX_SHD_SCR3_TRDDAPD;  	if (orig != val) -		bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val); +		bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); -	val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD); +	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);  	if (val < 0)  		return; @@ -204,7 +172,7 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)  		val &= ~BCM54XX_SHD_APD_EN;  	if (orig != val) -		bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val); +		bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);  }  static int bcm54xx_config_init(struct phy_device *phydev) @@ -232,7 +200,7 @@ static int bcm54xx_config_init(struct phy_device *phydev)  	if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||  	     BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&  	    (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE)) -		bcm54xx_shadow_write(phydev, BCM54XX_SHD_RGMII_MODE, 0); +		bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);  	if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) ||  	    (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) || @@ -254,8 +222,8 @@ static int bcm5482_config_init(struct phy_device *phydev)  		/*  		 * Enable secondary SerDes and its use as an LED source  		 */ -		reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_SSD); -		bcm54xx_shadow_write(phydev, BCM5482_SHD_SSD, +		reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_SSD); +		bcm_phy_write_shadow(phydev, BCM5482_SHD_SSD,  				     reg |  				     BCM5482_SHD_SSD_LEDM |  				     BCM5482_SHD_SSD_EN); @@ -264,10 +232,10 @@ static int bcm5482_config_init(struct phy_device *phydev)  		 * Enable SGMII slave mode and auto-detection  		 */  		reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD; -		err = bcm54xx_exp_read(phydev, reg); +		err = bcm_phy_read_exp(phydev, reg);  		if (err < 0)  			return err; -		err = bcm54xx_exp_write(phydev, reg, err | +		err = bcm_phy_write_exp(phydev, reg, err |  					BCM5482_SSD_SGMII_SLAVE_EN |  					BCM5482_SSD_SGMII_SLAVE_AD);  		if (err < 0) @@ -277,10 +245,10 @@ static int bcm5482_config_init(struct phy_device *phydev)  		 * Disable secondary SerDes powerdown  		 */  		reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD; -		err = bcm54xx_exp_read(phydev, reg); +		err = bcm_phy_read_exp(phydev, reg);  		if (err < 0)  			return err; -		err = bcm54xx_exp_write(phydev, reg, +		err = bcm_phy_write_exp(phydev, reg,  					err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);  		if (err < 0)  			return err; @@ -288,15 +256,15 @@ static int bcm5482_config_init(struct phy_device *phydev)  		/*  		 * Select 1000BASE-X register set (primary SerDes)  		 */ -		reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_MODE); -		bcm54xx_shadow_write(phydev, BCM5482_SHD_MODE, +		reg = bcm_phy_read_shadow(phydev, BCM5482_SHD_MODE); +		bcm_phy_write_shadow(phydev, BCM5482_SHD_MODE,  				     reg | BCM5482_SHD_MODE_1000BX);  		/*  		 * LED1=ACTIVITYLED, LED3=LINKSPD[2]  		 * (Use LED1 as secondary SerDes ACTIVITY LED)  		 */ -		bcm54xx_shadow_write(phydev, BCM5482_SHD_LEDS1, +		bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1,  			BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |  			BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2)); @@ -334,35 +302,6 @@ static int bcm5482_read_status(struct phy_device *phydev)  	return err;  } -static int bcm54xx_ack_interrupt(struct phy_device *phydev) -{ -	int reg; - -	/* Clear pending interrupts.  */ -	reg = phy_read(phydev, MII_BCM54XX_ISR); -	if (reg < 0) -		return reg; - -	return 0; -} - -static int bcm54xx_config_intr(struct phy_device *phydev) -{ -	int reg, err; - -	reg = phy_read(phydev, MII_BCM54XX_ECR); -	if (reg < 0) -		return reg; - -	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) -		reg &= ~MII_BCM54XX_ECR_IM; -	else -		reg |= MII_BCM54XX_ECR_IM; - -	err = phy_write(phydev, MII_BCM54XX_ECR, reg); -	return err; -} -  static int bcm5481_config_aneg(struct phy_device *phydev)  {  	int ret; @@ -519,8 +458,8 @@ static struct phy_driver broadcom_drivers[] = {  	.config_init	= bcm54xx_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm54xx_ack_interrupt, -	.config_intr	= bcm54xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	.phy_id		= PHY_ID_BCM5421, @@ -532,8 +471,8 @@ static struct phy_driver broadcom_drivers[] = {  	.config_init	= bcm54xx_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm54xx_ack_interrupt, -	.config_intr	= bcm54xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	.phy_id		= PHY_ID_BCM5461, @@ -545,8 +484,8 @@ static struct phy_driver broadcom_drivers[] = {  	.config_init	= bcm54xx_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm54xx_ack_interrupt, -	.config_intr	= bcm54xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	.phy_id		= PHY_ID_BCM54616S, @@ -558,8 +497,8 @@ static struct phy_driver broadcom_drivers[] = {  	.config_init	= bcm54xx_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm54xx_ack_interrupt, -	.config_intr	= bcm54xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	.phy_id		= PHY_ID_BCM5464, @@ -571,8 +510,8 @@ static struct phy_driver broadcom_drivers[] = {  	.config_init	= bcm54xx_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm54xx_ack_interrupt, -	.config_intr	= bcm54xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	.phy_id		= PHY_ID_BCM5481, @@ -584,8 +523,8 @@ static struct phy_driver broadcom_drivers[] = {  	.config_init	= bcm54xx_config_init,  	.config_aneg	= bcm5481_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm54xx_ack_interrupt, -	.config_intr	= bcm54xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	.phy_id		= PHY_ID_BCM5482, @@ -597,8 +536,8 @@ static struct phy_driver broadcom_drivers[] = {  	.config_init	= bcm5482_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= bcm5482_read_status, -	.ack_interrupt	= bcm54xx_ack_interrupt, -	.config_intr	= bcm54xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	.phy_id		= PHY_ID_BCM50610, @@ -610,8 +549,8 @@ static struct phy_driver broadcom_drivers[] = {  	.config_init	= bcm54xx_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm54xx_ack_interrupt, -	.config_intr	= bcm54xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	.phy_id		= PHY_ID_BCM50610M, @@ -623,8 +562,8 @@ static struct phy_driver broadcom_drivers[] = {  	.config_init	= bcm54xx_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm54xx_ack_interrupt, -	.config_intr	= bcm54xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	.phy_id		= PHY_ID_BCM57780, @@ -636,8 +575,8 @@ static struct phy_driver broadcom_drivers[] = {  	.config_init	= bcm54xx_config_init,  	.config_aneg	= genphy_config_aneg,  	.read_status	= genphy_read_status, -	.ack_interrupt	= bcm54xx_ack_interrupt, -	.config_intr	= bcm54xx_config_intr, +	.ack_interrupt	= bcm_phy_ack_intr, +	.config_intr	= bcm_phy_config_intr,  	.driver		= { .owner = THIS_MODULE },  }, {  	.phy_id		= PHY_ID_BCMAC131, diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 185b03c08e16..47b711739ba9 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -20,6 +20,7 @@  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/crc32.h>  #include <linux/ethtool.h>  #include <linux/kernel.h>  #include <linux/list.h> @@ -36,8 +37,6 @@  #define DP83640_PHY_ID	0x20005ce1  #define PAGESEL		0x13 -#define LAYER4		0x02 -#define LAYER2		0x01  #define MAX_RXTS	64  #define N_EXT_TS	6  #define N_PER_OUT	7 @@ -68,6 +67,8 @@  /* phyter seems to miss the mark by 16 ns */  #define ADJTIME_FIX	16 +#define SKB_TIMESTAMP_TIMEOUT	2 /* jiffies */ +  #if defined(__BIG_ENDIAN)  #define ENDIAN_FLAG	0  #elif defined(__LITTLE_ENDIAN) @@ -110,7 +111,7 @@ struct dp83640_private {  	struct list_head list;  	struct dp83640_clock *clock;  	struct phy_device *phydev; -	struct work_struct ts_work; +	struct delayed_work ts_work;  	int hwts_tx_en;  	int hwts_rx_en;  	int layer; @@ -284,7 +285,7 @@ static void phy2rxts(struct phy_rxts *p, struct rxts *rxts)  	rxts->seqid = p->seqid;  	rxts->msgtype = (p->msgtype >> 12) & 0xf;  	rxts->hash = p->msgtype & 0x0fff; -	rxts->tmo = jiffies + 2; +	rxts->tmo = jiffies + SKB_TIMESTAMP_TIMEOUT;  }  static u64 phy2txts(struct phy_txts *p) @@ -787,9 +788,12 @@ static int decode_evnt(struct dp83640_private *dp83640,  	return parsed;  } +#define DP83640_PACKET_HASH_OFFSET	20 +#define DP83640_PACKET_HASH_LEN		10 +  static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts)  { -	u16 *seqid; +	u16 *seqid, hash;  	unsigned int offset = 0;  	u8 *msgtype, *data = skb_mac_header(skb); @@ -819,11 +823,19 @@ static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts)  		msgtype = data + offset + OFF_PTP_CONTROL;  	else  		msgtype = data + offset; +	if (rxts->msgtype != (*msgtype & 0xf)) +		return 0;  	seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID); +	if (rxts->seqid != ntohs(*seqid)) +		return 0; + +	hash = ether_crc(DP83640_PACKET_HASH_LEN, +			 data + offset + DP83640_PACKET_HASH_OFFSET) >> 20; +	if (rxts->hash != hash) +		return 0; -	return rxts->msgtype == (*msgtype & 0xf) && -		rxts->seqid   == ntohs(*seqid); +	return 1;  }  static void decode_rxts(struct dp83640_private *dp83640, @@ -1103,7 +1115,7 @@ static int dp83640_probe(struct phy_device *phydev)  		goto no_memory;  	dp83640->phydev = phydev; -	INIT_WORK(&dp83640->ts_work, rx_timestamp_work); +	INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work);  	INIT_LIST_HEAD(&dp83640->rxts);  	INIT_LIST_HEAD(&dp83640->rxpool); @@ -1150,7 +1162,7 @@ static void dp83640_remove(struct phy_device *phydev)  		return;  	enable_status_frames(phydev, false); -	cancel_work_sync(&dp83640->ts_work); +	cancel_delayed_work_sync(&dp83640->ts_work);  	skb_queue_purge(&dp83640->rx_queue);  	skb_queue_purge(&dp83640->tx_queue); @@ -1282,29 +1294,29 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)  	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:  	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:  		dp83640->hwts_rx_en = 1; -		dp83640->layer = LAYER4; -		dp83640->version = 1; +		dp83640->layer = PTP_CLASS_L4; +		dp83640->version = PTP_CLASS_V1;  		break;  	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:  	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:  	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:  		dp83640->hwts_rx_en = 1; -		dp83640->layer = LAYER4; -		dp83640->version = 2; +		dp83640->layer = PTP_CLASS_L4; +		dp83640->version = PTP_CLASS_V2;  		break;  	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:  	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:  	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:  		dp83640->hwts_rx_en = 1; -		dp83640->layer = LAYER2; -		dp83640->version = 2; +		dp83640->layer = PTP_CLASS_L2; +		dp83640->version = PTP_CLASS_V2;  		break;  	case HWTSTAMP_FILTER_PTP_V2_EVENT:  	case HWTSTAMP_FILTER_PTP_V2_SYNC:  	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:  		dp83640->hwts_rx_en = 1; -		dp83640->layer = LAYER4|LAYER2; -		dp83640->version = 2; +		dp83640->layer = PTP_CLASS_L4 | PTP_CLASS_L2; +		dp83640->version = PTP_CLASS_V2;  		break;  	default:  		return -ERANGE; @@ -1313,11 +1325,11 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)  	txcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT;  	rxcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT; -	if (dp83640->layer & LAYER2) { +	if (dp83640->layer & PTP_CLASS_L2) {  		txcfg0 |= TX_L2_EN;  		rxcfg0 |= RX_L2_EN;  	} -	if (dp83640->layer & LAYER4) { +	if (dp83640->layer & PTP_CLASS_L4) {  		txcfg0 |= TX_IPV6_EN | TX_IPV4_EN;  		rxcfg0 |= RX_IPV6_EN | RX_IPV4_EN;  	} @@ -1344,7 +1356,7 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)  static void rx_timestamp_work(struct work_struct *work)  {  	struct dp83640_private *dp83640 = -		container_of(work, struct dp83640_private, ts_work); +		container_of(work, struct dp83640_private, ts_work.work);  	struct sk_buff *skb;  	/* Deliver expired packets. */ @@ -1361,7 +1373,7 @@ static void rx_timestamp_work(struct work_struct *work)  	}  	if (!skb_queue_empty(&dp83640->rx_queue)) -		schedule_work(&dp83640->ts_work); +		schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT);  }  static bool dp83640_rxtstamp(struct phy_device *phydev, @@ -1383,7 +1395,11 @@ static bool dp83640_rxtstamp(struct phy_device *phydev,  	if (!dp83640->hwts_rx_en)  		return false; +	if ((type & dp83640->version) == 0 || (type & dp83640->layer) == 0) +		return false; +  	spin_lock_irqsave(&dp83640->rx_lock, flags); +	prune_rx_ts(dp83640);  	list_for_each_safe(this, next, &dp83640->rxts) {  		rxts = list_entry(this, struct rxts, list);  		if (match(skb, type, rxts)) { @@ -1400,9 +1416,11 @@ static bool dp83640_rxtstamp(struct phy_device *phydev,  	if (!shhwtstamps) {  		skb_info->ptp_type = type; -		skb_info->tmo = jiffies + 2; +		skb_info->tmo = jiffies + SKB_TIMESTAMP_TIMEOUT;  		skb_queue_tail(&dp83640->rx_queue, skb); -		schedule_work(&dp83640->ts_work); +		schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT); +	} else { +		netif_rx_ni(skb);  	}  	return true; diff --git a/drivers/net/phy/dp83848.c b/drivers/net/phy/dp83848.c new file mode 100644 index 000000000000..5ce9bef54468 --- /dev/null +++ b/drivers/net/phy/dp83848.c @@ -0,0 +1,99 @@ +/* + * Driver for the Texas Instruments DP83848 PHY + * + * Copyright (C) 2015 Texas Instruments Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/phy.h> + +#define DP83848_PHY_ID			0x20005c90 + +/* Registers */ +#define DP83848_MICR			0x11 +#define DP83848_MISR			0x12 + +/* MICR Register Fields */ +#define DP83848_MICR_INT_OE		BIT(0) /* Interrupt Output Enable */ +#define DP83848_MICR_INTEN		BIT(1) /* Interrupt Enable */ + +/* MISR Register Fields */ +#define DP83848_MISR_RHF_INT_EN		BIT(0) /* Receive Error Counter */ +#define DP83848_MISR_FHF_INT_EN		BIT(1) /* False Carrier Counter */ +#define DP83848_MISR_ANC_INT_EN		BIT(2) /* Auto-negotiation complete */ +#define DP83848_MISR_DUP_INT_EN		BIT(3) /* Duplex Status */ +#define DP83848_MISR_SPD_INT_EN		BIT(4) /* Speed status */ +#define DP83848_MISR_LINK_INT_EN	BIT(5) /* Link status */ +#define DP83848_MISR_ED_INT_EN		BIT(6) /* Energy detect */ +#define DP83848_MISR_LQM_INT_EN		BIT(7) /* Link Quality Monitor */ + +static int dp83848_ack_interrupt(struct phy_device *phydev) +{ +	int err = phy_read(phydev, DP83848_MISR); + +	return err < 0 ? err : 0; +} + +static int dp83848_config_intr(struct phy_device *phydev) +{ +	int err; + +	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +		err = phy_write(phydev, DP83848_MICR, +				DP83848_MICR_INT_OE | +				DP83848_MICR_INTEN); +		if (err < 0) +			return err; + +		return phy_write(phydev, DP83848_MISR, +				 DP83848_MISR_ANC_INT_EN | +				 DP83848_MISR_DUP_INT_EN | +				 DP83848_MISR_SPD_INT_EN | +				 DP83848_MISR_LINK_INT_EN); +	} + +	return phy_write(phydev, DP83848_MICR, 0x0); +} + +static struct mdio_device_id __maybe_unused dp83848_tbl[] = { +	{ DP83848_PHY_ID, 0xfffffff0 }, +	{ } +}; +MODULE_DEVICE_TABLE(mdio, dp83848_tbl); + +static struct phy_driver dp83848_driver[] = { +	{ +		.phy_id		= DP83848_PHY_ID, +		.phy_id_mask	= 0xfffffff0, +		.name		= "TI DP83848", +		.features	= PHY_BASIC_FEATURES, +		.flags		= PHY_HAS_INTERRUPT, + +		.soft_reset	= genphy_soft_reset, +		.config_init	= genphy_config_init, +		.suspend	= genphy_suspend, +		.resume		= genphy_resume, +		.config_aneg	= genphy_config_aneg, +		.read_status	= genphy_read_status, + +		/* IRQ related */ +		.ack_interrupt	= dp83848_ack_interrupt, +		.config_intr	= dp83848_config_intr, + +		.driver		= { .owner = THIS_MODULE, }, +	}, +}; +module_phy_driver(dp83848_driver); + +MODULE_DESCRIPTION("Texas Instruments DP83848 PHY driver"); +MODULE_AUTHOR("Andrew F. Davis <[email protected]"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 5de8d5827536..0240552b50f3 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1154,6 +1154,21 @@ static struct phy_driver marvell_drivers[] = {  		.driver = { .owner = THIS_MODULE },  	},  	{ +		.phy_id = MARVELL_PHY_ID_88E1540, +		.phy_id_mask = MARVELL_PHY_ID_MASK, +		.name = "Marvell 88E1540", +		.features = PHY_GBIT_FEATURES, +		.flags = PHY_HAS_INTERRUPT, +		.config_aneg = &m88e1510_config_aneg, +		.read_status = &marvell_read_status, +		.ack_interrupt = &marvell_ack_interrupt, +		.config_intr = &marvell_config_intr, +		.did_interrupt = &m88e1121_did_interrupt, +		.resume = &genphy_resume, +		.suspend = &genphy_suspend, +		.driver = { .owner = THIS_MODULE }, +	}, +	{  		.phy_id = MARVELL_PHY_ID_88E3016,  		.phy_id_mask = MARVELL_PHY_ID_MASK,  		.name = "Marvell 88E3016", @@ -1186,6 +1201,7 @@ static struct mdio_device_id __maybe_unused marvell_tbl[] = {  	{ MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },  	{ MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },  	{ MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK }, +	{ MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK },  	{ MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },  	{ }  }; diff --git a/drivers/net/phy/mdio-bcm-iproc.c b/drivers/net/phy/mdio-bcm-iproc.c new file mode 100644 index 000000000000..c0b4e65267af --- /dev/null +++ b/drivers/net/phy/mdio-bcm-iproc.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_mdio.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <linux/sched.h> + +#define IPROC_GPHY_MDCDIV    0x1a + +#define MII_CTRL_OFFSET      0x000 + +#define MII_CTRL_DIV_SHIFT   0 +#define MII_CTRL_PRE_SHIFT   7 +#define MII_CTRL_BUSY_SHIFT  8 + +#define MII_DATA_OFFSET      0x004 +#define MII_DATA_MASK        0xffff +#define MII_DATA_TA_SHIFT    16 +#define MII_DATA_TA_VAL      2 +#define MII_DATA_RA_SHIFT    18 +#define MII_DATA_PA_SHIFT    23 +#define MII_DATA_OP_SHIFT    28 +#define MII_DATA_OP_WRITE    1 +#define MII_DATA_OP_READ     2 +#define MII_DATA_SB_SHIFT    30 + +struct iproc_mdio_priv { +	struct mii_bus *mii_bus; +	void __iomem *base; +}; + +static inline int iproc_mdio_wait_for_idle(void __iomem *base) +{ +	u32 val; +	unsigned int timeout = 1000; /* loop for 1s */ + +	do { +		val = readl(base + MII_CTRL_OFFSET); +		if ((val & BIT(MII_CTRL_BUSY_SHIFT)) == 0) +			return 0; + +		usleep_range(1000, 2000); +	} while (timeout--); + +	return -ETIMEDOUT; +} + +static inline void iproc_mdio_config_clk(void __iomem *base) +{ +	u32 val; + +	val = (IPROC_GPHY_MDCDIV << MII_CTRL_DIV_SHIFT) | +		  BIT(MII_CTRL_PRE_SHIFT); +	writel(val, base + MII_CTRL_OFFSET); +} + +static int iproc_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ +	struct iproc_mdio_priv *priv = bus->priv; +	u32 cmd; +	int rc; + +	rc = iproc_mdio_wait_for_idle(priv->base); +	if (rc) +		return rc; + +	iproc_mdio_config_clk(priv->base); + +	/* Prepare the read operation */ +	cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | +		(reg << MII_DATA_RA_SHIFT) | +		(phy_id << MII_DATA_PA_SHIFT) | +		BIT(MII_DATA_SB_SHIFT) | +		(MII_DATA_OP_READ << MII_DATA_OP_SHIFT); + +	writel(cmd, priv->base + MII_DATA_OFFSET); + +	rc = iproc_mdio_wait_for_idle(priv->base); +	if (rc) +		return rc; + +	cmd = readl(priv->base + MII_DATA_OFFSET) & MII_DATA_MASK; + +	return cmd; +} + +static int iproc_mdio_write(struct mii_bus *bus, int phy_id, +			    int reg, u16 val) +{ +	struct iproc_mdio_priv *priv = bus->priv; +	u32 cmd; +	int rc; + +	rc = iproc_mdio_wait_for_idle(priv->base); +	if (rc) +		return rc; + +	iproc_mdio_config_clk(priv->base); + +	/* Prepare the write operation */ +	cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) | +		(reg << MII_DATA_RA_SHIFT) | +		(phy_id << MII_DATA_PA_SHIFT) | +		BIT(MII_DATA_SB_SHIFT) | +		(MII_DATA_OP_WRITE << MII_DATA_OP_SHIFT) | +		((u32)(val) & MII_DATA_MASK); + +	writel(cmd, priv->base + MII_DATA_OFFSET); + +	rc = iproc_mdio_wait_for_idle(priv->base); +	if (rc) +		return rc; + +	return 0; +} + +static int iproc_mdio_probe(struct platform_device *pdev) +{ +	struct iproc_mdio_priv *priv; +	struct mii_bus *bus; +	struct resource *res; +	int rc; + +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); +	if (!priv) +		return -ENOMEM; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	priv->base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(priv->base)) { +		dev_err(&pdev->dev, "failed to ioremap register\n"); +		return PTR_ERR(priv->base); +	} + +	priv->mii_bus = mdiobus_alloc(); +	if (!priv->mii_bus) { +		dev_err(&pdev->dev, "MDIO bus alloc failed\n"); +		return -ENOMEM; +	} + +	bus = priv->mii_bus; +	bus->priv = priv; +	bus->name = "iProc MDIO bus"; +	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); +	bus->parent = &pdev->dev; +	bus->read = iproc_mdio_read; +	bus->write = iproc_mdio_write; + +	rc = of_mdiobus_register(bus, pdev->dev.of_node); +	if (rc) { +		dev_err(&pdev->dev, "MDIO bus registration failed\n"); +		goto err_iproc_mdio; +	} + +	platform_set_drvdata(pdev, priv); + +	dev_info(&pdev->dev, "Broadcom iProc MDIO bus at 0x%p\n", priv->base); + +	return 0; + +err_iproc_mdio: +	mdiobus_free(bus); +	return rc; +} + +static int iproc_mdio_remove(struct platform_device *pdev) +{ +	struct iproc_mdio_priv *priv = platform_get_drvdata(pdev); + +	mdiobus_unregister(priv->mii_bus); +	mdiobus_free(priv->mii_bus); + +	return 0; +} + +static const struct of_device_id iproc_mdio_of_match[] = { +	{ .compatible = "brcm,iproc-mdio", }, +	{ /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, iproc_mdio_of_match); + +static struct platform_driver iproc_mdio_driver = { +	.driver = { +		.name = "iproc-mdio", +		.of_match_table = iproc_mdio_of_match, +	}, +	.probe = iproc_mdio_probe, +	.remove = iproc_mdio_remove, +}; + +module_platform_driver(iproc_mdio_driver); + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:iproc-mdio"); diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 3bc9f03349f3..95f51d7267b3 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -25,7 +25,7 @@  #include <linux/interrupt.h>  #include <linux/platform_device.h>  #include <linux/gpio.h> -#include <linux/mdio-gpio.h> +#include <linux/platform_data/mdio-gpio.h>  #include <linux/of_gpio.h>  #include <linux/of_mdio.h> diff --git a/drivers/net/phy/mdio-mux-mmioreg.c b/drivers/net/phy/mdio-mux-mmioreg.c index 2377c1341172..7fde454fbc4f 100644 --- a/drivers/net/phy/mdio-mux-mmioreg.c +++ b/drivers/net/phy/mdio-mux-mmioreg.c @@ -113,12 +113,14 @@ static int mdio_mux_mmioreg_probe(struct platform_device *pdev)  		if (!iprop || len != sizeof(uint32_t)) {  			dev_err(&pdev->dev, "mdio-mux child node %s is "  				"missing a 'reg' property\n", np2->full_name); +			of_node_put(np2);  			return -ENODEV;  		}  		if (be32_to_cpup(iprop) & ~s->mask) {  			dev_err(&pdev->dev, "mdio-mux child node %s has "  				"a 'reg' value with unmasked bits\n",  				np2->full_name); +			of_node_put(np2);  			return -ENODEV;  		}  	} diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index 280c7c311f72..908e8d486342 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -144,6 +144,7 @@ int mdio_mux_init(struct device *dev,  			dev_err(dev,  				"Error: Failed to allocate memory for child\n");  			ret_val = -ENOMEM; +			of_node_put(child_bus_node);  			break;  		}  		cb->bus_number = v; diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 12f44c53cc8e..88cb4592b6fb 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -372,6 +372,33 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)  EXPORT_SYMBOL(mdiobus_scan);  /** + * mdiobus_read_nested - Nested version of the mdiobus_read function + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to read + * + * In case of nested MDIO bus access avoid lockdep false positives by + * using mutex_lock_nested(). + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ +int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum) +{ +	int retval; + +	BUG_ON(in_interrupt()); + +	mutex_lock_nested(&bus->mdio_lock, SINGLE_DEPTH_NESTING); +	retval = bus->read(bus, addr, regnum); +	mutex_unlock(&bus->mdio_lock); + +	return retval; +} +EXPORT_SYMBOL(mdiobus_read_nested); + +/**   * mdiobus_read - Convenience function for reading a given MII mgmt register   * @bus: the mii_bus struct   * @addr: the phy address @@ -396,6 +423,34 @@ int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)  EXPORT_SYMBOL(mdiobus_read);  /** + * mdiobus_write_nested - Nested version of the mdiobus_write function + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to write + * @val: value to write to @regnum + * + * In case of nested MDIO bus access avoid lockdep false positives by + * using mutex_lock_nested(). + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ +int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val) +{ +	int err; + +	BUG_ON(in_interrupt()); + +	mutex_lock_nested(&bus->mdio_lock, SINGLE_DEPTH_NESTING); +	err = bus->write(bus, addr, regnum, val); +	mutex_unlock(&bus->mdio_lock); + +	return err; +} +EXPORT_SYMBOL(mdiobus_write_nested); + +/**   * mdiobus_write - Convenience function for writing a given MII mgmt register   * @bus: the mii_bus struct   * @addr: the phy address diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 499185eaf413..cf6312fafea5 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -514,6 +514,27 @@ static int ksz8873mll_read_status(struct phy_device *phydev)  	return 0;  } +static int ksz9031_read_status(struct phy_device *phydev) +{ +	int err; +	int regval; + +	err = genphy_read_status(phydev); +	if (err) +		return err; + +	/* Make sure the PHY is not broken. Read idle error count, +	 * and reset the PHY if it is maxed out. +	 */ +	regval = phy_read(phydev, MII_STAT1000); +	if ((regval & 0xFF) == 0xFF) { +		phy_init_hw(phydev); +		phydev->link = 0; +	} + +	return 0; +} +  static int ksz8873mll_config_aneg(struct phy_device *phydev)  {  	return 0; @@ -772,7 +793,7 @@ static struct phy_driver ksphy_driver[] = {  	.driver_data	= &ksz9021_type,  	.config_init	= ksz9031_config_init,  	.config_aneg	= genphy_config_aneg, -	.read_status	= genphy_read_status, +	.read_status	= ksz9031_read_status,  	.ack_interrupt	= kszphy_ack_interrupt,  	.config_intr	= kszphy_config_intr,  	.suspend	= genphy_suspend, diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index adb48abafc87..48ce6ef400fe 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -863,6 +863,9 @@ void phy_state_machine(struct work_struct *work)  			needs_aneg = true;  		break;  	case PHY_NOLINK: +		if (phy_interrupt_is_valid(phydev)) +			break; +  		err = phy_read_status(phydev);  		if (err)  			break; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index f761288abe66..0bfbabad4431 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -205,6 +205,37 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,  }  EXPORT_SYMBOL(phy_device_create); +/* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers. + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * @dev_addr: MMD address in the PHY. + * @devices_in_package: where to store the devices in package information. + * + * Description: reads devices in package registers of a MMD at @dev_addr + * from PHY at @addr on @bus. + * + * Returns: 0 on success, -EIO on failure. + */ +static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, +				   u32 *devices_in_package) +{ +	int phy_reg, reg_addr; + +	reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS2; +	phy_reg = mdiobus_read(bus, addr, reg_addr); +	if (phy_reg < 0) +		return -EIO; +	*devices_in_package = (phy_reg & 0xffff) << 16; + +	reg_addr = MII_ADDR_C45 | dev_addr << 16 | MDIO_DEVS1; +	phy_reg = mdiobus_read(bus, addr, reg_addr); +	if (phy_reg < 0) +		return -EIO; +	*devices_in_package |= (phy_reg & 0xffff); + +	return 0; +} +  /**   * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs.   * @bus: the target MII bus @@ -223,38 +254,31 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id,  	int phy_reg;  	int i, reg_addr;  	const int num_ids = ARRAY_SIZE(c45_ids->device_ids); +	u32 *devs = &c45_ids->devices_in_package; -	/* Find first non-zero Devices In package.  Device -	 * zero is reserved, so don't probe it. +	/* Find first non-zero Devices In package. Device zero is reserved +	 * for 802.3 c45 complied PHYs, so don't probe it at first.  	 */ -	for (i = 1; -	     i < num_ids && c45_ids->devices_in_package == 0; -	     i++) { -retry:		reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS2; -		phy_reg = mdiobus_read(bus, addr, reg_addr); +	for (i = 1; i < num_ids && *devs == 0; i++) { +		phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, devs);  		if (phy_reg < 0)  			return -EIO; -		c45_ids->devices_in_package = (phy_reg & 0xffff) << 16; -		reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS1; -		phy_reg = mdiobus_read(bus, addr, reg_addr); -		if (phy_reg < 0) -			return -EIO; -		c45_ids->devices_in_package |= (phy_reg & 0xffff); - -		if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) { -			if (i) { -				/*  If mostly Fs, there is no device there, -				 *  then let's continue to probe more, as some -				 *  10G PHYs have zero Devices In package, -				 *  e.g. Cortina CS4315/CS4340 PHY. -				 */ -				i = 0; -				goto retry; -			} else { -				/* no device there, let's get out of here */ +		if ((*devs & 0x1fffffff) == 0x1fffffff) { +			/*  If mostly Fs, there is no device there, +			 *  then let's continue to probe more, as some +			 *  10G PHYs have zero Devices In package, +			 *  e.g. Cortina CS4315/CS4340 PHY. +			 */ +			phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, devs); +			if (phy_reg < 0) +				return -EIO; +			/* no device there, let's get out of here */ +			if ((*devs & 0x1fffffff) == 0x1fffffff) {  				*phy_id = 0xffffffff;  				return 0; +			} else { +				break;  			}  		}  	} @@ -1239,6 +1263,44 @@ static int gen10g_resume(struct phy_device *phydev)  	return 0;  } +static int __set_phy_supported(struct phy_device *phydev, u32 max_speed) +{ +	/* The default values for phydev->supported are provided by the PHY +	 * driver "features" member, we want to reset to sane defaults first +	 * before supporting higher speeds. +	 */ +	phydev->supported &= PHY_DEFAULT_FEATURES; + +	switch (max_speed) { +	default: +		return -ENOTSUPP; +	case SPEED_1000: +		phydev->supported |= PHY_1000BT_FEATURES; +		/* fall through */ +	case SPEED_100: +		phydev->supported |= PHY_100BT_FEATURES; +		/* fall through */ +	case SPEED_10: +		phydev->supported |= PHY_10BT_FEATURES; +	} + +	return 0; +} + +int phy_set_max_speed(struct phy_device *phydev, u32 max_speed) +{ +	int err; + +	err = __set_phy_supported(phydev, max_speed); +	if (err) +		return err; + +	phydev->advertising = phydev->supported; + +	return 0; +} +EXPORT_SYMBOL(phy_set_max_speed); +  static void of_set_phy_supported(struct phy_device *phydev)  {  	struct device_node *node = phydev->dev.of_node; @@ -1250,25 +1312,8 @@ static void of_set_phy_supported(struct phy_device *phydev)  	if (!node)  		return; -	if (!of_property_read_u32(node, "max-speed", &max_speed)) { -		/* The default values for phydev->supported are provided by the PHY -		 * driver "features" member, we want to reset to sane defaults fist -		 * before supporting higher speeds. -		 */ -		phydev->supported &= PHY_DEFAULT_FEATURES; - -		switch (max_speed) { -		default: -			return; - -		case SPEED_1000: -			phydev->supported |= PHY_1000BT_FEATURES; -		case SPEED_100: -			phydev->supported |= PHY_100BT_FEATURES; -		case SPEED_10: -			phydev->supported |= PHY_10BT_FEATURES; -		} -	} +	if (!of_property_read_u32(node, "max-speed", &max_speed)) +		__set_phy_supported(phydev, max_speed);  }  /** diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 70b08958763a..dc2da8770918 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -43,16 +43,25 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev)  static int smsc_phy_config_init(struct phy_device *phydev)  { +	int __maybe_unused len; +	struct device *dev __maybe_unused = &phydev->dev; +	struct device_node *of_node __maybe_unused = dev->of_node;  	int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); +	int enable_energy = 1;  	if (rc < 0)  		return rc; -	/* Enable energy detect mode for this SMSC Transceivers */ -	rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, -		       rc | MII_LAN83C185_EDPWRDOWN); -	if (rc < 0) -		return rc; +	if (of_find_property(of_node, "smsc,disable-energy-detect", &len)) +		enable_energy = 0; + +	if (enable_energy) { +		/* Enable energy detect mode for this SMSC Transceivers */ +		rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, +			       rc | MII_LAN83C185_EDPWRDOWN); +		if (rc < 0) +			return rc; +	}  	return smsc_phy_ack_interrupt(phydev);  } diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c index f091d691cf6f..c72c42206850 100644 --- a/drivers/net/phy/spi_ks8995.c +++ b/drivers/net/phy/spi_ks8995.c @@ -343,7 +343,6 @@ static int ks8995_remove(struct spi_device *spi)  static struct spi_driver ks8995_driver = {  	.driver = {  		.name	    = "spi-ks8995", -		.owner	   = THIS_MODULE,  	},  	.probe	  = ks8995_probe,  	.remove	  = ks8995_remove, diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c index 91e1bec6079f..07463fcca212 100644 --- a/drivers/net/phy/teranetics.c +++ b/drivers/net/phy/teranetics.c @@ -112,20 +112,7 @@ static struct phy_driver teranetics_driver[] = {  },  }; -static int __init teranetics_init(void) -{ -	return phy_drivers_register(teranetics_driver, -				    ARRAY_SIZE(teranetics_driver)); -} - -static void __exit teranetics_exit(void) -{ -	return phy_drivers_unregister(teranetics_driver, -				      ARRAY_SIZE(teranetics_driver)); -} - -module_init(teranetics_init); -module_exit(teranetics_exit); +module_phy_driver(teranetics_driver);  static struct mdio_device_id __maybe_unused teranetics_tbl[] = {  	{ PHY_ID_TN2020, 0xffffffff }, diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index 76cad712ddb2..dd295dbaa074 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -66,6 +66,7 @@  #define PHY_ID_VSC8244			0x000fc6c0  #define PHY_ID_VSC8514			0x00070670  #define PHY_ID_VSC8574			0x000704a0 +#define PHY_ID_VSC8601			0x00070420  #define PHY_ID_VSC8662			0x00070660  #define PHY_ID_VSC8221			0x000fc550  #define PHY_ID_VSC8211			0x000fc4b0 @@ -133,7 +134,8 @@ static int vsc82xx_config_intr(struct phy_device *phydev)  			(phydev->drv->phy_id == PHY_ID_VSC8234 ||  			 phydev->drv->phy_id == PHY_ID_VSC8244 ||  			 phydev->drv->phy_id == PHY_ID_VSC8514 || -			 phydev->drv->phy_id == PHY_ID_VSC8574) ? +			 phydev->drv->phy_id == PHY_ID_VSC8574 || +			 phydev->drv->phy_id == PHY_ID_VSC8601) ?  				MII_VSC8244_IMASK_MASK :  				MII_VSC8221_IMASK_MASK);  	else { @@ -272,6 +274,18 @@ static struct phy_driver vsc82xx_driver[] = {  	.config_intr    = &vsc82xx_config_intr,  	.driver         = { .owner = THIS_MODULE,},  }, { +	.phy_id         = PHY_ID_VSC8601, +	.name           = "Vitesse VSC8601", +	.phy_id_mask    = 0x000ffff0, +	.features       = PHY_GBIT_FEATURES, +	.flags          = PHY_HAS_INTERRUPT, +	.config_init    = &genphy_config_init, +	.config_aneg    = &genphy_config_aneg, +	.read_status    = &genphy_read_status, +	.ack_interrupt  = &vsc824x_ack_interrupt, +	.config_intr    = &vsc82xx_config_intr, +	.driver         = { .owner = THIS_MODULE,}, +}, {  	.phy_id         = PHY_ID_VSC8662,  	.name           = "Vitesse VSC8662",  	.phy_id_mask    = 0x000ffff0, |