summaryrefslogtreecommitdiffstats
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/Kconfig21
-rw-r--r--drivers/net/phy/Makefile3
-rw-r--r--drivers/net/phy/adin.c6
-rw-r--r--drivers/net/phy/aquantia/aquantia.h1
-rw-r--r--drivers/net/phy/aquantia/aquantia_leds.c19
-rw-r--r--drivers/net/phy/aquantia/aquantia_main.c116
-rw-r--r--drivers/net/phy/bcm-phy-lib.c5
-rw-r--r--drivers/net/phy/dp83822.c31
-rw-r--r--drivers/net/phy/dp83869.c20
-rw-r--r--drivers/net/phy/icplus.c3
-rw-r--r--drivers/net/phy/intel-xway.c253
-rw-r--r--drivers/net/phy/marvell-88q2xxx.c124
-rw-r--r--drivers/net/phy/marvell.c26
-rw-r--r--drivers/net/phy/mediatek/Kconfig27
-rw-r--r--drivers/net/phy/mediatek/Makefile4
-rw-r--r--drivers/net/phy/mediatek/mtk-ge-soc.c (renamed from drivers/net/phy/mediatek-ge-soc.c)419
-rw-r--r--drivers/net/phy/mediatek/mtk-ge.c (renamed from drivers/net/phy/mediatek-ge.c)31
-rw-r--r--drivers/net/phy/mediatek/mtk-phy-lib.c270
-rw-r--r--drivers/net/phy/mediatek/mtk.h89
-rw-r--r--drivers/net/phy/micrel.c8
-rw-r--r--drivers/net/phy/microchip_t1.c233
-rw-r--r--drivers/net/phy/microchip_t1s.c300
-rw-r--r--drivers/net/phy/mscc/mscc_main.c3
-rw-r--r--drivers/net/phy/mxl-gpy.c227
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx.c36
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx.h1
-rw-r--r--drivers/net/phy/nxp-cbtx.c2
-rw-r--r--drivers/net/phy/phy-c45.c34
-rw-r--r--drivers/net/phy/phy-core.c52
-rw-r--r--drivers/net/phy/phy_device.c47
-rw-r--r--drivers/net/phy/phylink.c235
-rw-r--r--drivers/net/phy/qcom/qca83xx.c6
-rw-r--r--drivers/net/phy/realtek.c121
-rw-r--r--drivers/net/phy/sfp.c5
-rw-r--r--drivers/net/phy/smsc.c5
35 files changed, 2039 insertions, 744 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 01b235b3bb7e..15828f4710a9 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -266,22 +266,7 @@ config MAXLINEAR_GPHY
Support for the Maxlinear GPY115, GPY211, GPY212, GPY215,
GPY241, GPY245 PHYs.
-config MEDIATEK_GE_PHY
- tristate "MediaTek Gigabit Ethernet PHYs"
- help
- Supports the MediaTek Gigabit Ethernet PHYs.
-
-config MEDIATEK_GE_SOC_PHY
- tristate "MediaTek SoC Ethernet PHYs"
- depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST
- depends on NVMEM_MTK_EFUSE
- help
- Supports MediaTek SoC built-in Gigabit Ethernet PHYs.
-
- Include support for built-in Ethernet PHYs which are present in
- the MT7981 and MT7988 SoCs. These PHYs need calibration data
- present in the SoCs efuse and will dynamically calibrate VCM
- (common-mode voltage) during startup.
+source "drivers/net/phy/mediatek/Kconfig"
config MICREL_PHY
tristate "Micrel PHYs"
@@ -292,8 +277,8 @@ config MICREL_PHY
config MICROCHIP_T1S_PHY
tristate "Microchip 10BASE-T1S Ethernet PHYs"
help
- Currently supports the LAN8670/1/2 Rev.B1 and LAN8650/1 Rev.B0 Internal
- PHYs.
+ Currently supports the LAN8670/1/2 Rev.B1/C1/C2 and
+ LAN8650/1 Rev.B0/B1 Internal PHYs.
config MICROCHIP_PHY
tristate "Microchip PHYs"
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 90f886844381..e6145153e837 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -74,8 +74,7 @@ obj-$(CONFIG_MARVELL_PHY) += marvell.o
obj-$(CONFIG_MARVELL_88Q2XXX_PHY) += marvell-88q2xxx.o
obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o
obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o
-obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o
-obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mediatek-ge-soc.o
+obj-y += mediatek/
obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o
obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o
obj-$(CONFIG_MICREL_PHY) += micrel.o
diff --git a/drivers/net/phy/adin.c b/drivers/net/phy/adin.c
index 2e1a46e121d9..a2a862bae2ed 100644
--- a/drivers/net/phy/adin.c
+++ b/drivers/net/phy/adin.c
@@ -801,10 +801,8 @@ static void adin_get_strings(struct phy_device *phydev, u8 *data)
{
int i;
- for (i = 0; i < ARRAY_SIZE(adin_hw_stats); i++) {
- strscpy(&data[i * ETH_GSTRING_LEN],
- adin_hw_stats[i].string, ETH_GSTRING_LEN);
- }
+ for (i = 0; i < ARRAY_SIZE(adin_hw_stats); i++)
+ ethtool_puts(&data, adin_hw_stats[i].string);
}
static int adin_read_mmd_stat_regs(struct phy_device *phydev,
diff --git a/drivers/net/phy/aquantia/aquantia.h b/drivers/net/phy/aquantia/aquantia.h
index 2465345081f8..0c78bfabace5 100644
--- a/drivers/net/phy/aquantia/aquantia.h
+++ b/drivers/net/phy/aquantia/aquantia.h
@@ -177,6 +177,7 @@ static const struct aqr107_hw_stat aqr107_hw_stats[] = {
struct aqr107_priv {
u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
unsigned long leds_active_low;
+ unsigned long leds_active_high;
};
#if IS_REACHABLE(CONFIG_HWMON)
diff --git a/drivers/net/phy/aquantia/aquantia_leds.c b/drivers/net/phy/aquantia/aquantia_leds.c
index 201c8df93fad..00ad2313fed3 100644
--- a/drivers/net/phy/aquantia/aquantia_leds.c
+++ b/drivers/net/phy/aquantia/aquantia_leds.c
@@ -121,13 +121,13 @@ int aqr_phy_led_active_low_set(struct phy_device *phydev, int index, bool enable
{
return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_DRIVE(index),
VEND1_GLOBAL_LED_DRIVE_VDD,
- enable ? VEND1_GLOBAL_LED_DRIVE_VDD : 0);
+ enable ? 0 : VEND1_GLOBAL_LED_DRIVE_VDD);
}
int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes)
{
+ bool force_active_low = false, force_active_high = false;
struct aqr107_priv *priv = phydev->priv;
- bool active_low = false;
u32 mode;
if (index >= AQR_MAX_LEDS)
@@ -136,7 +136,10 @@ int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long
for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
switch (mode) {
case PHY_LED_ACTIVE_LOW:
- active_low = true;
+ force_active_low = true;
+ break;
+ case PHY_LED_ACTIVE_HIGH:
+ force_active_high = true;
break;
default:
return -EINVAL;
@@ -144,8 +147,14 @@ int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long
}
/* Save LED driver vdd state to restore on SW reset */
- if (active_low)
+ if (force_active_low)
priv->leds_active_low |= BIT(index);
- return aqr_phy_led_active_low_set(phydev, index, active_low);
+ if (force_active_high)
+ priv->leds_active_high |= BIT(index);
+
+ if (force_active_high || force_active_low)
+ return aqr_phy_led_active_low_set(phydev, index, force_active_low);
+
+ unreachable();
}
diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c
index c33a5ef34ba0..bb56a66d2a48 100644
--- a/drivers/net/phy/aquantia/aquantia_main.c
+++ b/drivers/net/phy/aquantia/aquantia_main.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/bitfield.h>
+#include <linux/of.h>
#include <linux/phy.h>
#include "aquantia.h"
@@ -41,6 +42,7 @@
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI 4
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII 6
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI 7
+#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OFF 9
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII 10
#define MDIO_AN_VEND_PROV 0xc400
@@ -52,6 +54,12 @@
#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK GENMASK(3, 0)
#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT 4
+#define MDIO_AN_RESVD_VEND_PROV 0xc410
+#define MDIO_AN_RESVD_VEND_PROV_MDIX_AUTO 0
+#define MDIO_AN_RESVD_VEND_PROV_MDIX_MDI 1
+#define MDIO_AN_RESVD_VEND_PROV_MDIX_MDIX 2
+#define MDIO_AN_RESVD_VEND_PROV_MDIX_MASK GENMASK(1, 0)
+
#define MDIO_AN_TX_VEND_STATUS1 0xc800
#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1)
#define MDIO_AN_TX_VEND_STATUS1_10BASET 0
@@ -62,6 +70,9 @@
#define MDIO_AN_TX_VEND_STATUS1_5000BASET 5
#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0)
+#define MDIO_AN_RESVD_VEND_STATUS1 0xc810
+#define MDIO_AN_RESVD_VEND_STATUS1_MDIX BIT(8)
+
#define MDIO_AN_TX_VEND_INT_STATUS1 0xcc00
#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1)
@@ -71,6 +82,11 @@
#define MDIO_AN_TX_VEND_INT_MASK2 0xd401
#define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0)
+#define PMAPMD_RSVD_VEND_PROV 0xe400
+#define PMAPMD_RSVD_VEND_PROV_MDI_CONF GENMASK(1, 0)
+#define PMAPMD_RSVD_VEND_PROV_MDI_REVERSE BIT(0)
+#define PMAPMD_RSVD_VEND_PROV_MDI_FORCE BIT(1)
+
#define MDIO_AN_RX_LP_STAT1 0xe820
#define MDIO_AN_RX_LP_STAT1_1000BASET_FULL BIT(15)
#define MDIO_AN_RX_LP_STAT1_1000BASET_HALF BIT(14)
@@ -148,12 +164,40 @@ static void aqr107_get_stats(struct phy_device *phydev,
}
}
+static int aqr_set_mdix(struct phy_device *phydev, int mdix)
+{
+ u16 val = 0;
+
+ switch (mdix) {
+ case ETH_TP_MDI:
+ val = MDIO_AN_RESVD_VEND_PROV_MDIX_MDI;
+ break;
+ case ETH_TP_MDI_X:
+ val = MDIO_AN_RESVD_VEND_PROV_MDIX_MDIX;
+ break;
+ case ETH_TP_MDI_AUTO:
+ case ETH_TP_MDI_INVALID:
+ default:
+ val = MDIO_AN_RESVD_VEND_PROV_MDIX_AUTO;
+ break;
+ }
+
+ return phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_RESVD_VEND_PROV,
+ MDIO_AN_RESVD_VEND_PROV_MDIX_MASK, val);
+}
+
static int aqr_config_aneg(struct phy_device *phydev)
{
bool changed = false;
u16 reg;
int ret;
+ ret = aqr_set_mdix(phydev, phydev->mdix_ctrl);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true;
+
if (phydev->autoneg == AUTONEG_DISABLE)
return genphy_c45_pma_setup_forced(phydev);
@@ -271,6 +315,21 @@ static int aqr_read_status(struct phy_device *phydev)
val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF);
}
+ val = genphy_c45_aneg_done(phydev);
+ if (val < 0)
+ return val;
+ if (val) {
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RESVD_VEND_STATUS1);
+ if (val < 0)
+ return val;
+ if (val & MDIO_AN_RESVD_VEND_STATUS1_MDIX)
+ phydev->mdix = ETH_TP_MDI_X;
+ else
+ phydev->mdix = ETH_TP_MDI;
+ } else {
+ phydev->mdix = ETH_TP_MDI_INVALID;
+ }
+
return genphy_c45_read_status(phydev);
}
@@ -342,9 +401,19 @@ static int aqr107_read_status(struct phy_device *phydev)
if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE)
return 0;
- val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS);
- if (val < 0)
- return val;
+ /**
+ * The status register is not immediately correct on line side link up.
+ * Poll periodically until it reflects the correct ON state.
+ * Only return fail for read error, timeout defaults to OFF state.
+ */
+ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PHYXS,
+ MDIO_PHYXS_VEND_IF_STATUS, val,
+ (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val) !=
+ MDIO_PHYXS_VEND_IF_STATUS_TYPE_OFF),
+ AQR107_OP_IN_PROG_SLEEP,
+ AQR107_OP_IN_PROG_TIMEOUT, false);
+ if (ret && ret != -ETIMEDOUT)
+ return ret;
switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) {
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR:
@@ -371,7 +440,9 @@ static int aqr107_read_status(struct phy_device *phydev)
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII:
phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
break;
+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OFF:
default:
+ phydev->link = false;
phydev->interface = PHY_INTERFACE_MODE_NA;
break;
}
@@ -485,10 +556,33 @@ static void aqr107_chip_info(struct phy_device *phydev)
fw_major, fw_minor, build_id, prov_id);
}
+static int aqr107_config_mdi(struct phy_device *phydev)
+{
+ struct device_node *np = phydev->mdio.dev.of_node;
+ u32 mdi_conf;
+ int ret;
+
+ ret = of_property_read_u32(np, "marvell,mdi-cfg-order", &mdi_conf);
+
+ /* Do nothing in case property "marvell,mdi-cfg-order" is not present */
+ if (ret == -EINVAL || ret == -ENOSYS)
+ return 0;
+
+ if (ret)
+ return ret;
+
+ if (mdi_conf & ~PMAPMD_RSVD_VEND_PROV_MDI_REVERSE)
+ return -EINVAL;
+
+ return phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_RSVD_VEND_PROV,
+ PMAPMD_RSVD_VEND_PROV_MDI_CONF,
+ mdi_conf | PMAPMD_RSVD_VEND_PROV_MDI_FORCE);
+}
+
static int aqr107_config_init(struct phy_device *phydev)
{
struct aqr107_priv *priv = phydev->priv;
- u32 led_active_low;
+ u32 led_idx;
int ret;
/* Check that the PHY interface type is compatible */
@@ -514,9 +608,19 @@ static int aqr107_config_init(struct phy_device *phydev)
if (ret)
return ret;
+ ret = aqr107_config_mdi(phydev);
+ if (ret)
+ return ret;
+
/* Restore LED polarity state after reset */
- for_each_set_bit(led_active_low, &priv->leds_active_low, AQR_MAX_LEDS) {
- ret = aqr_phy_led_active_low_set(phydev, led_active_low, true);
+ for_each_set_bit(led_idx, &priv->leds_active_low, AQR_MAX_LEDS) {
+ ret = aqr_phy_led_active_low_set(phydev, led_idx, true);
+ if (ret)
+ return ret;
+ }
+
+ for_each_set_bit(led_idx, &priv->leds_active_high, AQR_MAX_LEDS) {
+ ret = aqr_phy_led_active_low_set(phydev, led_idx, false);
if (ret)
return ret;
}
diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c
index 6c52f7dda514..5198d66dbbc0 100644
--- a/drivers/net/phy/bcm-phy-lib.c
+++ b/drivers/net/phy/bcm-phy-lib.c
@@ -523,8 +523,7 @@ void bcm_phy_get_strings(struct phy_device *phydev, u8 *data)
unsigned int i;
for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
- strscpy(data + i * ETH_GSTRING_LEN,
- bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN);
+ ethtool_puts(&data, bcm_phy_hw_stats[i].string);
}
EXPORT_SYMBOL_GPL(bcm_phy_get_strings);
@@ -1137,7 +1136,7 @@ int bcm_config_lre_aneg(struct phy_device *phydev, bool changed)
{
int err;
- if (genphy_config_eee_advert(phydev))
+ if (genphy_c45_an_config_eee_aneg(phydev) > 0)
changed = true;
err = bcm_setup_lre_master_slave(phydev);
diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c
index 3ab64e04a01c..cf8b6d0bfaa9 100644
--- a/drivers/net/phy/dp83822.c
+++ b/drivers/net/phy/dp83822.c
@@ -506,7 +506,7 @@ static int dp83822_config_init(struct phy_device *phydev)
return dp83822_config_wol(phydev, &dp83822->wol);
}
-static int dp83826_config_rmii_mode(struct phy_device *phydev)
+static int dp8382x_config_rmii_mode(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
const char *of_val;
@@ -544,7 +544,7 @@ static int dp83826_config_init(struct phy_device *phydev)
if (ret)
return ret;
- ret = dp83826_config_rmii_mode(phydev);
+ ret = dp8382x_config_rmii_mode(phydev);
if (ret)
return ret;
} else {
@@ -585,9 +585,14 @@ static int dp83826_config_init(struct phy_device *phydev)
return dp83822_config_wol(phydev, &dp83822->wol);
}
-static int dp8382x_config_init(struct phy_device *phydev)
+static int dp83825_config_init(struct phy_device *phydev)
{
struct dp83822_private *dp83822 = phydev->priv;
+ int ret;
+
+ ret = dp8382x_config_rmii_mode(phydev);
+ if (ret)
+ return ret;
return dp83822_config_wol(phydev, &dp83822->wol);
}
@@ -782,14 +787,14 @@ static int dp83822_resume(struct phy_device *phydev)
.resume = dp83822_resume, \
}
-#define DP83826_PHY_DRIVER(_id, _name) \
+#define DP83825_PHY_DRIVER(_id, _name) \
{ \
PHY_ID_MATCH_MODEL(_id), \
.name = (_name), \
/* PHY_BASIC_FEATURES */ \
- .probe = dp83826_probe, \
+ .probe = dp8382x_probe, \
.soft_reset = dp83822_phy_reset, \
- .config_init = dp83826_config_init, \
+ .config_init = dp83825_config_init, \
.get_wol = dp83822_get_wol, \
.set_wol = dp83822_set_wol, \
.config_intr = dp83822_config_intr, \
@@ -798,14 +803,14 @@ static int dp83822_resume(struct phy_device *phydev)
.resume = dp83822_resume, \
}
-#define DP8382X_PHY_DRIVER(_id, _name) \
+#define DP83826_PHY_DRIVER(_id, _name) \
{ \
PHY_ID_MATCH_MODEL(_id), \
.name = (_name), \
/* PHY_BASIC_FEATURES */ \
- .probe = dp8382x_probe, \
+ .probe = dp83826_probe, \
.soft_reset = dp83822_phy_reset, \
- .config_init = dp8382x_config_init, \
+ .config_init = dp83826_config_init, \
.get_wol = dp83822_get_wol, \
.set_wol = dp83822_set_wol, \
.config_intr = dp83822_config_intr, \
@@ -816,12 +821,12 @@ static int dp83822_resume(struct phy_device *phydev)
static struct phy_driver dp83822_driver[] = {
DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"),
- DP8382X_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"),
+ DP83825_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"),
+ DP83825_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"),
+ DP83825_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"),
+ DP83825_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"),
DP83826_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"),
DP83826_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"),
- DP8382X_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"),
- DP8382X_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"),
- DP8382X_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"),
};
module_phy_driver(dp83822_driver);
diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index 5f056d7db83e..b6b38caf9c0e 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c
@@ -153,19 +153,32 @@ struct dp83869_private {
int mode;
};
+static int dp83869_config_aneg(struct phy_device *phydev)
+{
+ struct dp83869_private *dp83869 = phydev->priv;
+
+ if (dp83869->mode != DP83869_RGMII_1000_BASE)
+ return genphy_config_aneg(phydev);
+
+ return genphy_c37_config_aneg(phydev);
+}
+
static int dp83869_read_status(struct phy_device *phydev)
{
struct dp83869_private *dp83869 = phydev->priv;
+ bool changed;
int ret;
+ if (dp83869->mode == DP83869_RGMII_1000_BASE)
+ return genphy_c37_read_status(phydev, &changed);
+
ret = genphy_read_status(phydev);
if (ret)
return ret;
- if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
+ if (dp83869->mode == DP83869_RGMII_100_BASE) {
if (phydev->link) {
- if (dp83869->mode == DP83869_RGMII_100_BASE)
- phydev->speed = SPEED_100;
+ phydev->speed = SPEED_100;
} else {
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
@@ -898,6 +911,7 @@ static int dp83869_phy_reset(struct phy_device *phydev)
.soft_reset = dp83869_phy_reset, \
.config_intr = dp83869_config_intr, \
.handle_interrupt = dp83869_handle_interrupt, \
+ .config_aneg = dp83869_config_aneg, \
.read_status = dp83869_read_status, \
.get_tunable = dp83869_get_tunable, \
.set_tunable = dp83869_set_tunable, \
diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c
index a00a667454a9..ee438b71a0b4 100644
--- a/drivers/net/phy/icplus.c
+++ b/drivers/net/phy/icplus.c
@@ -540,8 +540,7 @@ static void ip101g_get_strings(struct phy_device *phydev, u8 *data)
int i;
for (i = 0; i < ARRAY_SIZE(ip101g_hw_stats); i++)
- strscpy(data + i * ETH_GSTRING_LEN,
- ip101g_hw_stats[i].name, ETH_GSTRING_LEN);
+ ethtool_puts(&data, ip101g_hw_stats[i].name);
}
static u64 ip101g_get_stat(struct phy_device *phydev, int i)
diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c
index 3c032868ef04..b672c55a7a4e 100644
--- a/drivers/net/phy/intel-xway.c
+++ b/drivers/net/phy/intel-xway.c
@@ -151,6 +151,13 @@
#define XWAY_MMD_LED3H 0x01E8
#define XWAY_MMD_LED3L 0x01E9
+#define XWAY_GPHY_MAX_LEDS 3
+#define XWAY_GPHY_LED_INV(idx) BIT(12 + (idx))
+#define XWAY_GPHY_LED_EN(idx) BIT(8 + (idx))
+#define XWAY_GPHY_LED_DA(idx) BIT(idx)
+#define XWAY_MMD_LEDxH(idx) (XWAY_MMD_LED0H + 2 * (idx))
+#define XWAY_MMD_LEDxL(idx) (XWAY_MMD_LED0L + 2 * (idx))
+
#define PHY_ID_PHY11G_1_3 0x030260D1
#define PHY_ID_PHY22F_1_3 0x030260E1
#define PHY_ID_PHY11G_1_4 0xD565A400
@@ -229,20 +236,12 @@ static int xway_gphy_rgmii_init(struct phy_device *phydev)
XWAY_MDIO_MIICTRL_TXSKEW_MASK, val);
}
-static int xway_gphy_config_init(struct phy_device *phydev)
+static int xway_gphy_init_leds(struct phy_device *phydev)
{
int err;
u32 ledxh;
u32 ledxl;
- /* Mask all interrupts */
- err = phy_write(phydev, XWAY_MDIO_IMASK, 0);
- if (err)
- return err;
-
- /* Clear all pending interrupts */
- phy_read(phydev, XWAY_MDIO_ISTAT);
-
/* Ensure that integrated led function is enabled for all leds */
err = phy_write(phydev, XWAY_MDIO_LED,
XWAY_MDIO_LED_LED0_EN |
@@ -276,6 +275,26 @@ static int xway_gphy_config_init(struct phy_device *phydev)
phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh);
phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl);
+ return 0;
+}
+
+static int xway_gphy_config_init(struct phy_device *phydev)
+{
+ struct device_node *np = phydev->mdio.dev.of_node;
+ int err;
+
+ /* Mask all interrupts */
+ err = phy_write(phydev, XWAY_MDIO_IMASK, 0);
+ if (err)
+ return err;
+
+ /* Use default LED configuration if 'leds' node isn't defined */
+ if (!of_get_child_by_name(np, "leds"))
+ xway_gphy_init_leds(phydev);
+
+ /* Clear all pending interrupts */
+ phy_read(phydev, XWAY_MDIO_ISTAT);
+
err = xway_gphy_rgmii_init(phydev);
if (err)
return err;
@@ -347,6 +366,172 @@ static irqreturn_t xway_gphy_handle_interrupt(struct phy_device *phydev)
return IRQ_HANDLED;
}
+static int xway_gphy_led_brightness_set(struct phy_device *phydev,
+ u8 index, enum led_brightness value)
+{
+ int ret;
+
+ if (index >= XWAY_GPHY_MAX_LEDS)
+ return -EINVAL;
+
+ /* clear EN and set manual LED state */
+ ret = phy_modify(phydev, XWAY_MDIO_LED,
+ ((value == LED_OFF) ? XWAY_GPHY_LED_EN(index) : 0) |
+ XWAY_GPHY_LED_DA(index),
+ (value == LED_OFF) ? 0 : XWAY_GPHY_LED_DA(index));
+ if (ret)
+ return ret;
+
+ /* clear HW LED setup */
+ if (value == LED_OFF) {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index), 0);
+ if (ret)
+ return ret;
+
+ return phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index), 0);
+ } else {
+ return 0;
+ }
+}
+
+static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_RX) |
+ BIT(TRIGGER_NETDEV_TX));
+
+static int xway_gphy_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ if (index >= XWAY_GPHY_MAX_LEDS)
+ return -EINVAL;
+
+ /* activity triggers are not possible without combination with a link
+ * trigger.
+ */
+ if (rules & (BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX)) &&
+ !(rules & (BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_1000))))
+ return -EOPNOTSUPP;
+
+ /* All other combinations of the supported triggers are allowed */
+ if (rules & ~supported_triggers)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int xway_gphy_led_hw_control_get(struct phy_device *phydev, u8 index,
+ unsigned long *rules)
+{
+ int lval, hval;
+
+ if (index >= XWAY_GPHY_MAX_LEDS)
+ return -EINVAL;
+
+ hval = phy_read_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index));
+ if (hval < 0)
+ return hval;
+
+ lval = phy_read_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index));
+ if (lval < 0)
+ return lval;
+
+ if (hval & XWAY_MMD_LEDxH_CON_LINK10)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_10);
+
+ if (hval & XWAY_MMD_LEDxH_CON_LINK100)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_100);
+
+ if (hval & XWAY_MMD_LEDxH_CON_LINK1000)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
+
+ if ((hval & XWAY_MMD_LEDxH_CON_LINK10) &&
+ (hval & XWAY_MMD_LEDxH_CON_LINK100) &&
+ (hval & XWAY_MMD_LEDxH_CON_LINK1000))
+ *rules |= BIT(TRIGGER_NETDEV_LINK);
+
+ if (lval & XWAY_MMD_LEDxL_PULSE_TXACT)
+ *rules |= BIT(TRIGGER_NETDEV_TX);
+
+ if (lval & XWAY_MMD_LEDxL_PULSE_RXACT)
+ *rules |= BIT(TRIGGER_NETDEV_RX);
+
+ return 0;
+}
+
+static int xway_gphy_led_hw_control_set(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ u16 hval = 0, lval = 0;
+ int ret;
+
+ if (index >= XWAY_GPHY_MAX_LEDS)
+ return -EINVAL;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK) ||
+ rules & BIT(TRIGGER_NETDEV_LINK_10))
+ hval |= XWAY_MMD_LEDxH_CON_LINK10;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK) ||
+ rules & BIT(TRIGGER_NETDEV_LINK_100))
+ hval |= XWAY_MMD_LEDxH_CON_LINK100;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK) ||
+ rules & BIT(TRIGGER_NETDEV_LINK_1000))
+ hval |= XWAY_MMD_LEDxH_CON_LINK1000;
+
+ if (rules & BIT(TRIGGER_NETDEV_TX))
+ lval |= XWAY_MMD_LEDxL_PULSE_TXACT;
+
+ if (rules & BIT(TRIGGER_NETDEV_RX))
+ lval |= XWAY_MMD_LEDxL_PULSE_RXACT;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index), hval);
+ if (ret)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index), lval);
+ if (ret)
+ return ret;
+
+ return phy_set_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_EN(index));
+}
+
+static int xway_gphy_led_polarity_set(struct phy_device *phydev, int index,
+ unsigned long modes)
+{
+ bool force_active_low = false, force_active_high = false;
+ u32 mode;
+
+ if (index >= XWAY_GPHY_MAX_LEDS)
+ return -EINVAL;
+
+ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
+ switch (mode) {
+ case PHY_LED_ACTIVE_LOW:
+ force_active_low = true;
+ break;
+ case PHY_LED_ACTIVE_HIGH:
+ force_active_high = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ if (force_active_low)
+ return phy_set_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index));
+
+ if (force_active_high)
+ return phy_clear_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index));
+
+ unreachable();
+}
+
static struct phy_driver xway_gphy[] = {
{
.phy_id = PHY_ID_PHY11G_1_3,
@@ -359,6 +544,11 @@ static struct phy_driver xway_gphy[] = {
.config_intr = xway_gphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .led_brightness_set = xway_gphy_led_brightness_set,
+ .led_hw_is_supported = xway_gphy_led_hw_is_supported,
+ .led_hw_control_get = xway_gphy_led_hw_control_get,
+ .led_hw_control_set = xway_gphy_led_hw_control_set,
+ .led_polarity_set = xway_gphy_led_polarity_set,
}, {
.phy_id = PHY_ID_PHY22F_1_3,
.phy_id_mask = 0xffffffff,
@@ -370,6 +560,11 @@ static struct phy_driver xway_gphy[] = {
.config_intr = xway_gphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .led_brightness_set = xway_gphy_led_brightness_set,
+ .led_hw_is_supported = xway_gphy_led_hw_is_supported,
+ .led_hw_control_get = xway_gphy_led_hw_control_get,
+ .led_hw_control_set = xway_gphy_led_hw_control_set,
+ .led_polarity_set = xway_gphy_led_polarity_set,
}, {
.phy_id = PHY_ID_PHY11G_1_4,
.phy_id_mask = 0xffffffff,
@@ -381,6 +576,11 @@ static struct phy_driver xway_gphy[] = {
.config_intr = xway_gphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .led_brightness_set = xway_gphy_led_brightness_set,
+ .led_hw_is_supported = xway_gphy_led_hw_is_supported,
+ .led_hw_control_get = xway_gphy_led_hw_control_get,
+ .led_hw_control_set = xway_gphy_led_hw_control_set,
+ .led_polarity_set = xway_gphy_led_polarity_set,
}, {
.phy_id = PHY_ID_PHY22F_1_4,
.phy_id_mask = 0xffffffff,
@@ -392,6 +592,11 @@ static struct phy_driver xway_gphy[] = {
.config_intr = xway_gphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .led_brightness_set = xway_gphy_led_brightness_set,
+ .led_hw_is_supported = xway_gphy_led_hw_is_supported,
+ .led_hw_control_get = xway_gphy_led_hw_control_get,
+ .led_hw_control_set = xway_gphy_led_hw_control_set,
+ .led_polarity_set = xway_gphy_led_polarity_set,
}, {
.phy_id = PHY_ID_PHY11G_1_5,
.phy_id_mask = 0xffffffff,
@@ -402,6 +607,11 @@ static struct phy_driver xway_gphy[] = {
.config_intr = xway_gphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .led_brightness_set = xway_gphy_led_brightness_set,
+ .led_hw_is_supported = xway_gphy_led_hw_is_supported,
+ .led_hw_control_get = xway_gphy_led_hw_control_get,
+ .led_hw_control_set = xway_gphy_led_hw_control_set,
+ .led_polarity_set = xway_gphy_led_polarity_set,
}, {
.phy_id = PHY_ID_PHY22F_1_5,
.phy_id_mask = 0xffffffff,
@@ -412,6 +622,11 @@ static struct phy_driver xway_gphy[] = {
.config_intr = xway_gphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .led_brightness_set = xway_gphy_led_brightness_set,
+ .led_hw_is_supported = xway_gphy_led_hw_is_supported,
+ .led_hw_control_get = xway_gphy_led_hw_control_get,
+ .led_hw_control_set = xway_gphy_led_hw_control_set,
+ .led_polarity_set = xway_gphy_led_polarity_set,
}, {
.phy_id = PHY_ID_PHY11G_VR9_1_1,
.phy_id_mask = 0xffffffff,
@@ -422,6 +637,11 @@ static struct phy_driver xway_gphy[] = {
.config_intr = xway_gphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .led_brightness_set = xway_gphy_led_brightness_set,
+ .led_hw_is_supported = xway_gphy_led_hw_is_supported,
+ .led_hw_control_get = xway_gphy_led_hw_control_get,
+ .led_hw_control_set = xway_gphy_led_hw_control_set,
+ .led_polarity_set = xway_gphy_led_polarity_set,
}, {
.phy_id = PHY_ID_PHY22F_VR9_1_1,
.phy_id_mask = 0xffffffff,
@@ -432,6 +652,11 @@ static struct phy_driver xway_gphy[] = {
.config_intr = xway_gphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .led_brightness_set = xway_gphy_led_brightness_set,
+ .led_hw_is_supported = xway_gphy_led_hw_is_supported,
+ .led_hw_control_get = xway_gphy_led_hw_control_get,
+ .led_hw_control_set = xway_gphy_led_hw_control_set,
+ .led_polarity_set = xway_gphy_led_polarity_set,
}, {
.phy_id = PHY_ID_PHY11G_VR9_1_2,
.phy_id_mask = 0xffffffff,
@@ -442,6 +667,11 @@ static struct phy_driver xway_gphy[] = {
.config_intr = xway_gphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .led_brightness_set = xway_gphy_led_brightness_set,
+ .led_hw_is_supported = xway_gphy_led_hw_is_supported,
+ .led_hw_control_get = xway_gphy_led_hw_control_get,
+ .led_hw_control_set = xway_gphy_led_hw_control_set,
+ .led_polarity_set = xway_gphy_led_polarity_set,
}, {
.phy_id = PHY_ID_PHY22F_VR9_1_2,
.phy_id_mask = 0xffffffff,
@@ -452,6 +682,11 @@ static struct phy_driver xway_gphy[] = {
.config_intr = xway_gphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .led_brightness_set = xway_gphy_led_brightness_set,
+ .led_hw_is_supported = xway_gphy_led_hw_is_supported,
+ .led_hw_control_get = xway_gphy_led_hw_control_get,
+ .led_hw_control_set = xway_gphy_led_hw_control_set,
+ .led_polarity_set = xway_gphy_led_polarity_set,
},
};
module_phy_driver(xway_gphy);
diff --git a/drivers/net/phy/marvell-88q2xxx.c b/drivers/net/phy/marvell-88q2xxx.c
index c812f16eaa3a..5107f58338af 100644
--- a/drivers/net/phy/marvell-88q2xxx.c
+++ b/drivers/net/phy/marvell-88q2xxx.c
@@ -101,6 +101,22 @@ struct mmd_val {
u16 val;
};
+static const struct mmd_val mv88q2110_init_seq0[] = {
+ { MDIO_MMD_PCS, 0xffe4, 0x07b5 },
+ { MDIO_MMD_PCS, 0xffe4, 0x06b6 },
+};
+
+static const struct mmd_val mv88q2110_init_seq1[] = {
+ { MDIO_MMD_PCS, 0xffde, 0x402f },
+ { MDIO_MMD_PCS, 0xfe34, 0x4040 },
+ { MDIO_MMD_PCS, 0xfe2a, 0x3c1d },
+ { MDIO_MMD_PCS, 0xfe34, 0x0040 },
+ { MDIO_MMD_AN, 0x8032, 0x0064 },
+ { MDIO_MMD_AN, 0x8031, 0x0a01 },
+ { MDIO_MMD_AN, 0x8031, 0x0c01 },
+ { MDIO_MMD_PCS, 0xffdb, 0x0010 },
+};
+
static const struct mmd_val mv88q222x_revb0_init_seq0[] = {
{ MDIO_MMD_PCS, 0x8033, 0x6801 },
{ MDIO_MMD_AN, MDIO_AN_T1_CTRL, 0x0 },
@@ -174,20 +190,54 @@ static const struct mmd_val mv88q222x_revb1_revb2_init_seq1[] = {
{ MDIO_MMD_PCS, 0xfe11, 0x1105 },
};
+static int mv88q2xxx_write_mmd_vals(struct phy_device *phydev,
+ const struct mmd_val *vals, size_t len)
+{
+ int ret;
+
+ for (; len; vals++, len--) {
+ ret = phy_write_mmd(phydev, vals->devad, vals->regnum,
+ vals->val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static int mv88q2xxx_soft_reset(struct phy_device *phydev)
{
int ret;
int val;
- ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
- MDIO_PCS_1000BT1_CTRL, MDIO_PCS_1000BT1_CTRL_RESET);
+ /* Enable RESET of DCL */
+ if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000) {
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x48);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_CTRL,
+ MDIO_PCS_1000BT1_CTRL_RESET);
if (ret < 0)
return ret;
- return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS,
- MDIO_PCS_1000BT1_CTRL, val,
- !(val & MDIO_PCS_1000BT1_CTRL_RESET),
- 50000, 600000, true);
+ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS,
+ MDIO_PCS_1000BT1_CTRL, val,
+ !(val & MDIO_PCS_1000BT1_CTRL_RESET),
+ 50000, 600000, true);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xffe4, 0xc);
+ if (ret < 0)
+ return ret;
+
+ /* Disable RESET of DCL */
+ if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000)
+ return phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x58);
+
+ return 0;
}
static int mv88q2xxx_read_link_gbit(struct phy_device *phydev)
@@ -390,15 +440,6 @@ static int mv88q2xxx_get_features(struct phy_device *phydev)
if (ret)
return ret;
- /* The PHY signalizes it supports autonegotiation. Unfortunately, so
- * far it was not possible to get a link even when following the init
- * sequence provided by Marvell. Disable it for now until a proper
- * workaround is found or a new PHY revision is released.
- */
- if (phydev->drv->phy_id == MARVELL_PHY_ID_88Q2110)
- linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
- phydev->supported);
-
return 0;
}
@@ -705,60 +746,37 @@ static int mv88q2xxx_probe(struct phy_device *phydev)
return mv88q2xxx_hwmon_probe(phydev);
}
-static int mv88q222x_soft_reset(struct phy_device *phydev)
+static int mv88q2110_config_init(struct phy_device *phydev)
{
int ret;
- /* Enable RESET of DCL */
- if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000) {
- ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x48);
- if (ret < 0)
- return ret;
- }
-
- ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_CTRL,
- MDIO_PCS_1000BT1_CTRL_RESET);
+ ret = mv88q2xxx_write_mmd_vals(phydev, mv88q2110_init_seq0,
+ ARRAY_SIZE(mv88q2110_init_seq0));
if (ret < 0)
return ret;
- ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xffe4, 0xc);
+ usleep_range(5000, 10000);
+
+ ret = mv88q2xxx_write_mmd_vals(phydev, mv88q2110_init_seq1,
+ ARRAY_SIZE(mv88q2110_init_seq1));
if (ret < 0)
return ret;
- /* Disable RESET of DCL */
- if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000)
- return phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x58);
-
- return 0;
-}
-
-static int mv88q222x_write_mmd_vals(struct phy_device *phydev,
- const struct mmd_val *vals, size_t len)
-{
- int ret;
-
- for (; len; vals++, len--) {
- ret = phy_write_mmd(phydev, vals->devad, vals->regnum,
- vals->val);
- if (ret < 0)
- return ret;
- }
-
- return 0;
+ return mv88q2xxx_config_init(phydev);
}
static int mv88q222x_revb0_config_init(struct phy_device *phydev)
{
int ret;
- ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb0_init_seq0,
+ ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb0_init_seq0,
ARRAY_SIZE(mv88q222x_revb0_init_seq0));
if (ret < 0)
return ret;
usleep_range(5000, 10000);
- ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb0_init_seq1,
+ ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb0_init_seq1,
ARRAY_SIZE(mv88q222x_revb0_init_seq1));
if (ret < 0)
return ret;
@@ -772,17 +790,17 @@ static int mv88q222x_revb1_revb2_config_init(struct phy_device *phydev)
int ret;
if (is_rev_b1)
- ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb1_init_seq0,
+ ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb1_init_seq0,
ARRAY_SIZE(mv88q222x_revb1_init_seq0));
else
- ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb2_init_seq0,
+ ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb2_init_seq0,
ARRAY_SIZE(mv88q222x_revb2_init_seq0));
if (ret < 0)
return ret;
usleep_range(3000, 5000);
- ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb1_revb2_init_seq1,
+ ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb1_revb2_init_seq1,
ARRAY_SIZE(mv88q222x_revb1_revb2_init_seq1));
if (ret < 0)
return ret;
@@ -888,7 +906,7 @@ static struct phy_driver mv88q2xxx_driver[] = {
.name = "mv88q2110",
.get_features = mv88q2xxx_get_features,
.config_aneg = mv88q2xxx_config_aneg,
- .config_init = mv88q2xxx_config_init,
+ .config_init = mv88q2110_config_init,
.read_status = mv88q2xxx_read_status,
.soft_reset = mv88q2xxx_soft_reset,
.set_loopback = genphy_c45_loopback,
@@ -906,7 +924,7 @@ static struct phy_driver mv88q2xxx_driver[] = {
.aneg_done = genphy_c45_aneg_done,
.config_init = mv88q222x_config_init,
.read_status = mv88q2xxx_read_status,
- .soft_reset = mv88q222x_soft_reset,
+ .soft_reset = mv88q2xxx_soft_reset,
.config_intr = mv88q2xxx_config_intr,
.handle_interrupt = mv88q2xxx_handle_interrupt,
.set_loopback = genphy_c45_loopback,
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 9964bf3dea2f..cd50cd6a7f75 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -176,6 +176,7 @@
#define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000
#define MII_M1011_PHY_STATUS_RESOLVED 0x0800
#define MII_M1011_PHY_STATUS_LINK 0x0400
+#define MII_M1011_PHY_STATUS_MDIX BIT(6)
#define MII_88E3016_PHY_SPEC_CTRL 0x10
#define MII_88E3016_DISABLE_SCRAMBLER 0x0200
@@ -1722,6 +1723,19 @@ static int marvell_read_status_page(struct phy_device *phydev, int page)
phydev->duplex = DUPLEX_UNKNOWN;
phydev->port = fiber ? PORT_FIBRE : PORT_TP;
+ if (fiber) {
+ phydev->mdix = ETH_TP_MDI_INVALID;
+ } else {
+ /* The MDI-X state is set regardless of Autoneg being enabled
+ * and reflects forced MDI-X state as well as auto resolution
+ */
+ if (status & MII_M1011_PHY_STATUS_RESOLVED)
+ phydev->mdix = status & MII_M1011_PHY_STATUS_MDIX ?
+ ETH_TP_MDI_X : ETH_TP_MDI;
+ else
+ phydev->mdix = ETH_TP_MDI_INVALID;
+ }
+
if (phydev->autoneg == AUTONEG_ENABLE)
err = marvell_read_status_page_an(phydev, fiber, status);
else
@@ -2006,10 +2020,8 @@ static void marvell_get_strings(struct phy_device *phydev, u8 *data)
int count = marvell_get_sset_count(phydev);
int i;
- for (i = 0; i < count; i++) {
- strscpy(data + i * ETH_GSTRING_LEN,
- marvell_hw_stats[i].string, ETH_GSTRING_LEN);
- }
+ for (i = 0; i < count; i++)
+ ethtool_puts(&data, marvell_hw_stats[i].string);
}
static void marvell_get_strings_simple(struct phy_device *phydev, u8 *data)
@@ -2017,10 +2029,8 @@ static void marvell_get_strings_simple(struct phy_device *phydev, u8 *data)
int count = marvell_get_sset_count_simple(phydev);
int i;
- for (i = 0; i < count; i++) {
- strscpy(data + i * ETH_GSTRING_LEN,
- marvell_hw_stats_simple[i].string, ETH_GSTRING_LEN);
- }
+ for (i = 0; i < count; i++)
+ ethtool_puts(&data, marvell_hw_stats_simple[i].string);
}
static u64 marvell_get_stat(struct phy_device *phydev, int i)
diff --git a/drivers/net/phy/mediatek/Kconfig b/drivers/net/phy/mediatek/Kconfig
new file mode 100644
index 000000000000..2a8ac5aed0f8
--- /dev/null
+++ b/drivers/net/phy/mediatek/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config MTK_NET_PHYLIB
+ tristate
+
+config MEDIATEK_GE_PHY
+ tristate "MediaTek Gigabit Ethernet PHYs"
+ select MTK_NET_PHYLIB
+ help
+ Supports the MediaTek non-built-in Gigabit Ethernet PHYs.
+
+ Non-built-in Gigabit Ethernet PHYs include mt7530/mt7531.
+ You may find mt7530 inside mt7621. This driver shares some
+ common operations with MediaTek SoC built-in Gigabit
+ Ethernet PHYs.
+
+config MEDIATEK_GE_SOC_PHY
+ tristate "MediaTek SoC Ethernet PHYs"
+ depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST
+ depends on NVMEM_MTK_EFUSE
+ select MTK_NET_PHYLIB
+ help
+ Supports MediaTek SoC built-in Gigabit Ethernet PHYs.
+
+ Include support for built-in Ethernet PHYs which are present in
+ the MT7981 and MT7988 SoCs. These PHYs need calibration data
+ present in the SoCs efuse and will dynamically calibrate VCM
+ (common-mode voltage) during startup.
diff --git a/drivers/net/phy/mediatek/Makefile b/drivers/net/phy/mediatek/Makefile
new file mode 100644
index 000000000000..814879d0abe5
--- /dev/null
+++ b/drivers/net/phy/mediatek/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_MTK_NET_PHYLIB) += mtk-phy-lib.o
+obj-$(CONFIG_MEDIATEK_GE_PHY) += mtk-ge.o
+obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mtk-ge-soc.o
diff --git a/drivers/net/phy/mediatek-ge-soc.c b/drivers/net/phy/mediatek/mtk-ge-soc.c
index f4f9412d0cd7..38dc898eaf7b 100644
--- a/drivers/net/phy/mediatek-ge-soc.c
+++ b/drivers/net/phy/mediatek/mtk-ge-soc.c
@@ -8,6 +8,8 @@
#include <linux/phy.h>
#include <linux/regmap.h>
+#include "mtk.h"
+
#define MTK_GPHY_ID_MT7981 0x03a29461
#define MTK_GPHY_ID_MT7988 0x03a29481
@@ -110,7 +112,7 @@
#define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0)
#define MTK_PHY_RG_AD_CAL_COMP 0x17a
-#define MTK_PHY_AD_CAL_COMP_OUT_SHIFT (8)
+#define MTK_PHY_AD_CAL_COMP_OUT_MASK GENMASK(8, 8)
#define MTK_PHY_RG_AD_CAL_CLK 0x17b
#define MTK_PHY_DA_CAL_CLK BIT(0)
@@ -210,41 +212,6 @@
#define MTK_PHY_DA_TX_R50_PAIR_D 0x540
/* Registers on MDIO_MMD_VEND2 */
-#define MTK_PHY_LED0_ON_CTRL 0x24
-#define MTK_PHY_LED1_ON_CTRL 0x26
-#define MTK_PHY_LED_ON_MASK GENMASK(6, 0)
-#define MTK_PHY_LED_ON_LINK1000 BIT(0)
-#define MTK_PHY_LED_ON_LINK100 BIT(1)
-#define MTK_PHY_LED_ON_LINK10 BIT(2)
-#define MTK_PHY_LED_ON_LINK (MTK_PHY_LED_ON_LINK10 |\
- MTK_PHY_LED_ON_LINK100 |\
- MTK_PHY_LED_ON_LINK1000)
-#define MTK_PHY_LED_ON_LINKDOWN BIT(3)
-#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */
-#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */
-#define MTK_PHY_LED_ON_FORCE_ON BIT(6)
-#define MTK_PHY_LED_ON_POLARITY BIT(14)
-#define MTK_PHY_LED_ON_ENABLE BIT(15)
-
-#define MTK_PHY_LED0_BLINK_CTRL 0x25
-#define MTK_PHY_LED1_BLINK_CTRL 0x27
-#define MTK_PHY_LED_BLINK_1000TX BIT(0)
-#define MTK_PHY_LED_BLINK_1000RX BIT(1)
-#define MTK_PHY_LED_BLINK_100TX BIT(2)
-#define MTK_PHY_LED_BLINK_100RX BIT(3)
-#define MTK_PHY_LED_BLINK_10TX BIT(4)
-#define MTK_PHY_LED_BLINK_10RX BIT(5)
-#define MTK_PHY_LED_BLINK_RX (MTK_PHY_LED_BLINK_10RX |\
- MTK_PHY_LED_BLINK_100RX |\
- MTK_PHY_LED_BLINK_1000RX)
-#define MTK_PHY_LED_BLINK_TX (MTK_PHY_LED_BLINK_10TX |\
- MTK_PHY_LED_BLINK_100TX |\
- MTK_PHY_LED_BLINK_1000TX)
-#define MTK_PHY_LED_BLINK_COLLISION BIT(6)
-#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7)
-#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8)
-#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9)
-
#define MTK_PHY_LED1_DEFAULT_POLARITIES BIT(1)
#define MTK_PHY_RG_BG_RASEL 0x115
@@ -299,29 +266,11 @@ enum CAL_MODE {
SW_M
};
-#define MTK_PHY_LED_STATE_FORCE_ON 0
-#define MTK_PHY_LED_STATE_FORCE_BLINK 1
-#define MTK_PHY_LED_STATE_NETDEV 2
-
-struct mtk_socphy_priv {
- unsigned long led_state;
-};
-
struct mtk_socphy_shared {
u32 boottrap;
struct mtk_socphy_priv priv[4];
};
-static int mtk_socphy_read_page(struct phy_device *phydev)
-{
- return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
-}
-
-static int mtk_socphy_write_page(struct phy_device *phydev, int page)
-{
- return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
-}
-
/* One calibration cycle consists of:
* 1.Set DA_CALIN_FLAG high to start calibration. Keep it high
* until AD_CAL_COMP is ready to output calibration result.
@@ -342,7 +291,8 @@ static int cal_cycle(struct phy_device *phydev, int devad,
ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
MTK_PHY_RG_AD_CAL_CLK, reg_val,
reg_val & MTK_PHY_DA_CAL_CLK, 500,
- ANALOG_INTERNAL_OPERATION_MAX_US, false);
+ ANALOG_INTERNAL_OPERATION_MAX_US,
+ false);
if (ret) {
phydev_err(phydev, "Calibration cycle timeout\n");
return ret;
@@ -350,8 +300,10 @@ static int cal_cycle(struct phy_device *phydev, int devad,
phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN,
MTK_PHY_DA_CALIN_FLAG);
- ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP) >>
- MTK_PHY_AD_CAL_COMP_OUT_SHIFT;
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP);
+ if (ret < 0)
+ return ret;
+ ret = FIELD_GET(MTK_PHY_AD_CAL_COMP_OUT_MASK, ret);
phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret);
return ret;
@@ -408,16 +360,17 @@ static int tx_offset_cal_efuse(struct phy_device *phydev, u32 *buf)
static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf)
{
- int i;
- int bias[16] = {};
- const int vals_9461[16] = { 7, 1, 4, 7,
- 7, 1, 4, 7,
- 7, 1, 4, 7,
- 7, 1, 4, 7 };
const int vals_9481[16] = { 10, 6, 6, 10,
10, 6, 6, 10,
10, 6, 6, 10,
10, 6, 6, 10 };
+ const int vals_9461[16] = { 7, 1, 4, 7,
+ 7, 1, 4, 7,
+ 7, 1, 4, 7,
+ 7, 1, 4, 7 };
+ int bias[16] = {};
+ int i;
+
switch (phydev->drv->phy_id) {
case MTK_GPHY_ID_MT7981:
/* We add some calibration to efuse values
@@ -440,40 +393,72 @@ static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf)
}
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG,
- MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, (buf[0] + bias[0]) << 10);
+ MTK_PHY_DA_TX_I2MPB_A_GBE_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_GBE_MASK,
+ buf[0] + bias[0]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG,
- MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, buf[0] + bias[1]);
+ MTK_PHY_DA_TX_I2MPB_A_TBT_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TBT_MASK,
+ buf[0] + bias[1]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2,
- MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, (buf[0] + bias[2]) << 10);
+ MTK_PHY_DA_TX_I2MPB_A_HBT_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_HBT_MASK,
+ buf[0] + bias[2]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2,
- MTK_PHY_DA_TX_I2MPB_A_TST_MASK, buf[0] + bias[3]);
+ MTK_PHY_DA_TX_I2MPB_A_TST_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TST_MASK,
+ buf[0] + bias[3]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1,
- MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, (buf[1] + bias[4]) << 8);
+ MTK_PHY_DA_TX_I2MPB_B_GBE_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_GBE_MASK,
+ buf[1] + bias[4]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1,
- MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, buf[1] + bias[5]);
+ MTK_PHY_DA_TX_I2MPB_B_TBT_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TBT_MASK,
+ buf[1] + bias[5]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2,
- MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, (buf[1] + bias[6]) << 8);
+ MTK_PHY_DA_TX_I2MPB_B_HBT_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_HBT_MASK,
+ buf[1] + bias[6]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2,
- MTK_PHY_DA_TX_I2MPB_B_TST_MASK, buf[1] + bias[7]);
+ MTK_PHY_DA_TX_I2MPB_B_TST_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TST_MASK,
+ buf[1] + bias[7]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1,
- MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, (buf[2] + bias[8]) << 8);
+ MTK_PHY_DA_TX_I2MPB_C_GBE_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_GBE_MASK,
+ buf[2] + bias[8]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1,
- MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, buf[2] + bias[9]);
+ MTK_PHY_DA_TX_I2MPB_C_TBT_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TBT_MASK,
+ buf[2] + bias[9]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2,
- MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, (buf[2] + bias[10]) << 8);
+ MTK_PHY_DA_TX_I2MPB_C_HBT_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_HBT_MASK,
+ buf[2] + bias[10]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2,
- MTK_PHY_DA_TX_I2MPB_C_TST_MASK, buf[2] + bias[11]);
+ MTK_PHY_DA_TX_I2MPB_C_TST_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TST_MASK,
+ buf[2] + bias[11]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1,
- MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, (buf[3] + bias[12]) << 8);
+ MTK_PHY_DA_TX_I2MPB_D_GBE_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_GBE_MASK,
+ buf[3] + bias[12]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1,
- MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, buf[3] + bias[13]);
+ MTK_PHY_DA_TX_I2MPB_D_TBT_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TBT_MASK,
+ buf[3] + bias[13]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2,
- MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, (buf[3] + bias[14]) << 8);
+ MTK_PHY_DA_TX_I2MPB_D_HBT_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_HBT_MASK,
+ buf[3] + bias[14]));
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2,
- MTK_PHY_DA_TX_I2MPB_D_TST_MASK, buf[3] + bias[15]);
+ MTK_PHY_DA_TX_I2MPB_D_TST_MASK,
+ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TST_MASK,
+ buf[3] + bias[15]));
return 0;
}
@@ -662,7 +647,8 @@ static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x)
goto restore;
/* We calibrate TX-VCM in different logic. Check upper index and then
- * lower index. If this calibration is valid, apply lower index's result.
+ * lower index. If this calibration is valid, apply lower index's
+ * result.
*/
ret = upper_ret - lower_ret;
if (ret == 1) {
@@ -691,7 +677,8 @@ static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x)
} else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 &&
lower_ret == 0) {
ret = 0;
- phydev_warn(phydev, "TX-VCM SW cal result at high margin 0x%x\n",
+ phydev_warn(phydev,
+ "TX-VCM SW cal result at high margin 0x%x\n",
upper_idx);
} else {
ret = -EINVAL;
@@ -795,7 +782,8 @@ static void mt7981_phy_finetune(struct phy_device *phydev)
/* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234,
- MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK,
+ MTK_PHY_TR_OPEN_LOOP_EN_MASK |
+ MTK_PHY_LPF_X_AVERAGE_MASK,
BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9));
/* rg_tr_lpf_cnt_val = 512 */
@@ -864,7 +852,8 @@ static void mt7988_phy_finetune(struct phy_device *phydev)
/* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */
phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234,
- MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK,
+ MTK_PHY_TR_OPEN_LOOP_EN_MASK |
+ MTK_PHY_LPF_X_AVERAGE_MASK,
BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0xa));
/* rg_tr_lpf_cnt_val = 1023 */
@@ -976,7 +965,8 @@ static void mt798x_phy_eee(struct phy_device *phydev)
phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3);
- __phy_modify(phydev, MTK_PHY_LPI_REG_14, MTK_PHY_LPI_WAKE_TIMER_1000_MASK,
+ __phy_modify(phydev, MTK_PHY_LPI_REG_14,
+ MTK_PHY_LPI_WAKE_TIMER_1000_MASK,
FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c));
__phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK,
@@ -986,7 +976,8 @@ static void mt798x_phy_eee(struct phy_device *phydev)
phy_modify_mmd(phydev, MDIO_MMD_VEND1,
MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122,
MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK,
- FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, 0xff));
+ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK,
+ 0xff));
}
static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item,
@@ -1069,10 +1060,10 @@ static int start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item,
static int mt798x_phy_calibration(struct phy_device *phydev)
{
+ struct nvmem_cell *cell;
int ret = 0;
- u32 *buf;
size_t len;
- struct nvmem_cell *cell;
+ u32 *buf;
cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data");
if (IS_ERR(cell)) {
@@ -1130,72 +1121,23 @@ static int mt798x_phy_config_init(struct phy_device *phydev)
return mt798x_phy_calibration(phydev);
}
-static int mt798x_phy_hw_led_on_set(struct phy_device *phydev, u8 index,
- bool on)
-{
- unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0);
- struct mtk_socphy_priv *priv = phydev->priv;
- bool changed;
-
- if (on)
- changed = !test_and_set_bit(bit_on, &priv->led_state);
- else
- changed = !!test_and_clear_bit(bit_on, &priv->led_state);
-
- changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV +
- (index ? 16 : 0), &priv->led_state);
- if (changed)
- return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ?
- MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL,
- MTK_PHY_LED_ON_MASK,
- on ? MTK_PHY_LED_ON_FORCE_ON : 0);
- else
- return 0;
-}
-
-static int mt798x_phy_hw_led_blink_set(struct phy_device *phydev, u8 index,
- bool blinking)
-{
- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + (index ? 16 : 0);
- struct mtk_socphy_priv *priv = phydev->priv;
- bool changed;
-
- if (blinking)
- changed = !test_and_set_bit(bit_blink, &priv->led_state);
- else
- changed = !!test_and_clear_bit(bit_blink, &priv->led_state);
-
- changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV +
- (index ? 16 : 0), &priv->led_state);
- if (changed)
- return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ?
- MTK_PHY_LED1_BLINK_CTRL : MTK_PHY_LED0_BLINK_CTRL,
- blinking ? MTK_PHY_LED_BLINK_FORCE_BLINK : 0);
- else
- return 0;
-}
-
static int mt798x_phy_led_blink_set(struct phy_device *phydev, u8 index,
unsigned long *delay_on,
unsigned long *delay_off)
{
bool blinking = false;
- int err = 0;
-
- if (index > 1)
- return -EINVAL;
+ int err;
- if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) {
- blinking = true;
- *delay_on = 50;
- *delay_off = 50;
- }
+ err = mtk_phy_led_num_dly_cfg(index, delay_on, delay_off, &blinking);
+ if (err < 0)
+ return err;
- err = mt798x_phy_hw_led_blink_set(phydev, index, blinking);
+ err = mtk_phy_hw_led_blink_set(phydev, index, blinking);
if (err)
return err;
- return mt798x_phy_hw_led_on_set(phydev, index, false);
+ return mtk_phy_hw_led_on_set(phydev, index, MTK_GPHY_LED_ON_MASK,
+ false);
}
static int mt798x_phy_led_brightness_set(struct phy_device *phydev,
@@ -1203,167 +1145,47 @@ static int mt798x_phy_led_brightness_set(struct phy_device *phydev,
{
int err;
- err = mt798x_phy_hw_led_blink_set(phydev, index, false);
+ err = mtk_phy_hw_led_blink_set(phydev, index, false);
if (err)
return err;
- return mt798x_phy_hw_led_on_set(phydev, index, (value != LED_OFF));
+ return mtk_phy_hw_led_on_set(phydev, index, MTK_GPHY_LED_ON_MASK,
+ (value != LED_OFF));
}
-static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
- BIT(TRIGGER_NETDEV_HALF_DUPLEX) |
- BIT(TRIGGER_NETDEV_LINK) |
- BIT(TRIGGER_NETDEV_LINK_10) |
- BIT(TRIGGER_NETDEV_LINK_100) |
- BIT(TRIGGER_NETDEV_LINK_1000) |
- BIT(TRIGGER_NETDEV_RX) |
- BIT(TRIGGER_NETDEV_TX));
+static const unsigned long supported_triggers =
+ BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
+ BIT(TRIGGER_NETDEV_HALF_DUPLEX) |
+ BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_RX) |
+ BIT(TRIGGER_NETDEV_TX);
static int mt798x_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
unsigned long rules)
{
- if (index > 1)
- return -EINVAL;
-
- /* All combinations of the supported triggers are allowed */
- if (rules & ~supported_triggers)
- return -EOPNOTSUPP;
-
- return 0;
-};
+ return mtk_phy_led_hw_is_supported(phydev, index, rules,
+ supported_triggers);
+}
static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index,
unsigned long *rules)
{
- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + (index ? 16 : 0);
- unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0);
- unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0);
- struct mtk_socphy_priv *priv = phydev->priv;
- int on, blink;
-
- if (index > 1)
- return -EINVAL;
-
- on = phy_read_mmd(phydev, MDIO_MMD_VEND2,
- index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL);
-
- if (on < 0)
- return -EIO;
-
- blink = phy_read_mmd(phydev, MDIO_MMD_VEND2,
- index ? MTK_PHY_LED1_BLINK_CTRL :
- MTK_PHY_LED0_BLINK_CTRL);
- if (blink < 0)
- return -EIO;
-
- if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX |
- MTK_PHY_LED_ON_LINKDOWN)) ||
- (blink & (MTK_PHY_LED_BLINK_RX | MTK_PHY_LED_BLINK_TX)))
- set_bit(bit_netdev, &priv->led_state);
- else
- clear_bit(bit_netdev, &priv->led_state);
-
- if (on & MTK_PHY_LED_ON_FORCE_ON)
- set_bit(bit_on, &priv->led_state);
- else
- clear_bit(bit_on, &priv->led_state);
-
- if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK)
- set_bit(bit_blink, &priv->led_state);
- else
- clear_bit(bit_blink, &priv->led_state);
-
- if (!rules)
- return 0;
-
- if (on & MTK_PHY_LED_ON_LINK)
- *rules |= BIT(TRIGGER_NETDEV_LINK);
-
- if (on & MTK_PHY_LED_ON_LINK10)
- *rules |= BIT(TRIGGER_NETDEV_LINK_10);
-
- if (on & MTK_PHY_LED_ON_LINK100)
- *rules |= BIT(TRIGGER_NETDEV_LINK_100);
-
- if (on & MTK_PHY_LED_ON_LINK1000)
- *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
-
- if (on & MTK_PHY_LED_ON_FDX)
- *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX);
-
- if (on & MTK_PHY_LED_ON_HDX)
- *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX);
-
- if (blink & MTK_PHY_LED_BLINK_RX)
- *rules |= BIT(TRIGGER_NETDEV_RX);
-
- if (blink & MTK_PHY_LED_BLINK_TX)
- *rules |= BIT(TRIGGER_NETDEV_TX);
-
- return 0;
+ return mtk_phy_led_hw_ctrl_get(phydev, index, rules,
+ MTK_GPHY_LED_ON_SET,
+ MTK_GPHY_LED_RX_BLINK_SET,
+ MTK_GPHY_LED_TX_BLINK_SET);
};
static int mt798x_phy_led_hw_control_set(struct phy_device *phydev, u8 index,
unsigned long rules)
{
- unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0);
- struct mtk_socphy_priv *priv = phydev->priv;
- u16 on = 0, blink = 0;
- int ret;
-
- if (index > 1)
- return -EINVAL;
-
- if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
- on |= MTK_PHY_LED_ON_FDX;
-
- if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
- on |= MTK_PHY_LED_ON_HDX;
-
- if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK)))
- on |= MTK_PHY_LED_ON_LINK10;
-
- if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
- on |= MTK_PHY_LED_ON_LINK100;
-
- if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
- on |= MTK_PHY_LED_ON_LINK1000;
-
- if (rules & BIT(TRIGGER_NETDEV_RX)) {
- blink |= (on & MTK_PHY_LED_ON_LINK) ?
- (((on & MTK_PHY_LED_ON_LINK10) ? MTK_PHY_LED_BLINK_10RX : 0) |
- ((on & MTK_PHY_LED_ON_LINK100) ? MTK_PHY_LED_BLINK_100RX : 0) |
- ((on & MTK_PHY_LED_ON_LINK1000) ? MTK_PHY_LED_BLINK_1000RX : 0)) :
- MTK_PHY_LED_BLINK_RX;
- }
-
- if (rules & BIT(TRIGGER_NETDEV_TX)) {
- blink |= (on & MTK_PHY_LED_ON_LINK) ?
- (((on & MTK_PHY_LED_ON_LINK10) ? MTK_PHY_LED_BLINK_10TX : 0) |
- ((on & MTK_PHY_LED_ON_LINK100) ? MTK_PHY_LED_BLINK_100TX : 0) |
- ((on & MTK_PHY_LED_ON_LINK1000) ? MTK_PHY_LED_BLINK_1000TX : 0)) :
- MTK_PHY_LED_BLINK_TX;
- }
-
- if (blink || on)
- set_bit(bit_netdev, &priv->led_state);
- else
- clear_bit(bit_netdev, &priv->led_state);
-
- ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ?
- MTK_PHY_LED1_ON_CTRL :
- MTK_PHY_LED0_ON_CTRL,
- MTK_PHY_LED_ON_FDX |
- MTK_PHY_LED_ON_HDX |
- MTK_PHY_LED_ON_LINK,
- on);
-
- if (ret)
- return ret;
-
- return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ?
- MTK_PHY_LED1_BLINK_CTRL :
- MTK_PHY_LED0_BLINK_CTRL, blink);
+ return mtk_phy_led_hw_ctrl_set(phydev, index, rules,
+ MTK_GPHY_LED_ON_SET,
+ MTK_GPHY_LED_RX_BLINK_SET,
+ MTK_GPHY_LED_TX_BLINK_SET);
};
static bool mt7988_phy_led_get_polarity(struct phy_device *phydev, int led_num)
@@ -1398,7 +1220,8 @@ static int mt7988_phy_fix_leds_polarities(struct phy_device *phydev)
/* Only now setup pinctrl to avoid bogus blinking */
pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led");
if (IS_ERR(pinctrl))
- dev_err(&phydev->mdio.bus->dev, "Failed to setup PHY LED pinctrl\n");
+ dev_err(&phydev->mdio.bus->dev,
+ "Failed to setup PHY LED pinctrl\n");
return 0;
}
@@ -1415,7 +1238,7 @@ static int mt7988_phy_probe_shared(struct phy_device *phydev)
* LED_C and LED_D respectively. At the same time those pins are used to
* bootstrap configuration of the reference clock source (LED_A),
* DRAM DDRx16b x2/x1 (LED_B) and boot device (LED_C, LED_D).
- * In practise this is done using a LED and a resistor pulling the pin
+ * In practice this is done using a LED and a resistor pulling the pin
* either to GND or to VIO.
* The detected value at boot time is accessible at run-time using the
* TPBANK0 register located in the gpio base of the pinctrl, in order
@@ -1437,14 +1260,6 @@ static int mt7988_phy_probe_shared(struct phy_device *phydev)
return 0;
}
-static void mt798x_phy_leds_state_init(struct phy_device *phydev)
-{
- int i;
-
- for (i = 0; i < 2; ++i)
- mt798x_phy_led_hw_control_get(phydev, i, NULL);
-}
-
static int mt7988_phy_probe(struct phy_device *phydev)
{
struct mtk_socphy_shared *shared;
@@ -1470,7 +1285,7 @@ static int mt7988_phy_probe(struct phy_device *phydev)
phydev->priv = priv;
- mt798x_phy_leds_state_init(phydev);
+ mtk_phy_leds_state_init(phydev);
err = mt7988_phy_fix_leds_polarities(phydev);
if (err)
@@ -1497,7 +1312,7 @@ static int mt7981_phy_probe(struct phy_device *phydev)
phydev->priv = priv;
- mt798x_phy_leds_state_init(phydev);
+ mtk_phy_leds_state_init(phydev);
return mt798x_phy_calibration(phydev);
}
@@ -1512,8 +1327,8 @@ static struct phy_driver mtk_socphy_driver[] = {
.probe = mt7981_phy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .read_page = mtk_socphy_read_page,
- .write_page = mtk_socphy_write_page,
+ .read_page = mtk_phy_read_page,
+ .write_page = mtk_phy_write_page,
.led_blink_set = mt798x_phy_led_blink_set,
.led_brightness_set = mt798x_phy_led_brightness_set,
.led_hw_is_supported = mt798x_phy_led_hw_is_supported,
@@ -1529,8 +1344,8 @@ static struct phy_driver mtk_socphy_driver[] = {
.probe = mt7988_phy_probe,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .read_page = mtk_socphy_read_page,
- .write_page = mtk_socphy_write_page,
+ .read_page = mtk_phy_read_page,
+ .write_page = mtk_phy_write_page,
.led_blink_set = mt798x_phy_led_blink_set,
.led_brightness_set = mt798x_phy_led_brightness_set,
.led_hw_is_supported = mt798x_phy_led_hw_is_supported,
diff --git a/drivers/net/phy/mediatek-ge.c b/drivers/net/phy/mediatek/mtk-ge.c
index 54ea64a37ab3..ed2617bc20f4 100644
--- a/drivers/net/phy/mediatek-ge.c
+++ b/drivers/net/phy/mediatek/mtk-ge.c
@@ -3,6 +3,11 @@
#include <linux/module.h>
#include <linux/phy.h>
+#include "mtk.h"
+
+#define MTK_GPHY_ID_MT7530 0x03a29412
+#define MTK_GPHY_ID_MT7531 0x03a29441
+
#define MTK_EXT_PAGE_ACCESS 0x1f
#define MTK_PHY_PAGE_STANDARD 0x0000
#define MTK_PHY_PAGE_EXTENDED 0x0001
@@ -11,16 +16,6 @@
#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30
#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5
-static int mtk_gephy_read_page(struct phy_device *phydev)
-{
- return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
-}
-
-static int mtk_gephy_write_page(struct phy_device *phydev, int page)
-{
- return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
-}
-
static void mtk_gephy_config_init(struct phy_device *phydev)
{
/* Enable HW auto downshift */
@@ -67,7 +62,7 @@ static int mt7531_phy_config_init(struct phy_device *phydev)
static struct phy_driver mtk_gephy_driver[] = {
{
- PHY_ID_MATCH_EXACT(0x03a29412),
+ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530),
.name = "MediaTek MT7530 PHY",
.config_init = mt7530_phy_config_init,
/* Interrupts are handled by the switch, not the PHY
@@ -77,11 +72,11 @@ static struct phy_driver mtk_gephy_driver[] = {
.handle_interrupt = genphy_handle_interrupt_no_ack,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .read_page = mtk_gephy_read_page,
- .write_page = mtk_gephy_write_page,
+ .read_page = mtk_phy_read_page,
+ .write_page = mtk_phy_write_page,
},
{
- PHY_ID_MATCH_EXACT(0x03a29441),
+ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7531),
.name = "MediaTek MT7531 PHY",
.config_init = mt7531_phy_config_init,
/* Interrupts are handled by the switch, not the PHY
@@ -91,16 +86,16 @@ static struct phy_driver mtk_gephy_driver[] = {
.handle_interrupt = genphy_handle_interrupt_no_ack,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .read_page = mtk_gephy_read_page,
- .write_page = mtk_gephy_write_page,
+ .read_page = mtk_phy_read_page,
+ .write_page = mtk_phy_write_page,
},
};
module_phy_driver(mtk_gephy_driver);
static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = {
- { PHY_ID_MATCH_EXACT(0x03a29441) },
- { PHY_ID_MATCH_EXACT(0x03a29412) },
+ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530) },
+ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7531) },
{ }
};
diff --git a/drivers/net/phy/mediatek/mtk-phy-lib.c b/drivers/net/phy/mediatek/mtk-phy-lib.c
new file mode 100644
index 000000000000..98a09d670e9c
--- /dev/null
+++ b/drivers/net/phy/mediatek/mtk-phy-lib.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/phy.h>
+#include <linux/module.h>
+
+#include <linux/netdevice.h>
+
+#include "mtk.h"
+
+int mtk_phy_read_page(struct phy_device *phydev)
+{
+ return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
+}
+EXPORT_SYMBOL_GPL(mtk_phy_read_page);
+
+int mtk_phy_write_page(struct phy_device *phydev, int page)
+{
+ return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
+}
+EXPORT_SYMBOL_GPL(mtk_phy_write_page);
+
+int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules,
+ unsigned long supported_triggers)
+{
+ if (index > 1)
+ return -EINVAL;
+
+ /* All combinations of the supported triggers are allowed */
+ if (rules & ~supported_triggers)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_phy_led_hw_is_supported);
+
+int mtk_phy_led_hw_ctrl_get(struct phy_device *phydev, u8 index,
+ unsigned long *rules, u16 on_set,
+ u16 rx_blink_set, u16 tx_blink_set)
+{
+ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK +
+ (index ? 16 : 0);
+ unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0);
+ unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0);
+ struct mtk_socphy_priv *priv = phydev->priv;
+ int on, blink;
+
+ if (index > 1)
+ return -EINVAL;
+
+ on = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL);
+
+ if (on < 0)
+ return -EIO;
+
+ blink = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ index ? MTK_PHY_LED1_BLINK_CTRL :
+ MTK_PHY_LED0_BLINK_CTRL);
+ if (blink < 0)
+ return -EIO;
+
+ if ((on & (on_set | MTK_PHY_LED_ON_FDX |
+ MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) ||
+ (blink & (rx_blink_set | tx_blink_set)))
+ set_bit(bit_netdev, &priv->led_state);
+ else
+ clear_bit(bit_netdev, &priv->led_state);
+
+ if (on & MTK_PHY_LED_ON_FORCE_ON)
+ set_bit(bit_on, &priv->led_state);
+ else
+ clear_bit(bit_on, &priv->led_state);
+
+ if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK)
+ set_bit(bit_blink, &priv->led_state);
+ else
+ clear_bit(bit_blink, &priv->led_state);
+
+ if (!rules)
+ return 0;
+
+ if (on & on_set)
+ *rules |= BIT(TRIGGER_NETDEV_LINK);
+
+ if (on & MTK_PHY_LED_ON_LINK10)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_10);
+
+ if (on & MTK_PHY_LED_ON_LINK100)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_100);
+
+ if (on & MTK_PHY_LED_ON_LINK1000)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
+
+ if (on & MTK_PHY_LED_ON_LINK2500)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_2500);
+
+ if (on & MTK_PHY_LED_ON_FDX)
+ *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX);
+
+ if (on & MTK_PHY_LED_ON_HDX)
+ *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX);
+
+ if (blink & rx_blink_set)
+ *rules |= BIT(TRIGGER_NETDEV_RX);
+
+ if (blink & tx_blink_set)
+ *rules |= BIT(TRIGGER_NETDEV_TX);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_phy_led_hw_ctrl_get);
+
+int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index,
+ unsigned long rules, u16 on_set,
+ u16 rx_blink_set, u16 tx_blink_set)
+{
+ unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0);
+ struct mtk_socphy_priv *priv = phydev->priv;
+ u16 on = 0, blink = 0;
+ int ret;
+
+ if (index > 1)
+ return -EINVAL;
+
+ if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
+ on |= MTK_PHY_LED_ON_FDX;
+
+ if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
+ on |= MTK_PHY_LED_ON_HDX;
+
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK)))
+ on |= MTK_PHY_LED_ON_LINK10;
+
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
+ on |= MTK_PHY_LED_ON_LINK100;
+
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
+ on |= MTK_PHY_LED_ON_LINK1000;
+
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
+ on |= MTK_PHY_LED_ON_LINK2500;
+
+ if (rules & BIT(TRIGGER_NETDEV_RX)) {
+ if (on & on_set) {
+ if (on & MTK_PHY_LED_ON_LINK10)
+ blink |= MTK_PHY_LED_BLINK_10RX;
+ if (on & MTK_PHY_LED_ON_LINK100)
+ blink |= MTK_PHY_LED_BLINK_100RX;
+ if (on & MTK_PHY_LED_ON_LINK1000)
+ blink |= MTK_PHY_LED_BLINK_1000RX;
+ if (on & MTK_PHY_LED_ON_LINK2500)
+ blink |= MTK_PHY_LED_BLINK_2500RX;
+ } else {
+ blink |= rx_blink_set;
+ }
+ }
+
+ if (rules & BIT(TRIGGER_NETDEV_TX)) {
+ if (on & on_set) {
+ if (on & MTK_PHY_LED_ON_LINK10)
+ blink |= MTK_PHY_LED_BLINK_10TX;
+ if (on & MTK_PHY_LED_ON_LINK100)
+ blink |= MTK_PHY_LED_BLINK_100TX;
+ if (on & MTK_PHY_LED_ON_LINK1000)
+ blink |= MTK_PHY_LED_BLINK_1000TX;
+ if (on & MTK_PHY_LED_ON_LINK2500)
+ blink |= MTK_PHY_LED_BLINK_2500TX;
+ } else {
+ blink |= tx_blink_set;
+ }
+ }
+
+ if (blink || on)
+ set_bit(bit_netdev, &priv->led_state);
+ else
+ clear_bit(bit_netdev, &priv->led_state);
+
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ?
+ MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL,
+ MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX | on_set,
+ on);
+
+ if (ret)
+ return ret;
+
+ return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ?
+ MTK_PHY_LED1_BLINK_CTRL :
+ MTK_PHY_LED0_BLINK_CTRL, blink);
+}
+EXPORT_SYMBOL_GPL(mtk_phy_led_hw_ctrl_set);
+
+int mtk_phy_led_num_dly_cfg(u8 index, unsigned long *delay_on,
+ unsigned long *delay_off, bool *blinking)
+{
+ if (index > 1)
+ return -EINVAL;
+
+ if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) {
+ *blinking = true;
+ *delay_on = 50;
+ *delay_off = 50;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_phy_led_num_dly_cfg);
+
+int mtk_phy_hw_led_on_set(struct phy_device *phydev, u8 index,
+ u16 led_on_mask, bool on)
+{
+ unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0);
+ struct mtk_socphy_priv *priv = phydev->priv;
+ bool changed;
+
+ if (on)
+ changed = !test_and_set_bit(bit_on, &priv->led_state);
+ else
+ changed = !!test_and_clear_bit(bit_on, &priv->led_state);
+
+ changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV +
+ (index ? 16 : 0), &priv->led_state);
+ if (changed)
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ?
+ MTK_PHY_LED1_ON_CTRL :
+ MTK_PHY_LED0_ON_CTRL,
+ led_on_mask,
+ on ? MTK_PHY_LED_ON_FORCE_ON : 0);
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_phy_hw_led_on_set);
+
+int mtk_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, bool blinking)
+{
+ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK +
+ (index ? 16 : 0);
+ struct mtk_socphy_priv *priv = phydev->priv;
+ bool changed;
+
+ if (blinking)
+ changed = !test_and_set_bit(bit_blink, &priv->led_state);
+ else
+ changed = !!test_and_clear_bit(bit_blink, &priv->led_state);
+
+ changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV +
+ (index ? 16 : 0), &priv->led_state);
+ if (changed)
+ return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ?
+ MTK_PHY_LED1_BLINK_CTRL :
+ MTK_PHY_LED0_BLINK_CTRL,
+ blinking ?
+ MTK_PHY_LED_BLINK_FORCE_BLINK : 0);
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_phy_hw_led_blink_set);
+
+void mtk_phy_leds_state_init(struct phy_device *phydev)
+{
+ int i;
+
+ for (i = 0; i < 2; ++i)
+ phydev->drv->led_hw_control_get(phydev, i, NULL);
+}
+EXPORT_SYMBOL_GPL(mtk_phy_leds_state_init);
+
+MODULE_DESCRIPTION("MediaTek Ethernet PHY driver common");
+MODULE_AUTHOR("Sky Huang <SkyLake.Huang@mediatek.com>");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/mediatek/mtk.h b/drivers/net/phy/mediatek/mtk.h
new file mode 100644
index 000000000000..63d9fe179b8f
--- /dev/null
+++ b/drivers/net/phy/mediatek/mtk.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Common definition for Mediatek Ethernet PHYs
+ * Author: SkyLake Huang <SkyLake.Huang@mediatek.com>
+ * Copyright (c) 2024 MediaTek Inc.
+ */
+
+#ifndef _MTK_EPHY_H_
+#define _MTK_EPHY_H_
+
+#define MTK_EXT_PAGE_ACCESS 0x1f
+
+/* Registers on MDIO_MMD_VEND2 */
+#define MTK_PHY_LED0_ON_CTRL 0x24
+#define MTK_PHY_LED1_ON_CTRL 0x26
+#define MTK_GPHY_LED_ON_MASK GENMASK(6, 0)
+#define MTK_2P5GPHY_LED_ON_MASK GENMASK(7, 0)
+#define MTK_PHY_LED_ON_LINK1000 BIT(0)
+#define MTK_PHY_LED_ON_LINK100 BIT(1)
+#define MTK_PHY_LED_ON_LINK10 BIT(2)
+#define MTK_PHY_LED_ON_LINKDOWN BIT(3)
+#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */
+#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */
+#define MTK_PHY_LED_ON_FORCE_ON BIT(6)
+#define MTK_PHY_LED_ON_LINK2500 BIT(7)
+#define MTK_PHY_LED_ON_POLARITY BIT(14)
+#define MTK_PHY_LED_ON_ENABLE BIT(15)
+
+#define MTK_PHY_LED0_BLINK_CTRL 0x25
+#define MTK_PHY_LED1_BLINK_CTRL 0x27
+#define MTK_PHY_LED_BLINK_1000TX BIT(0)
+#define MTK_PHY_LED_BLINK_1000RX BIT(1)
+#define MTK_PHY_LED_BLINK_100TX BIT(2)
+#define MTK_PHY_LED_BLINK_100RX BIT(3)
+#define MTK_PHY_LED_BLINK_10TX BIT(4)
+#define MTK_PHY_LED_BLINK_10RX BIT(5)
+#define MTK_PHY_LED_BLINK_COLLISION BIT(6)
+#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7)
+#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8)
+#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9)
+#define MTK_PHY_LED_BLINK_2500TX BIT(10)
+#define MTK_PHY_LED_BLINK_2500RX BIT(11)
+
+#define MTK_GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK1000 | \
+ MTK_PHY_LED_ON_LINK100 | \
+ MTK_PHY_LED_ON_LINK10)
+#define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \
+ MTK_PHY_LED_BLINK_100RX | \
+ MTK_PHY_LED_BLINK_10RX)
+#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \
+ MTK_PHY_LED_BLINK_100RX | \
+ MTK_PHY_LED_BLINK_10RX)
+
+#define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \
+ MTK_GPHY_LED_ON_SET)
+#define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \
+ MTK_GPHY_LED_RX_BLINK_SET)
+#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \
+ MTK_GPHY_LED_TX_BLINK_SET)
+
+#define MTK_PHY_LED_STATE_FORCE_ON 0
+#define MTK_PHY_LED_STATE_FORCE_BLINK 1
+#define MTK_PHY_LED_STATE_NETDEV 2
+
+struct mtk_socphy_priv {
+ unsigned long led_state;
+};
+
+int mtk_phy_read_page(struct phy_device *phydev);
+int mtk_phy_write_page(struct phy_device *phydev, int page);
+
+int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules,
+ unsigned long supported_triggers);
+int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index,
+ unsigned long rules, u16 on_set,
+ u16 rx_blink_set, u16 tx_blink_set);
+int mtk_phy_led_hw_ctrl_get(struct phy_device *phydev, u8 index,
+ unsigned long *rules, u16 on_set,
+ u16 rx_blink_set, u16 tx_blink_set);
+int mtk_phy_led_num_dly_cfg(u8 index, unsigned long *delay_on,
+ unsigned long *delay_off, bool *blinking);
+int mtk_phy_hw_led_on_set(struct phy_device *phydev, u8 index,
+ u16 led_on_mask, bool on);
+int mtk_phy_hw_led_blink_set(struct phy_device *phydev, u8 index,
+ bool blinking);
+void mtk_phy_leds_state_init(struct phy_device *phydev);
+
+#endif /* _MTK_EPHY_H_ */
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 65b0a3115e14..3ef508840674 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -2004,7 +2004,7 @@ static int ksz9477_config_init(struct phy_device *phydev)
* in this switch shall be regarded as broken.
*/
if (phydev->dev_flags & MICREL_NO_EEE)
- phydev->eee_broken_modes = -1;
+ linkmode_fill(phydev->eee_broken_modes);
return kszphy_config_init(phydev);
}
@@ -2018,10 +2018,8 @@ static void kszphy_get_strings(struct phy_device *phydev, u8 *data)
{
int i;
- for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) {
- strscpy(data + i * ETH_GSTRING_LEN,
- kszphy_hw_stats[i].string, ETH_GSTRING_LEN);
- }
+ for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++)
+ ethtool_puts(&data, kszphy_hw_stats[i].string);
}
static u64 kszphy_get_stat(struct phy_device *phydev, int i)
diff --git a/drivers/net/phy/microchip_t1.c b/drivers/net/phy/microchip_t1.c
index a5ef8fe50704..b17bf6708003 100644
--- a/drivers/net/phy/microchip_t1.c
+++ b/drivers/net/phy/microchip_t1.c
@@ -6,6 +6,7 @@
#include <linux/delay.h>
#include <linux/mii.h>
#include <linux/phy.h>
+#include <linux/sort.h>
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include <linux/bitfield.h>
@@ -226,6 +227,47 @@
#define MICROCHIP_CABLE_MAX_TIME_DIFF \
(MICROCHIP_CABLE_MIN_TIME_DIFF + MICROCHIP_CABLE_TIME_MARGIN)
+#define LAN887X_INT_STS 0xf000
+#define LAN887X_INT_MSK 0xf001
+#define LAN887X_INT_MSK_T1_PHY_INT_MSK BIT(2)
+#define LAN887X_INT_MSK_LINK_UP_MSK BIT(1)
+#define LAN887X_INT_MSK_LINK_DOWN_MSK BIT(0)
+
+#define LAN887X_MX_CHIP_TOP_LINK_MSK (LAN887X_INT_MSK_LINK_UP_MSK |\
+ LAN887X_INT_MSK_LINK_DOWN_MSK)
+
+#define LAN887X_MX_CHIP_TOP_ALL_MSK (LAN887X_INT_MSK_T1_PHY_INT_MSK |\
+ LAN887X_MX_CHIP_TOP_LINK_MSK)
+
+#define LAN887X_COEFF_PWR_DN_CONFIG_100 0x0404
+#define LAN887X_COEFF_PWR_DN_CONFIG_100_V 0x16d6
+#define LAN887X_SQI_CONFIG_100 0x042e
+#define LAN887X_SQI_CONFIG_100_V 0x9572
+#define LAN887X_SQI_MSE_100 0x483
+
+#define LAN887X_POKE_PEEK_100 0x040d
+#define LAN887X_POKE_PEEK_100_EN BIT(0)
+
+#define LAN887X_COEFF_MOD_CONFIG 0x080d
+#define LAN887X_COEFF_MOD_CONFIG_DCQ_COEFF_EN BIT(8)
+
+#define LAN887X_DCQ_SQI_STATUS 0x08b2
+
+/* SQI raw samples count */
+#define SQI_SAMPLES 200
+
+/* Samples percentage considered for SQI calculation */
+#define SQI_INLINERS_PERCENT 60
+
+/* Samples count considered for SQI calculation */
+#define SQI_INLIERS_NUM (SQI_SAMPLES * SQI_INLINERS_PERCENT / 100)
+
+/* Start offset of samples */
+#define SQI_INLIERS_START ((SQI_SAMPLES - SQI_INLIERS_NUM) / 2)
+
+/* End offset of samples */
+#define SQI_INLIERS_END (SQI_INLIERS_START + SQI_INLIERS_NUM)
+
#define DRIVER_AUTHOR "Nisar Sayed <nisar.sayed@microchip.com>"
#define DRIVER_DESC "Microchip LAN87XX/LAN937x/LAN887x T1 PHY driver"
@@ -1474,6 +1516,49 @@ static void lan887x_get_strings(struct phy_device *phydev, u8 *data)
ethtool_puts(&data, lan887x_hw_stats[i].string);
}
+static int lan887x_config_intr(struct phy_device *phydev)
+{
+ int rc;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ /* Clear the interrupt status before enabling interrupts */
+ rc = phy_read_mmd(phydev, MDIO_MMD_VEND1, LAN887X_INT_STS);
+ if (rc < 0)
+ return rc;
+
+ /* Unmask for enabling interrupt */
+ rc = phy_write_mmd(phydev, MDIO_MMD_VEND1, LAN887X_INT_MSK,
+ (u16)~LAN887X_MX_CHIP_TOP_ALL_MSK);
+ } else {
+ rc = phy_write_mmd(phydev, MDIO_MMD_VEND1, LAN887X_INT_MSK,
+ GENMASK(15, 0));
+ if (rc < 0)
+ return rc;
+
+ rc = phy_read_mmd(phydev, MDIO_MMD_VEND1, LAN887X_INT_STS);
+ }
+
+ return rc < 0 ? rc : 0;
+}
+
+static irqreturn_t lan887x_handle_interrupt(struct phy_device *phydev)
+{
+ int irq_status;
+
+ irq_status = phy_read_mmd(phydev, MDIO_MMD_VEND1, LAN887X_INT_STS);
+ if (irq_status < 0) {
+ phy_error(phydev);
+ return IRQ_NONE;
+ }
+
+ if (irq_status & LAN887X_MX_CHIP_TOP_LINK_MSK) {
+ phy_trigger_machine(phydev);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
static int lan887x_cd_reset(struct phy_device *phydev,
enum cable_diag_state cd_done)
{
@@ -1504,6 +1589,10 @@ static int lan887x_cd_reset(struct phy_device *phydev,
if (rc < 0)
return rc;
+ rc = lan887x_config_intr(phydev);
+ if (rc < 0)
+ return rc;
+
rc = lan887x_phy_reconfig(phydev);
if (rc < 0)
return rc;
@@ -1830,6 +1919,145 @@ static int lan887x_cable_test_get_status(struct phy_device *phydev,
return lan887x_cable_test_report(phydev);
}
+/* Compare block to sort in ascending order */
+static int sqi_compare(const void *a, const void *b)
+{
+ return *(u16 *)a - *(u16 *)b;
+}
+
+static int lan887x_get_sqi_100M(struct phy_device *phydev)
+{
+ u16 rawtable[SQI_SAMPLES];
+ u32 sqiavg = 0;
+ u8 sqinum = 0;
+ int rc, i;
+
+ /* Configuration of SQI 100M */
+ rc = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+ LAN887X_COEFF_PWR_DN_CONFIG_100,
+ LAN887X_COEFF_PWR_DN_CONFIG_100_V);
+ if (rc < 0)
+ return rc;
+
+ rc = phy_write_mmd(phydev, MDIO_MMD_VEND1, LAN887X_SQI_CONFIG_100,
+ LAN887X_SQI_CONFIG_100_V);
+ if (rc < 0)
+ return rc;
+
+ rc = phy_read_mmd(phydev, MDIO_MMD_VEND1, LAN887X_SQI_CONFIG_100);
+ if (rc != LAN887X_SQI_CONFIG_100_V)
+ return -EINVAL;
+
+ rc = phy_modify_mmd(phydev, MDIO_MMD_VEND1, LAN887X_POKE_PEEK_100,
+ LAN887X_POKE_PEEK_100_EN,
+ LAN887X_POKE_PEEK_100_EN);
+ if (rc < 0)
+ return rc;
+
+ /* Required before reading register
+ * otherwise it will return high value
+ */
+ msleep(50);
+
+ /* Link check before raw readings */
+ rc = genphy_c45_read_link(phydev);
+ if (rc < 0)
+ return rc;
+
+ if (!phydev->link)
+ return -ENETDOWN;
+
+ /* Get 200 SQI raw readings */
+ for (i = 0; i < SQI_SAMPLES; i++) {
+ rc = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+ LAN887X_POKE_PEEK_100,
+ LAN887X_POKE_PEEK_100_EN);
+ if (rc < 0)
+ return rc;
+
+ rc = phy_read_mmd(phydev, MDIO_MMD_VEND1,
+ LAN887X_SQI_MSE_100);
+ if (rc < 0)
+ return rc;
+
+ rawtable[i] = (u16)rc;
+ }
+
+ /* Link check after raw readings */
+ rc = genphy_c45_read_link(phydev);
+ if (rc < 0)
+ return rc;
+
+ if (!phydev->link)
+ return -ENETDOWN;
+
+ /* Sort SQI raw readings in ascending order */
+ sort(rawtable, SQI_SAMPLES, sizeof(u16), sqi_compare, NULL);
+
+ /* Keep inliers and discard outliers */
+ for (i = SQI_INLIERS_START; i < SQI_INLIERS_END; i++)
+ sqiavg += rawtable[i];
+
+ /* Handle invalid samples */
+ if (sqiavg != 0) {
+ /* Get SQI average */
+ sqiavg /= SQI_INLIERS_NUM;
+
+ if (sqiavg < 75)
+ sqinum = 7;
+ else if (sqiavg < 94)
+ sqinum = 6;
+ else if (sqiavg < 119)
+ sqinum = 5;
+ else if (sqiavg < 150)
+ sqinum = 4;
+ else if (sqiavg < 189)
+ sqinum = 3;
+ else if (sqiavg < 237)
+ sqinum = 2;
+ else if (sqiavg < 299)
+ sqinum = 1;
+ else
+ sqinum = 0;
+ }
+
+ return sqinum;
+}
+
+static int lan887x_get_sqi(struct phy_device *phydev)
+{
+ int rc, val;
+
+ if (phydev->speed != SPEED_1000 &&
+ phydev->speed != SPEED_100)
+ return -ENETDOWN;
+
+ if (phydev->speed == SPEED_100)
+ return lan887x_get_sqi_100M(phydev);
+
+ /* Writing DCQ_COEFF_EN to trigger a SQI read */
+ rc = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
+ LAN887X_COEFF_MOD_CONFIG,
+ LAN887X_COEFF_MOD_CONFIG_DCQ_COEFF_EN);
+ if (rc < 0)
+ return rc;
+
+ /* Wait for DCQ done */
+ rc = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
+ LAN887X_COEFF_MOD_CONFIG, val, ((val &
+ LAN887X_COEFF_MOD_CONFIG_DCQ_COEFF_EN) !=
+ LAN887X_COEFF_MOD_CONFIG_DCQ_COEFF_EN),
+ 10, 200, true);
+ if (rc < 0)
+ return rc;
+
+ rc = phy_read_mmd(phydev, MDIO_MMD_VEND1, LAN887X_DCQ_SQI_STATUS);
+ if (rc < 0)
+ return rc;
+
+ return FIELD_GET(T1_DCQ_SQI_MSK, rc);
+}
+
static struct phy_driver microchip_t1_phy_driver[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_LAN87XX),
@@ -1881,6 +2109,11 @@ static struct phy_driver microchip_t1_phy_driver[] = {
.read_status = genphy_c45_read_status,
.cable_test_start = lan887x_cable_test_start,
.cable_test_get_status = lan887x_cable_test_get_status,
+ .config_intr = lan887x_config_intr,
+ .handle_interrupt = lan887x_handle_interrupt,
+ .get_sqi = lan887x_get_sqi,
+ .get_sqi_max = lan87xx_get_sqi_max,
+ .set_loopback = genphy_c45_loopback,
}
};
diff --git a/drivers/net/phy/microchip_t1s.c b/drivers/net/phy/microchip_t1s.c
index 3614839a8e51..75d291154b4c 100644
--- a/drivers/net/phy/microchip_t1s.c
+++ b/drivers/net/phy/microchip_t1s.c
@@ -3,8 +3,8 @@
* Driver for Microchip 10BASE-T1S PHYs
*
* Support: Microchip Phys:
- * lan8670/1/2 Rev.B1
- * lan8650/1 Rev.B0 Internal PHYs
+ * lan8670/1/2 Rev.B1/C1/C2
+ * lan8650/1 Rev.B0/B1 Internal PHYs
*/
#include <linux/kernel.h>
@@ -12,7 +12,10 @@
#include <linux/phy.h>
#define PHY_ID_LAN867X_REVB1 0x0007C162
-#define PHY_ID_LAN865X_REVB0 0x0007C1B3
+#define PHY_ID_LAN867X_REVC1 0x0007C164
+#define PHY_ID_LAN867X_REVC2 0x0007C165
+/* Both Rev.B0 and B1 clause 22 PHYID's are same due to B1 chip limitation */
+#define PHY_ID_LAN865X_REVB 0x0007C1B3
#define LAN867X_REG_STS2 0x0019
@@ -23,6 +26,12 @@
#define LAN865X_REG_CFGPARAM_CTRL 0x00DA
#define LAN865X_REG_STS2 0x0019
+/* Collision Detector Control 0 Register */
+#define LAN86XX_REG_COL_DET_CTRL0 0x0087
+#define COL_DET_CTRL0_ENABLE_BIT_MASK BIT(15)
+#define COL_DET_ENABLE BIT(15)
+#define COL_DET_DISABLE 0x0000
+
#define LAN865X_CFGPARAM_READ_ENABLE BIT(1)
/* The arrays below are pulled from the following table from AN1699
@@ -59,29 +68,45 @@ static const u16 lan867x_revb1_fixup_masks[12] = {
0x0600, 0x7F00, 0x2000, 0xFFFF,
};
-/* LAN865x Rev.B0 configuration parameters from AN1760 */
-static const u32 lan865x_revb0_fixup_registers[28] = {
- 0x0091, 0x0081, 0x0043, 0x0044,
- 0x0045, 0x0053, 0x0054, 0x0055,
- 0x0040, 0x0050, 0x00D0, 0x00E9,
- 0x00F5, 0x00F4, 0x00F8, 0x00F9,
+/* LAN865x Rev.B0/B1 configuration parameters from AN1760
+ * As per the Configuration Application Note AN1760 published in the below link,
+ * https://www.microchip.com/en-us/application-notes/an1760
+ * Revision F (DS60001760G - June 2024)
+ */
+static const u32 lan865x_revb_fixup_registers[17] = {
+ 0x00D0, 0x00E0, 0x00E9, 0x00F5,
+ 0x00F4, 0x00F8, 0x00F9, 0x0081,
+ 0x0091, 0x0043, 0x0044, 0x0045,
+ 0x0053, 0x0054, 0x0055, 0x0040,
+ 0x0050,
+};
+
+static const u16 lan865x_revb_fixup_values[17] = {
+ 0x3F31, 0xC000, 0x9E50, 0x1CF8,
+ 0xC020, 0xB900, 0x4E53, 0x0080,
+ 0x9660, 0x00FF, 0xFFFF, 0x0000,
+ 0x00FF, 0xFFFF, 0x0000, 0x0002,
+ 0x0002,
+};
+
+static const u16 lan865x_revb_fixup_cfg_regs[2] = {
+ 0x0084, 0x008A,
+};
+
+static const u32 lan865x_revb_sqi_fixup_regs[12] = {
0x00B0, 0x00B1, 0x00B2, 0x00B3,
0x00B4, 0x00B5, 0x00B6, 0x00B7,
0x00B8, 0x00B9, 0x00BA, 0x00BB,
};
-static const u16 lan865x_revb0_fixup_values[28] = {
- 0x9660, 0x00C0, 0x00FF, 0xFFFF,
- 0x0000, 0x00FF, 0xFFFF, 0x0000,
- 0x0002, 0x0002, 0x5F21, 0x9E50,
- 0x1CF8, 0xC020, 0x9B00, 0x4E53,
+static const u16 lan865x_revb_sqi_fixup_values[12] = {
0x0103, 0x0910, 0x1D26, 0x002A,
0x0103, 0x070D, 0x1720, 0x0027,
0x0509, 0x0E13, 0x1C25, 0x002B,
};
-static const u16 lan865x_revb0_fixup_cfg_regs[5] = {
- 0x0084, 0x008A, 0x00AD, 0x00AE, 0x00AF
+static const u16 lan865x_revb_sqi_fixup_cfg_regs[3] = {
+ 0x00AD, 0x00AE, 0x00AF,
};
/* Pulled from AN1760 describing 'indirect read'
@@ -92,7 +117,7 @@ static const u16 lan865x_revb0_fixup_cfg_regs[5] = {
*
* 0x4 refers to memory map selector 4, which maps to MDIO_MMD_VEND2
*/
-static int lan865x_revb0_indirect_read(struct phy_device *phydev, u16 addr)
+static int lan865x_revb_indirect_read(struct phy_device *phydev, u16 addr)
{
int ret;
@@ -112,15 +137,18 @@ static int lan865x_revb0_indirect_read(struct phy_device *phydev, u16 addr)
/* This is pulled straight from AN1760 from 'calculation of offset 1' &
* 'calculation of offset 2'
*/
-static int lan865x_generate_cfg_offsets(struct phy_device *phydev, s8 offsets[2])
+static int lan865x_generate_cfg_offsets(struct phy_device *phydev, s8 offsets[])
{
const u16 fixup_regs[2] = {0x0004, 0x0008};
int ret;
for (int i = 0; i < ARRAY_SIZE(fixup_regs); i++) {
- ret = lan865x_revb0_indirect_read(phydev, fixup_regs[i]);
+ ret = lan865x_revb_indirect_read(phydev, fixup_regs[i]);
if (ret < 0)
return ret;
+
+ /* 5-bit signed value, sign extend */
+ ret &= GENMASK(4, 0);
if (ret & BIT(4))
offsets[i] = ret | 0xE0;
else
@@ -130,13 +158,15 @@ static int lan865x_generate_cfg_offsets(struct phy_device *phydev, s8 offsets[2]
return 0;
}
-static int lan865x_read_cfg_params(struct phy_device *phydev, u16 cfg_params[])
+static int lan865x_read_cfg_params(struct phy_device *phydev,
+ const u16 cfg_regs[], u16 cfg_params[],
+ u8 count)
{
int ret;
- for (int i = 0; i < ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs); i++) {
+ for (int i = 0; i < count; i++) {
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
- lan865x_revb0_fixup_cfg_regs[i]);
+ cfg_regs[i]);
if (ret < 0)
return ret;
cfg_params[i] = (u16)ret;
@@ -145,13 +175,14 @@ static int lan865x_read_cfg_params(struct phy_device *phydev, u16 cfg_params[])
return 0;
}
-static int lan865x_write_cfg_params(struct phy_device *phydev, u16 cfg_params[])
+static int lan865x_write_cfg_params(struct phy_device *phydev,
+ const u16 cfg_regs[], u16 cfg_params[],
+ u8 count)
{
int ret;
- for (int i = 0; i < ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs); i++) {
- ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
- lan865x_revb0_fixup_cfg_regs[i],
+ for (int i = 0; i < count; i++) {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, cfg_regs[i],
cfg_params[i]);
if (ret)
return ret;
@@ -160,60 +191,90 @@ static int lan865x_write_cfg_params(struct phy_device *phydev, u16 cfg_params[])
return 0;
}
-static int lan865x_setup_cfgparam(struct phy_device *phydev)
+static int lan865x_setup_cfgparam(struct phy_device *phydev, s8 offsets[])
{
- u16 cfg_params[ARRAY_SIZE(lan865x_revb0_fixup_cfg_regs)];
- u16 cfg_results[5];
- s8 offsets[2];
+ u16 cfg_results[ARRAY_SIZE(lan865x_revb_fixup_cfg_regs)];
+ u16 cfg_params[ARRAY_SIZE(lan865x_revb_fixup_cfg_regs)];
int ret;
- ret = lan865x_generate_cfg_offsets(phydev, offsets);
+ ret = lan865x_read_cfg_params(phydev, lan865x_revb_fixup_cfg_regs,
+ cfg_params, ARRAY_SIZE(cfg_params));
if (ret)
return ret;
- ret = lan865x_read_cfg_params(phydev, cfg_params);
+ cfg_results[0] = FIELD_PREP(GENMASK(15, 10), 9 + offsets[0]) |
+ FIELD_PREP(GENMASK(9, 4), 14 + offsets[0]) |
+ 0x03;
+ cfg_results[1] = FIELD_PREP(GENMASK(15, 10), 40 + offsets[1]);
+
+ return lan865x_write_cfg_params(phydev, lan865x_revb_fixup_cfg_regs,
+ cfg_results, ARRAY_SIZE(cfg_results));
+}
+
+static int lan865x_setup_sqi_cfgparam(struct phy_device *phydev, s8 offsets[])
+{
+ u16 cfg_results[ARRAY_SIZE(lan865x_revb_sqi_fixup_cfg_regs)];
+ u16 cfg_params[ARRAY_SIZE(lan865x_revb_sqi_fixup_cfg_regs)];
+ int ret;
+
+ ret = lan865x_read_cfg_params(phydev, lan865x_revb_sqi_fixup_cfg_regs,
+ cfg_params, ARRAY_SIZE(cfg_params));
if (ret)
return ret;
- cfg_results[0] = (cfg_params[0] & 0x000F) |
- FIELD_PREP(GENMASK(15, 10), 9 + offsets[0]) |
- FIELD_PREP(GENMASK(15, 4), 14 + offsets[0]);
- cfg_results[1] = (cfg_params[1] & 0x03FF) |
- FIELD_PREP(GENMASK(15, 10), 40 + offsets[1]);
- cfg_results[2] = (cfg_params[2] & 0xC0C0) |
- FIELD_PREP(GENMASK(15, 8), 5 + offsets[0]) |
- (9 + offsets[0]);
- cfg_results[3] = (cfg_params[3] & 0xC0C0) |
- FIELD_PREP(GENMASK(15, 8), 9 + offsets[0]) |
- (14 + offsets[0]);
- cfg_results[4] = (cfg_params[4] & 0xC0C0) |
- FIELD_PREP(GENMASK(15, 8), 17 + offsets[0]) |
- (22 + offsets[0]);
-
- return lan865x_write_cfg_params(phydev, cfg_results);
+ cfg_results[0] = FIELD_PREP(GENMASK(13, 8), 5 + offsets[0]) |
+ (9 + offsets[0]);
+ cfg_results[1] = FIELD_PREP(GENMASK(13, 8), 9 + offsets[0]) |
+ (14 + offsets[0]);
+ cfg_results[2] = FIELD_PREP(GENMASK(13, 8), 17 + offsets[0]) |
+ (22 + offsets[0]);
+
+ return lan865x_write_cfg_params(phydev, lan865x_revb_sqi_fixup_cfg_regs,
+ cfg_results, ARRAY_SIZE(cfg_results));
}
-static int lan865x_revb0_config_init(struct phy_device *phydev)
+static int lan865x_revb_config_init(struct phy_device *phydev)
{
+ s8 offsets[2];
int ret;
/* Reference to AN1760
* https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/SupportingCollateral/AN-LAN8650-1-Configuration-60001760.pdf
*/
- for (int i = 0; i < ARRAY_SIZE(lan865x_revb0_fixup_registers); i++) {
+ ret = lan865x_generate_cfg_offsets(phydev, offsets);
+ if (ret)
+ return ret;
+
+ for (int i = 0; i < ARRAY_SIZE(lan865x_revb_fixup_registers); i++) {
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
- lan865x_revb0_fixup_registers[i],
- lan865x_revb0_fixup_values[i]);
+ lan865x_revb_fixup_registers[i],
+ lan865x_revb_fixup_values[i]);
if (ret)
return ret;
+
+ if (i == 1) {
+ ret = lan865x_setup_cfgparam(phydev, offsets);
+ if (ret)
+ return ret;
+ }
}
- /* Function to calculate and write the configuration parameters in the
- * 0x0084, 0x008A, 0x00AD, 0x00AE and 0x00AF registers (from AN1760)
- */
- return lan865x_setup_cfgparam(phydev);
+
+ ret = lan865x_setup_sqi_cfgparam(phydev, offsets);
+ if (ret)
+ return ret;
+
+ for (int i = 0; i < ARRAY_SIZE(lan865x_revb_sqi_fixup_regs); i++) {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ lan865x_revb_sqi_fixup_regs[i],
+ lan865x_revb_sqi_fixup_values[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
-static int lan867x_revb1_config_init(struct phy_device *phydev)
+static int lan867x_check_reset_complete(struct phy_device *phydev)
{
int err;
@@ -235,6 +296,69 @@ static int lan867x_revb1_config_init(struct phy_device *phydev)
}
}
+ return 0;
+}
+
+static int lan867x_revc_config_init(struct phy_device *phydev)
+{
+ s8 offsets[2];
+ int ret;
+
+ ret = lan867x_check_reset_complete(phydev);
+ if (ret)
+ return ret;
+
+ ret = lan865x_generate_cfg_offsets(phydev, offsets);
+ if (ret)
+ return ret;
+
+ /* LAN867x Rev.C1/C2 configuration settings are equal to the first 9
+ * configuration settings and all the sqi fixup settings from LAN865x
+ * Rev.B0/B1. So the same fixup registers and values from LAN865x
+ * Rev.B0/B1 are used for LAN867x Rev.C1/C2 to avoid duplication.
+ * Refer the below links for the comparison.
+ * https://www.microchip.com/en-us/application-notes/an1760
+ * Revision F (DS60001760G - June 2024)
+ * https://www.microchip.com/en-us/application-notes/an1699
+ * Revision E (DS60001699F - June 2024)
+ */
+ for (int i = 0; i < 9; i++) {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ lan865x_revb_fixup_registers[i],
+ lan865x_revb_fixup_values[i]);
+ if (ret)
+ return ret;
+
+ if (i == 1) {
+ ret = lan865x_setup_cfgparam(phydev, offsets);
+ if (ret)
+ return ret;
+ }
+ }
+
+ ret = lan865x_setup_sqi_cfgparam(phydev, offsets);
+ if (ret)
+ return ret;
+
+ for (int i = 0; i < ARRAY_SIZE(lan865x_revb_sqi_fixup_regs); i++) {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ lan865x_revb_sqi_fixup_regs[i],
+ lan865x_revb_sqi_fixup_values[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lan867x_revb1_config_init(struct phy_device *phydev)
+{
+ int err;
+
+ err = lan867x_check_reset_complete(phydev);
+ if (err)
+ return err;
+
/* Reference to AN1699
* https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/SupportingCollateral/AN-LAN8670-1-2-config-60001699.pdf
* AN1699 says Read, Modify, Write, but the Write is not required if the
@@ -253,6 +377,36 @@ static int lan867x_revb1_config_init(struct phy_device *phydev)
return 0;
}
+/* As per LAN8650/1 Rev.B0/B1 AN1760 (Revision F (DS60001760G - June 2024)) and
+ * LAN8670/1/2 Rev.C1/C2 AN1699 (Revision E (DS60001699F - June 2024)), under
+ * normal operation, the device should be operated in PLCA mode. Disabling
+ * collision detection is recommended to allow the device to operate in noisy
+ * environments or when reflections and other inherent transmission line
+ * distortion cause poor signal quality. Collision detection must be re-enabled
+ * if the device is configured to operate in CSMA/CD mode.
+ *
+ * AN1760: https://www.microchip.com/en-us/application-notes/an1760
+ * AN1699: https://www.microchip.com/en-us/application-notes/an1699
+ */
+static int lan86xx_plca_set_cfg(struct phy_device *phydev,
+ const struct phy_plca_cfg *plca_cfg)
+{
+ int ret;
+
+ ret = genphy_c45_plca_set_cfg(phydev, plca_cfg);
+ if (ret)
+ return ret;
+
+ if (plca_cfg->enabled)
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
+ LAN86XX_REG_COL_DET_CTRL0,
+ COL_DET_CTRL0_ENABLE_BIT_MASK,
+ COL_DET_DISABLE);
+
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LAN86XX_REG_COL_DET_CTRL0,
+ COL_DET_CTRL0_ENABLE_BIT_MASK, COL_DET_ENABLE);
+}
+
static int lan86xx_read_status(struct phy_device *phydev)
{
/* The phy has some limitations, namely:
@@ -308,15 +462,35 @@ static struct phy_driver microchip_t1s_driver[] = {
.get_plca_status = genphy_c45_plca_get_status,
},
{
- PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB0),
- .name = "LAN865X Rev.B0 Internal Phy",
+ PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVC1),
+ .name = "LAN867X Rev.C1",
.features = PHY_BASIC_T1S_P2MP_FEATURES,
- .config_init = lan865x_revb0_config_init,
+ .config_init = lan867x_revc_config_init,
+ .read_status = lan86xx_read_status,
+ .get_plca_cfg = genphy_c45_plca_get_cfg,
+ .set_plca_cfg = lan86xx_plca_set_cfg,
+ .get_plca_status = genphy_c45_plca_get_status,
+ },
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVC2),
+ .name = "LAN867X Rev.C2",
+ .features = PHY_BASIC_T1S_P2MP_FEATURES,
+ .config_init = lan867x_revc_config_init,
+ .read_status = lan86xx_read_status,
+ .get_plca_cfg = genphy_c45_plca_get_cfg,
+ .set_plca_cfg = lan86xx_plca_set_cfg,
+ .get_plca_status = genphy_c45_plca_get_status,
+ },
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB),
+ .name = "LAN865X Rev.B0/B1 Internal Phy",
+ .features = PHY_BASIC_T1S_P2MP_FEATURES,
+ .config_init = lan865x_revb_config_init,
.read_status = lan86xx_read_status,
.read_mmd = lan865x_phy_read_mmd,
.write_mmd = lan865x_phy_write_mmd,
.get_plca_cfg = genphy_c45_plca_get_cfg,
- .set_plca_cfg = genphy_c45_plca_set_cfg,
+ .set_plca_cfg = lan86xx_plca_set_cfg,
.get_plca_status = genphy_c45_plca_get_status,
},
};
@@ -325,7 +499,9 @@ module_phy_driver(microchip_t1s_driver);
static struct mdio_device_id __maybe_unused tbl[] = {
{ PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1) },
- { PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB0) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVC1) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVC2) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB) },
{ }
};
diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c
index 6f74ce0ab1aa..bee381200ab8 100644
--- a/drivers/net/phy/mscc/mscc_main.c
+++ b/drivers/net/phy/mscc/mscc_main.c
@@ -139,8 +139,7 @@ static void vsc85xx_get_strings(struct phy_device *phydev, u8 *data)
return;
for (i = 0; i < priv->nstats; i++)
- strscpy(data + i * ETH_GSTRING_LEN, priv->hw_stats[i].string,
- ETH_GSTRING_LEN);
+ ethtool_puts(&data, priv->hw_stats[i].string);
}
static u64 vsc85xx_get_stat(struct phy_device *phydev, int i)
diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
index e5f8ac4b4604..db3c1f72b407 100644
--- a/drivers/net/phy/mxl-gpy.c
+++ b/drivers/net/phy/mxl-gpy.c
@@ -38,6 +38,7 @@
#define PHY_MIISTAT 0x18 /* MII state */
#define PHY_IMASK 0x19 /* interrupt mask */
#define PHY_ISTAT 0x1A /* interrupt status */
+#define PHY_LED 0x1B /* LEDs */
#define PHY_FWV 0x1E /* firmware version */
#define PHY_MIISTAT_SPD_MASK GENMASK(2, 0)
@@ -61,6 +62,11 @@
PHY_IMASK_ADSC | \
PHY_IMASK_ANC)
+#define GPY_MAX_LEDS 4
+#define PHY_LED_POLARITY(idx) BIT(12 + (idx))
+#define PHY_LED_HWCONTROL(idx) BIT(8 + (idx))
+#define PHY_LED_ON(idx) BIT(idx)
+
#define PHY_FWV_REL_MASK BIT(15)
#define PHY_FWV_MAJOR_MASK GENMASK(11, 8)
#define PHY_FWV_MINOR_MASK GENMASK(7, 0)
@@ -72,6 +78,23 @@
#define PHY_MDI_MDI_X_CD 0x1
#define PHY_MDI_MDI_X_CROSS 0x0
+/* LED */
+#define VSPEC1_LED(idx) (1 + (idx))
+#define VSPEC1_LED_BLINKS GENMASK(15, 12)
+#define VSPEC1_LED_PULSE GENMASK(11, 8)
+#define VSPEC1_LED_CON GENMASK(7, 4)
+#define VSPEC1_LED_BLINKF GENMASK(3, 0)
+
+#define VSPEC1_LED_LINK10 BIT(0)
+#define VSPEC1_LED_LINK100 BIT(1)
+#define VSPEC1_LED_LINK1000 BIT(2)
+#define VSPEC1_LED_LINK2500 BIT(3)
+
+#define VSPEC1_LED_TXACT BIT(0)
+#define VSPEC1_LED_RXACT BIT(1)
+#define VSPEC1_LED_COL BIT(2)
+#define VSPEC1_LED_NO_CON BIT(3)
+
/* SGMII */
#define VSPEC1_SGMII_CTRL 0x08
#define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */
@@ -835,6 +858,165 @@ static int gpy115_loopback(struct phy_device *phydev, bool enable)
return genphy_soft_reset(phydev);
}
+static int gpy_led_brightness_set(struct phy_device *phydev,
+ u8 index, enum led_brightness value)
+{
+ int ret;
+
+ if (index >= GPY_MAX_LEDS)
+ return -EINVAL;
+
+ /* clear HWCONTROL and set manual LED state */
+ ret = phy_modify(phydev, PHY_LED,
+ ((value == LED_OFF) ? PHY_LED_HWCONTROL(index) : 0) |
+ PHY_LED_ON(index),
+ (value == LED_OFF) ? 0 : PHY_LED_ON(index));
+ if (ret)
+ return ret;
+
+ /* ToDo: set PWM brightness */
+
+ /* clear HW LED setup */
+ if (value == LED_OFF)
+ return phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index), 0);
+ else
+ return 0;
+}
+
+static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_LINK_2500) |
+ BIT(TRIGGER_NETDEV_RX) |
+ BIT(TRIGGER_NETDEV_TX));
+
+static int gpy_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ if (index >= GPY_MAX_LEDS)
+ return -EINVAL;
+
+ /* All combinations of the supported triggers are allowed */
+ if (rules & ~supported_triggers)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int gpy_led_hw_control_get(struct phy_device *phydev, u8 index,
+ unsigned long *rules)
+{
+ int val;
+
+ if (index >= GPY_MAX_LEDS)
+ return -EINVAL;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index));
+ if (val < 0)
+ return val;
+
+ if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK10)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_10);
+
+ if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK100)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_100);
+
+ if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK1000)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
+
+ if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK2500)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_2500);
+
+ if (FIELD_GET(VSPEC1_LED_CON, val) == (VSPEC1_LED_LINK10 |
+ VSPEC1_LED_LINK100 |
+ VSPEC1_LED_LINK1000 |
+ VSPEC1_LED_LINK2500))
+ *rules |= BIT(TRIGGER_NETDEV_LINK);
+
+ if (FIELD_GET(VSPEC1_LED_PULSE, val) & VSPEC1_LED_TXACT)
+ *rules |= BIT(TRIGGER_NETDEV_TX);
+
+ if (FIELD_GET(VSPEC1_LED_PULSE, val) & VSPEC1_LED_RXACT)
+ *rules |= BIT(TRIGGER_NETDEV_RX);
+
+ return 0;
+}
+
+static int gpy_led_hw_control_set(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ u16 val = 0;
+ int ret;
+
+ if (index >= GPY_MAX_LEDS)
+ return -EINVAL;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK) ||
+ rules & BIT(TRIGGER_NETDEV_LINK_10))
+ val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK10);
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK) ||
+ rules & BIT(TRIGGER_NETDEV_LINK_100))
+ val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK100);
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK) ||
+ rules & BIT(TRIGGER_NETDEV_LINK_1000))
+ val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK1000);
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK) ||
+ rules & BIT(TRIGGER_NETDEV_LINK_2500))
+ val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK2500);
+
+ if (rules & BIT(TRIGGER_NETDEV_TX))
+ val |= FIELD_PREP(VSPEC1_LED_PULSE, VSPEC1_LED_TXACT);
+
+ if (rules & BIT(TRIGGER_NETDEV_RX))
+ val |= FIELD_PREP(VSPEC1_LED_PULSE, VSPEC1_LED_RXACT);
+
+ /* allow RX/TX pulse without link indication */
+ if ((rules & BIT(TRIGGER_NETDEV_TX) || rules & BIT(TRIGGER_NETDEV_RX)) &&
+ !(val & VSPEC1_LED_CON))
+ val |= FIELD_PREP(VSPEC1_LED_PULSE, VSPEC1_LED_NO_CON) | VSPEC1_LED_CON;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index), val);
+ if (ret)
+ return ret;
+
+ return phy_set_bits(phydev, PHY_LED, PHY_LED_HWCONTROL(index));
+}
+
+static int gpy_led_polarity_set(struct phy_device *phydev, int index,
+ unsigned long modes)
+{
+ bool force_active_low = false, force_active_high = false;
+ u32 mode;
+
+ if (index >= GPY_MAX_LEDS)
+ return -EINVAL;
+
+ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
+ switch (mode) {
+ case PHY_LED_ACTIVE_LOW:
+ force_active_low = true;
+ break;
+ case PHY_LED_ACTIVE_HIGH:
+ force_active_high = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ if (force_active_low)
+ return phy_set_bits(phydev, PHY_LED, PHY_LED_POLARITY(index));
+
+ if (force_active_high)
+ return phy_clear_bits(phydev, PHY_LED, PHY_LED_POLARITY(index));
+
+ unreachable();
+}
+
static struct phy_driver gpy_drivers[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
@@ -852,6 +1034,11 @@ static struct phy_driver gpy_drivers[] = {
.set_wol = gpy_set_wol,
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
},
{
.phy_id = PHY_ID_GPY115B,
@@ -870,6 +1057,11 @@ static struct phy_driver gpy_drivers[] = {
.set_wol = gpy_set_wol,
.get_wol = gpy_get_wol,
.set_loopback = gpy115_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
@@ -887,6 +1079,11 @@ static struct phy_driver gpy_drivers[] = {
.set_wol = gpy_set_wol,
.get_wol = gpy_get_wol,
.set_loopback = gpy115_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
},
{
.phy_id = PHY_ID_GPY211B,
@@ -905,6 +1102,11 @@ static struct phy_driver gpy_drivers[] = {
.set_wol = gpy_set_wol,
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
@@ -922,6 +1124,11 @@ static struct phy_driver gpy_drivers[] = {
.set_wol = gpy_set_wol,
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
},
{
.phy_id = PHY_ID_GPY212B,
@@ -940,6 +1147,11 @@ static struct phy_driver gpy_drivers[] = {
.set_wol = gpy_set_wol,
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
@@ -957,6 +1169,11 @@ static struct phy_driver gpy_drivers[] = {
.set_wol = gpy_set_wol,
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
},
{
.phy_id = PHY_ID_GPY215B,
@@ -975,6 +1192,11 @@ static struct phy_driver gpy_drivers[] = {
.set_wol = gpy_set_wol,
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
@@ -992,6 +1214,11 @@ static struct phy_driver gpy_drivers[] = {
.set_wol = gpy_set_wol,
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
diff --git a/drivers/net/phy/nxp-c45-tja11xx.c b/drivers/net/phy/nxp-c45-tja11xx.c
index 5af5ade4fc64..ade544bc007d 100644
--- a/drivers/net/phy/nxp-c45-tja11xx.c
+++ b/drivers/net/phy/nxp-c45-tja11xx.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/mii.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/phy.h>
#include <linux/processor.h>
#include <linux/property.h>
@@ -185,6 +186,8 @@
#define NXP_C45_SKB_CB(skb) ((struct nxp_c45_skb_cb *)(skb)->cb)
+#define TJA11XX_REVERSE_MODE BIT(0)
+
struct nxp_c45_phy;
struct nxp_c45_skb_cb {
@@ -1137,13 +1140,11 @@ static void nxp_c45_get_strings(struct phy_device *phydev, u8 *data)
for (i = 0; i < count; i++) {
if (i < ARRAY_SIZE(common_hw_stats)) {
- strscpy(data + i * ETH_GSTRING_LEN,
- common_hw_stats[i].name, ETH_GSTRING_LEN);
+ ethtool_puts(&data, common_hw_stats[i].name);
continue;
}
idx = i - ARRAY_SIZE(common_hw_stats);
- strscpy(data + i * ETH_GSTRING_LEN,
- phy_data->stats[idx].name, ETH_GSTRING_LEN);
+ ethtool_puts(&data, phy_data->stats[idx].name);
}
}
@@ -1510,6 +1511,8 @@ static int nxp_c45_get_delays(struct phy_device *phydev)
static int nxp_c45_set_phy_mode(struct phy_device *phydev)
{
+ struct nxp_c45_phy *priv = phydev->priv;
+ u16 basic_config;
int ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_ABILITIES);
@@ -1561,8 +1564,15 @@ static int nxp_c45_set_phy_mode(struct phy_device *phydev)
phydev_err(phydev, "rmii mode not supported\n");
return -EINVAL;
}
+
+ basic_config = MII_BASIC_CONFIG_RMII;
+
+ /* This is not PHY_INTERFACE_MODE_REVRMII */
+ if (priv->flags & TJA11XX_REVERSE_MODE)
+ basic_config |= MII_BASIC_CONFIG_REV;
+
phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG,
- MII_BASIC_CONFIG_RMII);
+ basic_config);
break;
case PHY_INTERFACE_MODE_SGMII:
if (!(ret & SGMII_ABILITY)) {
@@ -1623,6 +1633,20 @@ static int nxp_c45_get_features(struct phy_device *phydev)
return genphy_c45_pma_read_abilities(phydev);
}
+static int nxp_c45_parse_dt(struct phy_device *phydev)
+{
+ struct device_node *node = phydev->mdio.dev.of_node;
+ struct nxp_c45_phy *priv = phydev->priv;
+
+ if (!IS_ENABLED(CONFIG_OF_MDIO))
+ return 0;
+
+ if (of_property_read_bool(node, "nxp,rmii-refclk-out"))
+ priv->flags |= TJA11XX_REVERSE_MODE;
+
+ return 0;
+}
+
static int nxp_c45_probe(struct phy_device *phydev)
{
struct nxp_c45_phy *priv;
@@ -1642,6 +1666,8 @@ static int nxp_c45_probe(struct phy_device *phydev)
phydev->priv = priv;
+ nxp_c45_parse_dt(phydev);
+
mutex_init(&priv->ptp_lock);
phy_abilities = phy_read_mmd(phydev, MDIO_MMD_VEND1,
diff --git a/drivers/net/phy/nxp-c45-tja11xx.h b/drivers/net/phy/nxp-c45-tja11xx.h
index f364fca68f0b..8b5fc383752b 100644
--- a/drivers/net/phy/nxp-c45-tja11xx.h
+++ b/drivers/net/phy/nxp-c45-tja11xx.h
@@ -28,6 +28,7 @@ struct nxp_c45_phy {
int extts_index;
bool extts;
struct nxp_c45_macsec *macsec;
+ u32 flags;
};
#if IS_ENABLED(CONFIG_MACSEC)
diff --git a/drivers/net/phy/nxp-cbtx.c b/drivers/net/phy/nxp-cbtx.c
index 145703f0a406..3d25491043a3 100644
--- a/drivers/net/phy/nxp-cbtx.c
+++ b/drivers/net/phy/nxp-cbtx.c
@@ -182,7 +182,7 @@ static int cbtx_get_sset_count(struct phy_device *phydev)
static void cbtx_get_strings(struct phy_device *phydev, u8 *data)
{
- strncpy(data, "100btx_rx_err", ETH_GSTRING_LEN);
+ ethtool_puts(&data, "100btx_rx_err");
}
static void cbtx_get_stats(struct phy_device *phydev,
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 5695935fdce9..96d0b3a5a9d3 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -680,17 +680,16 @@ EXPORT_SYMBOL_GPL(genphy_c45_read_mdix);
* @phydev: target phy_device struct
* @adv: the linkmode advertisement settings
*/
-int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv)
+static int genphy_c45_write_eee_adv(struct phy_device *phydev,
+ unsigned long *adv)
{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp);
int val, changed = 0;
- if (linkmode_intersects(phydev->supported_eee, PHY_EEE_CAP1_FEATURES)) {
- val = linkmode_to_mii_eee_cap1_t(adv);
+ linkmode_andnot(tmp, adv, phydev->eee_broken_modes);
- /* In eee_broken_modes are stored MDIO_AN_EEE_ADV specific raw
- * register values.
- */
- val &= ~phydev->eee_broken_modes;
+ if (linkmode_intersects(phydev->supported_eee, PHY_EEE_CAP1_FEATURES)) {
+ val = linkmode_to_mii_eee_cap1_t(tmp);
/* IEEE 802.3-2018 45.2.7.13 EEE advertisement 1
* (Register 7.60)
@@ -708,7 +707,7 @@ int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv)
}
if (linkmode_intersects(phydev->supported_eee, PHY_EEE_CAP2_FEATURES)) {
- val = linkmode_to_mii_eee_cap2_t(adv);
+ val = linkmode_to_mii_eee_cap2_t(tmp);
/* IEEE 802.3-2022 45.2.7.16 EEE advertisement 2
* (Register 7.62)
@@ -942,7 +941,7 @@ EXPORT_SYMBOL_GPL(genphy_c45_read_eee_abilities);
*/
int genphy_c45_an_config_eee_aneg(struct phy_device *phydev)
{
- if (!phydev->eee_enabled) {
+ if (!phydev->eee_cfg.eee_enabled) {
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
return genphy_c45_write_eee_adv(phydev, adv);
@@ -950,6 +949,7 @@ int genphy_c45_an_config_eee_aneg(struct phy_device *phydev)
return genphy_c45_write_eee_adv(phydev, phydev->advertising_eee);
}
+EXPORT_SYMBOL_GPL(genphy_c45_an_config_eee_aneg);
/**
* genphy_c45_pma_baset1_read_abilities - read supported baset1 link modes from PMA
@@ -1521,20 +1521,17 @@ EXPORT_SYMBOL(genphy_c45_eee_is_active);
int genphy_c45_ethtool_get_eee(struct phy_device *phydev,
struct ethtool_keee *data)
{
- __ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
- __ETHTOOL_DECLARE_LINK_MODE_MASK(lp) = {};
bool is_enabled;
int ret;
- ret = genphy_c45_eee_is_active(phydev, adv, lp, &is_enabled);
+ ret = genphy_c45_eee_is_active(phydev, data->advertised,
+ data->lp_advertised, &is_enabled);
if (ret < 0)
return ret;
data->eee_enabled = is_enabled;
data->eee_active = ret;
linkmode_copy(data->supported, phydev->supported_eee);
- linkmode_copy(data->advertised, adv);
- linkmode_copy(data->lp_advertised, lp);
return 0;
}
@@ -1568,15 +1565,12 @@ int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
phydev_warn(phydev, "At least some EEE link modes are not supported.\n");
return -EINVAL;
}
- } else {
- adv = phydev->supported_eee;
+ linkmode_copy(phydev->advertising_eee, adv);
+ } else if (linkmode_empty(phydev->advertising_eee)) {
+ phy_advertise_eee_all(phydev);
}
-
- linkmode_copy(phydev->advertising_eee, adv);
}
- phydev->eee_enabled = data->eee_enabled;
-
ret = genphy_c45_an_config_eee_aneg(phydev);
if (ret > 0) {
ret = phy_restart_aneg(phydev);
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index 1f98b6a96c15..6bf3ec985f3d 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -388,28 +388,58 @@ void of_set_phy_supported(struct phy_device *phydev)
void of_set_phy_eee_broken(struct phy_device *phydev)
{
struct device_node *node = phydev->mdio.dev.of_node;
- u32 broken = 0;
+ unsigned long *modes = phydev->eee_broken_modes;
- if (!IS_ENABLED(CONFIG_OF_MDIO))
+ if (!IS_ENABLED(CONFIG_OF_MDIO) || !node)
return;
- if (!node)
- return;
+ linkmode_zero(modes);
if (of_property_read_bool(node, "eee-broken-100tx"))
- broken |= MDIO_EEE_100TX;
+ linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, modes);
if (of_property_read_bool(node, "eee-broken-1000t"))
- broken |= MDIO_EEE_1000T;
+ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, modes);
if (of_property_read_bool(node, "eee-broken-10gt"))
- broken |= MDIO_EEE_10GT;
+ linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, modes);
if (of_property_read_bool(node, "eee-broken-1000kx"))
- broken |= MDIO_EEE_1000KX;
+ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, modes);
if (of_property_read_bool(node, "eee-broken-10gkx4"))
- broken |= MDIO_EEE_10GKX4;
+ linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, modes);
if (of_property_read_bool(node, "eee-broken-10gkr"))
- broken |= MDIO_EEE_10GKR;
+ linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, modes);
+}
+
+/**
+ * of_set_phy_timing_role - Set the master/slave mode of the PHY
+ *
+ * @phydev: The phy_device struct
+ *
+ * Set master/slave configuration of the PHY based on the device tree.
+ */
+void of_set_phy_timing_role(struct phy_device *phydev)
+{
+ struct device_node *node = phydev->mdio.dev.of_node;
+ const char *master;
+
+ if (!IS_ENABLED(CONFIG_OF_MDIO))
+ return;
+
+ if (!node)
+ return;
+
+ if (of_property_read_string(node, "timing-role", &master))
+ return;
- phydev->eee_broken_modes = broken;
+ if (strcmp(master, "forced-master") == 0)
+ phydev->master_slave_set = MASTER_SLAVE_CFG_MASTER_FORCE;
+ else if (strcmp(master, "forced-slave") == 0)
+ phydev->master_slave_set = MASTER_SLAVE_CFG_SLAVE_FORCE;
+ else if (strcmp(master, "preferred-master") == 0)
+ phydev->master_slave_set = MASTER_SLAVE_CFG_MASTER_PREFERRED;
+ else if (strcmp(master, "preferred-slave") == 0)
+ phydev->master_slave_set = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
+ else
+ phydev_warn(phydev, "Unknown master-slave mode %s\n", master);
}
/**
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 499797646580..b26bb33cd1d4 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -2240,29 +2240,6 @@ static int genphy_c37_config_advert(struct phy_device *phydev)
}
/**
- * genphy_config_eee_advert - disable unwanted eee mode advertisement
- * @phydev: target phy_device struct
- *
- * Description: Writes MDIO_AN_EEE_ADV after disabling unsupported energy
- * efficent ethernet modes. Returns 0 if the PHY's advertisement hasn't
- * changed, and 1 if it has changed.
- */
-int genphy_config_eee_advert(struct phy_device *phydev)
-{
- int err;
-
- /* Nothing to disable */
- if (!phydev->eee_broken_modes)
- return 0;
-
- err = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV,
- phydev->eee_broken_modes, 0);
- /* If the call failed, we assume that EEE is not supported */
- return err < 0 ? 0 : err;
-}
-EXPORT_SYMBOL(genphy_config_eee_advert);
-
-/**
* genphy_setup_forced - configures/forces speed/duplex from @phydev
* @phydev: target phy_device struct
*
@@ -3358,11 +3335,17 @@ static int of_phy_led(struct phy_device *phydev,
if (index > U8_MAX)
return -EINVAL;
+ if (of_property_read_bool(led, "active-high"))
+ set_bit(PHY_LED_ACTIVE_HIGH, &modes);
if (of_property_read_bool(led, "active-low"))
set_bit(PHY_LED_ACTIVE_LOW, &modes);
if (of_property_read_bool(led, "inactive-high-impedance"))
set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes);
+ if (WARN_ON(modes & BIT(PHY_LED_ACTIVE_LOW) &&
+ modes & BIT(PHY_LED_ACTIVE_HIGH)))
+ return -EINVAL;
+
if (modes) {
/* Return error if asked to set polarity modes but not supported */
if (!phydev->drv->led_polarity_set)
@@ -3421,6 +3404,16 @@ static int of_phy_leds(struct phy_device *phydev)
if (!leds)
return 0;
+ /* Check if the PHY driver have at least an OP to
+ * set the LEDs.
+ */
+ if (!(phydev->drv->led_brightness_set ||
+ phydev->drv->led_blink_set ||
+ phydev->drv->led_hw_control_set)) {
+ phydev_dbg(phydev, "ignoring leds node defined with no PHY driver support\n");
+ goto exit;
+ }
+
for_each_available_child_of_node_scoped(leds, led) {
err = of_phy_led(phydev, led);
if (err) {
@@ -3430,6 +3423,7 @@ static int of_phy_leds(struct phy_device *phydev)
}
}
+exit:
of_node_put(leds);
return 0;
}
@@ -3595,12 +3589,12 @@ static int phy_probe(struct device *dev)
/* There is no "enabled" flag. If PHY is advertising, assume it is
* kind of enabled.
*/
- phydev->eee_enabled = !linkmode_empty(phydev->advertising_eee);
+ phydev->eee_cfg.eee_enabled = !linkmode_empty(phydev->advertising_eee);
/* Some PHYs may advertise, by default, not support EEE modes. So,
* we need to clean them.
*/
- if (phydev->eee_enabled)
+ if (phydev->eee_cfg.eee_enabled)
linkmode_and(phydev->advertising_eee, phydev->supported_eee,
phydev->advertising_eee);
@@ -3609,6 +3603,9 @@ static int phy_probe(struct device *dev)
*/
of_set_phy_eee_broken(phydev);
+ /* Get master/slave strap overrides */
+ of_set_phy_timing_role(phydev);
+
/* The Pause Frame bits indicate that the PHY can support passing
* pause frames. During autonegotiation, the PHYs will determine if
* they should allow pause frames to pass. The MAC driver should then
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 3e9957b6aa14..30a654e98352 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -79,7 +79,6 @@ struct phylink {
unsigned int pcs_state;
bool link_failed;
- bool using_mac_select_pcs;
struct sfp_bus *sfp_bus;
bool sfp_may_have_phy;
@@ -599,15 +598,8 @@ static unsigned long phylink_get_capabilities(phy_interface_t interface,
* max speed at full duplex.
*/
if (mac_capabilities &
- phylink_cap_from_speed_duplex(max_speed, DUPLEX_FULL)) {
- /* Although a duplex-matching phy might exist, we
- * conservatively remove these modes because the MAC
- * will not be aware of the half-duplex nature of the
- * link.
- */
+ phylink_cap_from_speed_duplex(max_speed, DUPLEX_FULL))
matched_caps = GENMASK(__fls(caps), __fls(MAC_10HD));
- matched_caps &= ~(MAC_1000HD | MAC_100HD | MAC_10HD);
- }
break;
}
case RATE_MATCH_CRS:
@@ -656,17 +648,15 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl,
unsigned long *supported,
struct phylink_link_state *state)
{
+ struct phylink_pcs *pcs = NULL;
unsigned long capabilities;
- struct phylink_pcs *pcs;
int ret;
/* Get the PCS for this interface mode */
- if (pl->using_mac_select_pcs) {
+ if (pl->mac_ops->mac_select_pcs) {
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
if (IS_ERR(pcs))
return PTR_ERR(pcs);
- } else {
- pcs = pl->pcs;
}
if (pcs) {
@@ -774,8 +764,8 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported,
static int phylink_parse_fixedlink(struct phylink *pl,
const struct fwnode_handle *fwnode)
{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
struct fwnode_handle *fixed_node;
- bool pause, asym_pause, autoneg;
const struct phy_setting *s;
struct gpio_desc *desc;
u32 speed;
@@ -848,22 +838,15 @@ static int phylink_parse_fixedlink(struct phylink *pl,
linkmode_copy(pl->link_config.advertising, pl->supported);
phylink_validate(pl, pl->supported, &pl->link_config);
- pause = phylink_test(pl->supported, Pause);
- asym_pause = phylink_test(pl->supported, Asym_Pause);
- autoneg = phylink_test(pl->supported, Autoneg);
s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex,
pl->supported, true);
- linkmode_zero(pl->supported);
- phylink_set(pl->supported, MII);
-
- if (pause)
- phylink_set(pl->supported, Pause);
- if (asym_pause)
- phylink_set(pl->supported, Asym_Pause);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mask);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
+ linkmode_and(pl->supported, pl->supported, mask);
- if (autoneg)
- phylink_set(pl->supported, Autoneg);
+ phylink_set(pl->supported, MII);
if (s) {
__set_bit(s->bit, pl->supported);
@@ -1182,7 +1165,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,
state->interface,
state->advertising);
- if (pl->using_mac_select_pcs) {
+ if (pl->mac_ops->mac_select_pcs) {
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
if (IS_ERR(pcs)) {
phylink_err(pl,
@@ -1191,7 +1174,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,
return;
}
- pcs_changed = pcs && pl->pcs != pcs;
+ pcs_changed = pl->pcs != pcs;
}
phylink_pcs_poll_stop(pl);
@@ -1480,76 +1463,66 @@ static void phylink_resolve(struct work_struct *w)
} else if (pl->link_failed) {
link_state.link = false;
retrigger = true;
+ } else if (pl->cur_link_an_mode == MLO_AN_FIXED) {
+ phylink_get_fixed_state(pl, &link_state);
+ mac_config = link_state.link;
+ } else if (pl->cur_link_an_mode == MLO_AN_PHY) {
+ link_state = pl->phy_state;
+ mac_config = link_state.link;
} else {
- switch (pl->cur_link_an_mode) {
- case MLO_AN_PHY:
- link_state = pl->phy_state;
- phylink_apply_manual_flow(pl, &link_state);
- mac_config = link_state.link;
- break;
+ phylink_mac_pcs_get_state(pl, &link_state);
- case MLO_AN_FIXED:
- phylink_get_fixed_state(pl, &link_state);
- mac_config = link_state.link;
- break;
+ /* The PCS may have a latching link-fail indicator. If the link
+ * was up, bring the link down and re-trigger the resolve.
+ * Otherwise, re-read the PCS state to get the current status
+ * of the link.
+ */
+ if (!link_state.link) {
+ if (cur_link_state)
+ retrigger = true;
+ else
+ phylink_mac_pcs_get_state(pl, &link_state);
+ }
- case MLO_AN_INBAND:
- phylink_mac_pcs_get_state(pl, &link_state);
+ /* If we have a phy, the "up" state is the union of both the
+ * PHY and the MAC
+ */
+ if (pl->phydev)
+ link_state.link &= pl->phy_state.link;
- /* The PCS may have a latching link-fail indicator.
- * If the link was up, bring the link down and
- * re-trigger the resolve. Otherwise, re-read the
- * PCS state to get the current status of the link.
+ /* Only update if the PHY link is up */
+ if (pl->phydev && pl->phy_state.link) {
+ /* If the interface has changed, force a link down
+ * event if the link isn't already down, and re-resolve.
*/
- if (!link_state.link) {
- if (cur_link_state)
- retrigger = true;
- else
- phylink_mac_pcs_get_state(pl,
- &link_state);
+ if (link_state.interface != pl->phy_state.interface) {
+ retrigger = true;
+ link_state.link = false;
}
- /* If we have a phy, the "up" state is the union of
- * both the PHY and the MAC
+ link_state.interface = pl->phy_state.interface;
+
+ /* If we are doing rate matching, then the link
+ * speed/duplex comes from the PHY
*/
- if (pl->phydev)
- link_state.link &= pl->phy_state.link;
-
- /* Only update if the PHY link is up */
- if (pl->phydev && pl->phy_state.link) {
- /* If the interface has changed, force a
- * link down event if the link isn't already
- * down, and re-resolve.
- */
- if (link_state.interface !=
- pl->phy_state.interface) {
- retrigger = true;
- link_state.link = false;
- }
- link_state.interface = pl->phy_state.interface;
-
- /* If we are doing rate matching, then the
- * link speed/duplex comes from the PHY
- */
- if (pl->phy_state.rate_matching) {
- link_state.rate_matching =
- pl->phy_state.rate_matching;
- link_state.speed = pl->phy_state.speed;
- link_state.duplex =
- pl->phy_state.duplex;
- }
-
- /* If we have a PHY, we need to update with
- * the PHY flow control bits.
- */
- link_state.pause = pl->phy_state.pause;
- mac_config = true;
+ if (pl->phy_state.rate_matching) {
+ link_state.rate_matching =
+ pl->phy_state.rate_matching;
+ link_state.speed = pl->phy_state.speed;
+ link_state.duplex = pl->phy_state.duplex;
}
- phylink_apply_manual_flow(pl, &link_state);
- break;
+
+ /* If we have a PHY, we need to update with the PHY
+ * flow control bits.
+ */
+ link_state.pause = pl->phy_state.pause;
+ mac_config = true;
}
}
+ if (pl->cur_link_an_mode != MLO_AN_FIXED)
+ phylink_apply_manual_flow(pl, &link_state);
+
if (mac_config) {
if (link_state.interface != pl->link_config.interface) {
/* The interface has changed, force the link down and
@@ -1698,7 +1671,6 @@ struct phylink *phylink_create(struct phylink_config *config,
phy_interface_t iface,
const struct phylink_mac_ops *mac_ops)
{
- bool using_mac_select_pcs = false;
struct phylink *pl;
int ret;
@@ -1709,11 +1681,6 @@ struct phylink *phylink_create(struct phylink_config *config,
return ERR_PTR(-EINVAL);
}
- if (mac_ops->mac_select_pcs &&
- mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
- ERR_PTR(-EOPNOTSUPP))
- using_mac_select_pcs = true;
-
pl = kzalloc(sizeof(*pl), GFP_KERNEL);
if (!pl)
return ERR_PTR(-ENOMEM);
@@ -1732,7 +1699,6 @@ struct phylink *phylink_create(struct phylink_config *config,
return ERR_PTR(-EINVAL);
}
- pl->using_mac_select_pcs = using_mac_select_pcs;
pl->phy_state.interface = iface;
pl->link_interface = iface;
if (iface == PHY_INTERFACE_MODE_MOCA)
@@ -2434,6 +2400,32 @@ int phylink_ethtool_set_wol(struct phylink *pl, struct ethtool_wolinfo *wol)
}
EXPORT_SYMBOL_GPL(phylink_ethtool_set_wol);
+static phy_interface_t phylink_sfp_select_interface(struct phylink *pl,
+ const unsigned long *link_modes)
+{
+ phy_interface_t interface;
+
+ interface = sfp_select_interface(pl->sfp_bus, link_modes);
+ if (interface == PHY_INTERFACE_MODE_NA) {
+ phylink_err(pl,
+ "selection of interface failed, advertisement %*pb\n",
+ __ETHTOOL_LINK_MODE_MASK_NBITS,
+ link_modes);
+ return interface;
+ }
+
+ if (!test_bit(interface, pl->config->supported_interfaces)) {
+ phylink_err(pl,
+ "selection of interface failed, SFP selected %s (%u) but MAC supports %*pbl\n",
+ phy_modes(interface), interface,
+ (int)PHY_INTERFACE_MODE_MAX,
+ pl->config->supported_interfaces);
+ return PHY_INTERFACE_MODE_NA;
+ }
+
+ return interface;
+}
+
static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask);
@@ -2616,15 +2608,10 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
* link can be configured correctly.
*/
if (pl->sfp_bus) {
- config.interface = sfp_select_interface(pl->sfp_bus,
+ config.interface = phylink_sfp_select_interface(pl,
config.advertising);
- if (config.interface == PHY_INTERFACE_MODE_NA) {
- phylink_err(pl,
- "selection of interface failed, advertisement %*pb\n",
- __ETHTOOL_LINK_MODE_MASK_NBITS,
- config.advertising);
+ if (config.interface == PHY_INTERFACE_MODE_NA)
return -EINVAL;
- }
/* Revalidate with the selected interface */
linkmode_copy(support, pl->supported);
@@ -3229,10 +3216,8 @@ static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
struct phy_device *phy)
{
- __ETHTOOL_DECLARE_LINK_MODE_MASK(support1);
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
struct phylink_link_state config;
- phy_interface_t iface;
int ret;
linkmode_copy(support, phy->supported);
@@ -3253,24 +3238,21 @@ static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
return ret;
}
- iface = sfp_select_interface(pl->sfp_bus, config.advertising);
- if (iface == PHY_INTERFACE_MODE_NA) {
- phylink_err(pl,
- "selection of interface failed, advertisement %*pb\n",
- __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising);
+ config.interface = phylink_sfp_select_interface(pl, config.advertising);
+ if (config.interface == PHY_INTERFACE_MODE_NA)
return -EINVAL;
- }
- config.interface = iface;
- linkmode_copy(support1, support);
- ret = phylink_validate(pl, support1, &config);
- if (ret) {
- phylink_err(pl,
- "validation of %s/%s with support %*pb failed: %pe\n",
- phylink_an_mode_str(mode),
- phy_modes(config.interface),
- __ETHTOOL_LINK_MODE_MASK_NBITS, support,
- ERR_PTR(ret));
+ /* Attach the PHY so that the PHY is present when we do the major
+ * configuration step.
+ */
+ ret = phylink_attach_phy(pl, phy, config.interface);
+ if (ret < 0)
+ return ret;
+
+ /* This will validate the configuration for us. */
+ ret = phylink_bringup_phy(pl, phy, config.interface);
+ if (ret < 0) {
+ phy_detach(phy);
return ret;
}
@@ -3428,9 +3410,7 @@ static bool phylink_phy_no_inband(struct phy_device *phy)
static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
{
struct phylink *pl = upstream;
- phy_interface_t interface;
u8 mode;
- int ret;
/*
* This is the new way of dealing with flow control for PHYs,
@@ -3451,20 +3431,7 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
pl->config->supported_interfaces);
/* Do the initial configuration */
- ret = phylink_sfp_config_phy(pl, mode, phy);
- if (ret < 0)
- return ret;
-
- interface = pl->link_config.interface;
- ret = phylink_attach_phy(pl, phy, interface);
- if (ret < 0)
- return ret;
-
- ret = phylink_bringup_phy(pl, phy, interface);
- if (ret)
- phy_detach(phy);
-
- return ret;
+ return phylink_sfp_config_phy(pl, mode, phy);
}
static void phylink_sfp_disconnect_phy(void *upstream,
diff --git a/drivers/net/phy/qcom/qca83xx.c b/drivers/net/phy/qcom/qca83xx.c
index a05d0df6fa16..7a5039920b9f 100644
--- a/drivers/net/phy/qcom/qca83xx.c
+++ b/drivers/net/phy/qcom/qca83xx.c
@@ -42,10 +42,8 @@ static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
{
int i;
- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
- strscpy(data + i * ETH_GSTRING_LEN,
- qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
- }
+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
+ ethtool_puts(&data, qca83xx_hw_stats[i].string);
}
static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
index 166f6a728373..f65d7f1f348e 100644
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -80,18 +80,22 @@
#define RTL822X_VND2_GANLPAR 0xa414
-#define RTL822X_VND2_PHYSR 0xa434
-
#define RTL8366RB_POWER_SAVE 0x15
#define RTL8366RB_POWER_SAVE_ON BIT(12)
#define RTL9000A_GINMR 0x14
#define RTL9000A_GINMR_LINK_STATUS BIT(4)
-#define RTLGEN_SPEED_MASK 0x0630
+#define RTL_VND2_PHYSR 0xa434
+#define RTL_VND2_PHYSR_DUPLEX BIT(3)
+#define RTL_VND2_PHYSR_SPEEDL GENMASK(5, 4)
+#define RTL_VND2_PHYSR_SPEEDH GENMASK(10, 9)
+#define RTL_VND2_PHYSR_MASTER BIT(11)
+#define RTL_VND2_PHYSR_SPEED_MASK (RTL_VND2_PHYSR_SPEEDL | RTL_VND2_PHYSR_SPEEDH)
#define RTL_GENERIC_PHYID 0x001cc800
#define RTL_8211FVD_PHYID 0x001cc878
+#define RTL_8221B 0x001cc840
#define RTL_8221B_VB_CG 0x001cc849
#define RTL_8221B_VN_CG 0x001cc84a
#define RTL_8251B 0x001cc862
@@ -660,9 +664,18 @@ static int rtl8366rb_config_init(struct phy_device *phydev)
}
/* get actual speed to cover the downshift case */
-static void rtlgen_decode_speed(struct phy_device *phydev, int val)
+static void rtlgen_decode_physr(struct phy_device *phydev, int val)
{
- switch (val & RTLGEN_SPEED_MASK) {
+ /* bit 3
+ * 0: Half Duplex
+ * 1: Full Duplex
+ */
+ if (val & RTL_VND2_PHYSR_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ switch (val & RTL_VND2_PHYSR_SPEED_MASK) {
case 0x0000:
phydev->speed = SPEED_10;
break;
@@ -684,6 +697,19 @@ static void rtlgen_decode_speed(struct phy_device *phydev, int val)
default:
break;
}
+
+ /* bit 11
+ * 0: Slave Mode
+ * 1: Master Mode
+ */
+ if (phydev->speed >= 1000) {
+ if (val & RTL_VND2_PHYSR_MASTER)
+ phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
+ else
+ phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
+ } else {
+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
+ }
}
static int rtlgen_read_status(struct phy_device *phydev)
@@ -701,7 +727,7 @@ static int rtlgen_read_status(struct phy_device *phydev)
if (val < 0)
return val;
- rtlgen_decode_speed(phydev, val);
+ rtlgen_decode_physr(phydev, val);
return 0;
}
@@ -924,17 +950,25 @@ static void rtl822xb_update_interface(struct phy_device *phydev)
static int rtl822x_read_status(struct phy_device *phydev)
{
- if (phydev->autoneg == AUTONEG_ENABLE) {
- int lpadv = phy_read_paged(phydev, 0xa5d, 0x13);
+ int lpadv, ret;
- if (lpadv < 0)
- return lpadv;
+ ret = rtlgen_read_status(phydev);
+ if (ret < 0)
+ return ret;
- mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising,
- lpadv);
+ if (phydev->autoneg == AUTONEG_DISABLE ||
+ !phydev->autoneg_complete) {
+ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
+ return 0;
}
- return rtlgen_read_status(phydev);
+ lpadv = phy_read_paged(phydev, 0xa5d, 0x13);
+ if (lpadv < 0)
+ return lpadv;
+
+ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv);
+
+ return 0;
}
static int rtl822xb_read_status(struct phy_device *phydev)
@@ -993,6 +1027,10 @@ static int rtl822x_c45_read_status(struct phy_device *phydev)
if (ret < 0)
return ret;
+ if (phydev->autoneg == AUTONEG_DISABLE ||
+ !genphy_c45_aneg_done(phydev))
+ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
+
/* Vendor register as C45 has no standardized support for 1000BaseT */
if (phydev->autoneg == AUTONEG_ENABLE) {
val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
@@ -1007,11 +1045,11 @@ static int rtl822x_c45_read_status(struct phy_device *phydev)
return 0;
/* Read actual speed from vendor register. */
- val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_PHYSR);
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_VND2_PHYSR);
if (val < 0)
return val;
- rtlgen_decode_speed(phydev, val);
+ rtlgen_decode_physr(phydev, val);
return 0;
}
@@ -1040,6 +1078,23 @@ static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
return val >= 0 && val & MDIO_PMA_SPEED_2_5G;
}
+/* On internal PHY's MMD reads over C22 always return 0.
+ * Check a MMD register which is known to be non-zero.
+ */
+static bool rtlgen_supports_mmd(struct phy_device *phydev)
+{
+ int val;
+
+ phy_lock_mdio_bus(phydev);
+ __phy_write(phydev, MII_MMD_CTRL, MDIO_MMD_PCS);
+ __phy_write(phydev, MII_MMD_DATA, MDIO_PCS_EEE_ABLE);
+ __phy_write(phydev, MII_MMD_CTRL, MDIO_MMD_PCS | MII_MMD_CTRL_NOINCR);
+ val = __phy_read(phydev, MII_MMD_DATA);
+ phy_unlock_mdio_bus(phydev);
+
+ return val > 0;
+}
+
static int rtlgen_match_phy_device(struct phy_device *phydev)
{
return phydev->phy_id == RTL_GENERIC_PHYID &&
@@ -1049,7 +1104,8 @@ static int rtlgen_match_phy_device(struct phy_device *phydev)
static int rtl8226_match_phy_device(struct phy_device *phydev)
{
return phydev->phy_id == RTL_GENERIC_PHYID &&
- rtlgen_supports_2_5gbps(phydev);
+ rtlgen_supports_2_5gbps(phydev) &&
+ rtlgen_supports_mmd(phydev);
}
static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
@@ -1061,6 +1117,11 @@ static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
return !is_c45 && (id == phydev->phy_id);
}
+static int rtl8221b_match_phy_device(struct phy_device *phydev)
+{
+ return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev);
+}
+
static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
{
return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false);
@@ -1081,9 +1142,22 @@ static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev)
return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
}
-static int rtl8251b_c22_match_phy_device(struct phy_device *phydev)
+static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev)
{
- return rtlgen_is_c45_match(phydev, RTL_8251B, false);
+ if (phydev->is_c45)
+ return false;
+
+ switch (phydev->phy_id) {
+ case RTL_GENERIC_PHYID:
+ case RTL_8221B:
+ case RTL_8251B:
+ case 0x001cc841:
+ break;
+ default:
+ return false;
+ }
+
+ return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev);
}
static int rtl8251b_c45_match_phy_device(struct phy_device *phydev)
@@ -1345,10 +1419,8 @@ static struct phy_driver realtek_drvs[] = {
.resume = rtlgen_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
- .read_mmd = rtl822x_read_mmd,
- .write_mmd = rtl822x_write_mmd,
}, {
- PHY_ID_MATCH_EXACT(0x001cc840),
+ .match_phy_device = rtl8221b_match_phy_device,
.name = "RTL8226B_RTL8221B 2.5Gbps PHY",
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
@@ -1359,8 +1431,6 @@ static struct phy_driver realtek_drvs[] = {
.resume = rtlgen_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
- .read_mmd = rtl822x_read_mmd,
- .write_mmd = rtl822x_write_mmd,
}, {
PHY_ID_MATCH_EXACT(0x001cc838),
.name = "RTL8226-CG 2.5Gbps PHY",
@@ -1438,8 +1508,9 @@ static struct phy_driver realtek_drvs[] = {
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
}, {
- .match_phy_device = rtl8251b_c22_match_phy_device,
- .name = "RTL8126A-internal 5Gbps PHY",
+ .match_phy_device = rtl_internal_nbaset_match_phy_device,
+ .name = "Realtek Internal NBASE-T PHY",
+ .flags = PHY_IS_INTERNAL,
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
.read_status = rtl822x_read_status,
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index a5684ef5884b..7dbcbf0a4ee2 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -466,7 +466,8 @@ static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
static const struct sfp_quirk sfp_quirks[] = {
// Alcatel Lucent G-010S-P can operate at 2500base-X, but incorrectly
// report 2500MBd NRZ in their EEPROM
- SFP_QUIRK_M("ALCATELLUCENT", "G010SP", sfp_quirk_2500basex),
+ SFP_QUIRK("ALCATELLUCENT", "G010SP", sfp_quirk_2500basex,
+ sfp_fixup_ignore_tx_fault),
// Alcatel Lucent G-010S-A can operate at 2500base-X, but report 3.2GBd
// NRZ in their EEPROM
@@ -3146,7 +3147,7 @@ static void sfp_shutdown(struct platform_device *pdev)
static struct platform_driver sfp_driver = {
.probe = sfp_probe,
- .remove_new = sfp_remove,
+ .remove = sfp_remove,
.shutdown = sfp_shutdown,
.driver = {
.name = "sfp",
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index 150aea7c9c36..e1853599d9ba 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -627,12 +627,13 @@ int smsc_phy_probe(struct phy_device *phydev)
phydev->priv = priv;
/* Make clk optional to keep DTB backward compatibility. */
- refclk = devm_clk_get_optional_enabled(dev, NULL);
+ refclk = devm_clk_get_optional_enabled_with_rate(dev, NULL,
+ 50 * 1000 * 1000);
if (IS_ERR(refclk))
return dev_err_probe(dev, PTR_ERR(refclk),
"Failed to request clock\n");
- return clk_set_rate(refclk, 50 * 1000 * 1000);
+ return 0;
}
EXPORT_SYMBOL_GPL(smsc_phy_probe);