aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/Kconfig28
-rw-r--r--drivers/net/phy/Makefile3
-rw-r--r--drivers/net/phy/aquantia.c15
-rw-r--r--drivers/net/phy/bcm-cygnus.c158
-rw-r--r--drivers/net/phy/bcm-phy-lib.c213
-rw-r--r--drivers/net/phy/bcm-phy-lib.h37
-rw-r--r--drivers/net/phy/bcm63xx.c38
-rw-r--r--drivers/net/phy/bcm7xxx.c136
-rw-r--r--drivers/net/phy/broadcom.c149
-rw-r--r--drivers/net/phy/dp83640.c64
-rw-r--r--drivers/net/phy/mdio-bcm-iproc.c213
-rw-r--r--drivers/net/phy/mdio-gpio.c2
-rw-r--r--drivers/net/phy/mdio_bus.c55
-rw-r--r--drivers/net/phy/phy_device.c59
-rw-r--r--drivers/net/phy/teranetics.c15
15 files changed, 871 insertions, 314 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 436972b2a746..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.
@@ -228,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 b74822463930..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
@@ -39,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/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/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_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/phy_device.c b/drivers/net/phy/phy_device.c
index 0a76cc5d13da..0bfbabad4431 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1263,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;
@@ -1274,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/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 },