summaryrefslogtreecommitdiff
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/Kconfig19
-rw-r--r--drivers/net/phy/Makefile6
-rw-r--r--drivers/net/phy/air_en8811h.c45
-rw-r--r--drivers/net/phy/aquantia/aquantia.h52
-rw-r--r--drivers/net/phy/aquantia/aquantia_main.c702
-rw-r--r--drivers/net/phy/as21xxx.c7
-rw-r--r--drivers/net/phy/ax88796b.c5
-rw-r--r--drivers/net/phy/bcm-phy-ptp.c6
-rw-r--r--drivers/net/phy/broadcom.c184
-rw-r--r--drivers/net/phy/dp83640.c58
-rw-r--r--drivers/net/phy/dp83822.c7
-rw-r--r--drivers/net/phy/dp83869.c7
-rw-r--r--drivers/net/phy/dp83tg720.c185
-rw-r--r--drivers/net/phy/fixed_phy.c217
-rw-r--r--drivers/net/phy/intel-xway.c7
-rw-r--r--drivers/net/phy/marvell-88x2222.c13
-rw-r--r--drivers/net/phy/marvell.c47
-rw-r--r--drivers/net/phy/marvell10g.c7
-rw-r--r--drivers/net/phy/mdio-boardinfo.c80
-rw-r--r--drivers/net/phy/mdio-boardinfo.h23
-rw-r--r--drivers/net/phy/mdio_bus.c1
-rw-r--r--drivers/net/phy/mdio_bus_provider.c37
-rw-r--r--drivers/net/phy/mdio_device.c5
-rw-r--r--drivers/net/phy/mediatek/Kconfig1
-rw-r--r--drivers/net/phy/mediatek/mtk-2p5ge.c104
-rw-r--r--drivers/net/phy/micrel.c1259
-rw-r--r--drivers/net/phy/motorcomm.c117
-rw-r--r--drivers/net/phy/mscc/mscc.h19
-rw-r--r--drivers/net/phy/mscc/mscc_main.c55
-rw-r--r--drivers/net/phy/mscc/mscc_ptp.c102
-rw-r--r--drivers/net/phy/mscc/mscc_ptp.h1
-rw-r--r--drivers/net/phy/mxl-86110.c392
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx-macsec.c8
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx.c23
-rw-r--r--drivers/net/phy/phy-c45.c7
-rw-r--r--drivers/net/phy/phy-caps.h2
-rw-r--r--drivers/net/phy/phy-core.c79
-rw-r--r--drivers/net/phy/phy.c27
-rw-r--r--drivers/net/phy/phy_caps.c15
-rw-r--r--drivers/net/phy/phy_device.c210
-rw-r--r--drivers/net/phy/phy_package.c71
-rw-r--r--drivers/net/phy/phylib-internal.h6
-rw-r--r--drivers/net/phy/phylink.c219
-rw-r--r--drivers/net/phy/qcom/Kconfig3
-rw-r--r--drivers/net/phy/qcom/at803x.c176
-rw-r--r--drivers/net/phy/qcom/qca807x.c47
-rw-r--r--drivers/net/phy/qcom/qca808x.c23
-rw-r--r--drivers/net/phy/qcom/qcom-phy-lib.c75
-rw-r--r--drivers/net/phy/qcom/qcom.h23
-rw-r--r--drivers/net/phy/realtek/realtek_main.c271
-rw-r--r--drivers/net/phy/sfp-bus.c107
-rw-r--r--drivers/net/phy/sfp.c109
-rw-r--r--drivers/net/phy/sfp.h4
-rw-r--r--drivers/net/phy/smsc.c1
-rw-r--r--drivers/net/phy/spi_ks8995.c506
55 files changed, 3754 insertions, 2028 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 53dad2482026..98700d069191 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -3,6 +3,11 @@
# PHY Layer Configuration
#
+config MDIO_BUS
+ tristate "MDIO bus consumer layer"
+ help
+ MDIO bus consumer layer
+
config PHYLINK
tristate
select PHYLIB
@@ -25,6 +30,9 @@ if PHYLIB
config SWPHY
bool
+config PHY_PACKAGE
+ tristate
+
config LED_TRIGGER_PHY
bool "Support LED triggers for tracking link state"
depends on LEDS_TRIGGERS
@@ -157,6 +165,7 @@ config BCM54140_PHY
tristate "Broadcom BCM54140 PHY"
depends on HWMON || HWMON=n
select BCM_NET_PHYLIB
+ select PHY_PACKAGE
help
Support the Broadcom BCM54140 Quad SGMII/QSGMII PHY.
@@ -292,8 +301,9 @@ source "drivers/net/phy/mediatek/Kconfig"
config MICREL_PHY
tristate "Micrel PHYs"
depends on PTP_1588_CLOCK_OPTIONAL
+ select PHY_PACKAGE
help
- Supports the KSZ9021, VSC8201, KS8001 PHYs.
+ Supports the KSZ8xxx, KSZ9xxx, and LAN88xx families of Micrel/Microchip PHYs.
config MICROCHIP_T1S_PHY
tristate "Microchip 10BASE-T1S Ethernet PHYs"
@@ -323,6 +333,7 @@ config MICROSEMI_PHY
depends on MACSEC || MACSEC=n
depends on PTP_1588_CLOCK_OPTIONAL || !NETWORK_PHY_TIMESTAMPING
select CRYPTO_LIB_AES if MACSEC
+ select PHY_PACKAGE
help
Currently supports VSC8514, VSC8530, VSC8531, VSC8540 and VSC8541 PHYs
@@ -355,7 +366,7 @@ config NXP_TJA11XX_PHY
tristate "NXP TJA11xx PHYs support"
depends on HWMON
help
- Currently supports the NXP TJA1100 and TJA1101 PHY.
+ Currently supports the NXP TJA1100, TJA1101 and TJA1102 PHYs.
config NCN26000_PHY
tristate "Onsemi 10BASE-T1S Ethernet PHY"
@@ -459,7 +470,3 @@ config XILINX_GMII2RGMII
Ethernet physical media devices and the Gigabit Ethernet controller.
endif # PHYLIB
-
-config MICREL_KS8995MA
- tristate "Micrel KS8995MA 5-ports 10/100 managed Ethernet switch"
- depends on SPI
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 7827609e9032..76e0db40f879 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -3,12 +3,12 @@
libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \
linkmode.o phy_link_topology.o \
- phy_package.o phy_caps.o mdio_bus_provider.o
+ phy_caps.o mdio_bus_provider.o
mdio-bus-y += mdio_bus.o mdio_device.o
ifdef CONFIG_PHYLIB
# built-in whenever PHYLIB is built-in or module
-obj-y += stubs.o mdio-boardinfo.o
+obj-y += stubs.o
endif
libphy-$(CONFIG_SWPHY) += swphy.o
@@ -19,6 +19,7 @@ obj-$(CONFIG_MDIO_BUS) += mdio-bus.o
obj-$(CONFIG_PHYLINK) += phylink.o
obj-$(CONFIG_PHYLIB) += libphy.o
obj-$(CONFIG_PHYLIB) += mdio_devres.o
+obj-$(CONFIG_PHY_PACKAGE) += phy_package.o
obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o
@@ -71,7 +72,6 @@ obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o
obj-$(CONFIG_MAXLINEAR_86110_PHY) += mxl-86110.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
obj-$(CONFIG_MICROCHIP_PHY) += microchip.o
obj-$(CONFIG_MICROCHIP_PHY_RDS_PTP) += microchip_rds_ptp.o
diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c
index 57fbd8df9438..badd65f0ccee 100644
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -11,6 +11,7 @@
* Copyright (C) 2023 Airoha Technology Corp.
*/
+#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/phy.h>
#include <linux/firmware.h>
@@ -157,6 +158,7 @@ struct en8811h_priv {
struct led led[EN8811H_LED_COUNT];
struct clk_hw hw;
struct phy_device *phydev;
+ unsigned int cko_is_enabled;
};
enum {
@@ -865,11 +867,30 @@ static int en8811h_clk_is_enabled(struct clk_hw *hw)
return (pbus_value & EN8811H_CLK_CGM_CKO);
}
+static int en8811h_clk_save_context(struct clk_hw *hw)
+{
+ struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
+
+ priv->cko_is_enabled = en8811h_clk_is_enabled(hw);
+
+ return 0;
+}
+
+static void en8811h_clk_restore_context(struct clk_hw *hw)
+{
+ struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw);
+
+ if (!priv->cko_is_enabled)
+ en8811h_clk_disable(hw);
+}
+
static const struct clk_ops en8811h_clk_ops = {
- .recalc_rate = en8811h_clk_recalc_rate,
- .enable = en8811h_clk_enable,
- .disable = en8811h_clk_disable,
- .is_enabled = en8811h_clk_is_enabled,
+ .recalc_rate = en8811h_clk_recalc_rate,
+ .enable = en8811h_clk_enable,
+ .disable = en8811h_clk_disable,
+ .is_enabled = en8811h_clk_is_enabled,
+ .save_context = en8811h_clk_save_context,
+ .restore_context = en8811h_clk_restore_context,
};
static int en8811h_clk_provider_setup(struct device *dev, struct clk_hw *hw)
@@ -1149,6 +1170,20 @@ static irqreturn_t en8811h_handle_interrupt(struct phy_device *phydev)
return IRQ_HANDLED;
}
+static int en8811h_resume(struct phy_device *phydev)
+{
+ clk_restore_context();
+
+ return genphy_resume(phydev);
+}
+
+static int en8811h_suspend(struct phy_device *phydev)
+{
+ clk_save_context();
+
+ return genphy_suspend(phydev);
+}
+
static struct phy_driver en8811h_driver[] = {
{
PHY_ID_MATCH_MODEL(EN8811H_PHY_ID),
@@ -1159,6 +1194,8 @@ static struct phy_driver en8811h_driver[] = {
.get_rate_matching = en8811h_get_rate_matching,
.config_aneg = en8811h_config_aneg,
.read_status = en8811h_read_status,
+ .resume = en8811h_resume,
+ .suspend = en8811h_suspend,
.config_intr = en8811h_clear_intr,
.handle_interrupt = en8811h_handle_interrupt,
.led_hw_is_supported = en8811h_led_hw_is_supported,
diff --git a/drivers/net/phy/aquantia/aquantia.h b/drivers/net/phy/aquantia/aquantia.h
index 0c78bfabace5..31427ee343e3 100644
--- a/drivers/net/phy/aquantia/aquantia.h
+++ b/drivers/net/phy/aquantia/aquantia.h
@@ -55,6 +55,7 @@
#define VEND1_GLOBAL_CFG_SERDES_MODE_SGMII 3
#define VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII 4
#define VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G 6
+#define VEND1_GLOBAL_CFG_AUTONEG_ENA BIT(3)
#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7)
#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
@@ -152,6 +153,28 @@
#define AQR_MAX_LEDS 3
+/* Custom driver definitions for constructing a single variable out of
+ * aggregate firmware build information. These do not represent hardware
+ * fields.
+ */
+#define AQR_FW_FINGERPRINT_MAJOR GENMASK_ULL(63, 56)
+#define AQR_FW_FINGERPRINT_MINOR GENMASK_ULL(55, 48)
+#define AQR_FW_FINGERPRINT_BUILD_ID GENMASK_ULL(47, 40)
+#define AQR_FW_FINGERPRINT_PROV_ID GENMASK_ULL(39, 32)
+#define AQR_FW_FINGERPRINT_MISC_ID GENMASK_ULL(31, 16)
+#define AQR_FW_FINGERPRINT_MISC_VER GENMASK_ULL(15, 0)
+#define AQR_FW_FINGERPRINT(major, minor, build_id, prov_id, misc_id, misc_ver) \
+ (FIELD_PREP(AQR_FW_FINGERPRINT_MAJOR, major) | \
+ FIELD_PREP(AQR_FW_FINGERPRINT_MINOR, minor) | \
+ FIELD_PREP(AQR_FW_FINGERPRINT_BUILD_ID, build_id) | \
+ FIELD_PREP(AQR_FW_FINGERPRINT_PROV_ID, prov_id) | \
+ FIELD_PREP(AQR_FW_FINGERPRINT_MISC_ID, misc_id) | \
+ FIELD_PREP(AQR_FW_FINGERPRINT_MISC_VER, misc_ver))
+
+/* 10G-QXGMII firmware for NXP SPF-30841 riser board (AQR412C) */
+#define AQR_G3_V4_3_C_AQR_NXP_SPF_30841_MUSX_ID40019_VER1198 \
+ AQR_FW_FINGERPRINT(4, 3, 0xc, 1, 40019, 1198)
+
struct aqr107_hw_stat {
const char *name;
int reg;
@@ -174,10 +197,39 @@ static const struct aqr107_hw_stat aqr107_hw_stats[] = {
#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)
+static const struct {
+ int speed;
+ u16 reg;
+} aqr_global_cfg_regs[] = {
+ { SPEED_10, VEND1_GLOBAL_CFG_10M, },
+ { SPEED_100, VEND1_GLOBAL_CFG_100M, },
+ { SPEED_1000, VEND1_GLOBAL_CFG_1G, },
+ { SPEED_2500, VEND1_GLOBAL_CFG_2_5G, },
+ { SPEED_5000, VEND1_GLOBAL_CFG_5G, },
+ { SPEED_10000, VEND1_GLOBAL_CFG_10G, },
+};
+
+#define AQR_NUM_GLOBAL_CFG ARRAY_SIZE(aqr_global_cfg_regs)
+
+enum aqr_rate_adaptation {
+ AQR_RATE_ADAPT_NONE,
+ AQR_RATE_ADAPT_USX,
+ AQR_RATE_ADAPT_PAUSE,
+};
+
+struct aqr_global_syscfg {
+ int speed;
+ phy_interface_t interface;
+ enum aqr_rate_adaptation rate_adapt;
+};
+
struct aqr107_priv {
u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
+ u64 fingerprint;
unsigned long leds_active_low;
unsigned long leds_active_high;
+ bool wait_on_global_cfg;
+ struct aqr_global_syscfg global_cfg[AQR_NUM_GLOBAL_CFG];
};
#if IS_REACHABLE(CONFIG_HWMON)
diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c
index 77a48635d7bf..41f3676c7f1e 100644
--- a/drivers/net/phy/aquantia/aquantia_main.c
+++ b/drivers/net/phy/aquantia/aquantia_main.c
@@ -26,13 +26,18 @@
#define PHY_ID_AQR111 0x03a1b610
#define PHY_ID_AQR111B0 0x03a1b612
#define PHY_ID_AQR112 0x03a1b662
-#define PHY_ID_AQR412 0x03a1b712
+#define PHY_ID_AQR412 0x03a1b6f2
+#define PHY_ID_AQR412C 0x03a1b712
#define PHY_ID_AQR113 0x31c31c40
#define PHY_ID_AQR113C 0x31c31c12
#define PHY_ID_AQR114C 0x31c31c22
+#define PHY_ID_AQR115 0x31c31c63
#define PHY_ID_AQR115C 0x31c31c33
#define PHY_ID_AQR813 0x31c31cb2
+#define MDIO_PHYXS_VEND_PROV2 0xc441
+#define MDIO_PHYXS_VEND_PROV2_USX_AN BIT(3)
+
#define MDIO_PHYXS_VEND_IF_STATUS 0xe812
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3)
#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR 0
@@ -83,6 +88,9 @@
#define MDIO_AN_TX_VEND_INT_MASK2 0xd401
#define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0)
+#define PMAPMD_FW_MISC_ID 0xc41d
+#define PMAPMD_FW_MISC_VER 0xc41e
+
#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)
@@ -465,7 +473,7 @@ static int aqr105_config_aneg(struct phy_device *phydev)
return genphy_c45_check_and_restart_aneg(phydev, changed);
}
-static int aqr105_read_rate(struct phy_device *phydev)
+static int aqr_gen1_read_rate(struct phy_device *phydev)
{
int val;
@@ -504,8 +512,31 @@ static int aqr105_read_rate(struct phy_device *phydev)
return 0;
}
-static int aqr105_read_status(struct phy_device *phydev)
+/* Quad port PHYs like AQR412(C) have 4 system interfaces, but they can also be
+ * used with a single system interface over which all 4 ports are multiplexed
+ * (10G-QXGMII). To the MDIO registers, this mode is indistinguishable from
+ * USXGMII (which implies a single 10G port).
+ *
+ * To not rely solely on the device tree, we allow the regular system interface
+ * detection to work as usual, but we replace USXGMII with 10G-QXGMII based on
+ * the specific fingerprint of firmware images that are known to be for MUSX.
+ */
+static phy_interface_t aqr_translate_interface(struct phy_device *phydev,
+ phy_interface_t interface)
{
+ struct aqr107_priv *priv = phydev->priv;
+
+ if (phy_id_compare(phydev->drv->phy_id, PHY_ID_AQR412C, phydev->drv->phy_id_mask) &&
+ priv->fingerprint == AQR_G3_V4_3_C_AQR_NXP_SPF_30841_MUSX_ID40019_VER1198 &&
+ interface == PHY_INTERFACE_MODE_USXGMII)
+ return PHY_INTERFACE_MODE_10G_QXGMII;
+
+ return interface;
+}
+
+static int aqr_gen1_read_status(struct phy_device *phydev)
+{
+ phy_interface_t interface;
int ret;
int val;
@@ -531,155 +562,65 @@ static int aqr105_read_status(struct phy_device *phydev)
switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) {
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR:
- phydev->interface = PHY_INTERFACE_MODE_10GKR;
+ interface = PHY_INTERFACE_MODE_10GKR;
break;
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX:
- phydev->interface = PHY_INTERFACE_MODE_1000BASEKX;
+ interface = PHY_INTERFACE_MODE_1000BASEKX;
break;
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI:
- phydev->interface = PHY_INTERFACE_MODE_10GBASER;
+ interface = PHY_INTERFACE_MODE_10GBASER;
break;
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII:
- phydev->interface = PHY_INTERFACE_MODE_USXGMII;
+ interface = PHY_INTERFACE_MODE_USXGMII;
break;
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI:
- phydev->interface = PHY_INTERFACE_MODE_XAUI;
+ interface = PHY_INTERFACE_MODE_XAUI;
break;
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
- phydev->interface = PHY_INTERFACE_MODE_SGMII;
+ interface = PHY_INTERFACE_MODE_SGMII;
break;
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI:
- phydev->interface = PHY_INTERFACE_MODE_RXAUI;
+ interface = PHY_INTERFACE_MODE_RXAUI;
break;
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII:
- phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+ interface = PHY_INTERFACE_MODE_2500BASEX;
break;
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OFF:
default:
phydev->link = false;
- phydev->interface = PHY_INTERFACE_MODE_NA;
+ interface = PHY_INTERFACE_MODE_NA;
break;
}
- /* Read rate from vendor register */
- return aqr105_read_rate(phydev);
-}
-
-static int aqr107_read_rate(struct phy_device *phydev)
-{
- u32 config_reg;
- int val;
+ phydev->interface = aqr_translate_interface(phydev, interface);
- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1);
- if (val < 0)
- return val;
-
- if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX)
- phydev->duplex = DUPLEX_FULL;
- else
- phydev->duplex = DUPLEX_HALF;
-
- switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) {
- case MDIO_AN_TX_VEND_STATUS1_10BASET:
- phydev->speed = SPEED_10;
- config_reg = VEND1_GLOBAL_CFG_10M;
- break;
- case MDIO_AN_TX_VEND_STATUS1_100BASETX:
- phydev->speed = SPEED_100;
- config_reg = VEND1_GLOBAL_CFG_100M;
- break;
- case MDIO_AN_TX_VEND_STATUS1_1000BASET:
- phydev->speed = SPEED_1000;
- config_reg = VEND1_GLOBAL_CFG_1G;
- break;
- case MDIO_AN_TX_VEND_STATUS1_2500BASET:
- phydev->speed = SPEED_2500;
- config_reg = VEND1_GLOBAL_CFG_2_5G;
- break;
- case MDIO_AN_TX_VEND_STATUS1_5000BASET:
- phydev->speed = SPEED_5000;
- config_reg = VEND1_GLOBAL_CFG_5G;
- break;
- case MDIO_AN_TX_VEND_STATUS1_10GBASET:
- phydev->speed = SPEED_10000;
- config_reg = VEND1_GLOBAL_CFG_10G;
- break;
- default:
- phydev->speed = SPEED_UNKNOWN;
- return 0;
- }
-
- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg);
- if (val < 0)
- return val;
-
- if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) ==
- VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE)
- phydev->rate_matching = RATE_MATCH_PAUSE;
- else
- phydev->rate_matching = RATE_MATCH_NONE;
-
- return 0;
+ /* Read rate from vendor register */
+ return aqr_gen1_read_rate(phydev);
}
-static int aqr107_read_status(struct phy_device *phydev)
+static int aqr_gen2_read_status(struct phy_device *phydev)
{
- int val, ret;
+ struct aqr107_priv *priv = phydev->priv;
+ int i, ret;
- ret = aqr_read_status(phydev);
+ ret = aqr_gen1_read_status(phydev);
if (ret)
return ret;
- if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE)
- return 0;
+ for (i = 0; i < AQR_NUM_GLOBAL_CFG; i++) {
+ struct aqr_global_syscfg *syscfg = &priv->global_cfg[i];
- /* 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;
+ if (syscfg->speed != phydev->speed)
+ continue;
- switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) {
- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR:
- phydev->interface = PHY_INTERFACE_MODE_10GKR;
- break;
- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX:
- phydev->interface = PHY_INTERFACE_MODE_1000BASEKX;
- break;
- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI:
- phydev->interface = PHY_INTERFACE_MODE_10GBASER;
- break;
- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII:
- phydev->interface = PHY_INTERFACE_MODE_USXGMII;
- break;
- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI:
- phydev->interface = PHY_INTERFACE_MODE_XAUI;
- break;
- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
- phydev->interface = PHY_INTERFACE_MODE_SGMII;
- break;
- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI:
- phydev->interface = PHY_INTERFACE_MODE_RXAUI;
- break;
- 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;
+ if (syscfg->rate_adapt == AQR_RATE_ADAPT_PAUSE)
+ phydev->rate_matching = RATE_MATCH_PAUSE;
+ else
+ phydev->rate_matching = RATE_MATCH_NONE;
break;
}
- /* Read possibly downshifted rate from vendor register */
- return aqr107_read_rate(phydev);
+ return 0;
}
static int aqr107_get_downshift(struct phy_device *phydev, u8 *data)
@@ -764,27 +705,46 @@ int aqr_wait_reset_complete(struct phy_device *phydev)
return ret;
}
-static void aqr107_chip_info(struct phy_device *phydev)
+static int aqr_build_fingerprint(struct phy_device *phydev)
{
u8 fw_major, fw_minor, build_id, prov_id;
+ struct aqr107_priv *priv = phydev->priv;
+ u16 misc_id, misc_ver;
int val;
val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
if (val < 0)
- return;
+ return val;
fw_major = FIELD_GET(VEND1_GLOBAL_FW_ID_MAJOR, val);
fw_minor = FIELD_GET(VEND1_GLOBAL_FW_ID_MINOR, val);
val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT1);
if (val < 0)
- return;
+ return val;
build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val);
prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val);
- phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n",
- fw_major, fw_minor, build_id, prov_id);
+ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_FW_MISC_ID);
+ if (val < 0)
+ return val;
+
+ misc_id = val;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_FW_MISC_VER);
+ if (val < 0)
+ return val;
+
+ misc_ver = val;
+
+ priv->fingerprint = AQR_FW_FINGERPRINT(fw_major, fw_minor, build_id,
+ prov_id, misc_id, misc_ver);
+
+ phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u, Misc ID %u, Version %u\n",
+ fw_major, fw_minor, build_id, prov_id, misc_id, misc_ver);
+
+ return 0;
}
static int aqr107_config_mdi(struct phy_device *phydev)
@@ -810,7 +770,7 @@ static int aqr107_config_mdi(struct phy_device *phydev)
mdi_conf | PMAPMD_RSVD_VEND_PROV_MDI_FORCE);
}
-static int aqr107_config_init(struct phy_device *phydev)
+static int aqr_gen1_config_init(struct phy_device *phydev)
{
struct aqr107_priv *priv = phydev->priv;
u32 led_idx;
@@ -822,6 +782,7 @@ static int aqr107_config_init(struct phy_device *phydev)
phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
phydev->interface != PHY_INTERFACE_MODE_XGMII &&
phydev->interface != PHY_INTERFACE_MODE_USXGMII &&
+ phydev->interface != PHY_INTERFACE_MODE_10G_QXGMII &&
phydev->interface != PHY_INTERFACE_MODE_10GKR &&
phydev->interface != PHY_INTERFACE_MODE_10GBASER &&
phydev->interface != PHY_INTERFACE_MODE_XAUI &&
@@ -832,8 +793,14 @@ static int aqr107_config_init(struct phy_device *phydev)
"Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n");
ret = aqr_wait_reset_complete(phydev);
- if (!ret)
- aqr107_chip_info(phydev);
+ if (!ret) {
+ /* The PHY might work without a firmware image, so only build a
+ * fingerprint if the firmware was initialized.
+ */
+ ret = aqr_build_fingerprint(phydev);
+ if (ret)
+ return ret;
+ }
ret = aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
if (ret)
@@ -859,20 +826,145 @@ static int aqr107_config_init(struct phy_device *phydev)
return 0;
}
-static int aqcs109_config_init(struct phy_device *phydev)
+/* Walk the media-speed configuration registers to determine which
+ * host-side serdes modes may be used by the PHY depending on the
+ * negotiated media speed.
+ */
+static int aqr_gen2_read_global_syscfg(struct phy_device *phydev)
+{
+ struct aqr107_priv *priv = phydev->priv;
+ unsigned int serdes_mode, rate_adapt;
+ phy_interface_t interface;
+ int i, val;
+
+ for (i = 0; i < AQR_NUM_GLOBAL_CFG; i++) {
+ struct aqr_global_syscfg *syscfg = &priv->global_cfg[i];
+
+ syscfg->speed = aqr_global_cfg_regs[i].speed;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1,
+ aqr_global_cfg_regs[i].reg);
+ if (val < 0)
+ return val;
+
+ serdes_mode = FIELD_GET(VEND1_GLOBAL_CFG_SERDES_MODE, val);
+ rate_adapt = FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val);
+
+ switch (serdes_mode) {
+ case VEND1_GLOBAL_CFG_SERDES_MODE_XFI:
+ if (rate_adapt == VEND1_GLOBAL_CFG_RATE_ADAPT_USX)
+ interface = PHY_INTERFACE_MODE_USXGMII;
+ else
+ interface = PHY_INTERFACE_MODE_10GBASER;
+ break;
+
+ case VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G:
+ interface = PHY_INTERFACE_MODE_5GBASER;
+ break;
+
+ case VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII:
+ interface = PHY_INTERFACE_MODE_2500BASEX;
+ break;
+
+ case VEND1_GLOBAL_CFG_SERDES_MODE_SGMII:
+ interface = PHY_INTERFACE_MODE_SGMII;
+ break;
+
+ default:
+ phydev_warn(phydev, "unrecognised serdes mode %u\n",
+ serdes_mode);
+ interface = PHY_INTERFACE_MODE_NA;
+ break;
+ }
+
+ syscfg->interface = aqr_translate_interface(phydev, interface);
+
+ switch (rate_adapt) {
+ case VEND1_GLOBAL_CFG_RATE_ADAPT_NONE:
+ syscfg->rate_adapt = AQR_RATE_ADAPT_NONE;
+ break;
+ case VEND1_GLOBAL_CFG_RATE_ADAPT_USX:
+ syscfg->rate_adapt = AQR_RATE_ADAPT_USX;
+ break;
+ case VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE:
+ syscfg->rate_adapt = AQR_RATE_ADAPT_PAUSE;
+ break;
+ default:
+ phydev_warn(phydev, "unrecognized rate adapt mode %u\n",
+ rate_adapt);
+ break;
+ }
+
+ phydev_dbg(phydev,
+ "Media speed %d uses host interface %s with %s\n",
+ syscfg->speed, phy_modes(syscfg->interface),
+ syscfg->rate_adapt == AQR_RATE_ADAPT_NONE ? "no rate adaptation" :
+ syscfg->rate_adapt == AQR_RATE_ADAPT_PAUSE ? "rate adaptation through flow control" :
+ syscfg->rate_adapt == AQR_RATE_ADAPT_USX ? "rate adaptation through symbol replication" :
+ "unrecognized rate adaptation type");
+ }
+
+ return 0;
+}
+
+static int aqr_gen2_fill_interface_modes(struct phy_device *phydev)
+{
+ unsigned long *possible = phydev->possible_interfaces;
+ struct aqr107_priv *priv = phydev->priv;
+ phy_interface_t interface;
+ int i, val, ret;
+
+ /* It's been observed on some models that - when coming out of suspend
+ * - the FW signals that the PHY is ready but the GLOBAL_CFG registers
+ * continue on returning zeroes for some time. Let's poll the 100M
+ * register until it returns a real value as both 113c and 115c support
+ * this mode.
+ */
+ if (priv->wait_on_global_cfg) {
+ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
+ VEND1_GLOBAL_CFG_100M, val,
+ val != 0, 1000, 100000, false);
+ if (ret)
+ return ret;
+ }
+
+ ret = aqr_gen2_read_global_syscfg(phydev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AQR_NUM_GLOBAL_CFG; i++) {
+ interface = priv->global_cfg[i].interface;
+ if (interface != PHY_INTERFACE_MODE_NA)
+ __set_bit(interface, possible);
+ }
+
+ return 0;
+}
+
+static int aqr_gen2_config_init(struct phy_device *phydev)
{
int ret;
+ ret = aqr_gen1_config_init(phydev);
+ if (ret)
+ return ret;
+
+ return aqr_gen2_fill_interface_modes(phydev);
+}
+
+static int aqr_gen3_config_init(struct phy_device *phydev)
+{
+ return aqr_gen2_config_init(phydev);
+}
+
+static int aqcs109_config_init(struct phy_device *phydev)
+{
/* Check that the PHY interface type is compatible */
if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
phydev->interface != PHY_INTERFACE_MODE_2500BASEX)
return -ENODEV;
- ret = aqr_wait_reset_complete(phydev);
- if (!ret)
- aqr107_chip_info(phydev);
-
- return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
+ return aqr_gen2_config_init(phydev);
}
static void aqr107_link_change_notify(struct phy_device *phydev)
@@ -920,7 +1012,7 @@ static void aqr107_link_change_notify(struct phy_device *phydev)
phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n");
}
-static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
+static int aqr_gen1_wait_processor_intensive_op(struct phy_device *phydev)
{
int val, err;
@@ -944,17 +1036,16 @@ static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
return 0;
}
-static int aqr107_get_rate_matching(struct phy_device *phydev,
- phy_interface_t iface)
+static int aqr_gen2_get_rate_matching(struct phy_device *phydev,
+ phy_interface_t iface)
{
if (iface == PHY_INTERFACE_MODE_10GBASER ||
- iface == PHY_INTERFACE_MODE_2500BASEX ||
- iface == PHY_INTERFACE_MODE_NA)
+ iface == PHY_INTERFACE_MODE_2500BASEX)
return RATE_MATCH_PAUSE;
return RATE_MATCH_NONE;
}
-static int aqr107_suspend(struct phy_device *phydev)
+static int aqr_gen1_suspend(struct phy_device *phydev)
{
int err;
@@ -963,10 +1054,10 @@ static int aqr107_suspend(struct phy_device *phydev)
if (err)
return err;
- return aqr107_wait_processor_intensive_op(phydev);
+ return aqr_gen1_wait_processor_intensive_op(phydev);
}
-static int aqr107_resume(struct phy_device *phydev)
+static int aqr_gen1_resume(struct phy_device *phydev)
{
int err;
@@ -975,89 +1066,7 @@ static int aqr107_resume(struct phy_device *phydev)
if (err)
return err;
- return aqr107_wait_processor_intensive_op(phydev);
-}
-
-static const u16 aqr_global_cfg_regs[] = {
- VEND1_GLOBAL_CFG_10M,
- VEND1_GLOBAL_CFG_100M,
- VEND1_GLOBAL_CFG_1G,
- VEND1_GLOBAL_CFG_2_5G,
- VEND1_GLOBAL_CFG_5G,
- VEND1_GLOBAL_CFG_10G
-};
-
-static int aqr107_fill_interface_modes(struct phy_device *phydev)
-{
- unsigned long *possible = phydev->possible_interfaces;
- unsigned int serdes_mode, rate_adapt;
- phy_interface_t interface;
- int i, val;
-
- /* Walk the media-speed configuration registers to determine which
- * host-side serdes modes may be used by the PHY depending on the
- * negotiated media speed.
- */
- for (i = 0; i < ARRAY_SIZE(aqr_global_cfg_regs); i++) {
- val = phy_read_mmd(phydev, MDIO_MMD_VEND1,
- aqr_global_cfg_regs[i]);
- if (val < 0)
- return val;
-
- serdes_mode = FIELD_GET(VEND1_GLOBAL_CFG_SERDES_MODE, val);
- rate_adapt = FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val);
-
- switch (serdes_mode) {
- case VEND1_GLOBAL_CFG_SERDES_MODE_XFI:
- if (rate_adapt == VEND1_GLOBAL_CFG_RATE_ADAPT_USX)
- interface = PHY_INTERFACE_MODE_USXGMII;
- else
- interface = PHY_INTERFACE_MODE_10GBASER;
- break;
-
- case VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G:
- interface = PHY_INTERFACE_MODE_5GBASER;
- break;
-
- case VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII:
- interface = PHY_INTERFACE_MODE_2500BASEX;
- break;
-
- case VEND1_GLOBAL_CFG_SERDES_MODE_SGMII:
- interface = PHY_INTERFACE_MODE_SGMII;
- break;
-
- default:
- phydev_warn(phydev, "unrecognised serdes mode %u\n",
- serdes_mode);
- interface = PHY_INTERFACE_MODE_NA;
- break;
- }
-
- if (interface != PHY_INTERFACE_MODE_NA)
- __set_bit(interface, possible);
- }
-
- return 0;
-}
-
-static int aqr113c_fill_interface_modes(struct phy_device *phydev)
-{
- int val, ret;
-
- /* It's been observed on some models that - when coming out of suspend
- * - the FW signals that the PHY is ready but the GLOBAL_CFG registers
- * continue on returning zeroes for some time. Let's poll the 100M
- * register until it returns a real value as both 113c and 115c support
- * this mode.
- */
- ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
- VEND1_GLOBAL_CFG_100M, val, val != 0,
- 1000, 100000, false);
- if (ret)
- return ret;
-
- return aqr107_fill_interface_modes(phydev);
+ return aqr_gen1_wait_processor_intensive_op(phydev);
}
static int aqr115c_get_features(struct phy_device *phydev)
@@ -1085,11 +1094,14 @@ static int aqr111_get_features(struct phy_device *phydev)
return 0;
}
-static int aqr113c_config_init(struct phy_device *phydev)
+static int aqr_gen4_config_init(struct phy_device *phydev)
{
+ struct aqr107_priv *priv = phydev->priv;
int ret;
- ret = aqr107_config_init(phydev);
+ priv->wait_on_global_cfg = true;
+
+ ret = aqr_gen3_config_init(phydev);
if (ret < 0)
return ret;
@@ -1098,11 +1110,55 @@ static int aqr113c_config_init(struct phy_device *phydev)
if (ret)
return ret;
- ret = aqr107_wait_processor_intensive_op(phydev);
- if (ret)
- return ret;
+ return aqr_gen1_wait_processor_intensive_op(phydev);
+}
+
+static unsigned int aqr_gen2_inband_caps(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ if (interface == PHY_INTERFACE_MODE_SGMII ||
+ interface == PHY_INTERFACE_MODE_USXGMII ||
+ interface == PHY_INTERFACE_MODE_10G_QXGMII)
+ return LINK_INBAND_ENABLE | LINK_INBAND_DISABLE;
+
+ return 0;
+}
+
+static int aqr_gen2_config_inband(struct phy_device *phydev, unsigned int modes)
+{
+ struct aqr107_priv *priv = phydev->priv;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
+ phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII) {
+ u16 set = 0;
+
+ if (modes == LINK_INBAND_ENABLE)
+ set = MDIO_PHYXS_VEND_PROV2_USX_AN;
+
+ return phy_modify_mmd(phydev, MDIO_MMD_PHYXS,
+ MDIO_PHYXS_VEND_PROV2,
+ MDIO_PHYXS_VEND_PROV2_USX_AN, set);
+ }
+
+ for (int i = 0; i < AQR_NUM_GLOBAL_CFG; i++) {
+ struct aqr_global_syscfg *syscfg = &priv->global_cfg[i];
+ u16 set = 0;
+ int err;
+
+ if (syscfg->interface != phydev->interface)
+ continue;
+
+ if (modes == LINK_INBAND_ENABLE)
+ set = VEND1_GLOBAL_CFG_AUTONEG_ENA;
+
+ err = phy_modify_mmd(phydev, MDIO_MMD_VEND1,
+ aqr_global_cfg_regs[i].reg,
+ VEND1_GLOBAL_CFG_AUTONEG_ENA, set);
+ if (err)
+ return err;
+ }
- return aqr113c_fill_interface_modes(phydev);
+ return 0;
}
static int aqr107_probe(struct phy_device *phydev)
@@ -1144,13 +1200,13 @@ static struct phy_driver aqr_driver[] = {
.name = "Aquantia AQR105",
.get_features = aqr105_get_features,
.probe = aqr107_probe,
- .config_init = aqr107_config_init,
+ .config_init = aqr_gen1_config_init,
.config_aneg = aqr105_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
- .read_status = aqr105_read_status,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
+ .read_status = aqr_gen1_read_status,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR106),
@@ -1164,16 +1220,16 @@ static struct phy_driver aqr_driver[] = {
PHY_ID_MATCH_MODEL(PHY_ID_AQR107),
.name = "Aquantia AQR107",
.probe = aqr107_probe,
- .get_rate_matching = aqr107_get_rate_matching,
- .config_init = aqr107_config_init,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
+ .config_init = aqr_gen2_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
- .read_status = aqr107_read_status,
+ .read_status = aqr_gen2_read_status,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
@@ -1183,21 +1239,23 @@ static struct phy_driver aqr_driver[] = {
.led_hw_control_set = aqr_phy_led_hw_control_set,
.led_hw_control_get = aqr_phy_led_hw_control_get,
.led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQCS109),
.name = "Aquantia AQCS109",
.probe = aqr107_probe,
- .get_rate_matching = aqr107_get_rate_matching,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
.config_init = aqcs109_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
- .read_status = aqr107_read_status,
+ .read_status = aqr_gen2_read_status,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
@@ -1208,21 +1266,23 @@ static struct phy_driver aqr_driver[] = {
.led_hw_control_set = aqr_phy_led_hw_control_set,
.led_hw_control_get = aqr_phy_led_hw_control_get,
.led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR111),
.name = "Aquantia AQR111",
.probe = aqr107_probe,
- .get_rate_matching = aqr107_get_rate_matching,
- .config_init = aqr107_config_init,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
+ .config_init = aqr_gen3_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
- .read_status = aqr107_read_status,
+ .read_status = aqr_gen2_read_status,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
@@ -1233,21 +1293,23 @@ static struct phy_driver aqr_driver[] = {
.led_hw_control_set = aqr_phy_led_hw_control_set,
.led_hw_control_get = aqr_phy_led_hw_control_get,
.led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0),
.name = "Aquantia AQR111B0",
.probe = aqr107_probe,
- .get_rate_matching = aqr107_get_rate_matching,
- .config_init = aqr107_config_init,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
+ .config_init = aqr_gen3_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
- .read_status = aqr107_read_status,
+ .read_status = aqr_gen2_read_status,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
@@ -1258,6 +1320,8 @@ static struct phy_driver aqr_driver[] = {
.led_hw_control_set = aqr_phy_led_hw_control_set,
.led_hw_control_get = aqr_phy_led_hw_control_get,
.led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR405),
@@ -1266,20 +1330,23 @@ static struct phy_driver aqr_driver[] = {
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
.read_status = aqr_read_status,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
.name = "Aquantia AQR112",
.probe = aqr107_probe,
+ .config_init = aqr_gen3_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
- .read_status = aqr107_read_status,
- .get_rate_matching = aqr107_get_rate_matching,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
+ .read_status = aqr_gen2_read_status,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
@@ -1289,39 +1356,65 @@ static struct phy_driver aqr_driver[] = {
.led_hw_control_set = aqr_phy_led_hw_control_set,
.led_hw_control_get = aqr_phy_led_hw_control_get,
.led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
.name = "Aquantia AQR412",
.probe = aqr107_probe,
+ .config_init = aqr_gen3_config_init,
+ .config_aneg = aqr_config_aneg,
+ .config_intr = aqr_config_intr,
+ .handle_interrupt = aqr_handle_interrupt,
+ .get_tunable = aqr107_get_tunable,
+ .set_tunable = aqr107_set_tunable,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
+ .read_status = aqr_gen2_read_status,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
+ .get_sset_count = aqr107_get_sset_count,
+ .get_strings = aqr107_get_strings,
+ .get_stats = aqr107_get_stats,
+ .link_change_notify = aqr107_link_change_notify,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
+},
+{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR412C),
+ .name = "Aquantia AQR412C",
+ .probe = aqr107_probe,
+ .config_init = aqr_gen3_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
- .read_status = aqr107_read_status,
- .get_rate_matching = aqr107_get_rate_matching,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
+ .read_status = aqr_gen2_read_status,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
.name = "Aquantia AQR113",
.probe = aqr107_probe,
- .get_rate_matching = aqr107_get_rate_matching,
- .config_init = aqr113c_config_init,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
+ .config_init = aqr_gen4_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
- .read_status = aqr107_read_status,
+ .read_status = aqr_gen2_read_status,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
@@ -1331,21 +1424,23 @@ static struct phy_driver aqr_driver[] = {
.led_hw_control_set = aqr_phy_led_hw_control_set,
.led_hw_control_get = aqr_phy_led_hw_control_get,
.led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
.name = "Aquantia AQR113C",
.probe = aqr107_probe,
- .get_rate_matching = aqr107_get_rate_matching,
- .config_init = aqr113c_config_init,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
+ .config_init = aqr_gen4_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
- .read_status = aqr107_read_status,
+ .read_status = aqr_gen2_read_status,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
@@ -1355,21 +1450,23 @@ static struct phy_driver aqr_driver[] = {
.led_hw_control_set = aqr_phy_led_hw_control_set,
.led_hw_control_get = aqr_phy_led_hw_control_get,
.led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR114C),
.name = "Aquantia AQR114C",
.probe = aqr107_probe,
- .get_rate_matching = aqr107_get_rate_matching,
- .config_init = aqr107_config_init,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
+ .config_init = aqr_gen4_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
- .read_status = aqr107_read_status,
+ .read_status = aqr_gen2_read_status,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
@@ -1380,21 +1477,50 @@ static struct phy_driver aqr_driver[] = {
.led_hw_control_set = aqr_phy_led_hw_control_set,
.led_hw_control_get = aqr_phy_led_hw_control_get,
.led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
+},
+{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR115),
+ .name = "Aquantia AQR115",
+ .probe = aqr107_probe,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
+ .config_init = aqr_gen4_config_init,
+ .config_aneg = aqr_config_aneg,
+ .config_intr = aqr_config_intr,
+ .handle_interrupt = aqr_handle_interrupt,
+ .read_status = aqr_gen2_read_status,
+ .get_tunable = aqr107_get_tunable,
+ .set_tunable = aqr107_set_tunable,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
+ .get_sset_count = aqr107_get_sset_count,
+ .get_strings = aqr107_get_strings,
+ .get_stats = aqr107_get_stats,
+ .get_features = aqr115c_get_features,
+ .link_change_notify = aqr107_link_change_notify,
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR115C),
.name = "Aquantia AQR115C",
.probe = aqr107_probe,
- .get_rate_matching = aqr107_get_rate_matching,
- .config_init = aqr113c_config_init,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
+ .config_init = aqr_gen4_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
- .read_status = aqr107_read_status,
+ .read_status = aqr_gen2_read_status,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
@@ -1405,21 +1531,23 @@ static struct phy_driver aqr_driver[] = {
.led_hw_control_set = aqr_phy_led_hw_control_set,
.led_hw_control_get = aqr_phy_led_hw_control_get,
.led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
.name = "Aquantia AQR813",
.probe = aqr107_probe,
- .get_rate_matching = aqr107_get_rate_matching,
- .config_init = aqr107_config_init,
+ .get_rate_matching = aqr_gen2_get_rate_matching,
+ .config_init = aqr_gen4_config_init,
.config_aneg = aqr_config_aneg,
.config_intr = aqr_config_intr,
.handle_interrupt = aqr_handle_interrupt,
- .read_status = aqr107_read_status,
+ .read_status = aqr_gen2_read_status,
.get_tunable = aqr107_get_tunable,
.set_tunable = aqr107_set_tunable,
- .suspend = aqr107_suspend,
- .resume = aqr107_resume,
+ .suspend = aqr_gen1_suspend,
+ .resume = aqr_gen1_resume,
.get_sset_count = aqr107_get_sset_count,
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
@@ -1429,6 +1557,8 @@ static struct phy_driver aqr_driver[] = {
.led_hw_control_set = aqr_phy_led_hw_control_set,
.led_hw_control_get = aqr_phy_led_hw_control_get,
.led_polarity_set = aqr_phy_led_polarity_set,
+ .inband_caps = aqr_gen2_inband_caps,
+ .config_inband = aqr_gen2_config_inband,
},
};
@@ -1446,9 +1576,11 @@ static const struct mdio_device_id __maybe_unused aqr_tbl[] = {
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR412C) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR114C) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR115) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR115C) },
{ PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
{ }
diff --git a/drivers/net/phy/as21xxx.c b/drivers/net/phy/as21xxx.c
index 92697f43087d..005277360656 100644
--- a/drivers/net/phy/as21xxx.c
+++ b/drivers/net/phy/as21xxx.c
@@ -884,11 +884,12 @@ static int as21xxx_match_phy_device(struct phy_device *phydev,
u32 phy_id;
int ret;
- /* Skip PHY that are not AS21xxx or already have firmware loaded */
- if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX)
+ /* Skip PHY that are not AS21xxx */
+ if (!phy_id_compare_vendor(phydev->c45_ids.device_ids[MDIO_MMD_PCS],
+ PHY_VENDOR_AEONSEMI))
return genphy_match_phy_device(phydev, phydrv);
- /* Read PHY ID to handle firmware just loaded */
+ /* Read PHY ID to handle firmware loaded or HW reset */
ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1);
if (ret < 0)
return ret;
diff --git a/drivers/net/phy/ax88796b.c b/drivers/net/phy/ax88796b.c
index 694df1401aa2..f20ddf649149 100644
--- a/drivers/net/phy/ax88796b.c
+++ b/drivers/net/phy/ax88796b.c
@@ -112,9 +112,8 @@ static struct phy_driver asix_driver[] = {
.resume = genphy_resume,
.soft_reset = asix_soft_reset,
}, {
- .phy_id = PHY_ID_ASIX_AX88796B,
+ PHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B),
.name = "Asix Electronics AX88796B",
- .phy_id_mask = 0xfffffff0,
/* PHY_BASIC_FEATURES */
.soft_reset = asix_soft_reset,
} };
@@ -124,7 +123,7 @@ module_phy_driver(asix_driver);
static const struct mdio_device_id __maybe_unused asix_tbl[] = {
{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A) },
{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C) },
- { PHY_ID_ASIX_AX88796B, 0xfffffff0 },
+ { PHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B) },
{ }
};
diff --git a/drivers/net/phy/bcm-phy-ptp.c b/drivers/net/phy/bcm-phy-ptp.c
index eba8b5fb1365..d3501f8487d9 100644
--- a/drivers/net/phy/bcm-phy-ptp.c
+++ b/drivers/net/phy/bcm-phy-ptp.c
@@ -597,10 +597,6 @@ static int bcm_ptp_perout_locked(struct bcm_ptp_private *priv,
period = BCM_MAX_PERIOD_8NS; /* write nonzero value */
- /* Reject unsupported flags */
- if (req->flags & ~PTP_PEROUT_DUTY_CYCLE)
- return -EOPNOTSUPP;
-
if (req->flags & PTP_PEROUT_DUTY_CYCLE)
pulse = ktime_to_ns(ktime_set(req->on.sec, req->on.nsec));
else
@@ -741,6 +737,8 @@ static const struct ptp_clock_info bcm_ptp_clock_info = {
.n_pins = 1,
.n_per_out = 1,
.n_ext_ts = 1,
+ .supported_perout_flags = PTP_PEROUT_DUTY_CYCLE,
+ .supported_extts_flags = PTP_STRICT_FLAGS | PTP_RISING_EDGE,
};
static void bcm_ptp_txtstamp(struct mii_timestamper *mii_ts,
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index 9b1de54fd483..3459a0e9d8b9 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -23,9 +23,6 @@
#include <linux/irq.h>
#include <linux/gpio/consumer.h>
-#define BRCM_PHY_MODEL(phydev) \
- ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
-
#define BRCM_PHY_REV(phydev) \
((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
@@ -249,8 +246,8 @@ static int bcm54xx_phydsp_config(struct phy_device *phydev)
if (err < 0)
return err;
- if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
- BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
+ if (phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM50610) ||
+ phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM50610M)) {
/* Clear bit 9 to fix a phy interop issue. */
err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
@@ -264,7 +261,7 @@ static int bcm54xx_phydsp_config(struct phy_device *phydev)
}
}
- if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
+ if (phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM57780)) {
int val;
val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
@@ -292,12 +289,12 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
bool clk125en = true;
/* Abort if we are using an untested phy. */
- if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
- BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
- BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M &&
- BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54210E &&
- BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 &&
- BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811)
+ if (!(phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM57780) ||
+ phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM50610) ||
+ phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM50610M) ||
+ phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM54210E) ||
+ phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM54810) ||
+ phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM54811)))
return;
val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
@@ -306,8 +303,8 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
orig = val;
- if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
- BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
+ if ((phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM50610) ||
+ phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM50610M)) &&
BRCM_PHY_REV(phydev) >= 0x3) {
/*
* Here, bit 0 _disables_ CLK125 when set.
@@ -316,7 +313,8 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
clk125en = false;
} else {
if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
- if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) {
+ if (!phy_id_compare_model(phydev->drv->phy_id,
+ PHY_ID_BCM54811)) {
/* Here, bit 0 _enables_ CLK125 when set */
val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
}
@@ -330,9 +328,9 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) {
- if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E ||
- BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810 ||
- BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54811)
+ if (phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM54210E) ||
+ phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM54810) ||
+ phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM54811))
val |= BCM54XX_SHD_SCR3_RXCTXC_DIS;
else
val |= BCM54XX_SHD_SCR3_TRDDAPD;
@@ -407,7 +405,7 @@ static int bcm5481x_set_brrmode(struct phy_device *phydev, bool on)
static int bcm54811_config_init(struct phy_device *phydev)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
- int err, reg;
+ int err, reg, exp_sync_ethernet;
/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
@@ -424,6 +422,18 @@ static int bcm54811_config_init(struct phy_device *phydev)
if (priv->brr_mode)
phydev->autoneg = 0;
+ /* Enable MII Lite (No TXER, RXER, CRS, COL) if configured */
+ if (phydev->interface == PHY_INTERFACE_MODE_MIILITE)
+ exp_sync_ethernet = BCM_EXP_SYNC_ETHERNET_MII_LITE;
+ else
+ exp_sync_ethernet = 0;
+
+ err = bcm_phy_modify_exp(phydev, BCM_EXP_SYNC_ETHERNET,
+ BCM_EXP_SYNC_ETHERNET_MII_LITE,
+ exp_sync_ethernet);
+ if (err < 0)
+ return err;
+
return bcm5481x_set_brrmode(phydev, priv->brr_mode);
}
@@ -449,14 +459,14 @@ static int bcm54xx_config_init(struct phy_device *phydev)
if (err < 0)
return err;
- if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
- BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
+ if ((phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM50610) ||
+ phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM50610M)) &&
(phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
bcm54xx_adjust_rxrefclk(phydev);
- switch (BRCM_PHY_MODEL(phydev)) {
+ switch (phydev->drv->phy_id & PHY_ID_MATCH_MODEL_MASK) {
case PHY_ID_BCM50610:
case PHY_ID_BCM50610M:
err = bcm54xx_config_clock_delay(phydev);
@@ -655,7 +665,7 @@ static int bcm5481x_read_abilities(struct phy_device *phydev)
{
struct device_node *np = phydev->mdio.dev.of_node;
struct bcm54xx_phy_priv *priv = phydev->priv;
- int i, val, err;
+ int i, val, err, aneg;
for (i = 0; i < ARRAY_SIZE(bcm54811_linkmodes); i++)
linkmode_clear_bit(bcm54811_linkmodes[i], phydev->supported);
@@ -676,9 +686,19 @@ static int bcm5481x_read_abilities(struct phy_device *phydev)
if (val < 0)
return val;
+ /* BCM54811 is not capable of LDS but the corresponding bit
+ * in LRESR is set to 1 and marked "Ignore" in the datasheet.
+ * So we must read the bcm54811 as unable to auto-negotiate
+ * in BroadR-Reach mode.
+ */
+ if (phy_id_compare_model(phydev->drv->phy_id, PHY_ID_BCM54811))
+ aneg = 0;
+ else
+ aneg = val & LRESR_LDSABILITY;
+
linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
phydev->supported,
- val & LRESR_LDSABILITY);
+ aneg);
linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
phydev->supported,
val & LRESR_100_1PAIR);
@@ -735,8 +755,15 @@ static int bcm54811_config_aneg(struct phy_device *phydev)
/* Aneg firstly. */
if (priv->brr_mode) {
- /* BCM54811 is only capable of autonegotiation in IEEE mode */
- phydev->autoneg = 0;
+ /* BCM54811 is only capable of autonegotiation in IEEE mode.
+ * In BroadR-Reach mode, disable the Long Distance Signaling,
+ * the BRR mode autoneg as supported in other Broadcom PHYs.
+ * This bit is marked as "Reserved" and "Default 1, must be
+ * written to 0 after every device reset" in the datasheet.
+ */
+ ret = phy_modify(phydev, MII_BCM54XX_LRECR, LRECR_LDSEN, 0);
+ if (ret < 0)
+ return ret;
ret = bcm_config_lre_aneg(phydev, false);
} else {
ret = genphy_config_aneg(phydev);
@@ -1409,8 +1436,7 @@ static int bcm54811_read_status(struct phy_device *phydev)
static struct phy_driver broadcom_drivers[] = {
{
- .phy_id = PHY_ID_BCM5411,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM5411),
.name = "Broadcom BCM5411",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1422,8 +1448,7 @@ static struct phy_driver broadcom_drivers[] = {
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
}, {
- .phy_id = PHY_ID_BCM5421,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM5421),
.name = "Broadcom BCM5421",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1435,8 +1460,7 @@ static struct phy_driver broadcom_drivers[] = {
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
}, {
- .phy_id = PHY_ID_BCM54210E,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM54210E),
.name = "Broadcom BCM54210E",
/* PHY_GBIT_FEATURES */
.flags = PHY_ALWAYS_CALL_SUSPEND,
@@ -1454,8 +1478,7 @@ static struct phy_driver broadcom_drivers[] = {
.set_wol = bcm54xx_phy_set_wol,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM5461,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM5461),
.name = "Broadcom BCM5461",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1468,8 +1491,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM54612E,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM54612E),
.name = "Broadcom BCM54612E",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1484,8 +1506,7 @@ static struct phy_driver broadcom_drivers[] = {
.suspend = bcm54xx_suspend,
.resume = bcm54xx_resume,
}, {
- .phy_id = PHY_ID_BCM54616S,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM54616S),
.name = "Broadcom BCM54616S",
/* PHY_GBIT_FEATURES */
.soft_reset = genphy_soft_reset,
@@ -1498,8 +1519,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM5464,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM5464),
.name = "Broadcom BCM5464",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1514,8 +1534,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM5481,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM5481),
.name = "Broadcom BCM5481",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1529,8 +1548,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM54810,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM54810),
.name = "Broadcom BCM54810",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1548,8 +1566,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM54811,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM54811),
.name = "Broadcom BCM54811",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1567,8 +1584,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM5482,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM5482),
.name = "Broadcom BCM5482",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1581,8 +1597,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM50610,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM50610),
.name = "Broadcom BCM50610",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1597,8 +1612,7 @@ static struct phy_driver broadcom_drivers[] = {
.resume = bcm54xx_resume,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM50610M,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM50610M),
.name = "Broadcom BCM50610M",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1613,8 +1627,7 @@ static struct phy_driver broadcom_drivers[] = {
.resume = bcm54xx_resume,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM57780,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM57780),
.name = "Broadcom BCM57780",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1627,8 +1640,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCMAC131,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCMAC131),
.name = "Broadcom BCMAC131",
/* PHY_BASIC_FEATURES */
.config_init = brcm_fet_config_init,
@@ -1637,8 +1649,7 @@ static struct phy_driver broadcom_drivers[] = {
.suspend = brcm_fet_suspend,
.resume = brcm_fet_config_init,
}, {
- .phy_id = PHY_ID_BCM5241,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM5241),
.name = "Broadcom BCM5241",
/* PHY_BASIC_FEATURES */
.config_init = brcm_fet_config_init,
@@ -1647,8 +1658,7 @@ static struct phy_driver broadcom_drivers[] = {
.suspend = brcm_fet_suspend,
.resume = brcm_fet_config_init,
}, {
- .phy_id = PHY_ID_BCM5221,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM5221),
.name = "Broadcom BCM5221",
/* PHY_BASIC_FEATURES */
.config_init = brcm_fet_config_init,
@@ -1659,8 +1669,7 @@ static struct phy_driver broadcom_drivers[] = {
.config_aneg = bcm5221_config_aneg,
.read_status = bcm5221_read_status,
}, {
- .phy_id = PHY_ID_BCM5395,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM5395),
.name = "Broadcom BCM5395",
.flags = PHY_IS_INTERNAL,
/* PHY_GBIT_FEATURES */
@@ -1671,8 +1680,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM53125,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM53125),
.name = "Broadcom BCM53125",
.flags = PHY_IS_INTERNAL,
/* PHY_GBIT_FEATURES */
@@ -1686,8 +1694,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM53128,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM53128),
.name = "Broadcom BCM53128",
.flags = PHY_IS_INTERNAL,
/* PHY_GBIT_FEATURES */
@@ -1701,8 +1708,7 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
- .phy_id = PHY_ID_BCM89610,
- .phy_id_mask = 0xfffffff0,
+ PHY_ID_MATCH_MODEL(PHY_ID_BCM89610),
.name = "Broadcom BCM89610",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
@@ -1718,27 +1724,27 @@ static struct phy_driver broadcom_drivers[] = {
module_phy_driver(broadcom_drivers);
static const struct mdio_device_id __maybe_unused broadcom_tbl[] = {
- { PHY_ID_BCM5411, 0xfffffff0 },
- { PHY_ID_BCM5421, 0xfffffff0 },
- { PHY_ID_BCM54210E, 0xfffffff0 },
- { PHY_ID_BCM5461, 0xfffffff0 },
- { PHY_ID_BCM54612E, 0xfffffff0 },
- { PHY_ID_BCM54616S, 0xfffffff0 },
- { PHY_ID_BCM5464, 0xfffffff0 },
- { PHY_ID_BCM5481, 0xfffffff0 },
- { PHY_ID_BCM54810, 0xfffffff0 },
- { PHY_ID_BCM54811, 0xfffffff0 },
- { PHY_ID_BCM5482, 0xfffffff0 },
- { PHY_ID_BCM50610, 0xfffffff0 },
- { PHY_ID_BCM50610M, 0xfffffff0 },
- { PHY_ID_BCM57780, 0xfffffff0 },
- { PHY_ID_BCMAC131, 0xfffffff0 },
- { PHY_ID_BCM5221, 0xfffffff0 },
- { PHY_ID_BCM5241, 0xfffffff0 },
- { PHY_ID_BCM5395, 0xfffffff0 },
- { PHY_ID_BCM53125, 0xfffffff0 },
- { PHY_ID_BCM53128, 0xfffffff0 },
- { PHY_ID_BCM89610, 0xfffffff0 },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM5411) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM5421) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM54210E) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM5461) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM54612E) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM54616S) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM5464) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM5481) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM54810) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM54811) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM5482) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM50610) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM50610M) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM57780) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCMAC131) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM5221) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM5241) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM5395) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM53125) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM53128) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_BCM89610) },
{ }
};
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index daab555721df..74396453f5bb 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -953,30 +953,6 @@ static void decode_status_frame(struct dp83640_private *dp83640,
}
}
-static void dp83640_free_clocks(void)
-{
- struct dp83640_clock *clock;
- struct list_head *this, *next;
-
- mutex_lock(&phyter_clocks_lock);
-
- list_for_each_safe(this, next, &phyter_clocks) {
- clock = list_entry(this, struct dp83640_clock, list);
- if (!list_empty(&clock->phylist)) {
- pr_warn("phy list non-empty while unloading\n");
- BUG();
- }
- list_del(&clock->list);
- mutex_destroy(&clock->extreg_lock);
- mutex_destroy(&clock->clock_lock);
- put_device(&clock->bus->dev);
- kfree(clock->caps.pin_config);
- kfree(clock);
- }
-
- mutex_unlock(&phyter_clocks_lock);
-}
-
static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
{
INIT_LIST_HEAD(&clock->list);
@@ -1479,6 +1455,7 @@ static void dp83640_remove(struct phy_device *phydev)
struct dp83640_clock *clock;
struct list_head *this, *next;
struct dp83640_private *tmp, *dp83640 = phydev->priv;
+ bool remove_clock = false;
if (phydev->mdio.addr == BROADCAST_ADDR)
return;
@@ -1506,11 +1483,27 @@ static void dp83640_remove(struct phy_device *phydev)
}
}
+ if (!clock->chosen && list_empty(&clock->phylist))
+ remove_clock = true;
+
dp83640_clock_put(clock);
kfree(dp83640);
+
+ if (remove_clock) {
+ mutex_lock(&phyter_clocks_lock);
+ list_del(&clock->list);
+ mutex_unlock(&phyter_clocks_lock);
+
+ mutex_destroy(&clock->extreg_lock);
+ mutex_destroy(&clock->clock_lock);
+ put_device(&clock->bus->dev);
+ kfree(clock->caps.pin_config);
+ kfree(clock);
+ }
}
-static struct phy_driver dp83640_driver = {
+static struct phy_driver dp83640_driver[] = {
+{
.phy_id = DP83640_PHY_ID,
.phy_id_mask = 0xfffffff0,
.name = "NatSemi DP83640",
@@ -1521,26 +1514,15 @@ static struct phy_driver dp83640_driver = {
.config_init = dp83640_config_init,
.config_intr = dp83640_config_intr,
.handle_interrupt = dp83640_handle_interrupt,
+},
};
-static int __init dp83640_init(void)
-{
- return phy_driver_register(&dp83640_driver, THIS_MODULE);
-}
-
-static void __exit dp83640_exit(void)
-{
- dp83640_free_clocks();
- phy_driver_unregister(&dp83640_driver);
-}
+module_phy_driver(dp83640_driver);
MODULE_DESCRIPTION("National Semiconductor DP83640 PHY driver");
MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
MODULE_LICENSE("GPL");
-module_init(dp83640_init);
-module_exit(dp83640_exit);
-
static const struct mdio_device_id __maybe_unused dp83640_tbl[] = {
{ DP83640_PHY_ID, 0xfffffff0 },
{ }
diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c
index 01255dada600..33db21251f2e 100644
--- a/drivers/net/phy/dp83822.c
+++ b/drivers/net/phy/dp83822.c
@@ -516,7 +516,6 @@ static int dp83822_config_init_leds(struct phy_device *phydev)
static int dp83822_config_init(struct phy_device *phydev)
{
struct dp83822_private *dp83822 = phydev->priv;
- struct device *dev = &phydev->mdio.dev;
int rgmii_delay = 0;
s32 rx_int_delay;
s32 tx_int_delay;
@@ -549,15 +548,13 @@ static int dp83822_config_init(struct phy_device *phydev)
return err;
if (phy_interface_is_rgmii(phydev)) {
- rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0,
- true);
+ rx_int_delay = phy_get_internal_delay(phydev, NULL, 0, true);
/* Set DP83822_RX_CLK_SHIFT to enable rx clk internal delay */
if (rx_int_delay > 0)
rgmii_delay |= DP83822_RX_CLK_SHIFT;
- tx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0,
- false);
+ tx_int_delay = phy_get_internal_delay(phydev, NULL, 0, false);
/* Set DP83822_TX_CLK_SHIFT to disable tx clk internal delay */
if (tx_int_delay <= 0)
diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index a62cd838a9ea..a2cd1cc35cde 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c
@@ -540,9 +540,8 @@ static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500,
static int dp83869_of_init(struct phy_device *phydev)
{
+ struct device_node *of_node = phydev->mdio.dev.of_node;
struct dp83869_private *dp83869 = phydev->priv;
- struct device *dev = &phydev->mdio.dev;
- struct device_node *of_node = dev->of_node;
int delay_size = ARRAY_SIZE(dp83869_internal_delay);
int ret;
@@ -597,13 +596,13 @@ static int dp83869_of_init(struct phy_device *phydev)
&dp83869->tx_fifo_depth))
dp83869->tx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB;
- dp83869->rx_int_delay = phy_get_internal_delay(phydev, dev,
+ dp83869->rx_int_delay = phy_get_internal_delay(phydev,
&dp83869_internal_delay[0],
delay_size, true);
if (dp83869->rx_int_delay < 0)
dp83869->rx_int_delay = DP83869_CLK_DELAY_DEF;
- dp83869->tx_int_delay = phy_get_internal_delay(phydev, dev,
+ dp83869->tx_int_delay = phy_get_internal_delay(phydev,
&dp83869_internal_delay[0],
delay_size, false);
if (dp83869->tx_int_delay < 0)
diff --git a/drivers/net/phy/dp83tg720.c b/drivers/net/phy/dp83tg720.c
index 7e76323409c4..391c1d868808 100644
--- a/drivers/net/phy/dp83tg720.c
+++ b/drivers/net/phy/dp83tg720.c
@@ -13,21 +13,92 @@
#include "open_alliance_helpers.h"
/*
+ * DP83TG720 PHY Limitations and Workarounds
+ *
+ * The DP83TG720 1000BASE-T1 PHY has several limitations that require
+ * software-side mitigations. These workarounds are implemented throughout
+ * this driver. This section documents the known issues and their corresponding
+ * mitigation strategies.
+ *
+ * 1. Unreliable Link Detection and Synchronized Reset Deadlock
+ * ------------------------------------------------------------
+ * After a link loss or during link establishment, the DP83TG720 PHY may fail
+ * to detect or report link status correctly. As of June 2025, no public
+ * errata sheet for the DP83TG720 PHY documents this behavior.
+ * The "DP83TC81x, DP83TG72x Software Implementation Guide" application note
+ * (SNLA404, available at https://www.ti.com/lit/an/snla404/snla404.pdf)
+ * recommends performing a soft restart if polling for a link fails to establish
+ * a connection after 100ms. This procedure is adopted as the workaround for the
+ * observed link detection issue.
+ *
+ * However, in point-to-point setups where both link partners use the same
+ * driver (e.g. Linux on both sides), a synchronized reset pattern may emerge.
+ * This leads to a deadlock, where both PHYs reset at the same time and
+ * continuously miss each other during auto-negotiation.
+ *
+ * To address this, the reset procedure includes two components:
+ *
+ * - A **fixed minimum delay of 1ms** after a hardware reset. The datasheet
+ * "DP83TG720S-Q1 1000BASE-T1 Automotive Ethernet PHY with SGMII and RGMII"
+ * specifies this as the "Post reset stabilization-time prior to MDC preamble
+ * for register access" (T6.2), ensuring the PHY is ready for MDIO
+ * operations.
+ *
+ * - An **additional asymmetric delay**, empirically chosen based on
+ * master/slave role. This reduces the risk of synchronized resets on both
+ * link partners. Values are selected to avoid periodic overlap and ensure
+ * the link is re-established within a few cycles.
+ *
+ * The functions that implement this logic are:
+ * - dp83tg720_soft_reset()
+ * - dp83tg720_get_next_update_time()
+ *
+ * 2. Polling-Based Link Detection and IRQ Support
+ * -----------------------------------------------
+ * Due to the PHY-specific limitation described in section 1, link-up events
+ * cannot be reliably detected via interrupts on the DP83TG720. Therefore,
+ * polling is required to detect transitions from link-down to link-up.
+ *
+ * While link-down events *can* be detected via IRQs on this PHY, this driver
+ * currently does **not** implement interrupt support. As a result, all link
+ * state changes must be detected using polling.
+ *
+ * Polling behavior:
+ * - When the link is up: slow polling (e.g. 1s).
+ * - When the link just went down: fast polling for a short time.
+ * - When the link stays down: fallback to slow polling.
+ *
+ * This design balances responsiveness and CPU usage. It sacrifices fast link-up
+ * times in cases where the link is expected to remain down for extended periods,
+ * assuming that such systems do not require immediate reactivity.
+ */
+
+/*
* DP83TG720S_POLL_ACTIVE_LINK - Polling interval in milliseconds when the link
* is active.
- * DP83TG720S_POLL_NO_LINK_MIN - Minimum polling interval in milliseconds when
- * the link is down.
- * DP83TG720S_POLL_NO_LINK_MAX - Maximum polling interval in milliseconds when
- * the link is down.
+ * DP83TG720S_POLL_NO_LINK - Polling interval in milliseconds when the
+ * link is down.
+ * DP83TG720S_FAST_POLL_DURATION_MS - Timeout in milliseconds for no-link
+ * polling after which polling interval is
+ * increased.
+ * DP83TG720S_POLL_SLOW - Slow polling interval when there is no
+ * link for a prolongued period.
+ * DP83TG720S_RESET_DELAY_MS_MASTER - Delay after a reset before attempting
+ * to establish a link again for master phy.
+ * DP83TG720S_RESET_DELAY_MS_SLAVE - Delay after a reset before attempting
+ * to establish a link again for slave phy.
*
* These values are not documented or officially recommended by the vendor but
* were determined through empirical testing. They achieve a good balance in
* minimizing the number of reset retries while ensuring reliable link recovery
* within a reasonable timeframe.
*/
-#define DP83TG720S_POLL_ACTIVE_LINK 1000
-#define DP83TG720S_POLL_NO_LINK_MIN 100
-#define DP83TG720S_POLL_NO_LINK_MAX 1000
+#define DP83TG720S_POLL_ACTIVE_LINK 421
+#define DP83TG720S_POLL_NO_LINK 149
+#define DP83TG720S_FAST_POLL_DURATION_MS 6000
+#define DP83TG720S_POLL_SLOW 1117
+#define DP83TG720S_RESET_DELAY_MS_MASTER 97
+#define DP83TG720S_RESET_DELAY_MS_SLAVE 149
#define DP83TG720S_PHY_ID 0x2000a284
@@ -124,6 +195,7 @@ struct dp83tg720_stats {
struct dp83tg720_priv {
struct dp83tg720_stats stats;
+ unsigned long last_link_down_jiffies;
};
/**
@@ -201,6 +273,26 @@ static int dp83tg720_update_stats(struct phy_device *phydev)
return 0;
}
+static int dp83tg720_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_write(phydev, DP83TG720S_PHY_RESET, DP83TG720S_HW_RESET);
+ if (ret)
+ return ret;
+
+ /* Include mandatory MDC-access delay (1ms) + extra asymmetric delay to
+ * avoid synchronized reset deadlock. See section 1 in the top-of-file
+ * comment block.
+ */
+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_SLAVE)
+ msleep(DP83TG720S_RESET_DELAY_MS_SLAVE);
+ else
+ msleep(DP83TG720S_RESET_DELAY_MS_MASTER);
+
+ return ret;
+}
+
static void dp83tg720_get_link_stats(struct phy_device *phydev,
struct ethtool_link_ext_stats *link_stats)
{
@@ -382,21 +474,11 @@ static int dp83tg720_read_status(struct phy_device *phydev)
/* According to the "DP83TC81x, DP83TG72x Software
* Implementation Guide", the PHY needs to be reset after a
* link loss or if no link is created after at least 100ms.
- *
- * Currently we are polling with the PHY_STATE_TIME (1000ms)
- * interval, which is still enough for not automotive use cases.
*/
ret = phy_init_hw(phydev);
if (ret)
return ret;
- /* Sleep 600ms for PHY stabilization post-reset.
- * Empirically chosen value (not documented).
- * Helps reduce reset bounces with link partners having similar
- * issues.
- */
- msleep(600);
-
/* After HW reset we need to restore master/slave configuration.
* genphy_c45_pma_baset1_read_master_slave() call will be done
* by the dp83tg720_config_aneg() function.
@@ -477,19 +559,11 @@ static int dp83tg720_config_init(struct phy_device *phydev)
{
int ret;
- /* Software Restart is not enough to recover from a link failure.
- * Using Hardware Reset instead.
- */
- ret = phy_write(phydev, DP83TG720S_PHY_RESET, DP83TG720S_HW_RESET);
+ /* Reset the PHY to recover from a link failure */
+ ret = dp83tg720_soft_reset(phydev);
if (ret)
return ret;
- /* Wait until MDC can be used again.
- * The wait value of one 1ms is documented in "DP83TG720S-Q1 1000BASE-T1
- * Automotive Ethernet PHY with SGMII and RGMII" datasheet.
- */
- usleep_range(1000, 2000);
-
if (phy_interface_is_rgmii(phydev)) {
ret = dp83tg720_config_rgmii_delay(phydev);
if (ret)
@@ -525,50 +599,42 @@ static int dp83tg720_probe(struct phy_device *phydev)
}
/**
- * dp83tg720_get_next_update_time - Determine the next update time for PHY
- * state
+ * dp83tg720_get_next_update_time - Return next polling interval for PHY state
* @phydev: Pointer to the phy_device structure
*
- * This function addresses a limitation of the DP83TG720 PHY, which cannot
- * reliably detect or report a stable link state. To recover from such
- * scenarios, the PHY must be periodically reset when the link is down. However,
- * if the link partner also runs Linux with the same driver, synchronized reset
- * intervals can lead to a deadlock where the link never establishes due to
- * simultaneous resets on both sides.
- *
- * To avoid this, the function implements randomized polling intervals when the
- * link is down. It ensures that reset intervals are desynchronized by
- * introducing a random delay between a configured minimum and maximum range.
- * When the link is up, a fixed polling interval is used to minimize overhead.
- *
- * This mechanism guarantees that the link will reestablish within 10 seconds
- * in the worst-case scenario.
+ * Implements adaptive polling interval logic depending on link state and
+ * downtime duration. See the "2. Polling-Based Link Detection and IRQ Support"
+ * section at the top of this file for details.
*
- * Return: Time (in jiffies) until the next update event for the PHY state
- * machine.
+ * Return: Time (in jiffies) until the next poll
*/
static unsigned int dp83tg720_get_next_update_time(struct phy_device *phydev)
{
+ struct dp83tg720_priv *priv = phydev->priv;
unsigned int next_time_jiffies;
if (phydev->link) {
- /* When the link is up, use a fixed 1000ms interval
- * (in jiffies)
- */
+ priv->last_link_down_jiffies = 0;
+
+ /* When the link is up, use a slower interval (in jiffies) */
next_time_jiffies =
msecs_to_jiffies(DP83TG720S_POLL_ACTIVE_LINK);
} else {
- unsigned int min_jiffies, max_jiffies, rand_jiffies;
-
- /* When the link is down, randomize interval between min/max
- * (in jiffies)
- */
- min_jiffies = msecs_to_jiffies(DP83TG720S_POLL_NO_LINK_MIN);
- max_jiffies = msecs_to_jiffies(DP83TG720S_POLL_NO_LINK_MAX);
-
- rand_jiffies = min_jiffies +
- get_random_u32_below(max_jiffies - min_jiffies + 1);
- next_time_jiffies = rand_jiffies;
+ unsigned long now = jiffies;
+
+ if (!priv->last_link_down_jiffies)
+ priv->last_link_down_jiffies = now;
+
+ if (time_before(now, priv->last_link_down_jiffies +
+ msecs_to_jiffies(DP83TG720S_FAST_POLL_DURATION_MS))) {
+ /* Link recently went down: fast polling */
+ next_time_jiffies =
+ msecs_to_jiffies(DP83TG720S_POLL_NO_LINK);
+ } else {
+ /* Link has been down for a while: slow polling */
+ next_time_jiffies =
+ msecs_to_jiffies(DP83TG720S_POLL_SLOW);
+ }
}
/* Ensure the polling time is at least one jiffy */
@@ -582,6 +648,7 @@ static struct phy_driver dp83tg720_driver[] = {
.flags = PHY_POLL_CABLE_TEST,
.probe = dp83tg720_probe,
+ .soft_reset = dp83tg720_soft_reset,
.config_aneg = dp83tg720_config_aneg,
.read_status = dp83tg720_read_status,
.get_features = genphy_c45_pma_read_ext_abilities,
diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c
index 033656d574b8..0e1b28f06f18 100644
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -10,7 +10,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/device/faux.h>
#include <linux/list.h>
#include <linux/mii.h>
#include <linux/phy.h>
@@ -18,83 +17,65 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/gpio/consumer.h>
#include <linux/idr.h>
#include <linux/netdevice.h>
#include <linux/linkmode.h>
#include "swphy.h"
-struct fixed_mdio_bus {
- struct mii_bus *mii_bus;
- struct list_head phys;
-};
-
struct fixed_phy {
int addr;
struct phy_device *phydev;
struct fixed_phy_status status;
- bool no_carrier;
int (*link_update)(struct net_device *, struct fixed_phy_status *);
struct list_head node;
- struct gpio_desc *link_gpiod;
};
-static struct faux_device *fdev;
-static struct fixed_mdio_bus platform_fmb = {
- .phys = LIST_HEAD_INIT(platform_fmb.phys),
-};
+static struct mii_bus *fmb_mii_bus;
+static LIST_HEAD(fmb_phys);
+
+static struct fixed_phy *fixed_phy_find(int addr)
+{
+ struct fixed_phy *fp;
+
+ list_for_each_entry(fp, &fmb_phys, node) {
+ if (fp->addr == addr)
+ return fp;
+ }
+
+ return NULL;
+}
int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier)
{
- struct fixed_mdio_bus *fmb = &platform_fmb;
struct phy_device *phydev = dev->phydev;
struct fixed_phy *fp;
if (!phydev || !phydev->mdio.bus)
return -EINVAL;
- list_for_each_entry(fp, &fmb->phys, node) {
- if (fp->addr == phydev->mdio.addr) {
- fp->no_carrier = !new_carrier;
- return 0;
- }
- }
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(fixed_phy_change_carrier);
+ fp = fixed_phy_find(phydev->mdio.addr);
+ if (!fp)
+ return -EINVAL;
-static void fixed_phy_update(struct fixed_phy *fp)
-{
- if (!fp->no_carrier && fp->link_gpiod)
- fp->status.link = !!gpiod_get_value_cansleep(fp->link_gpiod);
+ fp->status.link = new_carrier;
+
+ return 0;
}
+EXPORT_SYMBOL_GPL(fixed_phy_change_carrier);
static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
{
- struct fixed_mdio_bus *fmb = bus->priv;
struct fixed_phy *fp;
- list_for_each_entry(fp, &fmb->phys, node) {
- if (fp->addr == phy_addr) {
- struct fixed_phy_status state;
-
- fp->status.link = !fp->no_carrier;
-
- /* Issue callback if user registered it. */
- if (fp->link_update)
- fp->link_update(fp->phydev->attached_dev,
- &fp->status);
-
- /* Check the GPIO for change in status */
- fixed_phy_update(fp);
- state = fp->status;
+ fp = fixed_phy_find(phy_addr);
+ if (!fp)
+ return 0xffff;
- return swphy_read_reg(reg_num, &state);
- }
- }
+ if (fp->link_update)
+ fp->link_update(fp->phydev->attached_dev, &fp->status);
- return 0xFFFF;
+ return swphy_read_reg(reg_num, &fp->status);
}
static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
@@ -112,31 +93,27 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
int (*link_update)(struct net_device *,
struct fixed_phy_status *))
{
- struct fixed_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp;
if (!phydev || !phydev->mdio.bus)
return -EINVAL;
- list_for_each_entry(fp, &fmb->phys, node) {
- if (fp->addr == phydev->mdio.addr) {
- fp->link_update = link_update;
- fp->phydev = phydev;
- return 0;
- }
- }
+ fp = fixed_phy_find(phydev->mdio.addr);
+ if (!fp)
+ return -ENOENT;
- return -ENOENT;
+ fp->link_update = link_update;
+ fp->phydev = phydev;
+
+ return 0;
}
EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
-static int fixed_phy_add_gpiod(unsigned int irq, int phy_addr,
- const struct fixed_phy_status *status,
- struct gpio_desc *gpiod)
+static int __fixed_phy_add(int phy_addr,
+ const struct fixed_phy_status *status)
{
- int ret;
- struct fixed_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp;
+ int ret;
ret = swphy_validate_state(status);
if (ret < 0)
@@ -146,23 +123,17 @@ static int fixed_phy_add_gpiod(unsigned int irq, int phy_addr,
if (!fp)
return -ENOMEM;
- if (irq != PHY_POLL)
- fmb->mii_bus->irq[phy_addr] = irq;
-
fp->addr = phy_addr;
fp->status = *status;
- fp->link_gpiod = gpiod;
-
- fixed_phy_update(fp);
- list_add_tail(&fp->node, &fmb->phys);
+ list_add_tail(&fp->node, &fmb_phys);
return 0;
}
-int fixed_phy_add(int phy_addr, const struct fixed_phy_status *status)
+void fixed_phy_add(const struct fixed_phy_status *status)
{
- return fixed_phy_add_gpiod(PHY_POLL, phy_addr, status, NULL);
+ __fixed_phy_add(0, status);
}
EXPORT_SYMBOL_GPL(fixed_phy_add);
@@ -170,87 +141,39 @@ static DEFINE_IDA(phy_fixed_ida);
static void fixed_phy_del(int phy_addr)
{
- struct fixed_mdio_bus *fmb = &platform_fmb;
- struct fixed_phy *fp, *tmp;
-
- list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
- if (fp->addr == phy_addr) {
- list_del(&fp->node);
- if (fp->link_gpiod)
- gpiod_put(fp->link_gpiod);
- kfree(fp);
- ida_free(&phy_fixed_ida, phy_addr);
- return;
- }
- }
-}
+ struct fixed_phy *fp;
-#ifdef CONFIG_OF_GPIO
-static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)
-{
- struct device_node *fixed_link_node;
- struct gpio_desc *gpiod;
-
- if (!np)
- return NULL;
-
- fixed_link_node = of_get_child_by_name(np, "fixed-link");
- if (!fixed_link_node)
- return NULL;
-
- /*
- * As the fixed link is just a device tree node without any
- * Linux device associated with it, we simply have obtain
- * the GPIO descriptor from the device tree like this.
- */
- gpiod = fwnode_gpiod_get_index(of_fwnode_handle(fixed_link_node),
- "link", 0, GPIOD_IN, "mdio");
- if (IS_ERR(gpiod) && PTR_ERR(gpiod) != -EPROBE_DEFER) {
- if (PTR_ERR(gpiod) != -ENOENT)
- pr_err("error getting GPIO for fixed link %pOF, proceed without\n",
- fixed_link_node);
- gpiod = NULL;
- }
- of_node_put(fixed_link_node);
+ fp = fixed_phy_find(phy_addr);
+ if (!fp)
+ return;
- return gpiod;
-}
-#else
-static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)
-{
- return NULL;
+ list_del(&fp->node);
+ kfree(fp);
+ ida_free(&phy_fixed_ida, phy_addr);
}
-#endif
struct phy_device *fixed_phy_register(const struct fixed_phy_status *status,
struct device_node *np)
{
- struct fixed_mdio_bus *fmb = &platform_fmb;
- struct gpio_desc *gpiod;
struct phy_device *phy;
int phy_addr;
int ret;
- if (!fmb->mii_bus || fmb->mii_bus->state != MDIOBUS_REGISTERED)
+ if (!fmb_mii_bus || fmb_mii_bus->state != MDIOBUS_REGISTERED)
return ERR_PTR(-EPROBE_DEFER);
- /* Check if we have a GPIO associated with this fixed phy */
- gpiod = fixed_phy_get_gpiod(np);
- if (IS_ERR(gpiod))
- return ERR_CAST(gpiod);
-
/* Get the next available PHY address, up to PHY_MAX_ADDR */
phy_addr = ida_alloc_max(&phy_fixed_ida, PHY_MAX_ADDR - 1, GFP_KERNEL);
if (phy_addr < 0)
return ERR_PTR(phy_addr);
- ret = fixed_phy_add_gpiod(PHY_POLL, phy_addr, status, gpiod);
+ ret = __fixed_phy_add(phy_addr, status);
if (ret < 0) {
ida_free(&phy_fixed_ida, phy_addr);
return ERR_PTR(ret);
}
- phy = get_phy_device(fmb->mii_bus, phy_addr, false);
+ phy = get_phy_device(fmb_mii_bus, phy_addr, false);
if (IS_ERR(phy)) {
fixed_phy_del(phy_addr);
return ERR_PTR(-EINVAL);
@@ -309,56 +232,44 @@ void fixed_phy_unregister(struct phy_device *phy)
phy_device_remove(phy);
of_node_put(phy->mdio.dev.of_node);
fixed_phy_del(phy->mdio.addr);
+ phy_device_free(phy);
}
EXPORT_SYMBOL_GPL(fixed_phy_unregister);
static int __init fixed_mdio_bus_init(void)
{
- struct fixed_mdio_bus *fmb = &platform_fmb;
int ret;
- fdev = faux_device_create("Fixed MDIO bus", NULL, NULL);
- if (!fdev)
- return -ENODEV;
-
- fmb->mii_bus = mdiobus_alloc();
- if (fmb->mii_bus == NULL) {
- ret = -ENOMEM;
- goto err_mdiobus_reg;
- }
+ fmb_mii_bus = mdiobus_alloc();
+ if (!fmb_mii_bus)
+ return -ENOMEM;
- snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0");
- fmb->mii_bus->name = "Fixed MDIO Bus";
- fmb->mii_bus->priv = fmb;
- fmb->mii_bus->parent = &fdev->dev;
- fmb->mii_bus->read = &fixed_mdio_read;
- fmb->mii_bus->write = &fixed_mdio_write;
- fmb->mii_bus->phy_mask = ~0;
+ snprintf(fmb_mii_bus->id, MII_BUS_ID_SIZE, "fixed-0");
+ fmb_mii_bus->name = "Fixed MDIO Bus";
+ fmb_mii_bus->read = &fixed_mdio_read;
+ fmb_mii_bus->write = &fixed_mdio_write;
+ fmb_mii_bus->phy_mask = ~0;
- ret = mdiobus_register(fmb->mii_bus);
+ ret = mdiobus_register(fmb_mii_bus);
if (ret)
goto err_mdiobus_alloc;
return 0;
err_mdiobus_alloc:
- mdiobus_free(fmb->mii_bus);
-err_mdiobus_reg:
- faux_device_destroy(fdev);
+ mdiobus_free(fmb_mii_bus);
return ret;
}
module_init(fixed_mdio_bus_init);
static void __exit fixed_mdio_bus_exit(void)
{
- struct fixed_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp, *tmp;
- mdiobus_unregister(fmb->mii_bus);
- mdiobus_free(fmb->mii_bus);
- faux_device_destroy(fdev);
+ mdiobus_unregister(fmb_mii_bus);
+ mdiobus_free(fmb_mii_bus);
- list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
+ list_for_each_entry_safe(fp, tmp, &fmb_phys, node) {
list_del(&fp->node);
kfree(fp);
}
diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c
index a44771e8acdc..9766dd99afaa 100644
--- a/drivers/net/phy/intel-xway.c
+++ b/drivers/net/phy/intel-xway.c
@@ -174,7 +174,6 @@ static const int xway_internal_delay[] = {0, 500, 1000, 1500, 2000, 2500,
static int xway_gphy_rgmii_init(struct phy_device *phydev)
{
- struct device *dev = &phydev->mdio.dev;
unsigned int delay_size = ARRAY_SIZE(xway_internal_delay);
s32 int_delay;
int val = 0;
@@ -207,8 +206,7 @@ static int xway_gphy_rgmii_init(struct phy_device *phydev)
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
- int_delay = phy_get_internal_delay(phydev, dev,
- xway_internal_delay,
+ int_delay = phy_get_internal_delay(phydev, xway_internal_delay,
delay_size, true);
/* if rx-internal-delay-ps is missing, use default of 2.0 ns */
@@ -220,8 +218,7 @@ static int xway_gphy_rgmii_init(struct phy_device *phydev)
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
- int_delay = phy_get_internal_delay(phydev, dev,
- xway_internal_delay,
+ int_delay = phy_get_internal_delay(phydev, xway_internal_delay,
delay_size, false);
/* if tx-internal-delay-ps is missing, use default of 2.0 ns */
diff --git a/drivers/net/phy/marvell-88x2222.c b/drivers/net/phy/marvell-88x2222.c
index fad2f54c1eac..894bcee61e65 100644
--- a/drivers/net/phy/marvell-88x2222.c
+++ b/drivers/net/phy/marvell-88x2222.c
@@ -475,21 +475,20 @@ static int mv2222_config_init(struct phy_device *phydev)
static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{
- DECLARE_PHY_INTERFACE_MASK(interfaces);
struct phy_device *phydev = upstream;
+ const struct sfp_module_caps *caps;
phy_interface_t sfp_interface;
struct mv2222_data *priv;
struct device *dev;
int ret;
- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_supported) = { 0, };
-
priv = phydev->priv;
dev = &phydev->mdio.dev;
- sfp_parse_support(phydev->sfp_bus, id, sfp_supported, interfaces);
- phydev->port = sfp_parse_port(phydev->sfp_bus, id, sfp_supported);
- sfp_interface = sfp_select_interface(phydev->sfp_bus, sfp_supported);
+ caps = sfp_get_module_caps(phydev->sfp_bus);
+
+ phydev->port = caps->port;
+ sfp_interface = sfp_select_interface(phydev->sfp_bus, caps->link_modes);
dev_info(dev, "%s SFP module inserted\n", phy_modes(sfp_interface));
@@ -502,7 +501,7 @@ static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
}
priv->line_interface = sfp_interface;
- linkmode_and(priv->supported, phydev->supported, sfp_supported);
+ linkmode_and(priv->supported, phydev->supported, caps->link_modes);
ret = mv2222_config_line(phydev);
if (ret < 0)
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 623292948fa7..c248c90510ae 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -1902,6 +1902,43 @@ error:
return err;
}
+/* m88e1510_resume
+ *
+ * The 88e1510 PHY has an erratum where the phy downshift counter is not cleared
+ * after phy being suspended(BMCR_PDOWN set) and then later resumed(BMCR_PDOWN
+ * cleared). This can cause the link to intermittently downshift to a lower speed.
+ *
+ * Disabling and re-enabling the downshift feature clears the counter, allowing
+ * the PHY to retry gigabit link negotiation up to the programmed retry count
+ * before downshifting. This behavior has been observed on copper links.
+ */
+static int m88e1510_resume(struct phy_device *phydev)
+{
+ int err;
+ u8 cnt = 0;
+
+ err = marvell_resume(phydev);
+ if (err < 0)
+ return err;
+
+ /* read downshift counter value */
+ err = m88e1011_get_downshift(phydev, &cnt);
+ if (err < 0)
+ return err;
+
+ if (cnt) {
+ /* downshift disabled */
+ err = m88e1011_set_downshift(phydev, 0);
+ if (err < 0)
+ return err;
+
+ /* downshift enabled, with previous counter value */
+ err = m88e1011_set_downshift(phydev, cnt);
+ }
+
+ return err;
+}
+
static int marvell_aneg_done(struct phy_device *phydev)
{
int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
@@ -3563,20 +3600,18 @@ static int marvell_probe(struct phy_device *phydev)
static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{
- DECLARE_PHY_INTERFACE_MASK(interfaces);
struct phy_device *phydev = upstream;
+ const struct sfp_module_caps *caps;
phy_interface_t interface;
struct device *dev;
int oldpage;
int ret = 0;
u16 mode;
- __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
-
dev = &phydev->mdio.dev;
- sfp_parse_support(phydev->sfp_bus, id, supported, interfaces);
- interface = sfp_select_interface(phydev->sfp_bus, supported);
+ caps = sfp_get_module_caps(phydev->sfp_bus);
+ interface = sfp_select_interface(phydev->sfp_bus, caps->link_modes);
dev_info(dev, "%s SFP module inserted\n", phy_modes(interface));
@@ -3923,7 +3958,7 @@ static struct phy_driver marvell_drivers[] = {
.handle_interrupt = marvell_handle_interrupt,
.get_wol = m88e1318_get_wol,
.set_wol = m88e1318_set_wol,
- .resume = marvell_resume,
+ .resume = m88e1510_resume,
.suspend = marvell_suspend,
.read_page = marvell_read_page,
.write_page = marvell_write_page,
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
index 13e81dff42c1..8fd42131cdbf 100644
--- a/drivers/net/phy/marvell10g.c
+++ b/drivers/net/phy/marvell10g.c
@@ -466,12 +466,11 @@ static int mv3310_set_edpd(struct phy_device *phydev, u16 edpd)
static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{
struct phy_device *phydev = upstream;
- __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
- DECLARE_PHY_INTERFACE_MASK(interfaces);
+ const struct sfp_module_caps *caps;
phy_interface_t iface;
- sfp_parse_support(phydev->sfp_bus, id, support, interfaces);
- iface = sfp_select_interface(phydev->sfp_bus, support);
+ caps = sfp_get_module_caps(phydev->sfp_bus);
+ iface = sfp_select_interface(phydev->sfp_bus, caps->link_modes);
if (iface != PHY_INTERFACE_MODE_10GBASER) {
dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
diff --git a/drivers/net/phy/mdio-boardinfo.c b/drivers/net/phy/mdio-boardinfo.c
deleted file mode 100644
index 2de679a68115..000000000000
--- a/drivers/net/phy/mdio-boardinfo.c
+++ /dev/null
@@ -1,80 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * mdio-boardinfo - Collect pre-declarations for MDIO devices
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <linux/mutex.h>
-#include <linux/list.h>
-
-#include "mdio-boardinfo.h"
-
-static LIST_HEAD(mdio_board_list);
-static DEFINE_MUTEX(mdio_board_lock);
-
-/**
- * mdiobus_setup_mdiodev_from_board_info - create and setup MDIO devices
- * from pre-collected board specific MDIO information
- * @bus: Bus the board_info belongs to
- * @cb: Callback to create device on bus
- * Context: can sleep
- */
-void mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus,
- int (*cb)
- (struct mii_bus *bus,
- struct mdio_board_info *bi))
-{
- struct mdio_board_entry *be;
- struct mdio_board_entry *tmp;
- struct mdio_board_info *bi;
- int ret;
-
- mutex_lock(&mdio_board_lock);
- list_for_each_entry_safe(be, tmp, &mdio_board_list, list) {
- bi = &be->board_info;
-
- if (strcmp(bus->id, bi->bus_id))
- continue;
-
- mutex_unlock(&mdio_board_lock);
- ret = cb(bus, bi);
- mutex_lock(&mdio_board_lock);
- if (ret)
- continue;
-
- }
- mutex_unlock(&mdio_board_lock);
-}
-EXPORT_SYMBOL(mdiobus_setup_mdiodev_from_board_info);
-
-/**
- * mdiobus_register_board_info - register MDIO devices for a given board
- * @info: array of devices descriptors
- * @n: number of descriptors provided
- * Context: can sleep
- *
- * The board info passed can be marked with __initdata but be pointers
- * such as platform_data etc. are copied as-is
- */
-int mdiobus_register_board_info(const struct mdio_board_info *info,
- unsigned int n)
-{
- struct mdio_board_entry *be;
- unsigned int i;
-
- be = kcalloc(n, sizeof(*be), GFP_KERNEL);
- if (!be)
- return -ENOMEM;
-
- for (i = 0; i < n; i++, be++, info++) {
- memcpy(&be->board_info, info, sizeof(*info));
- mutex_lock(&mdio_board_lock);
- list_add_tail(&be->list, &mdio_board_list);
- mutex_unlock(&mdio_board_lock);
- }
-
- return 0;
-}
-EXPORT_SYMBOL(mdiobus_register_board_info);
diff --git a/drivers/net/phy/mdio-boardinfo.h b/drivers/net/phy/mdio-boardinfo.h
deleted file mode 100644
index 773bb51399be..000000000000
--- a/drivers/net/phy/mdio-boardinfo.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * mdio-boardinfo.h - board info interface internal to the mdio_bus
- * component
- */
-
-#ifndef __MDIO_BOARD_INFO_H
-#define __MDIO_BOARD_INFO_H
-
-#include <linux/phy.h>
-#include <linux/mutex.h>
-
-struct mdio_board_entry {
- struct list_head list;
- struct mdio_board_info board_info;
-};
-
-void mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus,
- int (*cb)
- (struct mii_bus *bus,
- struct mdio_board_info *bi));
-
-#endif /* __MDIO_BOARD_INFO_H */
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index fda2e27c1810..cad6ed3aa10b 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -91,6 +91,7 @@ int mdiobus_unregister_device(struct mdio_device *mdiodev)
if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev)
return -EINVAL;
+ gpiod_put(mdiodev->reset_gpio);
reset_control_put(mdiodev->reset_ctrl);
mdiodev->bus->mdio_map[mdiodev->addr] = NULL;
diff --git a/drivers/net/phy/mdio_bus_provider.c b/drivers/net/phy/mdio_bus_provider.c
index 65850e36284d..a2391d4b7e5c 100644
--- a/drivers/net/phy/mdio_bus_provider.c
+++ b/drivers/net/phy/mdio_bus_provider.c
@@ -29,8 +29,6 @@
#include <linux/uaccess.h>
#include <linux/unistd.h>
-#include "mdio-boardinfo.h"
-
/**
* mdiobus_alloc_size - allocate a mii_bus structure
* @size: extra amount of memory to allocate for private storage.
@@ -132,36 +130,6 @@ static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
}
#endif
-/**
- * mdiobus_create_device - create a full MDIO device given
- * a mdio_board_info structure
- * @bus: MDIO bus to create the devices on
- * @bi: mdio_board_info structure describing the devices
- *
- * Returns 0 on success or < 0 on error.
- */
-static int mdiobus_create_device(struct mii_bus *bus,
- struct mdio_board_info *bi)
-{
- struct mdio_device *mdiodev;
- int ret = 0;
-
- mdiodev = mdio_device_create(bus, bi->mdio_addr);
- if (IS_ERR(mdiodev))
- return -ENODEV;
-
- strscpy(mdiodev->modalias, bi->modalias,
- sizeof(mdiodev->modalias));
- mdiodev->bus_match = mdio_device_bus_match;
- mdiodev->dev.platform_data = (void *)bi->platform_data;
-
- ret = mdio_device_register(mdiodev);
- if (ret)
- mdio_device_free(mdiodev);
-
- return ret;
-}
-
static struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr, bool c45)
{
struct phy_device *phydev = ERR_PTR(-ENODEV);
@@ -405,8 +373,6 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
goto error;
}
- mdiobus_setup_mdiodev_from_board_info(bus, mdiobus_create_device);
-
bus->state = MDIOBUS_REGISTERED;
dev_dbg(&bus->dev, "probed\n");
return 0;
@@ -444,9 +410,6 @@ void mdiobus_unregister(struct mii_bus *bus)
if (!mdiodev)
continue;
- if (mdiodev->reset_gpio)
- gpiod_put(mdiodev->reset_gpio);
-
mdiodev->device_remove(mdiodev);
mdiodev->device_free(mdiodev);
}
diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c
index cce3f405d1a4..f64176e0e197 100644
--- a/drivers/net/phy/mdio_device.c
+++ b/drivers/net/phy/mdio_device.c
@@ -35,7 +35,8 @@ static void mdio_device_release(struct device *dev)
kfree(to_mdio_device(dev));
}
-int mdio_device_bus_match(struct device *dev, const struct device_driver *drv)
+static int mdio_device_bus_match(struct device *dev,
+ const struct device_driver *drv)
{
struct mdio_device *mdiodev = to_mdio_device(dev);
const struct mdio_driver *mdiodrv = to_mdio_driver(drv);
@@ -45,7 +46,6 @@ int mdio_device_bus_match(struct device *dev, const struct device_driver *drv)
return strcmp(mdiodev->modalias, drv->name) == 0;
}
-EXPORT_SYMBOL_GPL(mdio_device_bus_match);
struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
{
@@ -59,6 +59,7 @@ struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
mdiodev->dev.release = mdio_device_release;
mdiodev->dev.parent = &bus->dev;
mdiodev->dev.bus = &mdio_bus_type;
+ mdiodev->bus_match = mdio_device_bus_match;
mdiodev->device_free = mdio_device_free;
mdiodev->device_remove = mdio_device_remove;
mdiodev->bus = bus;
diff --git a/drivers/net/phy/mediatek/Kconfig b/drivers/net/phy/mediatek/Kconfig
index 9f30a91be8dd..bb7dc876271e 100644
--- a/drivers/net/phy/mediatek/Kconfig
+++ b/drivers/net/phy/mediatek/Kconfig
@@ -27,6 +27,7 @@ config MEDIATEK_GE_SOC_PHY
depends on ARCH_AIROHA || (ARCH_MEDIATEK && NVMEM_MTK_EFUSE) || \
COMPILE_TEST
select MTK_NET_PHYLIB
+ select PHY_PACKAGE
help
Supports MediaTek SoC built-in Gigabit Ethernet PHYs.
diff --git a/drivers/net/phy/mediatek/mtk-2p5ge.c b/drivers/net/phy/mediatek/mtk-2p5ge.c
index e147eab523ef..de8a41a1841d 100644
--- a/drivers/net/phy/mediatek/mtk-2p5ge.c
+++ b/drivers/net/phy/mediatek/mtk-2p5ge.c
@@ -249,8 +249,80 @@ static int mt798x_2p5ge_phy_get_rate_matching(struct phy_device *phydev,
return RATE_MATCH_PAUSE;
}
+static const unsigned long supported_triggers =
+ BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
+ 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 mt798x_2p5ge_phy_led_blink_set(struct phy_device *phydev, u8 index,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ bool blinking = false;
+ int err = 0;
+
+ err = mtk_phy_led_num_dly_cfg(index, delay_on, delay_off, &blinking);
+ if (err < 0)
+ return err;
+
+ err = mtk_phy_hw_led_blink_set(phydev, index, blinking);
+ if (err)
+ return err;
+
+ if (blinking)
+ mtk_phy_hw_led_on_set(phydev, index, MTK_2P5GPHY_LED_ON_MASK,
+ false);
+
+ return 0;
+}
+
+static int mt798x_2p5ge_phy_led_brightness_set(struct phy_device *phydev,
+ u8 index,
+ enum led_brightness value)
+{
+ int err;
+
+ err = mtk_phy_hw_led_blink_set(phydev, index, false);
+ if (err)
+ return err;
+
+ return mtk_phy_hw_led_on_set(phydev, index, MTK_2P5GPHY_LED_ON_MASK,
+ (value != LED_OFF));
+}
+
+static int mt798x_2p5ge_phy_led_hw_is_supported(struct phy_device *phydev,
+ u8 index, unsigned long rules)
+{
+ return mtk_phy_led_hw_is_supported(phydev, index, rules,
+ supported_triggers);
+}
+
+static int mt798x_2p5ge_phy_led_hw_control_get(struct phy_device *phydev,
+ u8 index, unsigned long *rules)
+{
+ return mtk_phy_led_hw_ctrl_get(phydev, index, rules,
+ MTK_2P5GPHY_LED_ON_SET,
+ MTK_2P5GPHY_LED_RX_BLINK_SET,
+ MTK_2P5GPHY_LED_TX_BLINK_SET);
+};
+
+static int mt798x_2p5ge_phy_led_hw_control_set(struct phy_device *phydev,
+ u8 index, unsigned long rules)
+{
+ return mtk_phy_led_hw_ctrl_set(phydev, index, rules,
+ MTK_2P5GPHY_LED_ON_SET,
+ MTK_2P5GPHY_LED_RX_BLINK_SET,
+ MTK_2P5GPHY_LED_TX_BLINK_SET);
+};
+
static int mt798x_2p5ge_phy_probe(struct phy_device *phydev)
{
+ struct mtk_socphy_priv *priv;
struct pinctrl *pinctrl;
int ret;
@@ -273,19 +345,34 @@ static int mt798x_2p5ge_phy_probe(struct phy_device *phydev)
if (ret < 0)
return ret;
- /* Setup LED */
+ /* Setup LED. On default, LED0 is on/off when link is up/down. As for
+ * LED1, it blinks as tx/rx transmission takes place.
+ */
phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
- MTK_PHY_LED_ON_POLARITY | MTK_PHY_LED_ON_LINK10 |
- MTK_PHY_LED_ON_LINK100 | MTK_PHY_LED_ON_LINK1000 |
- MTK_PHY_LED_ON_LINK2500);
- phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL,
- MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX);
+ MTK_PHY_LED_ON_POLARITY | MTK_2P5GPHY_LED_ON_SET);
+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_BLINK_CTRL,
+ MTK_2P5GPHY_LED_TX_BLINK_SET |
+ MTK_2P5GPHY_LED_RX_BLINK_SET);
+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL,
+ MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX |
+ MTK_2P5GPHY_LED_ON_SET);
+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_BLINK_CTRL,
+ MTK_2P5GPHY_LED_TX_BLINK_SET |
+ MTK_2P5GPHY_LED_RX_BLINK_SET);
/* Switch pinctrl after setting polarity to avoid bogus blinking */
pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "i2p5gbe-led");
if (IS_ERR(pinctrl))
dev_err(&phydev->mdio.dev, "Fail to set LED pins!\n");
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct mtk_socphy_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ phydev->priv = priv;
+
+ mtk_phy_leds_state_init(phydev);
+
return 0;
}
@@ -303,6 +390,11 @@ static struct phy_driver mtk_2p5gephy_driver[] = {
.resume = genphy_resume,
.read_page = mtk_phy_read_page,
.write_page = mtk_phy_write_page,
+ .led_blink_set = mt798x_2p5ge_phy_led_blink_set,
+ .led_brightness_set = mt798x_2p5ge_phy_led_brightness_set,
+ .led_hw_is_supported = mt798x_2p5ge_phy_led_hw_is_supported,
+ .led_hw_control_get = mt798x_2p5ge_phy_led_hw_control_get,
+ .led_hw_control_set = mt798x_2p5ge_phy_led_hw_control_set,
},
};
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 64aa03aed770..79ce3eb6752b 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -107,6 +107,7 @@
#define LAN8814_INTC 0x18
#define LAN8814_INTS 0x1B
+#define LAN8814_INT_FLF BIT(15)
#define LAN8814_INT_LINK_DOWN BIT(2)
#define LAN8814_INT_LINK_UP BIT(0)
#define LAN8814_INT_LINK (LAN8814_INT_LINK_UP |\
@@ -266,6 +267,8 @@
#define LAN8814_LED_CTRL_1 0x0
#define LAN8814_LED_CTRL_1_KSZ9031_LED_MODE_ BIT(6)
+#define LAN8814_LED_CTRL_2 0x1
+#define LAN8814_LED_CTRL_2_LED1_COM_DIS BIT(8)
/* PHY Control 1 */
#define MII_KSZPHY_CTRL_1 0x1e
@@ -362,6 +365,8 @@
/* Delay used to get the second part from the LTC */
#define LAN8841_GET_SEC_LTC_DELAY (500 * NSEC_PER_MSEC)
+#define LAN8842_REV_8832 0x8832
+
struct kszphy_hw_stat {
const char *string;
u8 reg;
@@ -431,6 +436,10 @@ struct kszphy_ptp_priv {
spinlock_t seconds_lock;
};
+struct kszphy_phy_stats {
+ u64 rx_err_pkt_cnt;
+};
+
struct kszphy_priv {
struct kszphy_ptp_priv ptp_priv;
const struct kszphy_type *type;
@@ -441,6 +450,20 @@ struct kszphy_priv {
bool rmii_ref_clk_sel_val;
bool clk_enable;
u64 stats[ARRAY_SIZE(kszphy_hw_stats)];
+ struct kszphy_phy_stats phy_stats;
+};
+
+struct lan8842_phy_stats {
+ u64 rx_packets;
+ u64 rx_errors;
+ u64 tx_packets;
+ u64 tx_errors;
+};
+
+struct lan8842_priv {
+ struct lan8842_phy_stats phy_stats;
+ struct kszphy_ptp_priv ptp_priv;
+ u16 rev;
};
static const struct kszphy_type lan8814_type = {
@@ -472,6 +495,8 @@ static const struct kszphy_type ksz8051_type = {
static const struct kszphy_type ksz8081_type = {
.led_mode_reg = MII_KSZPHY_CTRL_2,
+ .cable_diag_reg = KSZ8081_LMD,
+ .pair_mask = KSZPHY_WIRE_PAIR_MASK,
.has_broadcast_disable = true,
.has_nand_tree_disable = true,
.has_rmii_ref_clk_sel = true,
@@ -1718,7 +1743,8 @@ static int ksz9x31_cable_test_fault_length(struct phy_device *phydev, u16 stat)
*
* distance to fault = (VCT_DATA - 22) * 4 / cable propagation velocity
*/
- if (phydev_id_compare(phydev, PHY_ID_KSZ9131))
+ if (phydev_id_compare(phydev, PHY_ID_KSZ9131) ||
+ phydev_id_compare(phydev, PHY_ID_KSZ9477))
dt = clamp(dt - 22, 0, 255);
return (dt * 400) / 10;
@@ -1792,12 +1818,20 @@ static int ksz9x31_cable_test_get_status(struct phy_device *phydev,
bool *finished)
{
struct kszphy_priv *priv = phydev->priv;
- unsigned long pair_mask = 0xf;
+ unsigned long pair_mask;
int retries = 20;
int pair, ret, rv;
*finished = false;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ phydev->supported) ||
+ linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+ phydev->supported))
+ pair_mask = 0xf; /* All pairs */
+ else
+ pair_mask = 0x3; /* Pairs A and B only */
+
/* Try harder if link partner is active */
while (pair_mask && retries--) {
for_each_set_bit(pair, &pair_mask, 4) {
@@ -1948,6 +1982,56 @@ static int ksz886x_read_status(struct phy_device *phydev)
return genphy_read_status(phydev);
}
+static int ksz9477_mdix_update(struct phy_device *phydev)
+{
+ if (phydev->mdix_ctrl != ETH_TP_MDI_AUTO)
+ phydev->mdix = phydev->mdix_ctrl;
+ else
+ phydev->mdix = ETH_TP_MDI_INVALID;
+
+ return 0;
+}
+
+static int ksz9477_read_mdix_ctrl(struct phy_device *phydev)
+{
+ int val;
+
+ val = phy_read(phydev, MII_KSZ9131_AUTO_MDIX);
+ if (val < 0)
+ return val;
+
+ if (!(val & MII_KSZ9131_AUTO_MDIX_SWAP_OFF))
+ phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
+ else if (val & MII_KSZ9131_AUTO_MDI_SET)
+ phydev->mdix_ctrl = ETH_TP_MDI;
+ else
+ phydev->mdix_ctrl = ETH_TP_MDI_X;
+
+ return 0;
+}
+
+static int ksz9477_read_status(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = ksz9477_mdix_update(phydev);
+ if (ret)
+ return ret;
+
+ return genphy_read_status(phydev);
+}
+
+static int ksz9477_config_aneg(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = ksz9131_config_mdix(phydev, phydev->mdix_ctrl);
+ if (ret)
+ return ret;
+
+ return genphy_config_aneg(phydev);
+}
+
struct ksz9477_errata_write {
u8 dev_addr;
u8 reg_addr;
@@ -2029,6 +2113,13 @@ static int ksz9477_config_init(struct phy_device *phydev)
return err;
}
+ /* Read initial MDI-X config state. So, we do not need to poll it
+ * later on.
+ */
+ err = ksz9477_read_mdix_ctrl(phydev);
+ if (err)
+ return err;
+
return kszphy_config_init(phydev);
}
@@ -2073,6 +2164,165 @@ static void kszphy_get_stats(struct phy_device *phydev,
data[i] = kszphy_get_stat(phydev, i);
}
+/* KSZ9477 PHY RXER Counter. Probably supported by other PHYs like KSZ9313,
+ * etc. The counter is incremented when the PHY receives a frame with one or
+ * more symbol errors. The counter is cleared when the register is read.
+ */
+#define MII_KSZ9477_PHY_RXER_COUNTER 0x15
+
+static int kszphy_update_stats(struct phy_device *phydev)
+{
+ struct kszphy_priv *priv = phydev->priv;
+ int ret;
+
+ ret = phy_read(phydev, MII_KSZ9477_PHY_RXER_COUNTER);
+ if (ret < 0)
+ return ret;
+
+ priv->phy_stats.rx_err_pkt_cnt += ret;
+
+ return 0;
+}
+
+static void kszphy_get_phy_stats(struct phy_device *phydev,
+ struct ethtool_eth_phy_stats *eth_stats,
+ struct ethtool_phy_stats *stats)
+{
+ struct kszphy_priv *priv = phydev->priv;
+
+ stats->rx_errors = priv->phy_stats.rx_err_pkt_cnt;
+}
+
+/* Base register for Signal Quality Indicator (SQI) - Channel A
+ *
+ * MMD Address: MDIO_MMD_PMAPMD (0x01)
+ * Register: 0xAC (Channel A)
+ * Each channel (pair) has its own register:
+ * Channel A: 0xAC
+ * Channel B: 0xAD
+ * Channel C: 0xAE
+ * Channel D: 0xAF
+ */
+#define KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A 0xac
+
+/* SQI field mask for bits [14:8]
+ *
+ * SQI indicates relative quality of the signal.
+ * A lower value indicates better signal quality.
+ */
+#define KSZ9477_MMD_SQI_MASK GENMASK(14, 8)
+
+#define KSZ9477_MAX_CHANNELS 4
+#define KSZ9477_SQI_MAX 7
+
+/* Number of SQI samples to average for a stable result.
+ *
+ * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
+ * For noisy environments, a minimum of 30–50 readings is recommended.
+ */
+#define KSZ9477_SQI_SAMPLE_COUNT 40
+
+/* The hardware SQI register provides a raw value from 0-127, where a lower
+ * value indicates better signal quality. However, empirical testing has
+ * shown that only the 0-7 range is relevant for a functional link. A raw
+ * value of 8 or higher was measured directly before link drop. This aligns
+ * with the OPEN Alliance recommendation that SQI=0 should represent the
+ * pre-failure state.
+ *
+ * This table provides a non-linear mapping from the useful raw hardware
+ * values (0-7) to the standard 0-7 SQI scale, where higher is better.
+ */
+static const u8 ksz_sqi_mapping[] = {
+ 7, /* raw 0 -> SQI 7 */
+ 7, /* raw 1 -> SQI 7 */
+ 6, /* raw 2 -> SQI 6 */
+ 5, /* raw 3 -> SQI 5 */
+ 4, /* raw 4 -> SQI 4 */
+ 3, /* raw 5 -> SQI 3 */
+ 2, /* raw 6 -> SQI 2 */
+ 1, /* raw 7 -> SQI 1 */
+};
+
+/**
+ * kszphy_get_sqi - Read, average, and map Signal Quality Index (SQI)
+ * @phydev: the PHY device
+ *
+ * This function reads and processes the raw Signal Quality Index from the
+ * PHY. Based on empirical testing, a raw value of 8 or higher indicates a
+ * pre-failure state and is mapped to SQI 0. Raw values from 0-7 are
+ * mapped to the standard 0-7 SQI scale via a lookup table.
+ *
+ * Return: SQI value (0–7), or a negative errno on failure.
+ */
+static int kszphy_get_sqi(struct phy_device *phydev)
+{
+ int sum[KSZ9477_MAX_CHANNELS] = { 0 };
+ int worst_sqi = KSZ9477_SQI_MAX;
+ int i, val, raw_sqi, ch;
+ u8 channels;
+
+ /* Determine applicable channels based on link speed */
+ if (phydev->speed == SPEED_1000)
+ channels = 4;
+ else if (phydev->speed == SPEED_100)
+ channels = 1;
+ else
+ return -EOPNOTSUPP;
+
+ /* Sample and accumulate SQI readings for each pair (currently only one).
+ *
+ * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
+ * - The SQI register is updated every 2 µs.
+ * - Values may fluctuate significantly, even in low-noise environments.
+ * - For reliable estimation, average a minimum of 30–50 samples
+ * (recommended for noisy environments)
+ * - In noisy environments, individual readings are highly unreliable.
+ *
+ * We use 40 samples per pair with a delay of 3 µs between each
+ * read to ensure new values are captured (2 µs update interval).
+ */
+ for (i = 0; i < KSZ9477_SQI_SAMPLE_COUNT; i++) {
+ for (ch = 0; ch < channels; ch++) {
+ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+ KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A + ch);
+ if (val < 0)
+ return val;
+
+ raw_sqi = FIELD_GET(KSZ9477_MMD_SQI_MASK, val);
+ sum[ch] += raw_sqi;
+
+ /* We communicate with the PHY via MDIO via SPI or
+ * I2C, which is relatively slow. At least slower than
+ * the update interval of the SQI register.
+ * So, we can skip the delay between reads.
+ */
+ }
+ }
+
+ /* Calculate average for each channel and find the worst SQI */
+ for (ch = 0; ch < channels; ch++) {
+ int avg_raw_sqi = sum[ch] / KSZ9477_SQI_SAMPLE_COUNT;
+ int mapped_sqi;
+
+ /* Handle the pre-fail/failed state first. */
+ if (avg_raw_sqi >= ARRAY_SIZE(ksz_sqi_mapping))
+ mapped_sqi = 0;
+ else
+ /* Use the lookup table for the good signal range. */
+ mapped_sqi = ksz_sqi_mapping[avg_raw_sqi];
+
+ if (mapped_sqi < worst_sqi)
+ worst_sqi = mapped_sqi;
+ }
+
+ return worst_sqi;
+}
+
+static int kszphy_get_sqi_max(struct phy_device *phydev)
+{
+ return KSZ9477_SQI_MAX;
+}
+
static void kszphy_enable_clk(struct phy_device *phydev)
{
struct kszphy_priv *priv = phydev->priv;
@@ -2558,6 +2808,60 @@ static int ksz886x_cable_test_get_status(struct phy_device *phydev,
return ret;
}
+/**
+ * LAN8814_PAGE_PCS - Selects Extended Page 0.
+ *
+ * This page contains timers used for auto-negotiation, debug registers and
+ * register to configure fast link failure.
+ */
+#define LAN8814_PAGE_PCS 0
+
+/**
+ * LAN8814_PAGE_AFE_PMA - Selects Extended Page 1.
+ *
+ * This page appears to control the Analog Front-End (AFE) and Physical
+ * Medium Attachment (PMA) layers. It is used to access registers like
+ * LAN8814_PD_CONTROLS and LAN8814_LINK_QUALITY.
+ */
+#define LAN8814_PAGE_AFE_PMA 1
+
+/**
+ * LAN8814_PAGE_PCS_DIGITAL - Selects Extended Page 2.
+ *
+ * This page seems dedicated to the Physical Coding Sublayer (PCS) and other
+ * digital logic. It is used for MDI-X alignment (LAN8814_ALIGN_SWAP) and EEE
+ * state (LAN8814_EEE_STATE) in the LAN8814, and is repurposed for statistics
+ * and self-test counters in the LAN8842.
+ */
+#define LAN8814_PAGE_PCS_DIGITAL 2
+
+/**
+ * LAN8814_PAGE_COMMON_REGS - Selects Extended Page 4.
+ *
+ * This page contains device-common registers that affect the entire chip.
+ * It includes controls for chip-level resets, strap status, GPIO,
+ * QSGMII, the shared 1588 PTP block, and the PVT monitor.
+ */
+#define LAN8814_PAGE_COMMON_REGS 4
+
+/**
+ * LAN8814_PAGE_PORT_REGS - Selects Extended Page 5.
+ *
+ * This page contains port-specific registers that must be accessed
+ * on a per-port basis. It includes controls for port LEDs, QSGMII PCS,
+ * rate adaptation FIFOs, and the per-port 1588 TSU block.
+ */
+#define LAN8814_PAGE_PORT_REGS 5
+
+/**
+ * LAN8814_PAGE_SYSTEM_CTRL - Selects Extended Page 31.
+ *
+ * This page appears to hold fundamental system or global controls. In the
+ * driver, it is used by the related LAN8804 to access the
+ * LAN8814_CLOCK_MANAGEMENT register.
+ */
+#define LAN8814_PAGE_SYSTEM_CTRL 31
+
#define LAN_EXT_PAGE_ACCESS_CONTROL 0x16
#define LAN_EXT_PAGE_ACCESS_ADDRESS_DATA 0x17
#define LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC 0x4000
@@ -2608,6 +2912,27 @@ static int lanphy_write_page_reg(struct phy_device *phydev, int page, u16 addr,
return val;
}
+static int lanphy_modify_page_reg(struct phy_device *phydev, int page, u16 addr,
+ u16 mask, u16 set)
+{
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+ __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, page);
+ __phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, addr);
+ __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL,
+ (page | LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC));
+ ret = __phy_modify_changed(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA,
+ mask, set);
+ phy_unlock_mdio_bus(phydev);
+
+ if (ret < 0)
+ phydev_err(phydev, "__phy_modify_changed() failed: %pe\n",
+ ERR_PTR(ret));
+
+ return ret;
+}
+
static int lan8814_config_ts_intr(struct phy_device *phydev, bool enable)
{
u16 val = 0;
@@ -2618,35 +2943,46 @@ static int lan8814_config_ts_intr(struct phy_device *phydev, bool enable)
PTP_TSU_INT_EN_PTP_RX_TS_EN_ |
PTP_TSU_INT_EN_PTP_RX_TS_OVRFL_EN_;
- return lanphy_write_page_reg(phydev, 5, PTP_TSU_INT_EN, val);
+ return lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TSU_INT_EN, val);
}
static void lan8814_ptp_rx_ts_get(struct phy_device *phydev,
u32 *seconds, u32 *nano_seconds, u16 *seq_id)
{
- *seconds = lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_SEC_HI);
+ *seconds = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_RX_INGRESS_SEC_HI);
*seconds = (*seconds << 16) |
- lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_SEC_LO);
+ lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_RX_INGRESS_SEC_LO);
- *nano_seconds = lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_NS_HI);
+ *nano_seconds = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_RX_INGRESS_NS_HI);
*nano_seconds = ((*nano_seconds & 0x3fff) << 16) |
- lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_NS_LO);
+ lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_RX_INGRESS_NS_LO);
- *seq_id = lanphy_read_page_reg(phydev, 5, PTP_RX_MSG_HEADER2);
+ *seq_id = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_RX_MSG_HEADER2);
}
static void lan8814_ptp_tx_ts_get(struct phy_device *phydev,
u32 *seconds, u32 *nano_seconds, u16 *seq_id)
{
- *seconds = lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_SEC_HI);
+ *seconds = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_EGRESS_SEC_HI);
*seconds = *seconds << 16 |
- lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_SEC_LO);
+ lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_EGRESS_SEC_LO);
- *nano_seconds = lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_NS_HI);
+ *nano_seconds = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_EGRESS_NS_HI);
*nano_seconds = ((*nano_seconds & 0x3fff) << 16) |
- lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_NS_LO);
+ lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_EGRESS_NS_LO);
- *seq_id = lanphy_read_page_reg(phydev, 5, PTP_TX_MSG_HEADER2);
+ *seq_id = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_MSG_HEADER2);
}
static int lan8814_ts_info(struct mii_timestamper *mii_ts, struct kernel_ethtool_ts_info *info)
@@ -2680,11 +3016,11 @@ static void lan8814_flush_fifo(struct phy_device *phydev, bool egress)
int i;
for (i = 0; i < FIFO_SIZE; ++i)
- lanphy_read_page_reg(phydev, 5,
+ lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
egress ? PTP_TX_MSG_HEADER2 : PTP_RX_MSG_HEADER2);
/* Read to clear overflow status bit */
- lanphy_read_page_reg(phydev, 5, PTP_TSU_INT_STS);
+ lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS, PTP_TSU_INT_STS);
}
static int lan8814_hwtstamp(struct mii_timestamper *mii_ts,
@@ -2696,7 +3032,6 @@ static int lan8814_hwtstamp(struct mii_timestamper *mii_ts,
struct lan8814_ptp_rx_ts *rx_ts, *tmp;
int txcfg = 0, rxcfg = 0;
int pkt_ts_enable;
- int tx_mod;
ptp_priv->hwts_tx_type = config->tx_type;
ptp_priv->rx_filter = config->rx_filter;
@@ -2735,21 +3070,28 @@ static int lan8814_hwtstamp(struct mii_timestamper *mii_ts,
rxcfg |= PTP_RX_PARSE_CONFIG_IPV4_EN_ | PTP_RX_PARSE_CONFIG_IPV6_EN_;
txcfg |= PTP_TX_PARSE_CONFIG_IPV4_EN_ | PTP_TX_PARSE_CONFIG_IPV6_EN_;
}
- lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_RX_PARSE_CONFIG, rxcfg);
- lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_PARSE_CONFIG, txcfg);
+ lanphy_write_page_reg(ptp_priv->phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_RX_PARSE_CONFIG, rxcfg);
+ lanphy_write_page_reg(ptp_priv->phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_PARSE_CONFIG, txcfg);
pkt_ts_enable = PTP_TIMESTAMP_EN_SYNC_ | PTP_TIMESTAMP_EN_DREQ_ |
PTP_TIMESTAMP_EN_PDREQ_ | PTP_TIMESTAMP_EN_PDRES_;
- lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_RX_TIMESTAMP_EN, pkt_ts_enable);
- lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_TIMESTAMP_EN, pkt_ts_enable);
+ lanphy_write_page_reg(ptp_priv->phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_RX_TIMESTAMP_EN, pkt_ts_enable);
+ lanphy_write_page_reg(ptp_priv->phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_TIMESTAMP_EN, pkt_ts_enable);
- tx_mod = lanphy_read_page_reg(ptp_priv->phydev, 5, PTP_TX_MOD);
if (ptp_priv->hwts_tx_type == HWTSTAMP_TX_ONESTEP_SYNC) {
- lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_MOD,
- tx_mod | PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_);
+ lanphy_modify_page_reg(ptp_priv->phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_MOD,
+ PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_,
+ PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_);
} else if (ptp_priv->hwts_tx_type == HWTSTAMP_TX_ON) {
- lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_MOD,
- tx_mod & ~PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_);
+ lanphy_modify_page_reg(ptp_priv->phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_MOD,
+ PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_,
+ 0);
}
if (config->rx_filter != HWTSTAMP_FILTER_NONE)
@@ -2871,29 +3213,41 @@ static bool lan8814_rxtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb
static void lan8814_ptp_clock_set(struct phy_device *phydev,
time64_t sec, u32 nsec)
{
- lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_LO, lower_16_bits(sec));
- lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_MID, upper_16_bits(sec));
- lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_HI, upper_32_bits(sec));
- lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_LO, lower_16_bits(nsec));
- lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_HI, upper_16_bits(nsec));
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CLOCK_SET_SEC_LO, lower_16_bits(sec));
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CLOCK_SET_SEC_MID, upper_16_bits(sec));
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CLOCK_SET_SEC_HI, upper_32_bits(sec));
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CLOCK_SET_NS_LO, lower_16_bits(nsec));
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CLOCK_SET_NS_HI, upper_16_bits(nsec));
- lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_LOAD_);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_CMD_CTL,
+ PTP_CMD_CTL_PTP_CLOCK_LOAD_);
}
static void lan8814_ptp_clock_get(struct phy_device *phydev,
time64_t *sec, u32 *nsec)
{
- lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_READ_);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_CMD_CTL,
+ PTP_CMD_CTL_PTP_CLOCK_READ_);
- *sec = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_HI);
+ *sec = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CLOCK_READ_SEC_HI);
*sec <<= 16;
- *sec |= lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_MID);
+ *sec |= lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CLOCK_READ_SEC_MID);
*sec <<= 16;
- *sec |= lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_LO);
+ *sec |= lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CLOCK_READ_SEC_LO);
- *nsec = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_HI);
+ *nsec = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CLOCK_READ_NS_HI);
*nsec <<= 16;
- *nsec |= lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_LO);
+ *nsec |= lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CLOCK_READ_NS_LO);
}
static int lan8814_ptpci_gettime64(struct ptp_clock_info *ptpci,
@@ -2932,14 +3286,18 @@ static void lan8814_ptp_set_target(struct phy_device *phydev, int event,
s64 start_sec, u32 start_nsec)
{
/* Set the start time */
- lanphy_write_page_reg(phydev, 4, LAN8814_PTP_CLOCK_TARGET_SEC_LO(event),
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_PTP_CLOCK_TARGET_SEC_LO(event),
lower_16_bits(start_sec));
- lanphy_write_page_reg(phydev, 4, LAN8814_PTP_CLOCK_TARGET_SEC_HI(event),
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_PTP_CLOCK_TARGET_SEC_HI(event),
upper_16_bits(start_sec));
- lanphy_write_page_reg(phydev, 4, LAN8814_PTP_CLOCK_TARGET_NS_LO(event),
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_PTP_CLOCK_TARGET_NS_LO(event),
lower_16_bits(start_nsec));
- lanphy_write_page_reg(phydev, 4, LAN8814_PTP_CLOCK_TARGET_NS_HI(event),
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_PTP_CLOCK_TARGET_NS_HI(event),
upper_16_bits(start_nsec) & 0x3fff);
}
@@ -3037,9 +3395,11 @@ static void lan8814_ptp_clock_step(struct phy_device *phydev,
adjustment_value_lo = adjustment_value & 0xffff;
adjustment_value_hi = (adjustment_value >> 16) & 0x3fff;
- lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_LTC_STEP_ADJ_LO,
adjustment_value_lo);
- lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_LTC_STEP_ADJ_HI,
PTP_LTC_STEP_ADJ_DIR_ |
adjustment_value_hi);
seconds -= ((s32)adjustment_value);
@@ -3057,9 +3417,11 @@ static void lan8814_ptp_clock_step(struct phy_device *phydev,
adjustment_value_lo = adjustment_value & 0xffff;
adjustment_value_hi = (adjustment_value >> 16) & 0x3fff;
- lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_LTC_STEP_ADJ_LO,
adjustment_value_lo);
- lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_LTC_STEP_ADJ_HI,
adjustment_value_hi);
seconds += ((s32)adjustment_value);
@@ -3067,8 +3429,8 @@ static void lan8814_ptp_clock_step(struct phy_device *phydev,
set_seconds += adjustment_value;
lan8814_ptp_update_target(phydev, set_seconds);
}
- lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL,
- PTP_CMD_CTL_PTP_LTC_STEP_SEC_);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_CMD_CTL, PTP_CMD_CTL_PTP_LTC_STEP_SEC_);
}
if (nano_seconds) {
u16 nano_seconds_lo;
@@ -3077,12 +3439,14 @@ static void lan8814_ptp_clock_step(struct phy_device *phydev,
nano_seconds_lo = nano_seconds & 0xffff;
nano_seconds_hi = (nano_seconds >> 16) & 0x3fff;
- lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_LTC_STEP_ADJ_LO,
nano_seconds_lo);
- lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_LTC_STEP_ADJ_HI,
PTP_LTC_STEP_ADJ_DIR_ |
nano_seconds_hi);
- lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_CMD_CTL,
PTP_CMD_CTL_PTP_LTC_STEP_NSEC_);
}
}
@@ -3124,8 +3488,10 @@ static int lan8814_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm)
kszphy_rate_adj_hi |= PTP_CLOCK_RATE_ADJ_DIR_;
mutex_lock(&shared->shared_lock);
- lanphy_write_page_reg(phydev, 4, PTP_CLOCK_RATE_ADJ_HI, kszphy_rate_adj_hi);
- lanphy_write_page_reg(phydev, 4, PTP_CLOCK_RATE_ADJ_LO, kszphy_rate_adj_lo);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_CLOCK_RATE_ADJ_HI,
+ kszphy_rate_adj_hi);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_CLOCK_RATE_ADJ_LO,
+ kszphy_rate_adj_lo);
mutex_unlock(&shared->shared_lock);
return 0;
@@ -3134,17 +3500,17 @@ static int lan8814_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm)
static void lan8814_ptp_set_reload(struct phy_device *phydev, int event,
s64 period_sec, u32 period_nsec)
{
- lanphy_write_page_reg(phydev, 4,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
LAN8814_PTP_CLOCK_TARGET_RELOAD_SEC_LO(event),
lower_16_bits(period_sec));
- lanphy_write_page_reg(phydev, 4,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
LAN8814_PTP_CLOCK_TARGET_RELOAD_SEC_HI(event),
upper_16_bits(period_sec));
- lanphy_write_page_reg(phydev, 4,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
LAN8814_PTP_CLOCK_TARGET_RELOAD_NS_LO(event),
lower_16_bits(period_nsec));
- lanphy_write_page_reg(phydev, 4,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
LAN8814_PTP_CLOCK_TARGET_RELOAD_NS_HI(event),
upper_16_bits(period_nsec) & 0x3fff);
}
@@ -3152,73 +3518,72 @@ static void lan8814_ptp_set_reload(struct phy_device *phydev, int event,
static void lan8814_ptp_enable_event(struct phy_device *phydev, int event,
int pulse_width)
{
- u16 val;
-
- val = lanphy_read_page_reg(phydev, 4, LAN8814_PTP_GENERAL_CONFIG);
- /* Set the pulse width of the event */
- val &= ~(LAN8814_PTP_GENERAL_CONFIG_LTC_EVENT_MASK(event));
- /* Make sure that the target clock will be incremented each time when
+ /* Set the pulse width of the event,
+ * Make sure that the target clock will be incremented each time when
* local time reaches or pass it
+ * Set the polarity high
*/
- val |= LAN8814_PTP_GENERAL_CONFIG_LTC_EVENT_SET(event, pulse_width);
- val &= ~(LAN8814_PTP_GENERAL_CONFIG_RELOAD_ADD_X(event));
- /* Set the polarity high */
- val |= LAN8814_PTP_GENERAL_CONFIG_POLARITY_X(event);
- lanphy_write_page_reg(phydev, 4, LAN8814_PTP_GENERAL_CONFIG, val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, LAN8814_PTP_GENERAL_CONFIG,
+ LAN8814_PTP_GENERAL_CONFIG_LTC_EVENT_MASK(event) |
+ LAN8814_PTP_GENERAL_CONFIG_LTC_EVENT_SET(event, pulse_width) |
+ LAN8814_PTP_GENERAL_CONFIG_RELOAD_ADD_X(event) |
+ LAN8814_PTP_GENERAL_CONFIG_POLARITY_X(event),
+ LAN8814_PTP_GENERAL_CONFIG_LTC_EVENT_SET(event, pulse_width) |
+ LAN8814_PTP_GENERAL_CONFIG_POLARITY_X(event));
}
static void lan8814_ptp_disable_event(struct phy_device *phydev, int event)
{
- u16 val;
-
/* Set target to too far in the future, effectively disabling it */
lan8814_ptp_set_target(phydev, event, 0xFFFFFFFF, 0);
/* And then reload once it recheas the target */
- val = lanphy_read_page_reg(phydev, 4, LAN8814_PTP_GENERAL_CONFIG);
- val |= LAN8814_PTP_GENERAL_CONFIG_RELOAD_ADD_X(event);
- lanphy_write_page_reg(phydev, 4, LAN8814_PTP_GENERAL_CONFIG, val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, LAN8814_PTP_GENERAL_CONFIG,
+ LAN8814_PTP_GENERAL_CONFIG_RELOAD_ADD_X(event),
+ LAN8814_PTP_GENERAL_CONFIG_RELOAD_ADD_X(event));
}
static void lan8814_ptp_perout_off(struct phy_device *phydev, int pin)
{
- u16 val;
-
/* Disable gpio alternate function,
* 1: select as gpio,
* 0: select alt func
*/
- val = lanphy_read_page_reg(phydev, 4, LAN8814_GPIO_EN_ADDR(pin));
- val |= LAN8814_GPIO_EN_BIT(pin);
- lanphy_write_page_reg(phydev, 4, LAN8814_GPIO_EN_ADDR(pin), val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_GPIO_EN_ADDR(pin),
+ LAN8814_GPIO_EN_BIT(pin),
+ LAN8814_GPIO_EN_BIT(pin));
- val = lanphy_read_page_reg(phydev, 4, LAN8814_GPIO_DIR_ADDR(pin));
- val &= ~LAN8814_GPIO_DIR_BIT(pin);
- lanphy_write_page_reg(phydev, 4, LAN8814_GPIO_DIR_ADDR(pin), val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_GPIO_DIR_ADDR(pin),
+ LAN8814_GPIO_DIR_BIT(pin),
+ 0);
- val = lanphy_read_page_reg(phydev, 4, LAN8814_GPIO_BUF_ADDR(pin));
- val &= ~LAN8814_GPIO_BUF_BIT(pin);
- lanphy_write_page_reg(phydev, 4, LAN8814_GPIO_BUF_ADDR(pin), val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_GPIO_BUF_ADDR(pin),
+ LAN8814_GPIO_BUF_BIT(pin),
+ 0);
}
static void lan8814_ptp_perout_on(struct phy_device *phydev, int pin)
{
- int val;
-
/* Set as gpio output */
- val = lanphy_read_page_reg(phydev, 4, LAN8814_GPIO_DIR_ADDR(pin));
- val |= LAN8814_GPIO_DIR_BIT(pin);
- lanphy_write_page_reg(phydev, 4, LAN8814_GPIO_DIR_ADDR(pin), val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_GPIO_DIR_ADDR(pin),
+ LAN8814_GPIO_DIR_BIT(pin),
+ LAN8814_GPIO_DIR_BIT(pin));
/* Enable gpio 0:for alternate function, 1:gpio */
- val = lanphy_read_page_reg(phydev, 4, LAN8814_GPIO_EN_ADDR(pin));
- val &= ~LAN8814_GPIO_EN_BIT(pin);
- lanphy_write_page_reg(phydev, 4, LAN8814_GPIO_EN_ADDR(pin), val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_GPIO_EN_ADDR(pin),
+ LAN8814_GPIO_EN_BIT(pin),
+ 0);
/* Set buffer type to push pull */
- val = lanphy_read_page_reg(phydev, 4, LAN8814_GPIO_BUF_ADDR(pin));
- val |= LAN8814_GPIO_BUF_BIT(pin);
- lanphy_write_page_reg(phydev, 4, LAN8814_GPIO_BUF_ADDR(pin), val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_GPIO_BUF_ADDR(pin),
+ LAN8814_GPIO_BUF_BIT(pin),
+ LAN8814_GPIO_BUF_BIT(pin));
}
static int lan8814_ptp_perout(struct ptp_clock_info *ptpci,
@@ -3333,61 +3698,64 @@ static int lan8814_ptp_perout(struct ptp_clock_info *ptpci,
static void lan8814_ptp_extts_on(struct phy_device *phydev, int pin, u32 flags)
{
- u16 tmp;
-
/* Set as gpio input */
- tmp = lanphy_read_page_reg(phydev, 4, LAN8814_GPIO_DIR_ADDR(pin));
- tmp &= ~LAN8814_GPIO_DIR_BIT(pin);
- lanphy_write_page_reg(phydev, 4, LAN8814_GPIO_DIR_ADDR(pin), tmp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_GPIO_DIR_ADDR(pin),
+ LAN8814_GPIO_DIR_BIT(pin),
+ 0);
/* Map the pin to ltc pin 0 of the capture map registers */
- tmp = lanphy_read_page_reg(phydev, 4, PTP_GPIO_CAP_MAP_LO);
- tmp |= pin;
- lanphy_write_page_reg(phydev, 4, PTP_GPIO_CAP_MAP_LO, tmp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_CAP_MAP_LO, pin, pin);
/* Enable capture on the edges of the ltc pin */
- tmp = lanphy_read_page_reg(phydev, 4, PTP_GPIO_CAP_EN);
if (flags & PTP_RISING_EDGE)
- tmp |= PTP_GPIO_CAP_EN_GPIO_RE_CAPTURE_ENABLE(0);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_CAP_EN,
+ PTP_GPIO_CAP_EN_GPIO_RE_CAPTURE_ENABLE(0),
+ PTP_GPIO_CAP_EN_GPIO_RE_CAPTURE_ENABLE(0));
if (flags & PTP_FALLING_EDGE)
- tmp |= PTP_GPIO_CAP_EN_GPIO_FE_CAPTURE_ENABLE(0);
- lanphy_write_page_reg(phydev, 4, PTP_GPIO_CAP_EN, tmp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_CAP_EN,
+ PTP_GPIO_CAP_EN_GPIO_FE_CAPTURE_ENABLE(0),
+ PTP_GPIO_CAP_EN_GPIO_FE_CAPTURE_ENABLE(0));
/* Enable interrupt top interrupt */
- tmp = lanphy_read_page_reg(phydev, 4, PTP_COMMON_INT_ENA);
- tmp |= PTP_COMMON_INT_ENA_GPIO_CAP_EN;
- lanphy_write_page_reg(phydev, 4, PTP_COMMON_INT_ENA, tmp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_COMMON_INT_ENA,
+ PTP_COMMON_INT_ENA_GPIO_CAP_EN,
+ PTP_COMMON_INT_ENA_GPIO_CAP_EN);
}
static void lan8814_ptp_extts_off(struct phy_device *phydev, int pin)
{
- u16 tmp;
-
/* Set as gpio out */
- tmp = lanphy_read_page_reg(phydev, 4, LAN8814_GPIO_DIR_ADDR(pin));
- tmp |= LAN8814_GPIO_DIR_BIT(pin);
- lanphy_write_page_reg(phydev, 4, LAN8814_GPIO_DIR_ADDR(pin), tmp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_GPIO_DIR_ADDR(pin),
+ LAN8814_GPIO_DIR_BIT(pin),
+ LAN8814_GPIO_DIR_BIT(pin));
/* Enable alternate, 0:for alternate function, 1:gpio */
- tmp = lanphy_read_page_reg(phydev, 4, LAN8814_GPIO_EN_ADDR(pin));
- tmp &= ~LAN8814_GPIO_EN_BIT(pin);
- lanphy_write_page_reg(phydev, 4, LAN8814_GPIO_EN_ADDR(pin), tmp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_GPIO_EN_ADDR(pin),
+ LAN8814_GPIO_EN_BIT(pin),
+ 0);
/* Clear the mapping of pin to registers 0 of the capture registers */
- tmp = lanphy_read_page_reg(phydev, 4, PTP_GPIO_CAP_MAP_LO);
- tmp &= ~GENMASK(3, 0);
- lanphy_write_page_reg(phydev, 4, PTP_GPIO_CAP_MAP_LO, tmp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_CAP_MAP_LO,
+ GENMASK(3, 0),
+ 0);
/* Disable capture on both of the edges */
- tmp = lanphy_read_page_reg(phydev, 4, PTP_GPIO_CAP_EN);
- tmp &= ~PTP_GPIO_CAP_EN_GPIO_RE_CAPTURE_ENABLE(pin);
- tmp &= ~PTP_GPIO_CAP_EN_GPIO_FE_CAPTURE_ENABLE(pin);
- lanphy_write_page_reg(phydev, 4, PTP_GPIO_CAP_EN, tmp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_GPIO_CAP_EN,
+ PTP_GPIO_CAP_EN_GPIO_RE_CAPTURE_ENABLE(pin) |
+ PTP_GPIO_CAP_EN_GPIO_FE_CAPTURE_ENABLE(pin),
+ 0);
/* Disable interrupt top interrupt */
- tmp = lanphy_read_page_reg(phydev, 4, PTP_COMMON_INT_ENA);
- tmp &= ~PTP_COMMON_INT_ENA_GPIO_CAP_EN;
- lanphy_write_page_reg(phydev, 4, PTP_COMMON_INT_ENA, tmp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_COMMON_INT_ENA,
+ PTP_COMMON_INT_ENA_GPIO_CAP_EN,
+ 0);
}
static int lan8814_ptp_extts(struct ptp_clock_info *ptpci,
@@ -3517,7 +3885,8 @@ static void lan8814_get_tx_ts(struct kszphy_ptp_priv *ptp_priv)
/* If other timestamps are available in the FIFO,
* process them.
*/
- reg = lanphy_read_page_reg(phydev, 5, PTP_CAP_INFO);
+ reg = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_CAP_INFO);
} while (PTP_CAP_INFO_TX_TS_CNT_GET_(reg) > 0);
}
@@ -3590,7 +3959,8 @@ static void lan8814_get_rx_ts(struct kszphy_ptp_priv *ptp_priv)
/* If other timestamps are available in the FIFO,
* process them.
*/
- reg = lanphy_read_page_reg(phydev, 5, PTP_CAP_INFO);
+ reg = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_CAP_INFO);
} while (PTP_CAP_INFO_RX_TS_CNT_GET_(reg) > 0);
}
@@ -3627,31 +3997,40 @@ static int lan8814_gpio_process_cap(struct lan8814_shared_priv *shared)
/* This is 0 because whatever was the input pin it was mapped it to
* ltc gpio pin 0
*/
- tmp = lanphy_read_page_reg(phydev, 4, PTP_GPIO_SEL);
- tmp |= PTP_GPIO_SEL_GPIO_SEL(0);
- lanphy_write_page_reg(phydev, 4, PTP_GPIO_SEL, tmp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_GPIO_SEL,
+ PTP_GPIO_SEL_GPIO_SEL(0),
+ PTP_GPIO_SEL_GPIO_SEL(0));
- tmp = lanphy_read_page_reg(phydev, 4, PTP_GPIO_CAP_STS);
+ tmp = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_CAP_STS);
if (!(tmp & PTP_GPIO_CAP_STS_PTP_GPIO_RE_STS(0)) &&
!(tmp & PTP_GPIO_CAP_STS_PTP_GPIO_FE_STS(0)))
return -1;
if (tmp & BIT(0)) {
- sec = lanphy_read_page_reg(phydev, 4, PTP_GPIO_RE_LTC_SEC_HI_CAP);
+ sec = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_RE_LTC_SEC_HI_CAP);
sec <<= 16;
- sec |= lanphy_read_page_reg(phydev, 4, PTP_GPIO_RE_LTC_SEC_LO_CAP);
+ sec |= lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_RE_LTC_SEC_LO_CAP);
- nsec = lanphy_read_page_reg(phydev, 4, PTP_GPIO_RE_LTC_NS_HI_CAP) & 0x3fff;
+ nsec = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_RE_LTC_NS_HI_CAP) & 0x3fff;
nsec <<= 16;
- nsec |= lanphy_read_page_reg(phydev, 4, PTP_GPIO_RE_LTC_NS_LO_CAP);
+ nsec |= lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_RE_LTC_NS_LO_CAP);
} else {
- sec = lanphy_read_page_reg(phydev, 4, PTP_GPIO_FE_LTC_SEC_HI_CAP);
+ sec = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_FE_LTC_SEC_HI_CAP);
sec <<= 16;
- sec |= lanphy_read_page_reg(phydev, 4, PTP_GPIO_FE_LTC_SEC_LO_CAP);
+ sec |= lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_FE_LTC_SEC_LO_CAP);
- nsec = lanphy_read_page_reg(phydev, 4, PTP_GPIO_FE_LTC_NS_HI_CAP) & 0x3fff;
+ nsec = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_FE_LTC_NS_HI_CAP) & 0x3fff;
nsec <<= 16;
- nsec |= lanphy_read_page_reg(phydev, 4, PTP_GPIO_RE_LTC_NS_LO_CAP);
+ nsec |= lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ PTP_GPIO_RE_LTC_NS_LO_CAP);
}
ptp_event.index = 0;
@@ -3676,19 +4055,17 @@ static int lan8814_handle_gpio_interrupt(struct phy_device *phydev, u16 status)
static int lan8804_config_init(struct phy_device *phydev)
{
- int val;
-
/* MDI-X setting for swap A,B transmit */
- val = lanphy_read_page_reg(phydev, 2, LAN8804_ALIGN_SWAP);
- val &= ~LAN8804_ALIGN_TX_A_B_SWAP_MASK;
- val |= LAN8804_ALIGN_TX_A_B_SWAP;
- lanphy_write_page_reg(phydev, 2, LAN8804_ALIGN_SWAP, val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_PCS_DIGITAL, LAN8804_ALIGN_SWAP,
+ LAN8804_ALIGN_TX_A_B_SWAP_MASK,
+ LAN8804_ALIGN_TX_A_B_SWAP);
/* Make sure that the PHY will not stop generating the clock when the
* link partner goes down
*/
- lanphy_write_page_reg(phydev, 31, LAN8814_CLOCK_MANAGEMENT, 0x27e);
- lanphy_read_page_reg(phydev, 1, LAN8814_LINK_QUALITY);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_SYSTEM_CTRL,
+ LAN8814_CLOCK_MANAGEMENT, 0x27e);
+ lanphy_read_page_reg(phydev, LAN8814_PAGE_AFE_PMA, LAN8814_LINK_QUALITY);
return 0;
}
@@ -3770,7 +4147,8 @@ static irqreturn_t lan8814_handle_interrupt(struct phy_device *phydev)
}
while (true) {
- irq_status = lanphy_read_page_reg(phydev, 5, PTP_TSU_INT_STS);
+ irq_status = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TSU_INT_STS);
if (!irq_status)
break;
@@ -3798,7 +4176,7 @@ static int lan8814_config_intr(struct phy_device *phydev)
{
int err;
- lanphy_write_page_reg(phydev, 4, LAN8814_INTR_CTRL_REG,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, LAN8814_INTR_CTRL_REG,
LAN8814_INTR_CTRL_REG_POLARITY |
LAN8814_INTR_CTRL_REG_INTR_ENABLE);
@@ -3824,35 +4202,41 @@ static void lan8814_ptp_init(struct phy_device *phydev)
{
struct kszphy_priv *priv = phydev->priv;
struct kszphy_ptp_priv *ptp_priv = &priv->ptp_priv;
- u32 temp;
if (!IS_ENABLED(CONFIG_PTP_1588_CLOCK) ||
!IS_ENABLED(CONFIG_NETWORK_PHY_TIMESTAMPING))
return;
- lanphy_write_page_reg(phydev, 5, TSU_HARD_RESET, TSU_HARD_RESET_);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ TSU_HARD_RESET, TSU_HARD_RESET_);
- temp = lanphy_read_page_reg(phydev, 5, PTP_TX_MOD);
- temp |= PTP_TX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_;
- lanphy_write_page_reg(phydev, 5, PTP_TX_MOD, temp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_PORT_REGS, PTP_TX_MOD,
+ PTP_TX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_,
+ PTP_TX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_);
- temp = lanphy_read_page_reg(phydev, 5, PTP_RX_MOD);
- temp |= PTP_RX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_;
- lanphy_write_page_reg(phydev, 5, PTP_RX_MOD, temp);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_PORT_REGS, PTP_RX_MOD,
+ PTP_RX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_,
+ PTP_RX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_);
- lanphy_write_page_reg(phydev, 5, PTP_RX_PARSE_CONFIG, 0);
- lanphy_write_page_reg(phydev, 5, PTP_TX_PARSE_CONFIG, 0);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_RX_PARSE_CONFIG, 0);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_PARSE_CONFIG, 0);
/* Removing default registers configs related to L2 and IP */
- lanphy_write_page_reg(phydev, 5, PTP_TX_PARSE_L2_ADDR_EN, 0);
- lanphy_write_page_reg(phydev, 5, PTP_RX_PARSE_L2_ADDR_EN, 0);
- lanphy_write_page_reg(phydev, 5, PTP_TX_PARSE_IP_ADDR_EN, 0);
- lanphy_write_page_reg(phydev, 5, PTP_RX_PARSE_IP_ADDR_EN, 0);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_PARSE_L2_ADDR_EN, 0);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_RX_PARSE_L2_ADDR_EN, 0);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TX_PARSE_IP_ADDR_EN, 0);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_RX_PARSE_IP_ADDR_EN, 0);
/* Disable checking for minorVersionPTP field */
- lanphy_write_page_reg(phydev, 5, PTP_RX_VERSION,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS, PTP_RX_VERSION,
PTP_MAX_VERSION(0xff) | PTP_MIN_VERSION(0x0));
- lanphy_write_page_reg(phydev, 5, PTP_TX_VERSION,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS, PTP_TX_VERSION,
PTP_MAX_VERSION(0xff) | PTP_MIN_VERSION(0x0));
skb_queue_head_init(&ptp_priv->tx_queue);
@@ -3873,7 +4257,8 @@ static void lan8814_ptp_init(struct phy_device *phydev)
phydev->default_timestamp = true;
}
-static int lan8814_ptp_probe_once(struct phy_device *phydev)
+static int __lan8814_ptp_probe_once(struct phy_device *phydev, char *pin_name,
+ int gpios)
{
struct lan8814_shared_priv *shared = phy_package_get_priv(phydev);
@@ -3881,18 +4266,18 @@ static int lan8814_ptp_probe_once(struct phy_device *phydev)
mutex_init(&shared->shared_lock);
shared->pin_config = devm_kmalloc_array(&phydev->mdio.dev,
- LAN8814_PTP_GPIO_NUM,
+ gpios,
sizeof(*shared->pin_config),
GFP_KERNEL);
if (!shared->pin_config)
return -ENOMEM;
- for (int i = 0; i < LAN8814_PTP_GPIO_NUM; i++) {
+ for (int i = 0; i < gpios; i++) {
struct ptp_pin_desc *ptp_pin = &shared->pin_config[i];
memset(ptp_pin, 0, sizeof(*ptp_pin));
snprintf(ptp_pin->name,
- sizeof(ptp_pin->name), "lan8814_ptp_pin_%02d", i);
+ sizeof(ptp_pin->name), "%s_%02d", pin_name, i);
ptp_pin->index = i;
ptp_pin->func = PTP_PF_NONE;
}
@@ -3902,7 +4287,7 @@ static int lan8814_ptp_probe_once(struct phy_device *phydev)
shared->ptp_clock_info.max_adj = 31249999;
shared->ptp_clock_info.n_alarm = 0;
shared->ptp_clock_info.n_ext_ts = LAN8814_PTP_EXTTS_NUM;
- shared->ptp_clock_info.n_pins = LAN8814_PTP_GPIO_NUM;
+ shared->ptp_clock_info.n_pins = gpios;
shared->ptp_clock_info.pps = 0;
shared->ptp_clock_info.supported_extts_flags = PTP_RISING_EDGE |
PTP_FALLING_EDGE |
@@ -3921,8 +4306,8 @@ static int lan8814_ptp_probe_once(struct phy_device *phydev)
shared->ptp_clock = ptp_clock_register(&shared->ptp_clock_info,
&phydev->mdio.dev);
if (IS_ERR(shared->ptp_clock)) {
- phydev_err(phydev, "ptp_clock_register failed %lu\n",
- PTR_ERR(shared->ptp_clock));
+ phydev_err(phydev, "ptp_clock_register failed %pe\n",
+ shared->ptp_clock);
return -EINVAL;
}
@@ -3937,50 +4322,60 @@ static int lan8814_ptp_probe_once(struct phy_device *phydev)
/* The EP.4 is shared between all the PHYs in the package and also it
* can be accessed by any of the PHYs
*/
- lanphy_write_page_reg(phydev, 4, LTC_HARD_RESET, LTC_HARD_RESET_);
- lanphy_write_page_reg(phydev, 4, PTP_OPERATING_MODE,
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LTC_HARD_RESET, LTC_HARD_RESET_);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_OPERATING_MODE,
PTP_OPERATING_MODE_STANDALONE_);
/* Enable ptp to run LTC clock for ptp and gpio 1PPS operation */
- lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_ENABLE_);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, PTP_CMD_CTL,
+ PTP_CMD_CTL_PTP_ENABLE_);
return 0;
}
+static int lan8814_ptp_probe_once(struct phy_device *phydev)
+{
+ return __lan8814_ptp_probe_once(phydev, "lan8814_ptp_pin",
+ LAN8814_PTP_GPIO_NUM);
+}
+
static void lan8814_setup_led(struct phy_device *phydev, int val)
{
int temp;
- temp = lanphy_read_page_reg(phydev, 5, LAN8814_LED_CTRL_1);
+ temp = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ LAN8814_LED_CTRL_1);
if (val)
temp |= LAN8814_LED_CTRL_1_KSZ9031_LED_MODE_;
else
temp &= ~LAN8814_LED_CTRL_1_KSZ9031_LED_MODE_;
- lanphy_write_page_reg(phydev, 5, LAN8814_LED_CTRL_1, temp);
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ LAN8814_LED_CTRL_1, temp);
}
static int lan8814_config_init(struct phy_device *phydev)
{
struct kszphy_priv *lan8814 = phydev->priv;
- int val;
/* Reset the PHY */
- val = lanphy_read_page_reg(phydev, 4, LAN8814_QSGMII_SOFT_RESET);
- val |= LAN8814_QSGMII_SOFT_RESET_BIT;
- lanphy_write_page_reg(phydev, 4, LAN8814_QSGMII_SOFT_RESET, val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_QSGMII_SOFT_RESET,
+ LAN8814_QSGMII_SOFT_RESET_BIT,
+ LAN8814_QSGMII_SOFT_RESET_BIT);
/* Disable ANEG with QSGMII PCS Host side */
- val = lanphy_read_page_reg(phydev, 5, LAN8814_QSGMII_PCS1G_ANEG_CONFIG);
- val &= ~LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA;
- lanphy_write_page_reg(phydev, 5, LAN8814_QSGMII_PCS1G_ANEG_CONFIG, val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ LAN8814_QSGMII_PCS1G_ANEG_CONFIG,
+ LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA,
+ 0);
/* MDI-X setting for swap A,B transmit */
- val = lanphy_read_page_reg(phydev, 2, LAN8814_ALIGN_SWAP);
- val &= ~LAN8814_ALIGN_TX_A_B_SWAP_MASK;
- val |= LAN8814_ALIGN_TX_A_B_SWAP;
- lanphy_write_page_reg(phydev, 2, LAN8814_ALIGN_SWAP, val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_PCS_DIGITAL, LAN8814_ALIGN_SWAP,
+ LAN8814_ALIGN_TX_A_B_SWAP_MASK,
+ LAN8814_ALIGN_TX_A_B_SWAP);
if (lan8814->led_mode >= 0)
lan8814_setup_led(phydev, lan8814->led_mode);
@@ -4011,29 +4406,24 @@ static int lan8814_release_coma_mode(struct phy_device *phydev)
static void lan8814_clear_2psp_bit(struct phy_device *phydev)
{
- u16 val;
-
/* It was noticed that when traffic is passing through the PHY and the
* cable is removed then the LED was still one even though there is no
* link
*/
- val = lanphy_read_page_reg(phydev, 2, LAN8814_EEE_STATE);
- val &= ~LAN8814_EEE_STATE_MASK2P5P;
- lanphy_write_page_reg(phydev, 2, LAN8814_EEE_STATE, val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_PCS_DIGITAL, LAN8814_EEE_STATE,
+ LAN8814_EEE_STATE_MASK2P5P,
+ 0);
}
static void lan8814_update_meas_time(struct phy_device *phydev)
{
- u16 val;
-
/* By setting the measure time to a value of 0xb this will allow cables
* longer than 100m to be used. This configuration can be used
* regardless of the mode of operation of the PHY
*/
- val = lanphy_read_page_reg(phydev, 1, LAN8814_PD_CONTROLS);
- val &= ~LAN8814_PD_CONTROLS_PD_MEAS_TIME_MASK;
- val |= LAN8814_PD_CONTROLS_PD_MEAS_TIME_VAL;
- lanphy_write_page_reg(phydev, 1, LAN8814_PD_CONTROLS, val);
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_AFE_PMA, LAN8814_PD_CONTROLS,
+ LAN8814_PD_CONTROLS_PD_MEAS_TIME_MASK,
+ LAN8814_PD_CONTROLS_PD_MEAS_TIME_VAL);
}
static int lan8814_probe(struct phy_device *phydev)
@@ -4056,7 +4446,7 @@ static int lan8814_probe(struct phy_device *phydev)
/* Strap-in value for PHY address, below register read gives starting
* phy address value
*/
- addr = lanphy_read_page_reg(phydev, 4, 0) & 0x1F;
+ addr = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, 0) & 0x1F;
devm_phy_package_join(&phydev->mdio.dev, phydev,
addr, sizeof(struct lan8814_shared_priv));
@@ -5350,8 +5740,8 @@ static int lan8841_probe(struct phy_device *phydev)
ptp_priv->ptp_clock = ptp_clock_register(&ptp_priv->ptp_clock_info,
&phydev->mdio.dev);
if (IS_ERR(ptp_priv->ptp_clock)) {
- phydev_err(phydev, "ptp_clock_register failed: %lu\n",
- PTR_ERR(ptp_priv->ptp_clock));
+ phydev_err(phydev, "ptp_clock_register failed: %pe\n",
+ ptp_priv->ptp_clock);
return -EINVAL;
}
@@ -5403,10 +5793,375 @@ static int lan8841_suspend(struct phy_device *phydev)
return kszphy_generic_suspend(phydev);
}
+static int ksz9131_resume(struct phy_device *phydev)
+{
+ if (phydev->suspended && phy_interface_is_rgmii(phydev))
+ ksz9131_config_rgmii_delay(phydev);
+
+ return kszphy_resume(phydev);
+}
+
+#define LAN8842_PTP_GPIO_NUM 16
+
+static int lan8842_ptp_probe_once(struct phy_device *phydev)
+{
+ return __lan8814_ptp_probe_once(phydev, "lan8842_ptp_pin",
+ LAN8842_PTP_GPIO_NUM);
+}
+
+#define LAN8842_STRAP_REG 0 /* 0x0 */
+#define LAN8842_STRAP_REG_PHYADDR_MASK GENMASK(4, 0)
+#define LAN8842_SKU_REG 11 /* 0x0b */
+#define LAN8842_SELF_TEST 14 /* 0x0e */
+#define LAN8842_SELF_TEST_RX_CNT_ENA BIT(8)
+#define LAN8842_SELF_TEST_TX_CNT_ENA BIT(4)
+
+static int lan8842_probe(struct phy_device *phydev)
+{
+ struct lan8842_priv *priv;
+ int addr;
+ int ret;
+
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ phydev->priv = priv;
+
+ /* Similar to lan8814 this PHY has a pin which needs to be pulled down
+ * to enable to pass any traffic through it. Therefore use the same
+ * function as lan8814
+ */
+ ret = lan8814_release_coma_mode(phydev);
+ if (ret)
+ return ret;
+
+ /* Enable to count the RX and TX packets */
+ ret = lanphy_write_page_reg(phydev, LAN8814_PAGE_PCS_DIGITAL,
+ LAN8842_SELF_TEST,
+ LAN8842_SELF_TEST_RX_CNT_ENA |
+ LAN8842_SELF_TEST_TX_CNT_ENA);
+ if (ret < 0)
+ return ret;
+
+ /* Revision lan8832 doesn't have support for PTP, therefore don't add
+ * any PTP clocks
+ */
+ ret = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8842_SKU_REG);
+ if (ret < 0)
+ return ret;
+
+ priv->rev = ret;
+ if (priv->rev == LAN8842_REV_8832)
+ return 0;
+
+ /* As the lan8814 and lan8842 has the same IP for the PTP block, the
+ * only difference is the number of the GPIOs, then make sure that the
+ * lan8842 initialized also the shared data pointer as this is used in
+ * all the PTP functions for lan8814. The lan8842 doesn't have multiple
+ * PHYs in the same package.
+ */
+ addr = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8842_STRAP_REG);
+ if (addr < 0)
+ return addr;
+ addr &= LAN8842_STRAP_REG_PHYADDR_MASK;
+
+ ret = devm_phy_package_join(&phydev->mdio.dev, phydev, addr,
+ sizeof(struct lan8814_shared_priv));
+ if (ret)
+ return ret;
+
+ if (phy_package_init_once(phydev)) {
+ ret = lan8842_ptp_probe_once(phydev);
+ if (ret)
+ return ret;
+ }
+
+ lan8814_ptp_init(phydev);
+
+ return 0;
+}
+
+static int lan8842_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ /* Reset the PHY */
+ ret = lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_QSGMII_SOFT_RESET,
+ LAN8814_QSGMII_SOFT_RESET_BIT,
+ LAN8814_QSGMII_SOFT_RESET_BIT);
+ if (ret < 0)
+ return ret;
+
+ /* Even if the GPIOs are set to control the LEDs the behaviour of the
+ * LEDs is wrong, they are not blinking when there is traffic.
+ * To fix this it is required to set extended LED mode
+ */
+ ret = lanphy_modify_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ LAN8814_LED_CTRL_1,
+ LAN8814_LED_CTRL_1_KSZ9031_LED_MODE_, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = lanphy_modify_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ LAN8814_LED_CTRL_2,
+ LAN8814_LED_CTRL_2_LED1_COM_DIS,
+ LAN8814_LED_CTRL_2_LED1_COM_DIS);
+ if (ret < 0)
+ return ret;
+
+ /* To allow the PHY to control the LEDs the GPIOs of the PHY should have
+ * a function mode and not the GPIO. Apparently by default the value is
+ * GPIO and not function even though the datasheet it says that it is
+ * function. Therefore set this value.
+ */
+ return lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_GPIO_EN2, 0);
+}
+
+#define LAN8842_INTR_CTRL_REG 52 /* 0x34 */
+
+static int lan8842_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ lanphy_write_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8842_INTR_CTRL_REG,
+ LAN8814_INTR_CTRL_REG_INTR_ENABLE);
+
+ /* enable / disable interrupts */
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ err = lan8814_ack_interrupt(phydev);
+ if (err)
+ return err;
+
+ err = phy_write(phydev, LAN8814_INTC,
+ LAN8814_INT_LINK | LAN8814_INT_FLF);
+ } else {
+ err = phy_write(phydev, LAN8814_INTC, 0);
+ if (err)
+ return err;
+
+ err = lan8814_ack_interrupt(phydev);
+ }
+
+ return err;
+}
+
+static unsigned int lan8842_inband_caps(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ /* Inband configuration can be enabled or disabled using the registers
+ * PCS1G_ANEG_CONFIG.
+ */
+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+}
+
+static int lan8842_config_inband(struct phy_device *phydev, unsigned int modes)
+{
+ bool enable;
+
+ if (modes == LINK_INBAND_DISABLE)
+ enable = false;
+ else
+ enable = true;
+
+ /* Disable or enable in-band autoneg with PCS Host side
+ * It has the same address as lan8814
+ */
+ return lanphy_modify_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ LAN8814_QSGMII_PCS1G_ANEG_CONFIG,
+ LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA,
+ enable ? LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA : 0);
+}
+
+static void lan8842_handle_ptp_interrupt(struct phy_device *phydev, u16 status)
+{
+ struct kszphy_ptp_priv *ptp_priv;
+ struct lan8842_priv *priv;
+
+ priv = phydev->priv;
+ ptp_priv = &priv->ptp_priv;
+
+ if (status & PTP_TSU_INT_STS_PTP_TX_TS_EN_)
+ lan8814_get_tx_ts(ptp_priv);
+
+ if (status & PTP_TSU_INT_STS_PTP_RX_TS_EN_)
+ lan8814_get_rx_ts(ptp_priv);
+
+ if (status & PTP_TSU_INT_STS_PTP_TX_TS_OVRFL_INT_) {
+ lan8814_flush_fifo(phydev, true);
+ skb_queue_purge(&ptp_priv->tx_queue);
+ }
+
+ if (status & PTP_TSU_INT_STS_PTP_RX_TS_OVRFL_INT_) {
+ lan8814_flush_fifo(phydev, false);
+ skb_queue_purge(&ptp_priv->rx_queue);
+ }
+}
+
+static irqreturn_t lan8842_handle_interrupt(struct phy_device *phydev)
+{
+ struct lan8842_priv *priv = phydev->priv;
+ int ret = IRQ_NONE;
+ int irq_status;
+
+ irq_status = phy_read(phydev, LAN8814_INTS);
+ if (irq_status < 0) {
+ phy_error(phydev);
+ return IRQ_NONE;
+ }
+
+ if (irq_status & (LAN8814_INT_LINK | LAN8814_INT_FLF)) {
+ phy_trigger_machine(phydev);
+ ret = IRQ_HANDLED;
+ }
+
+ /* Phy revision lan8832 doesn't have support for PTP therefore there is
+ * not need to check the PTP and GPIO interrupts
+ */
+ if (priv->rev == LAN8842_REV_8832)
+ goto out;
+
+ while (true) {
+ irq_status = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
+ PTP_TSU_INT_STS);
+ if (!irq_status)
+ break;
+
+ lan8842_handle_ptp_interrupt(phydev, irq_status);
+ ret = IRQ_HANDLED;
+ }
+
+ if (!lan8814_handle_gpio_interrupt(phydev, irq_status))
+ ret = IRQ_HANDLED;
+
+out:
+ return ret;
+}
+
+static u64 lan8842_get_stat(struct phy_device *phydev, int count, int *regs)
+{
+ u64 ret = 0;
+ int val;
+
+ for (int j = 0; j < count; ++j) {
+ val = lanphy_read_page_reg(phydev, LAN8814_PAGE_PCS_DIGITAL,
+ regs[j]);
+ if (val < 0)
+ return U64_MAX;
+
+ ret <<= 16;
+ ret += val;
+ }
+ return ret;
+}
+
+static int lan8842_update_stats(struct phy_device *phydev)
+{
+ struct lan8842_priv *priv = phydev->priv;
+ int rx_packets_regs[] = {88, 61, 60};
+ int rx_errors_regs[] = {63, 62};
+ int tx_packets_regs[] = {89, 85, 84};
+ int tx_errors_regs[] = {87, 86};
+
+ priv->phy_stats.rx_packets = lan8842_get_stat(phydev,
+ ARRAY_SIZE(rx_packets_regs),
+ rx_packets_regs);
+ priv->phy_stats.rx_errors = lan8842_get_stat(phydev,
+ ARRAY_SIZE(rx_errors_regs),
+ rx_errors_regs);
+ priv->phy_stats.tx_packets = lan8842_get_stat(phydev,
+ ARRAY_SIZE(tx_packets_regs),
+ tx_packets_regs);
+ priv->phy_stats.tx_errors = lan8842_get_stat(phydev,
+ ARRAY_SIZE(tx_errors_regs),
+ tx_errors_regs);
+
+ return 0;
+}
+
+#define LAN8842_FLF 15 /* 0x0e */
+#define LAN8842_FLF_ENA BIT(1)
+#define LAN8842_FLF_ENA_LINK_DOWN BIT(0)
+
+static int lan8842_get_fast_down(struct phy_device *phydev, u8 *msecs)
+{
+ int ret;
+
+ ret = lanphy_read_page_reg(phydev, LAN8814_PAGE_PCS, LAN8842_FLF);
+ if (ret < 0)
+ return ret;
+
+ if (ret & LAN8842_FLF_ENA)
+ *msecs = ETHTOOL_PHY_FAST_LINK_DOWN_ON;
+ else
+ *msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF;
+
+ return 0;
+}
+
+static int lan8842_set_fast_down(struct phy_device *phydev, const u8 *msecs)
+{
+ u16 flf;
+
+ switch (*msecs) {
+ case ETHTOOL_PHY_FAST_LINK_DOWN_OFF:
+ flf = 0;
+ break;
+ case ETHTOOL_PHY_FAST_LINK_DOWN_ON:
+ flf = LAN8842_FLF_ENA | LAN8842_FLF_ENA_LINK_DOWN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return lanphy_modify_page_reg(phydev, LAN8814_PAGE_PCS,
+ LAN8842_FLF,
+ LAN8842_FLF_ENA |
+ LAN8842_FLF_ENA_LINK_DOWN, flf);
+}
+
+static int lan8842_get_tunable(struct phy_device *phydev,
+ struct ethtool_tunable *tuna, void *data)
+{
+ switch (tuna->id) {
+ case ETHTOOL_PHY_FAST_LINK_DOWN:
+ return lan8842_get_fast_down(phydev, data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lan8842_set_tunable(struct phy_device *phydev,
+ struct ethtool_tunable *tuna, const void *data)
+{
+ switch (tuna->id) {
+ case ETHTOOL_PHY_FAST_LINK_DOWN:
+ return lan8842_set_fast_down(phydev, data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void lan8842_get_phy_stats(struct phy_device *phydev,
+ struct ethtool_eth_phy_stats *eth_stats,
+ struct ethtool_phy_stats *stats)
+{
+ struct lan8842_priv *priv = phydev->priv;
+
+ stats->rx_packets = priv->phy_stats.rx_packets;
+ stats->rx_errors = priv->phy_stats.rx_errors;
+ stats->tx_packets = priv->phy_stats.tx_packets;
+ stats->tx_errors = priv->phy_stats.tx_errors;
+}
+
static struct phy_driver ksphy_driver[] = {
{
- .phy_id = PHY_ID_KS8737,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_KS8737),
.name = "Micrel KS8737",
/* PHY_BASIC_FEATURES */
.driver_data = &ks8737_type,
@@ -5447,8 +6202,7 @@ static struct phy_driver ksphy_driver[] = {
.suspend = kszphy_suspend,
.resume = kszphy_resume,
}, {
- .phy_id = PHY_ID_KSZ8041,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_KSZ8041),
.name = "Micrel KSZ8041",
/* PHY_BASIC_FEATURES */
.driver_data = &ksz8041_type,
@@ -5463,8 +6217,7 @@ static struct phy_driver ksphy_driver[] = {
.suspend = ksz8041_suspend,
.resume = ksz8041_resume,
}, {
- .phy_id = PHY_ID_KSZ8041RNLI,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_KSZ8041RNLI),
.name = "Micrel KSZ8041RNLI",
/* PHY_BASIC_FEATURES */
.driver_data = &ksz8041_type,
@@ -5507,9 +6260,8 @@ static struct phy_driver ksphy_driver[] = {
.suspend = kszphy_suspend,
.resume = kszphy_resume,
}, {
- .phy_id = PHY_ID_KSZ8081,
+ PHY_ID_MATCH_MODEL(PHY_ID_KSZ8081),
.name = "Micrel KSZ8081 or KSZ8091",
- .phy_id_mask = MICREL_PHY_ID_MASK,
.flags = PHY_POLL_CABLE_TEST,
/* PHY_BASIC_FEATURES */
.driver_data = &ksz8081_type,
@@ -5528,9 +6280,8 @@ static struct phy_driver ksphy_driver[] = {
.cable_test_start = ksz886x_cable_test_start,
.cable_test_get_status = ksz886x_cable_test_get_status,
}, {
- .phy_id = PHY_ID_KSZ8061,
+ PHY_ID_MATCH_MODEL(PHY_ID_KSZ8061),
.name = "Micrel KSZ8061",
- .phy_id_mask = MICREL_PHY_ID_MASK,
/* PHY_BASIC_FEATURES */
.probe = kszphy_probe,
.config_init = ksz8061_config_init,
@@ -5558,8 +6309,7 @@ static struct phy_driver ksphy_driver[] = {
.read_mmd = genphy_read_mmd_unsupported,
.write_mmd = genphy_write_mmd_unsupported,
}, {
- .phy_id = PHY_ID_KSZ9031,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_KSZ9031),
.name = "Micrel KSZ9031 Gigabit PHY",
.flags = PHY_POLL_CABLE_TEST,
.driver_data = &ksz9021_type,
@@ -5579,8 +6329,7 @@ static struct phy_driver ksphy_driver[] = {
.cable_test_get_status = ksz9x31_cable_test_get_status,
.set_loopback = ksz9031_set_loopback,
}, {
- .phy_id = PHY_ID_LAN8814,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_LAN8814),
.name = "Microchip INDY Gigabit Quad PHY",
.flags = PHY_POLL_CABLE_TEST,
.config_init = lan8814_config_init,
@@ -5598,8 +6347,7 @@ static struct phy_driver ksphy_driver[] = {
.cable_test_start = lan8814_cable_test_start,
.cable_test_get_status = ksz886x_cable_test_get_status,
}, {
- .phy_id = PHY_ID_LAN8804,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_LAN8804),
.name = "Microchip LAN966X Gigabit PHY",
.config_init = lan8804_config_init,
.driver_data = &ksz9021_type,
@@ -5614,8 +6362,7 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = lan8804_config_intr,
.handle_interrupt = lan8804_handle_interrupt,
}, {
- .phy_id = PHY_ID_LAN8841,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_LAN8841),
.name = "Microchip LAN8841 Gigabit PHY",
.flags = PHY_POLL_CABLE_TEST,
.driver_data = &lan8841_type,
@@ -5632,8 +6379,24 @@ static struct phy_driver ksphy_driver[] = {
.cable_test_start = lan8814_cable_test_start,
.cable_test_get_status = ksz886x_cable_test_get_status,
}, {
- .phy_id = PHY_ID_KSZ9131,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_LAN8842),
+ .name = "Microchip LAN8842 Gigabit PHY",
+ .flags = PHY_POLL_CABLE_TEST,
+ .driver_data = &lan8814_type,
+ .probe = lan8842_probe,
+ .config_init = lan8842_config_init,
+ .config_intr = lan8842_config_intr,
+ .inband_caps = lan8842_inband_caps,
+ .config_inband = lan8842_config_inband,
+ .handle_interrupt = lan8842_handle_interrupt,
+ .get_phy_stats = lan8842_get_phy_stats,
+ .update_stats = lan8842_update_stats,
+ .get_tunable = lan8842_get_tunable,
+ .set_tunable = lan8842_set_tunable,
+ .cable_test_start = lan8814_cable_test_start,
+ .cable_test_get_status = ksz886x_cable_test_get_status,
+}, {
+ PHY_ID_MATCH_MODEL(PHY_ID_KSZ9131),
.name = "Microchip KSZ9131 Gigabit PHY",
/* PHY_GBIT_FEATURES */
.flags = PHY_POLL_CABLE_TEST,
@@ -5649,13 +6412,12 @@ static struct phy_driver ksphy_driver[] = {
.get_strings = kszphy_get_strings,
.get_stats = kszphy_get_stats,
.suspend = kszphy_suspend,
- .resume = kszphy_resume,
+ .resume = ksz9131_resume,
.cable_test_start = ksz9x31_cable_test_start,
.cable_test_get_status = ksz9x31_cable_test_get_status,
.get_features = ksz9477_get_features,
}, {
- .phy_id = PHY_ID_KSZ8873MLL,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_KSZ8873MLL),
.name = "Micrel KSZ8873MLL Switch",
/* PHY_BASIC_FEATURES */
.config_init = kszphy_config_init,
@@ -5664,8 +6426,7 @@ static struct phy_driver ksphy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
- .phy_id = PHY_ID_KSZ886X,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_KSZ886X),
.name = "Micrel KSZ8851 Ethernet MAC or KSZ886X Switch",
.driver_data = &ksz886x_type,
/* PHY_BASIC_FEATURES */
@@ -5685,15 +6446,23 @@ static struct phy_driver ksphy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
- .phy_id = PHY_ID_KSZ9477,
- .phy_id_mask = MICREL_PHY_ID_MASK,
+ PHY_ID_MATCH_MODEL(PHY_ID_KSZ9477),
.name = "Microchip KSZ9477",
+ .probe = kszphy_probe,
/* PHY_GBIT_FEATURES */
.config_init = ksz9477_config_init,
.config_intr = kszphy_config_intr,
+ .config_aneg = ksz9477_config_aneg,
+ .read_status = ksz9477_read_status,
.handle_interrupt = kszphy_handle_interrupt,
.suspend = genphy_suspend,
.resume = ksz9477_resume,
+ .get_phy_stats = kszphy_get_phy_stats,
+ .update_stats = kszphy_update_stats,
+ .cable_test_start = ksz9x31_cable_test_start,
+ .cable_test_get_status = ksz9x31_cable_test_get_status,
+ .get_sqi = kszphy_get_sqi,
+ .get_sqi_max = kszphy_get_sqi_max,
} };
module_phy_driver(ksphy_driver);
@@ -5704,22 +6473,24 @@ MODULE_LICENSE("GPL");
static const struct mdio_device_id __maybe_unused micrel_tbl[] = {
{ PHY_ID_KSZ9021, 0x000ffffe },
- { PHY_ID_KSZ9031, MICREL_PHY_ID_MASK },
- { PHY_ID_KSZ9131, MICREL_PHY_ID_MASK },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KSZ9031) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KSZ9131) },
{ PHY_ID_KSZ8001, 0x00fffffc },
- { PHY_ID_KS8737, MICREL_PHY_ID_MASK },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KS8737) },
{ PHY_ID_KSZ8021, 0x00ffffff },
{ PHY_ID_KSZ8031, 0x00ffffff },
- { PHY_ID_KSZ8041, MICREL_PHY_ID_MASK },
- { PHY_ID_KSZ8051, MICREL_PHY_ID_MASK },
- { PHY_ID_KSZ8061, MICREL_PHY_ID_MASK },
- { PHY_ID_KSZ8081, MICREL_PHY_ID_MASK },
- { PHY_ID_KSZ8873MLL, MICREL_PHY_ID_MASK },
- { PHY_ID_KSZ886X, MICREL_PHY_ID_MASK },
- { PHY_ID_KSZ9477, MICREL_PHY_ID_MASK },
- { PHY_ID_LAN8814, MICREL_PHY_ID_MASK },
- { PHY_ID_LAN8804, MICREL_PHY_ID_MASK },
- { PHY_ID_LAN8841, MICREL_PHY_ID_MASK },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KSZ8041) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KSZ8041RNLI) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KSZ8051) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KSZ8061) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KSZ8081) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KSZ8873MLL) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KSZ886X) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_KSZ9477) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_LAN8814) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_LAN8804) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_LAN8841) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_LAN8842) },
{ }
};
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
index 0e91f5d1a4fd..a3593e663059 100644
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -213,6 +213,20 @@
#define YT8521_RC1R_RGMII_2_100_NS 14
#define YT8521_RC1R_RGMII_2_250_NS 15
+/* LED CONFIG */
+#define YT8521_MAX_LEDS 3
+#define YT8521_LED0_CFG_REG 0xA00C
+#define YT8521_LED1_CFG_REG 0xA00D
+#define YT8521_LED2_CFG_REG 0xA00E
+#define YT8521_LED_ACT_BLK_IND BIT(13)
+#define YT8521_LED_FDX_ON_EN BIT(12)
+#define YT8521_LED_HDX_ON_EN BIT(11)
+#define YT8521_LED_TXACT_BLK_EN BIT(10)
+#define YT8521_LED_RXACT_BLK_EN BIT(9)
+#define YT8521_LED_1000_ON_EN BIT(6)
+#define YT8521_LED_100_ON_EN BIT(5)
+#define YT8521_LED_10_ON_EN BIT(4)
+
#define YTPHY_MISC_CONFIG_REG 0xA006
#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0)
#define YTPHY_MCR_FIBER_1000BX (0x1 << 0)
@@ -1681,6 +1695,106 @@ err_restore_page:
return phy_restore_page(phydev, old_page, ret);
}
+static const unsigned long supported_trgs = (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 yt8521_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ if (index >= YT8521_MAX_LEDS)
+ return -EINVAL;
+
+ /* All combinations of the supported triggers are allowed */
+ if (rules & ~supported_trgs)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int yt8521_led_hw_control_set(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ u16 val = 0;
+
+ if (index >= YT8521_MAX_LEDS)
+ return -EINVAL;
+
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules)) {
+ val |= YT8521_LED_10_ON_EN;
+ val |= YT8521_LED_100_ON_EN;
+ val |= YT8521_LED_1000_ON_EN;
+ }
+
+ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
+ val |= YT8521_LED_10_ON_EN;
+
+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
+ val |= YT8521_LED_100_ON_EN;
+
+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
+ val |= YT8521_LED_1000_ON_EN;
+
+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
+ val |= YT8521_LED_HDX_ON_EN;
+
+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
+ val |= YT8521_LED_FDX_ON_EN;
+
+ if (test_bit(TRIGGER_NETDEV_TX, &rules) ||
+ test_bit(TRIGGER_NETDEV_RX, &rules))
+ val |= YT8521_LED_ACT_BLK_IND;
+
+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
+ val |= YT8521_LED_TXACT_BLK_EN;
+
+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
+ val |= YT8521_LED_RXACT_BLK_EN;
+
+ return ytphy_write_ext(phydev, YT8521_LED0_CFG_REG + index, val);
+}
+
+static int yt8521_led_hw_control_get(struct phy_device *phydev, u8 index,
+ unsigned long *rules)
+{
+ int val;
+
+ if (index >= YT8521_MAX_LEDS)
+ return -EINVAL;
+
+ val = ytphy_read_ext(phydev, YT8521_LED0_CFG_REG + index);
+ if (val < 0)
+ return val;
+
+ if (val & YT8521_LED_TXACT_BLK_EN || val & YT8521_LED_ACT_BLK_IND)
+ __set_bit(TRIGGER_NETDEV_TX, rules);
+
+ if (val & YT8521_LED_RXACT_BLK_EN || val & YT8521_LED_ACT_BLK_IND)
+ __set_bit(TRIGGER_NETDEV_RX, rules);
+
+ if (val & YT8521_LED_FDX_ON_EN)
+ __set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
+
+ if (val & YT8521_LED_HDX_ON_EN)
+ __set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
+
+ if (val & YT8521_LED_1000_ON_EN)
+ __set_bit(TRIGGER_NETDEV_LINK_1000, rules);
+
+ if (val & YT8521_LED_100_ON_EN)
+ __set_bit(TRIGGER_NETDEV_LINK_100, rules);
+
+ if (val & YT8521_LED_10_ON_EN)
+ __set_bit(TRIGGER_NETDEV_LINK_10, rules);
+
+ return 0;
+}
+
static int yt8531_config_init(struct phy_device *phydev)
{
struct device_node *node = phydev->mdio.dev.of_node;
@@ -2920,6 +3034,9 @@ static struct phy_driver motorcomm_phy_drvs[] = {
.soft_reset = yt8521_soft_reset,
.suspend = yt8521_suspend,
.resume = yt8521_resume,
+ .led_hw_is_supported = yt8521_led_hw_is_supported,
+ .led_hw_control_set = yt8521_led_hw_control_set,
+ .led_hw_control_get = yt8521_led_hw_control_get,
},
{
PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
diff --git a/drivers/net/phy/mscc/mscc.h b/drivers/net/phy/mscc/mscc.h
index 6a3d8a754eb8..2d8eca54c40a 100644
--- a/drivers/net/phy/mscc/mscc.h
+++ b/drivers/net/phy/mscc/mscc.h
@@ -196,6 +196,9 @@ enum rgmii_clock_delay {
#define MSCC_PHY_EXTENDED_INT_MS_EGR BIT(9)
/* Extended Page 3 Registers */
+#define MSCC_PHY_SERDES_PCS_CTRL 16
+#define MSCC_PHY_SERDES_ANEG BIT(7)
+
#define MSCC_PHY_SERDES_TX_VALID_CNT 21
#define MSCC_PHY_SERDES_TX_CRC_ERR_CNT 22
#define MSCC_PHY_SERDES_RX_VALID_CNT 28
@@ -362,6 +365,13 @@ struct vsc85xx_hw_stat {
u16 mask;
};
+struct vsc8531_skb_cb {
+ u32 ns;
+};
+
+#define VSC8531_SKB_CB(skb) \
+ ((struct vsc8531_skb_cb *)((skb)->cb))
+
struct vsc8531_private {
int rate_magic;
u16 supp_led_modes;
@@ -410,6 +420,11 @@ struct vsc8531_private {
*/
struct mutex ts_lock;
struct mutex phc_lock;
+
+ /* list of skbs that were received and need timestamp information but it
+ * didn't received it yet
+ */
+ struct sk_buff_head rx_skbs_list;
};
/* Shared structure between the PHYs of the same package.
@@ -469,6 +484,7 @@ static inline void vsc8584_config_macsec_intr(struct phy_device *phydev)
void vsc85xx_link_change_notify(struct phy_device *phydev);
void vsc8584_config_ts_intr(struct phy_device *phydev);
int vsc8584_ptp_init(struct phy_device *phydev);
+void vsc8584_ptp_deinit(struct phy_device *phydev);
int vsc8584_ptp_probe_once(struct phy_device *phydev);
int vsc8584_ptp_probe(struct phy_device *phydev);
irqreturn_t vsc8584_handle_ts_interrupt(struct phy_device *phydev);
@@ -483,6 +499,9 @@ static inline int vsc8584_ptp_init(struct phy_device *phydev)
{
return 0;
}
+static inline void vsc8584_ptp_deinit(struct phy_device *phydev)
+{
+}
static inline int vsc8584_ptp_probe_once(struct phy_device *phydev)
{
return 0;
diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c
index 7ff975efd8e7..ef0ef1570d39 100644
--- a/drivers/net/phy/mscc/mscc_main.c
+++ b/drivers/net/phy/mscc/mscc_main.c
@@ -530,7 +530,6 @@ static int vsc85xx_update_rgmii_cntl(struct phy_device *phydev, u32 rgmii_cntl,
u16 rgmii_rx_delay_pos = ffs(rgmii_rx_delay_mask) - 1;
u16 rgmii_tx_delay_pos = ffs(rgmii_tx_delay_mask) - 1;
int delay_size = ARRAY_SIZE(vsc85xx_internal_delay);
- struct device *dev = &phydev->mdio.dev;
u16 reg_val = 0;
u16 mask = 0;
s32 rx_delay;
@@ -549,7 +548,7 @@ static int vsc85xx_update_rgmii_cntl(struct phy_device *phydev, u32 rgmii_cntl,
if (phy_interface_is_rgmii(phydev))
mask |= rgmii_rx_delay_mask | rgmii_tx_delay_mask;
- rx_delay = phy_get_internal_delay(phydev, dev, vsc85xx_internal_delay,
+ rx_delay = phy_get_internal_delay(phydev, vsc85xx_internal_delay,
delay_size, true);
if (rx_delay < 0) {
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID ||
@@ -559,7 +558,7 @@ static int vsc85xx_update_rgmii_cntl(struct phy_device *phydev, u32 rgmii_cntl,
rx_delay = RGMII_CLK_DELAY_0_2_NS;
}
- tx_delay = phy_get_internal_delay(phydev, dev, vsc85xx_internal_delay,
+ tx_delay = phy_get_internal_delay(phydev, vsc85xx_internal_delay,
delay_size, false);
if (tx_delay < 0) {
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID ||
@@ -2203,6 +2202,28 @@ static int vsc85xx_read_status(struct phy_device *phydev)
return genphy_read_status(phydev);
}
+static unsigned int vsc85xx_inband_caps(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ if (interface != PHY_INTERFACE_MODE_SGMII &&
+ interface != PHY_INTERFACE_MODE_QSGMII)
+ return 0;
+
+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+}
+
+static int vsc85xx_config_inband(struct phy_device *phydev, unsigned int modes)
+{
+ u16 reg_val = 0;
+
+ if (modes == LINK_INBAND_ENABLE)
+ reg_val = MSCC_PHY_SERDES_ANEG;
+
+ return phy_modify_paged(phydev, MSCC_PHY_PAGE_EXTENDED_3,
+ MSCC_PHY_SERDES_PCS_CTRL, MSCC_PHY_SERDES_ANEG,
+ reg_val);
+}
+
static int vsc8514_probe(struct phy_device *phydev)
{
struct vsc8531_private *vsc8531;
@@ -2336,6 +2357,11 @@ static int vsc85xx_probe(struct phy_device *phydev)
return vsc85xx_dt_led_modes_get(phydev, default_mode);
}
+static void vsc85xx_remove(struct phy_device *phydev)
+{
+ vsc8584_ptp_deinit(phydev);
+}
+
/* Microsemi VSC85xx PHYs */
static struct phy_driver vsc85xx_driver[] = {
{
@@ -2410,6 +2436,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .inband_caps = vsc85xx_inband_caps,
+ .config_inband = vsc85xx_config_inband,
},
{
.phy_id = PHY_ID_VSC8514,
@@ -2433,6 +2461,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .inband_caps = vsc85xx_inband_caps,
+ .config_inband = vsc85xx_config_inband,
},
{
.phy_id = PHY_ID_VSC8530,
@@ -2553,6 +2583,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .inband_caps = vsc85xx_inband_caps,
+ .config_inband = vsc85xx_config_inband,
},
{
.phy_id = PHY_ID_VSC856X,
@@ -2575,6 +2607,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .inband_caps = vsc85xx_inband_caps,
+ .config_inband = vsc85xx_config_inband,
},
{
.phy_id = PHY_ID_VSC8572,
@@ -2590,6 +2624,7 @@ static struct phy_driver vsc85xx_driver[] = {
.config_intr = &vsc85xx_config_intr,
.suspend = &genphy_suspend,
.resume = &genphy_resume,
+ .remove = &vsc85xx_remove,
.probe = &vsc8574_probe,
.set_wol = &vsc85xx_wol_set,
.get_wol = &vsc85xx_wol_get,
@@ -2600,6 +2635,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .inband_caps = vsc85xx_inband_caps,
+ .config_inband = vsc85xx_config_inband,
},
{
.phy_id = PHY_ID_VSC8574,
@@ -2615,6 +2652,7 @@ static struct phy_driver vsc85xx_driver[] = {
.config_intr = &vsc85xx_config_intr,
.suspend = &genphy_suspend,
.resume = &genphy_resume,
+ .remove = &vsc85xx_remove,
.probe = &vsc8574_probe,
.set_wol = &vsc85xx_wol_set,
.get_wol = &vsc85xx_wol_get,
@@ -2625,6 +2663,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .inband_caps = vsc85xx_inband_caps,
+ .config_inband = vsc85xx_config_inband,
},
{
.phy_id = PHY_ID_VSC8575,
@@ -2640,6 +2680,7 @@ static struct phy_driver vsc85xx_driver[] = {
.config_intr = &vsc85xx_config_intr,
.suspend = &genphy_suspend,
.resume = &genphy_resume,
+ .remove = &vsc85xx_remove,
.probe = &vsc8584_probe,
.get_tunable = &vsc85xx_get_tunable,
.set_tunable = &vsc85xx_set_tunable,
@@ -2648,6 +2689,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .inband_caps = vsc85xx_inband_caps,
+ .config_inband = vsc85xx_config_inband,
},
{
.phy_id = PHY_ID_VSC8582,
@@ -2663,6 +2706,7 @@ static struct phy_driver vsc85xx_driver[] = {
.config_intr = &vsc85xx_config_intr,
.suspend = &genphy_suspend,
.resume = &genphy_resume,
+ .remove = &vsc85xx_remove,
.probe = &vsc8584_probe,
.get_tunable = &vsc85xx_get_tunable,
.set_tunable = &vsc85xx_set_tunable,
@@ -2671,6 +2715,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .inband_caps = vsc85xx_inband_caps,
+ .config_inband = vsc85xx_config_inband,
},
{
.phy_id = PHY_ID_VSC8584,
@@ -2686,6 +2732,7 @@ static struct phy_driver vsc85xx_driver[] = {
.config_intr = &vsc85xx_config_intr,
.suspend = &genphy_suspend,
.resume = &genphy_resume,
+ .remove = &vsc85xx_remove,
.probe = &vsc8584_probe,
.get_tunable = &vsc85xx_get_tunable,
.set_tunable = &vsc85xx_set_tunable,
@@ -2695,6 +2742,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
.link_change_notify = &vsc85xx_link_change_notify,
+ .inband_caps = vsc85xx_inband_caps,
+ .config_inband = vsc85xx_config_inband,
}
};
diff --git a/drivers/net/phy/mscc/mscc_ptp.c b/drivers/net/phy/mscc/mscc_ptp.c
index 6b800081eed5..d692df7d975c 100644
--- a/drivers/net/phy/mscc/mscc_ptp.c
+++ b/drivers/net/phy/mscc/mscc_ptp.c
@@ -456,12 +456,12 @@ static void vsc85xx_dequeue_skb(struct vsc85xx_ptp *ptp)
*p++ = (reg >> 24) & 0xff;
}
- len = skb_queue_len(&ptp->tx_queue);
+ len = skb_queue_len_lockless(&ptp->tx_queue);
if (len < 1)
return;
while (len--) {
- skb = __skb_dequeue(&ptp->tx_queue);
+ skb = skb_dequeue(&ptp->tx_queue);
if (!skb)
return;
@@ -486,7 +486,7 @@ static void vsc85xx_dequeue_skb(struct vsc85xx_ptp *ptp)
* packet in the FIFO right now, reschedule it for later
* packets.
*/
- __skb_queue_tail(&ptp->tx_queue, skb);
+ skb_queue_tail(&ptp->tx_queue, skb);
}
}
@@ -900,6 +900,7 @@ static int vsc85xx_eth1_conf(struct phy_device *phydev, enum ts_blk blk,
get_unaligned_be32(ptp_multicast));
} else {
val |= ANA_ETH1_FLOW_ADDR_MATCH2_ANY_MULTICAST;
+ val |= ANA_ETH1_FLOW_ADDR_MATCH2_ANY_UNICAST;
vsc85xx_ts_write_csr(phydev, blk,
MSCC_ANA_ETH1_FLOW_ADDR_MATCH2(0), val);
vsc85xx_ts_write_csr(phydev, blk,
@@ -1067,6 +1068,7 @@ static int vsc85xx_hwtstamp(struct mii_timestamper *mii_ts,
case HWTSTAMP_TX_ON:
break;
case HWTSTAMP_TX_OFF:
+ skb_queue_purge(&vsc8531->ptp->tx_queue);
break;
default:
return -ERANGE;
@@ -1091,9 +1093,6 @@ static int vsc85xx_hwtstamp(struct mii_timestamper *mii_ts,
mutex_lock(&vsc8531->ts_lock);
- __skb_queue_purge(&vsc8531->ptp->tx_queue);
- __skb_queue_head_init(&vsc8531->ptp->tx_queue);
-
/* Disable predictor while configuring the 1588 block */
val = vsc85xx_ts_read_csr(phydev, PROCESSOR,
MSCC_PHY_PTP_INGR_PREDICTOR);
@@ -1179,9 +1178,7 @@ static void vsc85xx_txtstamp(struct mii_timestamper *mii_ts,
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
- mutex_lock(&vsc8531->ts_lock);
- __skb_queue_tail(&vsc8531->ptp->tx_queue, skb);
- mutex_unlock(&vsc8531->ts_lock);
+ skb_queue_tail(&vsc8531->ptp->tx_queue, skb);
return;
out:
@@ -1193,9 +1190,7 @@ static bool vsc85xx_rxtstamp(struct mii_timestamper *mii_ts,
{
struct vsc8531_private *vsc8531 =
container_of(mii_ts, struct vsc8531_private, mii_ts);
- struct skb_shared_hwtstamps *shhwtstamps = NULL;
struct vsc85xx_ptphdr *ptphdr;
- struct timespec64 ts;
unsigned long ns;
if (!vsc8531->ptp->configured)
@@ -1205,27 +1200,52 @@ static bool vsc85xx_rxtstamp(struct mii_timestamper *mii_ts,
type == PTP_CLASS_NONE)
return false;
- vsc85xx_gettime(&vsc8531->ptp->caps, &ts);
-
ptphdr = get_ptp_header_rx(skb, vsc8531->ptp->rx_filter);
if (!ptphdr)
return false;
- shhwtstamps = skb_hwtstamps(skb);
- memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
-
ns = ntohl(ptphdr->rsrvd2);
- /* nsec is in reserved field */
- if (ts.tv_nsec < ns)
- ts.tv_sec--;
+ VSC8531_SKB_CB(skb)->ns = ns;
+ skb_queue_tail(&vsc8531->rx_skbs_list, skb);
- shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ns);
- netif_rx(skb);
+ ptp_schedule_worker(vsc8531->ptp->ptp_clock, 0);
return true;
}
+static long vsc85xx_do_aux_work(struct ptp_clock_info *info)
+{
+ struct vsc85xx_ptp *ptp = container_of(info, struct vsc85xx_ptp, caps);
+ struct skb_shared_hwtstamps *shhwtstamps = NULL;
+ struct phy_device *phydev = ptp->phydev;
+ struct vsc8531_private *priv = phydev->priv;
+ struct sk_buff_head received;
+ struct sk_buff *rx_skb;
+ struct timespec64 ts;
+ unsigned long flags;
+
+ __skb_queue_head_init(&received);
+ spin_lock_irqsave(&priv->rx_skbs_list.lock, flags);
+ skb_queue_splice_tail_init(&priv->rx_skbs_list, &received);
+ spin_unlock_irqrestore(&priv->rx_skbs_list.lock, flags);
+
+ vsc85xx_gettime(info, &ts);
+ while ((rx_skb = __skb_dequeue(&received)) != NULL) {
+ shhwtstamps = skb_hwtstamps(rx_skb);
+ memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
+
+ if (ts.tv_nsec < VSC8531_SKB_CB(rx_skb)->ns)
+ ts.tv_sec--;
+
+ shhwtstamps->hwtstamp = ktime_set(ts.tv_sec,
+ VSC8531_SKB_CB(rx_skb)->ns);
+ netif_rx(rx_skb);
+ }
+
+ return -1;
+}
+
static const struct ptp_clock_info vsc85xx_clk_caps = {
.owner = THIS_MODULE,
.name = "VSC85xx timer",
@@ -1239,6 +1259,7 @@ static const struct ptp_clock_info vsc85xx_clk_caps = {
.adjfine = &vsc85xx_adjfine,
.gettime64 = &vsc85xx_gettime,
.settime64 = &vsc85xx_settime,
+ .do_aux_work = &vsc85xx_do_aux_work,
};
static struct vsc8531_private *vsc8584_base_priv(struct phy_device *phydev)
@@ -1273,7 +1294,6 @@ static void vsc8584_set_input_clk_configured(struct phy_device *phydev)
static int __vsc8584_init_ptp(struct phy_device *phydev)
{
- struct vsc8531_private *vsc8531 = phydev->priv;
static const u32 ltc_seq_e[] = { 0, 400000, 0, 0, 0 };
static const u8 ltc_seq_a[] = { 8, 6, 5, 4, 2 };
u32 val;
@@ -1490,17 +1510,7 @@ static int __vsc8584_init_ptp(struct phy_device *phydev)
vsc85xx_ts_eth_cmp1_sig(phydev);
- vsc8531->mii_ts.rxtstamp = vsc85xx_rxtstamp;
- vsc8531->mii_ts.txtstamp = vsc85xx_txtstamp;
- vsc8531->mii_ts.hwtstamp = vsc85xx_hwtstamp;
- vsc8531->mii_ts.ts_info = vsc85xx_ts_info;
- phydev->mii_ts = &vsc8531->mii_ts;
-
- memcpy(&vsc8531->ptp->caps, &vsc85xx_clk_caps, sizeof(vsc85xx_clk_caps));
-
- vsc8531->ptp->ptp_clock = ptp_clock_register(&vsc8531->ptp->caps,
- &phydev->mdio.dev);
- return PTR_ERR_OR_ZERO(vsc8531->ptp->ptp_clock);
+ return 0;
}
void vsc8584_config_ts_intr(struct phy_device *phydev)
@@ -1527,6 +1537,17 @@ int vsc8584_ptp_init(struct phy_device *phydev)
return 0;
}
+void vsc8584_ptp_deinit(struct phy_device *phydev)
+{
+ struct vsc8531_private *vsc8531 = phydev->priv;
+
+ if (vsc8531->ptp->ptp_clock) {
+ ptp_clock_unregister(vsc8531->ptp->ptp_clock);
+ skb_queue_purge(&vsc8531->rx_skbs_list);
+ skb_queue_purge(&vsc8531->ptp->tx_queue);
+ }
+}
+
irqreturn_t vsc8584_handle_ts_interrupt(struct phy_device *phydev)
{
struct vsc8531_private *priv = phydev->priv;
@@ -1547,7 +1568,7 @@ irqreturn_t vsc8584_handle_ts_interrupt(struct phy_device *phydev)
if (rc & VSC85XX_1588_INT_FIFO_ADD) {
vsc85xx_get_tx_ts(priv->ptp);
} else if (rc & VSC85XX_1588_INT_FIFO_OVERFLOW) {
- __skb_queue_purge(&priv->ptp->tx_queue);
+ skb_queue_purge(&priv->ptp->tx_queue);
vsc85xx_ts_reset_fifo(phydev);
}
@@ -1566,6 +1587,8 @@ int vsc8584_ptp_probe(struct phy_device *phydev)
mutex_init(&vsc8531->phc_lock);
mutex_init(&vsc8531->ts_lock);
+ skb_queue_head_init(&vsc8531->rx_skbs_list);
+ skb_queue_head_init(&vsc8531->ptp->tx_queue);
/* Retrieve the shared load/save GPIO. Request it as non exclusive as
* the same GPIO can be requested by all the PHYs of the same package.
@@ -1586,7 +1609,16 @@ int vsc8584_ptp_probe(struct phy_device *phydev)
vsc8531->ptp->phydev = phydev;
- return 0;
+ vsc8531->mii_ts.rxtstamp = vsc85xx_rxtstamp;
+ vsc8531->mii_ts.txtstamp = vsc85xx_txtstamp;
+ vsc8531->mii_ts.hwtstamp = vsc85xx_hwtstamp;
+ vsc8531->mii_ts.ts_info = vsc85xx_ts_info;
+ phydev->mii_ts = &vsc8531->mii_ts;
+
+ memcpy(&vsc8531->ptp->caps, &vsc85xx_clk_caps, sizeof(vsc85xx_clk_caps));
+ vsc8531->ptp->ptp_clock = ptp_clock_register(&vsc8531->ptp->caps,
+ &phydev->mdio.dev);
+ return PTR_ERR_OR_ZERO(vsc8531->ptp->ptp_clock);
}
int vsc8584_ptp_probe_once(struct phy_device *phydev)
diff --git a/drivers/net/phy/mscc/mscc_ptp.h b/drivers/net/phy/mscc/mscc_ptp.h
index da3465360e90..ae9ad925bfa8 100644
--- a/drivers/net/phy/mscc/mscc_ptp.h
+++ b/drivers/net/phy/mscc/mscc_ptp.h
@@ -98,6 +98,7 @@
#define MSCC_ANA_ETH1_FLOW_ADDR_MATCH2(x) (MSCC_ANA_ETH1_FLOW_ENA(x) + 3)
#define ANA_ETH1_FLOW_ADDR_MATCH2_MASK_MASK GENMASK(22, 20)
#define ANA_ETH1_FLOW_ADDR_MATCH2_ANY_MULTICAST 0x400000
+#define ANA_ETH1_FLOW_ADDR_MATCH2_ANY_UNICAST 0x200000
#define ANA_ETH1_FLOW_ADDR_MATCH2_FULL_ADDR 0x100000
#define ANA_ETH1_FLOW_ADDR_MATCH2_SRC_DEST_MASK GENMASK(17, 16)
#define ANA_ETH1_FLOW_ADDR_MATCH2_SRC_DEST 0x020000
diff --git a/drivers/net/phy/mxl-86110.c b/drivers/net/phy/mxl-86110.c
index ff2a3a22bd5b..e5d137a37a1d 100644
--- a/drivers/net/phy/mxl-86110.c
+++ b/drivers/net/phy/mxl-86110.c
@@ -15,6 +15,7 @@
/* PHY ID */
#define PHY_ID_MXL86110 0xc1335580
+#define PHY_ID_MXL86111 0xc1335588
/* required to access extended registers */
#define MXL86110_EXTD_REG_ADDR_OFFSET 0x1E
@@ -22,7 +23,15 @@
#define PHY_IRQ_ENABLE_REG 0x12
#define PHY_IRQ_ENABLE_REG_WOL BIT(6)
-/* SyncE Configuration Register - COM_EXT SYNCE_CFG */
+/* different pages for EXTD access for MXL86111 */
+/* SerDes/PHY Control Access Register - COM_EXT_SMI_SDS_PHY */
+#define MXL86111_EXT_SMI_SDS_PHY_REG 0xA000
+#define MXL86111_EXT_SMI_SDS_PHYSPACE_MASK BIT(1)
+#define MXL86111_EXT_SMI_SDS_PHYFIBER_SPACE (0x1 << 1)
+#define MXL86111_EXT_SMI_SDS_PHYUTP_SPACE (0x0 << 1)
+#define MXL86111_EXT_SMI_SDS_PHY_AUTO 0xff
+
+/* SyncE Configuration Register - COM_EXT_SYNCE_CFG */
#define MXL86110_EXT_SYNCE_CFG_REG 0xA012
#define MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL BIT(4)
#define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E_DURING_LNKDN BIT(5)
@@ -71,6 +80,11 @@
#define MXL86110_MAX_LEDS 3
/* LED registers and defines */
+#define MXL86110_COM_EXT_LED_GEN_CFG 0xA00B
+# define MXL86110_COM_EXT_LED_GEN_CFG_LFM(x) ((BIT(0) | BIT(1)) << (3 * (x)))
+# define MXL86110_COM_EXT_LED_GEN_CFG_LFME(x) (BIT(0) << (3 * (x)))
+# define MXL86110_COM_EXT_LED_GEN_CFG_LFE(x) (BIT(2) << (3 * (x)))
+
#define MXL86110_LED0_CFG_REG 0xA00C
#define MXL86110_LED1_CFG_REG 0xA00D
#define MXL86110_LED2_CFG_REG 0xA00E
@@ -110,9 +124,67 @@
/* Chip Configuration Register - COM_EXT_CHIP_CFG */
#define MXL86110_EXT_CHIP_CFG_REG 0xA001
+#define MXL86111_EXT_CHIP_CFG_MODE_SEL_MASK GENMASK(2, 0)
+#define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_RGMII 0
+#define MXL86111_EXT_CHIP_CFG_MODE_FIBER_TO_RGMII 1
+#define MXL86111_EXT_CHIP_CFG_MODE_UTP_FIBER_TO_RGMII 2
+#define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_SGMII 3
+#define MXL86111_EXT_CHIP_CFG_MODE_SGPHY_TO_RGMAC 4
+#define MXL86111_EXT_CHIP_CFG_MODE_SGMAC_TO_RGPHY 5
+#define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_FIBER_AUTO 6
+#define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_FIBER_FORCE 7
+
+#define MXL86111_EXT_CHIP_CFG_CLDO_MASK GENMASK(5, 4)
+#define MXL86111_EXT_CHIP_CFG_CLDO_3V3 0
+#define MXL86111_EXT_CHIP_CFG_CLDO_2V5 1
+#define MXL86111_EXT_CHIP_CFG_CLDO_1V8_2 2
+#define MXL86111_EXT_CHIP_CFG_CLDO_1V8_3 3
+#define MXL86111_EXT_CHIP_CFG_CLDO_SHIFT 4
+#define MXL86111_EXT_CHIP_CFG_ELDO BIT(6)
#define MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE BIT(8)
#define MXL86110_EXT_CHIP_CFG_SW_RST_N_MODE BIT(15)
+/* Specific Status Register - PHY_STAT */
+#define MXL86111_PHY_STAT_REG 0x11
+#define MXL86111_PHY_STAT_SPEED_MASK GENMASK(15, 14)
+#define MXL86111_PHY_STAT_SPEED_OFFSET 14
+#define MXL86111_PHY_STAT_SPEED_10M 0x0
+#define MXL86111_PHY_STAT_SPEED_100M 0x1
+#define MXL86111_PHY_STAT_SPEED_1000M 0x2
+#define MXL86111_PHY_STAT_DPX_OFFSET 13
+#define MXL86111_PHY_STAT_DPX BIT(13)
+#define MXL86111_PHY_STAT_LSRT BIT(10)
+
+/* 3 phy reg page modes,auto mode combines utp and fiber mode*/
+#define MXL86111_MODE_FIBER 0x1
+#define MXL86111_MODE_UTP 0x2
+#define MXL86111_MODE_AUTO 0x3
+
+/* FIBER Auto-Negotiation link partner ability - SDS_AN_LPA */
+#define MXL86111_SDS_AN_LPA_PAUSE (0x3 << 7)
+#define MXL86111_SDS_AN_LPA_ASYM_PAUSE (0x2 << 7)
+
+/* Miscellaneous Control Register - COM_EXT _MISC_CFG */
+#define MXL86111_EXT_MISC_CONFIG_REG 0xa006
+#define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL BIT(0)
+#define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_1000BX (0x1 << 0)
+#define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_100BX (0x0 << 0)
+
+/* Phy fiber Link timer cfg2 Register - EXT_SDS_LINK_TIMER_CFG2 */
+#define MXL86111_EXT_SDS_LINK_TIMER_CFG2_REG 0xA5
+#define MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN BIT(15)
+
+/* default values of PHY register, required for Dual Media mode */
+#define MII_BMSR_DEFAULT_VAL 0x7949
+#define MII_ESTATUS_DEFAULT_VAL 0x2000
+
+/* Timeout in ms for PHY SW reset check in STD_CTRL/SDS_CTRL */
+#define BMCR_RESET_TIMEOUT 500
+
+/* PL P1 requires optimized RGMII timing for 1.8V RGMII voltage
+ */
+#define MXL86111_PL_P1 0x500
+
/**
* __mxl86110_write_extended_reg() - write to a PHY's extended register
* @phydev: pointer to the PHY device structure
@@ -236,6 +308,29 @@ static int mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum)
}
/**
+ * mxl86110_modify_extended_reg() - modify bits of a PHY's extended register
+ * @phydev: pointer to the PHY device structure
+ * @regnum: register number to write
+ * @mask: bit mask of bits to clear
+ * @set: bit mask of bits to set
+ *
+ * Note: register value = (old register value & ~mask) | set.
+ *
+ * Return: 0 or negative error code
+ */
+static int mxl86110_modify_extended_reg(struct phy_device *phydev,
+ u16 regnum, u16 mask, u16 set)
+{
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+ ret = __mxl86110_modify_extended_reg(phydev, regnum, mask, set);
+ phy_unlock_mdio_bus(phydev);
+
+ return ret;
+}
+
+/**
* mxl86110_get_wol() - report if wake-on-lan is enabled
* @phydev: pointer to the phy_device
* @wol: a pointer to a &struct ethtool_wolinfo
@@ -394,6 +489,7 @@ static int mxl86110_led_hw_control_set(struct phy_device *phydev, u8 index,
unsigned long rules)
{
u16 val = 0;
+ int ret;
if (index >= MXL86110_MAX_LEDS)
return -EINVAL;
@@ -423,8 +519,43 @@ static int mxl86110_led_hw_control_set(struct phy_device *phydev, u8 index,
rules & BIT(TRIGGER_NETDEV_RX))
val |= MXL86110_LEDX_CFG_BLINK;
- return mxl86110_write_extended_reg(phydev,
+ ret = mxl86110_write_extended_reg(phydev,
MXL86110_LED0_CFG_REG + index, val);
+ if (ret)
+ return ret;
+
+ /* clear manual control bit */
+ ret = mxl86110_modify_extended_reg(phydev,
+ MXL86110_COM_EXT_LED_GEN_CFG,
+ MXL86110_COM_EXT_LED_GEN_CFG_LFE(index),
+ 0);
+
+ return ret;
+}
+
+static int mxl86110_led_brightness_set(struct phy_device *phydev,
+ u8 index, enum led_brightness value)
+{
+ u16 mask, set;
+ int ret;
+
+ if (index >= MXL86110_MAX_LEDS)
+ return -EINVAL;
+
+ /* force manual control */
+ set = MXL86110_COM_EXT_LED_GEN_CFG_LFE(index);
+ /* clear previous force mode */
+ mask = MXL86110_COM_EXT_LED_GEN_CFG_LFM(index);
+
+ /* force LED to be permanently on */
+ if (value != LED_OFF)
+ set |= MXL86110_COM_EXT_LED_GEN_CFG_LFME(index);
+
+ ret = mxl86110_modify_extended_reg(phydev,
+ MXL86110_COM_EXT_LED_GEN_CFG,
+ mask, set);
+
+ return ret;
}
/**
@@ -521,22 +652,15 @@ static int mxl86110_enable_led_activity_blink(struct phy_device *phydev)
}
/**
- * mxl86110_config_init() - initialize the PHY
+ * mxl86110_config_rgmii_delay() - configure RGMII delays
* @phydev: pointer to the phy_device
*
* Return: 0 or negative errno code
*/
-static int mxl86110_config_init(struct phy_device *phydev)
+static int mxl86110_config_rgmii_delay(struct phy_device *phydev)
{
- u16 val = 0;
int ret;
-
- phy_lock_mdio_bus(phydev);
-
- /* configure syncE / clk output */
- ret = mxl86110_synce_clk_cfg(phydev);
- if (ret < 0)
- goto out;
+ u16 val;
switch (phydev->interface) {
case PHY_INTERFACE_MODE_RGMII:
@@ -578,6 +702,31 @@ static int mxl86110_config_init(struct phy_device *phydev)
if (ret < 0)
goto out;
+out:
+ return ret;
+}
+
+/**
+ * mxl86110_config_init() - initialize the MXL86110 PHY
+ * @phydev: pointer to the phy_device
+ *
+ * Return: 0 or negative errno code
+ */
+static int mxl86110_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+
+ /* configure syncE / clk output */
+ ret = mxl86110_synce_clk_cfg(phydev);
+ if (ret < 0)
+ goto out;
+
+ ret = mxl86110_config_rgmii_delay(phydev);
+ if (ret < 0)
+ goto out;
+
ret = mxl86110_enable_led_activity_blink(phydev);
if (ret < 0)
goto out;
@@ -589,6 +738,201 @@ out:
return ret;
}
+/**
+ * mxl86111_probe() - validate bootstrap chip config and set UTP page
+ * @phydev: pointer to the phy_device
+ *
+ * Return: 0 or negative errno code
+ */
+static int mxl86111_probe(struct phy_device *phydev)
+{
+ int chip_config;
+ u16 reg_page;
+ int ret;
+
+ chip_config = mxl86110_read_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG);
+ if (chip_config < 0)
+ return chip_config;
+
+ switch (chip_config & MXL86111_EXT_CHIP_CFG_MODE_SEL_MASK) {
+ case MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_SGMII:
+ case MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_RGMII:
+ phydev->port = PORT_TP;
+ reg_page = MXL86111_EXT_SMI_SDS_PHYUTP_SPACE;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = mxl86110_write_extended_reg(phydev,
+ MXL86111_EXT_SMI_SDS_PHY_REG,
+ reg_page);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * mxl86111_config_init() - initialize the MXL86111 PHY
+ * @phydev: pointer to the phy_device
+ *
+ * Return: 0 or negative errno code
+ */
+static int mxl86111_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+
+ /* configure syncE / clk output */
+ ret = mxl86110_synce_clk_cfg(phydev);
+ if (ret < 0)
+ goto out;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_100BASEX:
+ ret = __mxl86110_modify_extended_reg(phydev,
+ MXL86111_EXT_MISC_CONFIG_REG,
+ MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL,
+ MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_100BX);
+ if (ret < 0)
+ goto out;
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_SGMII:
+ ret = __mxl86110_modify_extended_reg(phydev,
+ MXL86111_EXT_MISC_CONFIG_REG,
+ MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL,
+ MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_1000BX);
+ if (ret < 0)
+ goto out;
+ break;
+ default:
+ /* RGMII modes */
+ ret = mxl86110_config_rgmii_delay(phydev);
+ if (ret < 0)
+ goto out;
+ ret = __mxl86110_modify_extended_reg(phydev, MXL86110_EXT_RGMII_CFG1_REG,
+ MXL86110_EXT_RGMII_CFG1_FULL_MASK, ret);
+
+ /* PL P1 requires optimized RGMII timing for 1.8V RGMII voltage
+ */
+ ret = __mxl86110_read_extended_reg(phydev, 0xf);
+ if (ret < 0)
+ goto out;
+
+ if (ret == MXL86111_PL_P1) {
+ ret = __mxl86110_read_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG);
+ if (ret < 0)
+ goto out;
+
+ /* check if LDO is in 1.8V mode */
+ switch (FIELD_GET(MXL86111_EXT_CHIP_CFG_CLDO_MASK, ret)) {
+ case MXL86111_EXT_CHIP_CFG_CLDO_1V8_3:
+ case MXL86111_EXT_CHIP_CFG_CLDO_1V8_2:
+ ret = __mxl86110_write_extended_reg(phydev, 0xa010, 0xabff);
+ if (ret < 0)
+ goto out;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ ret = mxl86110_enable_led_activity_blink(phydev);
+ if (ret < 0)
+ goto out;
+
+ ret = mxl86110_broadcast_cfg(phydev);
+out:
+ phy_unlock_mdio_bus(phydev);
+
+ return ret;
+}
+
+/**
+ * mxl86111_read_page() - read reg page
+ * @phydev: pointer to the phy_device
+ *
+ * Return: current reg space of mxl86111 or negative errno code
+ */
+static int mxl86111_read_page(struct phy_device *phydev)
+{
+ int page;
+
+ page = __mxl86110_read_extended_reg(phydev, MXL86111_EXT_SMI_SDS_PHY_REG);
+ if (page < 0)
+ return page;
+
+ return page & MXL86111_EXT_SMI_SDS_PHYSPACE_MASK;
+};
+
+/**
+ * mxl86111_write_page() - Set reg page
+ * @phydev: pointer to the phy_device
+ * @page: The reg page to set
+ *
+ * Return: 0 or negative errno code
+ */
+static int mxl86111_write_page(struct phy_device *phydev, int page)
+{
+ return __mxl86110_modify_extended_reg(phydev, MXL86111_EXT_SMI_SDS_PHY_REG,
+ MXL86111_EXT_SMI_SDS_PHYSPACE_MASK, page);
+};
+
+static int mxl86111_config_inband(struct phy_device *phydev, unsigned int modes)
+{
+ int ret;
+
+ ret = phy_modify_paged(phydev, MXL86111_EXT_SMI_SDS_PHYFIBER_SPACE,
+ MII_BMCR, BMCR_ANENABLE,
+ (modes == LINK_INBAND_DISABLE) ? 0 : BMCR_ANENABLE);
+ if (ret < 0)
+ goto out;
+
+ phy_lock_mdio_bus(phydev);
+
+ ret = __mxl86110_modify_extended_reg(phydev, MXL86111_EXT_SDS_LINK_TIMER_CFG2_REG,
+ MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN,
+ (modes == LINK_INBAND_DISABLE) ? 0 :
+ MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN);
+ if (ret < 0)
+ goto out;
+
+ ret = __mxl86110_modify_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG,
+ MXL86110_EXT_CHIP_CFG_SW_RST_N_MODE, 0);
+ if (ret < 0)
+ goto out;
+
+ /* For fiber forced mode, power down/up to re-aneg */
+ if (modes != LINK_INBAND_DISABLE) {
+ __phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);
+ usleep_range(1000, 1050);
+ __phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
+ }
+
+out:
+ phy_unlock_mdio_bus(phydev);
+
+ return ret;
+}
+
+static unsigned int mxl86111_inband_caps(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_100BASEX:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_SGMII:
+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+ default:
+ return 0;
+ }
+}
+
static struct phy_driver mxl_phy_drvs[] = {
{
PHY_ID_MATCH_EXACT(PHY_ID_MXL86110),
@@ -596,9 +940,26 @@ static struct phy_driver mxl_phy_drvs[] = {
.config_init = mxl86110_config_init,
.get_wol = mxl86110_get_wol,
.set_wol = mxl86110_set_wol,
+ .led_brightness_set = mxl86110_led_brightness_set,
+ .led_hw_is_supported = mxl86110_led_hw_is_supported,
+ .led_hw_control_get = mxl86110_led_hw_control_get,
+ .led_hw_control_set = mxl86110_led_hw_control_set,
+ },
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_MXL86111),
+ .name = "MXL86111 Gigabit Ethernet",
+ .probe = mxl86111_probe,
+ .config_init = mxl86111_config_init,
+ .get_wol = mxl86110_get_wol,
+ .set_wol = mxl86110_set_wol,
+ .inband_caps = mxl86111_inband_caps,
+ .config_inband = mxl86111_config_inband,
+ .read_page = mxl86111_read_page,
+ .write_page = mxl86111_write_page,
+ .led_brightness_set = mxl86110_led_brightness_set,
.led_hw_is_supported = mxl86110_led_hw_is_supported,
- .led_hw_control_get = mxl86110_led_hw_control_get,
- .led_hw_control_set = mxl86110_led_hw_control_set,
+ .led_hw_control_get = mxl86110_led_hw_control_get,
+ .led_hw_control_set = mxl86110_led_hw_control_set,
},
};
@@ -606,11 +967,12 @@ module_phy_driver(mxl_phy_drvs);
static const struct mdio_device_id __maybe_unused mxl_tbl[] = {
{ PHY_ID_MATCH_EXACT(PHY_ID_MXL86110) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_MXL86111) },
{ }
};
MODULE_DEVICE_TABLE(mdio, mxl_tbl);
-MODULE_DESCRIPTION("MaxLinear MXL86110 PHY driver");
+MODULE_DESCRIPTION("MaxLinear MXL86110/MXL86111 PHY driver");
MODULE_AUTHOR("Stefano Radaelli");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/nxp-c45-tja11xx-macsec.c b/drivers/net/phy/nxp-c45-tja11xx-macsec.c
index 550ef08970f4..fc897ba79b03 100644
--- a/drivers/net/phy/nxp-c45-tja11xx-macsec.c
+++ b/drivers/net/phy/nxp-c45-tja11xx-macsec.c
@@ -926,7 +926,6 @@ static int nxp_c45_mdo_dev_open(struct macsec_context *ctx)
struct phy_device *phydev = ctx->phydev;
struct nxp_c45_phy *priv = phydev->priv;
struct nxp_c45_secy *phy_secy;
- int any_bit_set;
phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci);
if (IS_ERR(phy_secy))
@@ -939,8 +938,7 @@ static int nxp_c45_mdo_dev_open(struct macsec_context *ctx)
if (phy_secy->rx_sc)
nxp_c45_rx_sc_en(phydev, phy_secy->rx_sc, true);
- any_bit_set = find_first_bit(priv->macsec->secy_bitmap, TX_SC_MAX);
- if (any_bit_set == TX_SC_MAX)
+ if (bitmap_empty(priv->macsec->secy_bitmap, TX_SC_MAX))
nxp_c45_macsec_en(phydev, true);
set_bit(phy_secy->secy_id, priv->macsec->secy_bitmap);
@@ -953,7 +951,6 @@ static int nxp_c45_mdo_dev_stop(struct macsec_context *ctx)
struct phy_device *phydev = ctx->phydev;
struct nxp_c45_phy *priv = phydev->priv;
struct nxp_c45_secy *phy_secy;
- int any_bit_set;
phy_secy = nxp_c45_find_secy(&priv->macsec->secy_list, ctx->secy->sci);
if (IS_ERR(phy_secy))
@@ -967,8 +964,7 @@ static int nxp_c45_mdo_dev_stop(struct macsec_context *ctx)
nxp_c45_set_rx_sc0_impl(phydev, false);
clear_bit(phy_secy->secy_id, priv->macsec->secy_bitmap);
- any_bit_set = find_first_bit(priv->macsec->secy_bitmap, TX_SC_MAX);
- if (any_bit_set == TX_SC_MAX)
+ if (bitmap_empty(priv->macsec->secy_bitmap, TX_SC_MAX))
nxp_c45_macsec_en(phydev, false);
return 0;
diff --git a/drivers/net/phy/nxp-c45-tja11xx.c b/drivers/net/phy/nxp-c45-tja11xx.c
index 4c6d905f0a9f..87adb6508017 100644
--- a/drivers/net/phy/nxp-c45-tja11xx.c
+++ b/drivers/net/phy/nxp-c45-tja11xx.c
@@ -1965,24 +1965,27 @@ static int nxp_c45_macsec_ability(struct phy_device *phydev)
return macsec_ability;
}
+static bool tja11xx_phy_id_compare(struct phy_device *phydev,
+ const struct phy_driver *phydrv)
+{
+ u32 id = phydev->is_c45 ? phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] :
+ phydev->phy_id;
+
+ return phy_id_compare(id, phydrv->phy_id, phydrv->phy_id_mask);
+}
+
static int tja11xx_no_macsec_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
- if (!phy_id_compare(phydev->phy_id, phydrv->phy_id,
- phydrv->phy_id_mask))
- return 0;
-
- return !nxp_c45_macsec_ability(phydev);
+ return tja11xx_phy_id_compare(phydev, phydrv) &&
+ !nxp_c45_macsec_ability(phydev);
}
static int tja11xx_macsec_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
- if (!phy_id_compare(phydev->phy_id, phydrv->phy_id,
- phydrv->phy_id_mask))
- return 0;
-
- return nxp_c45_macsec_ability(phydev);
+ return tja11xx_phy_id_compare(phydev, phydrv) &&
+ nxp_c45_macsec_ability(phydev);
}
static const struct nxp_c45_regmap tja1120_regmap = {
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index bdd70d424491..61670be0f095 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -1573,10 +1573,3 @@ int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
return ret;
}
EXPORT_SYMBOL(genphy_c45_ethtool_set_eee);
-
-struct phy_driver genphy_c45_driver = {
- .phy_id = 0xffffffff,
- .phy_id_mask = 0xffffffff,
- .name = "Generic Clause 45 PHY",
- .read_status = genphy_c45_read_status,
-};
diff --git a/drivers/net/phy/phy-caps.h b/drivers/net/phy/phy-caps.h
index 157759966650..b7f0c6a3037a 100644
--- a/drivers/net/phy/phy-caps.h
+++ b/drivers/net/phy/phy-caps.h
@@ -41,7 +41,7 @@ struct link_capabilities {
__ETHTOOL_DECLARE_LINK_MODE_MASK(linkmodes);
};
-int phy_caps_init(void);
+int __init phy_caps_init(void);
size_t phy_caps_speeds(unsigned int *speeds, size_t size,
unsigned long *linkmodes);
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index e177037f9110..605ca20ae192 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -115,6 +115,7 @@ int phy_interface_num_ports(phy_interface_t interface)
return 0;
case PHY_INTERFACE_MODE_INTERNAL:
case PHY_INTERFACE_MODE_MII:
+ case PHY_INTERFACE_MODE_MIILITE:
case PHY_INTERFACE_MODE_GMII:
case PHY_INTERFACE_MODE_TBI:
case PHY_INTERFACE_MODE_REVMII:
@@ -142,6 +143,9 @@ int phy_interface_num_ports(phy_interface_t interface)
case PHY_INTERFACE_MODE_RXAUI:
case PHY_INTERFACE_MODE_XAUI:
case PHY_INTERFACE_MODE_1000BASEKX:
+ case PHY_INTERFACE_MODE_50GBASER:
+ case PHY_INTERFACE_MODE_LAUI:
+ case PHY_INTERFACE_MODE_100GBASEP:
return 1;
case PHY_INTERFACE_MODE_QSGMII:
case PHY_INTERFACE_MODE_QUSGMII:
@@ -375,8 +379,8 @@ static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad,
devad | MII_MMD_CTRL_NOINCR);
}
-static int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45,
- int devad, u32 regnum)
+int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45,
+ int devad, u32 regnum)
{
if (is_c45)
return __mdiobus_c45_read(bus, phy_addr, devad, regnum);
@@ -385,9 +389,10 @@ static int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45,
/* Read the content of the MMD's selected register */
return __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
}
+EXPORT_SYMBOL_GPL(mmd_phy_read);
-static int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45,
- int devad, u32 regnum, u16 val)
+int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45,
+ int devad, u32 regnum, u16 val)
{
if (is_c45)
return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val);
@@ -396,6 +401,7 @@ static int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45,
/* Write the data into MMD's selected register */
return __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
}
+EXPORT_SYMBOL_GPL(mmd_phy_write);
/**
* __phy_read_mmd - Convenience function for reading a register
@@ -486,71 +492,6 @@ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
EXPORT_SYMBOL(phy_write_mmd);
/**
- * __phy_package_read_mmd - read MMD reg relative to PHY package base addr
- * @phydev: The phy_device struct
- * @addr_offset: The offset to be added to PHY package base_addr
- * @devad: The MMD to read from
- * @regnum: The register on the MMD to read
- *
- * Convenience helper for reading a register of an MMD on a given PHY
- * using the PHY package base address. The base address is added to
- * the addr_offset value.
- *
- * Same calling rules as for __phy_read();
- *
- * NOTE: It's assumed that the entire PHY package is either C22 or C45.
- */
-int __phy_package_read_mmd(struct phy_device *phydev,
- unsigned int addr_offset, int devad,
- u32 regnum)
-{
- int addr = phy_package_address(phydev, addr_offset);
-
- if (addr < 0)
- return addr;
-
- if (regnum > (u16)~0 || devad > 32)
- return -EINVAL;
-
- return mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad,
- regnum);
-}
-EXPORT_SYMBOL(__phy_package_read_mmd);
-
-/**
- * __phy_package_write_mmd - write MMD reg relative to PHY package base addr
- * @phydev: The phy_device struct
- * @addr_offset: The offset to be added to PHY package base_addr
- * @devad: The MMD to write to
- * @regnum: The register on the MMD to write
- * @val: value to write to @regnum
- *
- * Convenience helper for writing a register of an MMD on a given PHY
- * using the PHY package base address. The base address is added to
- * the addr_offset value.
- *
- * Same calling rules as for __phy_write();
- *
- * NOTE: It's assumed that the entire PHY package is either C22 or C45.
- */
-int __phy_package_write_mmd(struct phy_device *phydev,
- unsigned int addr_offset, int devad,
- u32 regnum, u16 val)
-{
- int addr = phy_package_address(phydev, addr_offset);
-
- if (addr < 0)
- return addr;
-
- if (regnum > (u16)~0 || devad > 32)
- return -EINVAL;
-
- return mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad,
- regnum, val);
-}
-EXPORT_SYMBOL(__phy_package_write_mmd);
-
-/**
* phy_modify_changed - Function for modifying a PHY register
* @phydev: the phy_device struct
* @regnum: register number to modify
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 13df28445f02..02da4a203ddd 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1065,23 +1065,19 @@ EXPORT_SYMBOL_GPL(phy_inband_caps);
*/
int phy_config_inband(struct phy_device *phydev, unsigned int modes)
{
- int err;
+ lockdep_assert_held(&phydev->lock);
if (!!(modes & LINK_INBAND_DISABLE) +
!!(modes & LINK_INBAND_ENABLE) +
!!(modes & LINK_INBAND_BYPASS) != 1)
return -EINVAL;
- mutex_lock(&phydev->lock);
if (!phydev->drv)
- err = -EIO;
+ return -EIO;
else if (!phydev->drv->config_inband)
- err = -EOPNOTSUPP;
- else
- err = phydev->drv->config_inband(phydev, modes);
- mutex_unlock(&phydev->lock);
+ return -EOPNOTSUPP;
- return err;
+ return phydev->drv->config_inband(phydev, modes);
}
EXPORT_SYMBOL(phy_config_inband);
@@ -1552,9 +1548,24 @@ static enum phy_state_work _phy_state_machine(struct phy_device *phydev)
}
break;
case PHY_HALTED:
+ if (phydev->link) {
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+ }
+ if (phydev->master_slave_state !=
+ MASTER_SLAVE_STATE_UNSUPPORTED)
+ phydev->master_slave_state =
+ MASTER_SLAVE_STATE_UNKNOWN;
+ phydev->mdix = ETH_TP_MDI_INVALID;
+ linkmode_zero(phydev->lp_advertising);
+ }
+ fallthrough;
case PHY_ERROR:
if (phydev->link) {
phydev->link = 0;
+ phydev->eee_active = false;
+ phydev->enable_tx_lpi = false;
phy_link_down(phydev);
}
state_work = PHY_STATE_WORK_SUSPEND;
diff --git a/drivers/net/phy/phy_caps.c b/drivers/net/phy/phy_caps.c
index 38417e288611..23c808b59b6f 100644
--- a/drivers/net/phy/phy_caps.c
+++ b/drivers/net/phy/phy_caps.c
@@ -70,7 +70,7 @@ static int speed_duplex_to_capa(int speed, unsigned int duplex)
* unexpected linkmode setting that requires LINK_CAPS update.
*
*/
-int phy_caps_init(void)
+int __init phy_caps_init(void)
{
const struct link_mode_info *linkmode;
int i, capa;
@@ -316,6 +316,10 @@ unsigned long phy_caps_from_interface(phy_interface_t interface)
link_caps |= BIT(LINK_CAPA_100HD) | BIT(LINK_CAPA_100FD);
break;
+ case PHY_INTERFACE_MODE_MIILITE:
+ link_caps |= BIT(LINK_CAPA_10FD) | BIT(LINK_CAPA_100FD);
+ break;
+
case PHY_INTERFACE_MODE_TBI:
case PHY_INTERFACE_MODE_MOCA:
case PHY_INTERFACE_MODE_RTBI:
@@ -351,6 +355,15 @@ unsigned long phy_caps_from_interface(phy_interface_t interface)
link_caps |= BIT(LINK_CAPA_40000FD);
break;
+ case PHY_INTERFACE_MODE_50GBASER:
+ case PHY_INTERFACE_MODE_LAUI:
+ link_caps |= BIT(LINK_CAPA_50000FD);
+ break;
+
+ case PHY_INTERFACE_MODE_100GBASEP:
+ link_caps |= BIT(LINK_CAPA_100000FD);
+ break;
+
case PHY_INTERFACE_MODE_INTERNAL:
link_caps |= LINK_CAPA_ALL;
break;
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index f76ee8489504..7a67c900e79a 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -59,6 +59,13 @@ struct phy_fixup {
int (*run)(struct phy_device *phydev);
};
+static struct phy_driver genphy_c45_driver = {
+ .phy_id = 0xffffffff,
+ .phy_id_mask = 0xffffffff,
+ .name = "Generic Clause 45 PHY",
+ .read_status = genphy_c45_read_status,
+};
+
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_features) __ro_after_init;
EXPORT_SYMBOL_GPL(phy_basic_features);
@@ -84,7 +91,7 @@ const int phy_basic_ports_array[3] = {
};
EXPORT_SYMBOL_GPL(phy_basic_ports_array);
-static const int phy_all_ports_features_array[7] = {
+static const int phy_all_ports_features_array[7] __initconst = {
ETHTOOL_LINK_MODE_Autoneg_BIT,
ETHTOOL_LINK_MODE_TP_BIT,
ETHTOOL_LINK_MODE_MII_BIT,
@@ -94,30 +101,30 @@ static const int phy_all_ports_features_array[7] = {
ETHTOOL_LINK_MODE_Backplane_BIT,
};
-static const int phy_10_100_features_array[4] = {
+static const int phy_10_100_features_array[4] __initconst = {
ETHTOOL_LINK_MODE_10baseT_Half_BIT,
ETHTOOL_LINK_MODE_10baseT_Full_BIT,
ETHTOOL_LINK_MODE_100baseT_Half_BIT,
ETHTOOL_LINK_MODE_100baseT_Full_BIT,
};
-static const int phy_basic_t1_features_array[3] = {
+static const int phy_basic_t1_features_array[3] __initconst = {
ETHTOOL_LINK_MODE_TP_BIT,
ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
};
-static const int phy_basic_t1s_p2mp_features_array[2] = {
+static const int phy_basic_t1s_p2mp_features_array[2] __initconst = {
ETHTOOL_LINK_MODE_TP_BIT,
ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
};
-static const int phy_gbit_features_array[2] = {
+static const int phy_gbit_features_array[2] __initconst = {
ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
};
-static const int phy_eee_cap1_features_array[] = {
+static const int phy_eee_cap1_features_array[] __initconst = {
ETHTOOL_LINK_MODE_100baseT_Full_BIT,
ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
@@ -129,7 +136,7 @@ static const int phy_eee_cap1_features_array[] = {
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap1_features) __ro_after_init;
EXPORT_SYMBOL_GPL(phy_eee_cap1_features);
-static const int phy_eee_cap2_features_array[] = {
+static const int phy_eee_cap2_features_array[] __initconst = {
ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
};
@@ -137,7 +144,7 @@ static const int phy_eee_cap2_features_array[] = {
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap2_features) __ro_after_init;
EXPORT_SYMBOL_GPL(phy_eee_cap2_features);
-static void features_init(void)
+static void __init features_init(void)
{
/* 10/100 half/full*/
linkmode_set_bit_array(phy_basic_ports_array,
@@ -280,8 +287,7 @@ static bool phy_uses_state_machine(struct phy_device *phydev)
if (phydev->phy_link_change == phy_link_change)
return phydev->attached_dev && phydev->adjust_link;
- /* phydev->phy_link_change is implicitly phylink_phy_change() */
- return true;
+ return !!phydev->phy_link_change;
}
static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
@@ -645,11 +651,119 @@ static struct attribute *phy_dev_attrs[] = {
&dev_attr_phy_dev_flags.attr,
NULL,
};
-ATTRIBUTE_GROUPS(phy_dev);
+
+static const struct attribute_group phy_dev_group = {
+ .attrs = phy_dev_attrs,
+};
+
+#define MMD_DEVICE_ID_ATTR(n) \
+static ssize_t mmd##n##_device_id_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct phy_device *phydev = to_phy_device(dev); \
+ return sysfs_emit(buf, "0x%.8lx\n", \
+ (unsigned long)phydev->c45_ids.device_ids[n]); \
+} \
+static DEVICE_ATTR_RO(mmd##n##_device_id)
+
+MMD_DEVICE_ID_ATTR(1);
+MMD_DEVICE_ID_ATTR(2);
+MMD_DEVICE_ID_ATTR(3);
+MMD_DEVICE_ID_ATTR(4);
+MMD_DEVICE_ID_ATTR(5);
+MMD_DEVICE_ID_ATTR(6);
+MMD_DEVICE_ID_ATTR(7);
+MMD_DEVICE_ID_ATTR(8);
+MMD_DEVICE_ID_ATTR(9);
+MMD_DEVICE_ID_ATTR(10);
+MMD_DEVICE_ID_ATTR(11);
+MMD_DEVICE_ID_ATTR(12);
+MMD_DEVICE_ID_ATTR(13);
+MMD_DEVICE_ID_ATTR(14);
+MMD_DEVICE_ID_ATTR(15);
+MMD_DEVICE_ID_ATTR(16);
+MMD_DEVICE_ID_ATTR(17);
+MMD_DEVICE_ID_ATTR(18);
+MMD_DEVICE_ID_ATTR(19);
+MMD_DEVICE_ID_ATTR(20);
+MMD_DEVICE_ID_ATTR(21);
+MMD_DEVICE_ID_ATTR(22);
+MMD_DEVICE_ID_ATTR(23);
+MMD_DEVICE_ID_ATTR(24);
+MMD_DEVICE_ID_ATTR(25);
+MMD_DEVICE_ID_ATTR(26);
+MMD_DEVICE_ID_ATTR(27);
+MMD_DEVICE_ID_ATTR(28);
+MMD_DEVICE_ID_ATTR(29);
+MMD_DEVICE_ID_ATTR(30);
+MMD_DEVICE_ID_ATTR(31);
+
+static struct attribute *phy_mmd_attrs[] = {
+ &dev_attr_mmd1_device_id.attr,
+ &dev_attr_mmd2_device_id.attr,
+ &dev_attr_mmd3_device_id.attr,
+ &dev_attr_mmd4_device_id.attr,
+ &dev_attr_mmd5_device_id.attr,
+ &dev_attr_mmd6_device_id.attr,
+ &dev_attr_mmd7_device_id.attr,
+ &dev_attr_mmd8_device_id.attr,
+ &dev_attr_mmd9_device_id.attr,
+ &dev_attr_mmd10_device_id.attr,
+ &dev_attr_mmd11_device_id.attr,
+ &dev_attr_mmd12_device_id.attr,
+ &dev_attr_mmd13_device_id.attr,
+ &dev_attr_mmd14_device_id.attr,
+ &dev_attr_mmd15_device_id.attr,
+ &dev_attr_mmd16_device_id.attr,
+ &dev_attr_mmd17_device_id.attr,
+ &dev_attr_mmd18_device_id.attr,
+ &dev_attr_mmd19_device_id.attr,
+ &dev_attr_mmd20_device_id.attr,
+ &dev_attr_mmd21_device_id.attr,
+ &dev_attr_mmd22_device_id.attr,
+ &dev_attr_mmd23_device_id.attr,
+ &dev_attr_mmd24_device_id.attr,
+ &dev_attr_mmd25_device_id.attr,
+ &dev_attr_mmd26_device_id.attr,
+ &dev_attr_mmd27_device_id.attr,
+ &dev_attr_mmd28_device_id.attr,
+ &dev_attr_mmd29_device_id.attr,
+ &dev_attr_mmd30_device_id.attr,
+ &dev_attr_mmd31_device_id.attr,
+ NULL
+};
+
+static umode_t phy_mmd_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct phy_device *phydev = to_phy_device(dev);
+ const int i = index + 1;
+
+ if (!phydev->is_c45)
+ return 0;
+ if (i >= ARRAY_SIZE(phydev->c45_ids.device_ids) ||
+ phydev->c45_ids.device_ids[i] == 0xffffffff)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group phy_mmd_group = {
+ .name = "c45_phy_ids",
+ .attrs = phy_mmd_attrs,
+ .is_visible = phy_mmd_is_visible,
+};
+
+static const struct attribute_group *phy_device_groups[] = {
+ &phy_dev_group,
+ &phy_mmd_group,
+ NULL,
+};
static const struct device_type mdio_bus_phy_type = {
.name = "PHY",
- .groups = phy_dev_groups,
+ .groups = phy_device_groups,
.release = phy_device_release,
.pm = pm_ptr(&mdio_bus_phy_pm_ops),
};
@@ -1515,7 +1629,6 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
struct mii_bus *bus = phydev->mdio.bus;
struct device *d = &phydev->mdio.dev;
struct module *ndev_owner = NULL;
- bool using_genphy = false;
int err;
/* For Ethernet device drivers that register their own MDIO bus, we
@@ -1541,7 +1654,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
else
d->driver = &genphy_driver.mdiodrv.driver;
- using_genphy = true;
+ phydev->is_genphy_driven = 1;
}
if (!try_module_get(d->driver->owner)) {
@@ -1550,7 +1663,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
goto error_put_device;
}
- if (using_genphy) {
+ if (phydev->is_genphy_driven) {
err = d->driver->probe(d);
if (err >= 0)
err = device_bind_driver(d);
@@ -1620,7 +1733,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
* the generic PHY driver we can't figure it out, thus set the old
* legacy PORT_MII value.
*/
- if (using_genphy)
+ if (phydev->is_genphy_driven)
phydev->port = PORT_MII;
/* Initial carrier state is off as the phy is about to be
@@ -1659,6 +1772,7 @@ error:
error_module_put:
module_put(d->driver->owner);
+ phydev->is_genphy_driven = 0;
d->driver = NULL;
error_put_device:
put_device(d);
@@ -1706,36 +1820,6 @@ struct phy_device *phy_attach(struct net_device *dev, const char *bus_id,
}
EXPORT_SYMBOL(phy_attach);
-static bool phy_driver_is_genphy_kind(struct phy_device *phydev,
- struct device_driver *driver)
-{
- struct device *d = &phydev->mdio.dev;
- bool ret = false;
-
- if (!phydev->drv)
- return ret;
-
- get_device(d);
- ret = d->driver == driver;
- put_device(d);
-
- return ret;
-}
-
-bool phy_driver_is_genphy(struct phy_device *phydev)
-{
- return phy_driver_is_genphy_kind(phydev,
- &genphy_driver.mdiodrv.driver);
-}
-EXPORT_SYMBOL_GPL(phy_driver_is_genphy);
-
-bool phy_driver_is_genphy_10g(struct phy_device *phydev)
-{
- return phy_driver_is_genphy_kind(phydev,
- &genphy_c45_driver.mdiodrv.driver);
-}
-EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g);
-
/**
* phy_detach - detach a PHY device from its network device
* @phydev: target phy_device struct
@@ -1779,6 +1863,8 @@ void phy_detach(struct phy_device *phydev)
phydev->attached_dev = NULL;
phy_link_topo_del_phy(dev, phydev);
}
+
+ phydev->phy_link_change = NULL;
phydev->phylink = NULL;
if (!phydev->is_on_sfp_module)
@@ -1792,9 +1878,10 @@ void phy_detach(struct phy_device *phydev)
* from the generic driver so that there's a chance a
* real driver could be loaded
*/
- if (phy_driver_is_genphy(phydev) ||
- phy_driver_is_genphy_10g(phydev))
+ if (phydev->is_genphy_driven) {
device_release_driver(&phydev->mdio.dev);
+ phydev->is_genphy_driven = 0;
+ }
/* Assert the reset signal */
phy_device_reset(phydev, 1);
@@ -2899,7 +2986,6 @@ static int phy_get_u32_property(struct device *dev, const char *name, u32 *val)
/**
* phy_get_internal_delay - returns the index of the internal delay
* @phydev: phy_device struct
- * @dev: pointer to the devices device struct
* @delay_values: array of delays the PHY supports
* @size: the size of the delay array
* @is_rx: boolean to indicate to get the rx internal delay
@@ -2912,9 +2998,10 @@ static int phy_get_u32_property(struct device *dev, const char *name, u32 *val)
* array then size = 0 and the value of the delay property is returned.
* Return -EINVAL if the delay is invalid or cannot be found.
*/
-s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev,
- const int *delay_values, int size, bool is_rx)
+s32 phy_get_internal_delay(struct phy_device *phydev, const int *delay_values,
+ int size, bool is_rx)
{
+ struct device *dev = &phydev->mdio.dev;
int i, ret;
u32 delay;
@@ -3416,8 +3503,7 @@ static int phy_probe(struct device *dev)
/* Get the LEDs from the device tree, and instantiate standard
* LEDs for them.
*/
- if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev) &&
- !phy_driver_is_genphy_10g(phydev))
+ if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev))
err = of_phy_leds(phydev);
out:
@@ -3434,8 +3520,7 @@ static int phy_remove(struct device *dev)
cancel_delayed_work_sync(&phydev->state_queue);
- if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev) &&
- !phy_driver_is_genphy_10g(phydev))
+ if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev))
phy_leds_unregister(phydev);
phydev->state = PHY_DOWN;
@@ -3459,7 +3544,8 @@ static int phy_remove(struct device *dev)
* @new_driver: new phy_driver to register
* @owner: module owning this PHY
*/
-int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
+static int phy_driver_register(struct phy_driver *new_driver,
+ struct module *owner)
{
int retval;
@@ -3502,7 +3588,11 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
return 0;
}
-EXPORT_SYMBOL(phy_driver_register);
+
+static void phy_driver_unregister(struct phy_driver *drv)
+{
+ driver_unregister(&drv->mdiodrv.driver);
+}
int phy_drivers_register(struct phy_driver *new_driver, int n,
struct module *owner)
@@ -3521,12 +3611,6 @@ int phy_drivers_register(struct phy_driver *new_driver, int n,
}
EXPORT_SYMBOL(phy_drivers_register);
-void phy_driver_unregister(struct phy_driver *drv)
-{
- driver_unregister(&drv->mdiodrv.driver);
-}
-EXPORT_SYMBOL(phy_driver_unregister);
-
void phy_drivers_unregister(struct phy_driver *drv, int n)
{
int i;
diff --git a/drivers/net/phy/phy_package.c b/drivers/net/phy/phy_package.c
index c738f76e8664..3024da0bbf7b 100644
--- a/drivers/net/phy/phy_package.c
+++ b/drivers/net/phy/phy_package.c
@@ -52,7 +52,8 @@ void *phy_package_get_priv(struct phy_device *phydev)
}
EXPORT_SYMBOL_GPL(phy_package_get_priv);
-int phy_package_address(struct phy_device *phydev, unsigned int addr_offset)
+static int phy_package_address(struct phy_device *phydev,
+ unsigned int addr_offset)
{
struct phy_package_shared *shared = phydev->shared;
u8 base_addr = shared->base_addr;
@@ -90,6 +91,71 @@ int __phy_package_write(struct phy_device *phydev, unsigned int addr_offset,
}
EXPORT_SYMBOL_GPL(__phy_package_write);
+/**
+ * __phy_package_read_mmd - read MMD reg relative to PHY package base addr
+ * @phydev: The phy_device struct
+ * @addr_offset: The offset to be added to PHY package base_addr
+ * @devad: The MMD to read from
+ * @regnum: The register on the MMD to read
+ *
+ * Convenience helper for reading a register of an MMD on a given PHY
+ * using the PHY package base address. The base address is added to
+ * the addr_offset value.
+ *
+ * Same calling rules as for __phy_read();
+ *
+ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
+ */
+int __phy_package_read_mmd(struct phy_device *phydev,
+ unsigned int addr_offset, int devad,
+ u32 regnum)
+{
+ int addr = phy_package_address(phydev, addr_offset);
+
+ if (addr < 0)
+ return addr;
+
+ if (regnum > (u16)~0 || devad > 32)
+ return -EINVAL;
+
+ return mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad,
+ regnum);
+}
+EXPORT_SYMBOL(__phy_package_read_mmd);
+
+/**
+ * __phy_package_write_mmd - write MMD reg relative to PHY package base addr
+ * @phydev: The phy_device struct
+ * @addr_offset: The offset to be added to PHY package base_addr
+ * @devad: The MMD to write to
+ * @regnum: The register on the MMD to write
+ * @val: value to write to @regnum
+ *
+ * Convenience helper for writing a register of an MMD on a given PHY
+ * using the PHY package base address. The base address is added to
+ * the addr_offset value.
+ *
+ * Same calling rules as for __phy_write();
+ *
+ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
+ */
+int __phy_package_write_mmd(struct phy_device *phydev,
+ unsigned int addr_offset, int devad,
+ u32 regnum, u16 val)
+{
+ int addr = phy_package_address(phydev, addr_offset);
+
+ if (addr < 0)
+ return addr;
+
+ if (regnum > (u16)~0 || devad > 32)
+ return -EINVAL;
+
+ return mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad,
+ regnum, val);
+}
+EXPORT_SYMBOL(__phy_package_write_mmd);
+
static bool __phy_package_set_once(struct phy_device *phydev, unsigned int b)
{
struct phy_package_shared *shared = phydev->shared;
@@ -348,3 +414,6 @@ int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
return ret;
}
EXPORT_SYMBOL_GPL(devm_of_phy_package_join);
+
+MODULE_DESCRIPTION("PHY package support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/phylib-internal.h b/drivers/net/phy/phylib-internal.h
index afac2bd15b50..ebda74eb60a5 100644
--- a/drivers/net/phy/phylib-internal.h
+++ b/drivers/net/phy/phylib-internal.h
@@ -7,6 +7,7 @@
#define __PHYLIB_INTERNAL_H
struct phy_device;
+struct mii_bus;
/*
* phy_supported_speeds - return all speeds currently supported by a PHY device
@@ -20,7 +21,10 @@ void of_set_phy_timing_role(struct phy_device *phydev);
int phy_speed_down_core(struct phy_device *phydev);
void phy_check_downshift(struct phy_device *phydev);
-int phy_package_address(struct phy_device *phydev, unsigned int addr_offset);
+int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45,
+ int devad, u32 regnum);
+int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45,
+ int devad, u32 regnum, u16 val);
int genphy_c45_read_eee_adv(struct phy_device *phydev, unsigned long *adv);
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 0faa3d97e06b..9d7799ea1c17 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -67,6 +67,8 @@ struct phylink {
struct timer_list link_poll;
struct mutex state_mutex;
+ /* Serialize updates to pl->phydev with phylink_resolve() */
+ struct mutex phydev_mutex;
struct phylink_link_state phy_state;
unsigned int phy_ib_mode;
struct work_struct resolve;
@@ -127,6 +129,9 @@ do { \
#endif
static const phy_interface_t phylink_sfp_interface_preference[] = {
+ PHY_INTERFACE_MODE_100GBASEP,
+ PHY_INTERFACE_MODE_50GBASER,
+ PHY_INTERFACE_MODE_LAUI,
PHY_INTERFACE_MODE_25GBASER,
PHY_INTERFACE_MODE_USXGMII,
PHY_INTERFACE_MODE_10GBASER,
@@ -234,6 +239,7 @@ static int phylink_interface_max_speed(phy_interface_t interface)
case PHY_INTERFACE_MODE_SMII:
case PHY_INTERFACE_MODE_REVMII:
case PHY_INTERFACE_MODE_MII:
+ case PHY_INTERFACE_MODE_MIILITE:
return SPEED_100;
case PHY_INTERFACE_MODE_TBI:
@@ -274,6 +280,13 @@ static int phylink_interface_max_speed(phy_interface_t interface)
case PHY_INTERFACE_MODE_XLGMII:
return SPEED_40000;
+ case PHY_INTERFACE_MODE_50GBASER:
+ case PHY_INTERFACE_MODE_LAUI:
+ return SPEED_50000;
+
+ case PHY_INTERFACE_MODE_100GBASEP:
+ return SPEED_100000;
+
case PHY_INTERFACE_MODE_INTERNAL:
case PHY_INTERFACE_MODE_NA:
case PHY_INTERFACE_MODE_MAX:
@@ -689,6 +702,9 @@ static int phylink_parse_fixedlink(struct phylink *pl,
return -EINVAL;
}
+ phylink_warn(pl, "%pfw uses deprecated array-style fixed-link binding!\n",
+ fwnode);
+
ret = fwnode_property_read_u32_array(fwnode, "fixed-link",
prop, ARRAY_SIZE(prop));
if (!ret) {
@@ -798,6 +814,9 @@ static int phylink_parse_mode(struct phylink *pl,
case PHY_INTERFACE_MODE_10GKR:
case PHY_INTERFACE_MODE_10GBASER:
case PHY_INTERFACE_MODE_XLGMII:
+ case PHY_INTERFACE_MODE_50GBASER:
+ case PHY_INTERFACE_MODE_LAUI:
+ case PHY_INTERFACE_MODE_100GBASEP:
caps = ~(MAC_SYM_PAUSE | MAC_ASYM_PAUSE);
caps = phylink_get_capabilities(pl->link_config.interface, caps,
RATE_MATCH_NONE);
@@ -1002,6 +1021,42 @@ static void phylink_pcs_an_restart(struct phylink *pl)
pl->pcs->ops->pcs_an_restart(pl->pcs);
}
+enum inband_type {
+ INBAND_NONE,
+ INBAND_CISCO_SGMII,
+ INBAND_BASEX,
+};
+
+static enum inband_type phylink_get_inband_type(phy_interface_t interface)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_QUSGMII:
+ case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10G_QXGMII:
+ /* These protocols are designed for use with a PHY which
+ * communicates its negotiation result back to the MAC via
+ * inband communication. Note: there exist PHYs that run
+ * with SGMII but do not send the inband data.
+ */
+ return INBAND_CISCO_SGMII;
+
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ /* 1000base-X is designed for use media-side for Fibre
+ * connections, and thus the Autoneg bit needs to be
+ * taken into account. We also do this for 2500base-X
+ * as well, but drivers may not support this, so may
+ * need to override this.
+ */
+ return INBAND_BASEX;
+
+ default:
+ return INBAND_NONE;
+ }
+}
+
/**
* phylink_pcs_neg_mode() - helper to determine PCS inband mode
* @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -1029,46 +1084,19 @@ static void phylink_pcs_neg_mode(struct phylink *pl, struct phylink_pcs *pcs,
unsigned int pcs_ib_caps = 0;
unsigned int phy_ib_caps = 0;
unsigned int neg_mode, mode;
- enum {
- INBAND_CISCO_SGMII,
- INBAND_BASEX,
- } type;
-
- mode = pl->req_link_an_mode;
-
- pl->phy_ib_mode = 0;
-
- switch (interface) {
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_QSGMII:
- case PHY_INTERFACE_MODE_QUSGMII:
- case PHY_INTERFACE_MODE_USXGMII:
- case PHY_INTERFACE_MODE_10G_QXGMII:
- /* These protocols are designed for use with a PHY which
- * communicates its negotiation result back to the MAC via
- * inband communication. Note: there exist PHYs that run
- * with SGMII but do not send the inband data.
- */
- type = INBAND_CISCO_SGMII;
- break;
+ enum inband_type type;
- case PHY_INTERFACE_MODE_1000BASEX:
- case PHY_INTERFACE_MODE_2500BASEX:
- /* 1000base-X is designed for use media-side for Fibre
- * connections, and thus the Autoneg bit needs to be
- * taken into account. We also do this for 2500base-X
- * as well, but drivers may not support this, so may
- * need to override this.
- */
- type = INBAND_BASEX;
- break;
-
- default:
+ type = phylink_get_inband_type(interface);
+ if (type == INBAND_NONE) {
pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE;
- pl->act_link_an_mode = mode;
+ pl->act_link_an_mode = pl->req_link_an_mode;
return;
}
+ mode = pl->req_link_an_mode;
+
+ pl->phy_ib_mode = 0;
+
if (pcs)
pcs_ib_caps = phylink_pcs_inband_caps(pcs, interface);
@@ -1409,6 +1437,7 @@ static void phylink_get_fixed_state(struct phylink *pl,
static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
{
struct phylink_link_state link_state;
+ struct phy_device *phy = pl->phydev;
switch (pl->req_link_an_mode) {
case MLO_AN_PHY:
@@ -1432,7 +1461,11 @@ static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
link_state.link = false;
phylink_apply_manual_flow(pl, &link_state);
+ if (phy)
+ mutex_lock(&phy->lock);
phylink_major_config(pl, force_restart, &link_state);
+ if (phy)
+ mutex_unlock(&phy->lock);
}
static const char *phylink_pause_to_str(int pause)
@@ -1568,8 +1601,13 @@ static void phylink_resolve(struct work_struct *w)
struct phylink_link_state link_state;
bool mac_config = false;
bool retrigger = false;
+ struct phy_device *phy;
bool cur_link_state;
+ mutex_lock(&pl->phydev_mutex);
+ phy = pl->phydev;
+ if (phy)
+ mutex_lock(&phy->lock);
mutex_lock(&pl->state_mutex);
cur_link_state = phylink_link_is_up(pl);
@@ -1603,11 +1641,11 @@ static void phylink_resolve(struct work_struct *w)
/* If we have a phy, the "up" state is the union of both the
* PHY and the MAC
*/
- if (pl->phydev)
+ if (phy)
link_state.link &= pl->phy_state.link;
/* Only update if the PHY link is up */
- if (pl->phydev && pl->phy_state.link) {
+ if (phy && pl->phy_state.link) {
/* If the interface has changed, force a link down
* event if the link isn't already down, and re-resolve.
*/
@@ -1671,6 +1709,9 @@ static void phylink_resolve(struct work_struct *w)
queue_work(system_power_efficient_wq, &pl->resolve);
}
mutex_unlock(&pl->state_mutex);
+ if (phy)
+ mutex_unlock(&phy->lock);
+ mutex_unlock(&pl->phydev_mutex);
}
static void phylink_run_resolve(struct phylink *pl)
@@ -1806,6 +1847,7 @@ struct phylink *phylink_create(struct phylink_config *config,
if (!pl)
return ERR_PTR(-ENOMEM);
+ mutex_init(&pl->phydev_mutex);
mutex_init(&pl->state_mutex);
INIT_WORK(&pl->resolve, phylink_resolve);
@@ -2066,6 +2108,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
dev_name(&phy->mdio.dev), phy->drv->name, irq_str);
kfree(irq_str);
+ mutex_lock(&pl->phydev_mutex);
mutex_lock(&phy->lock);
mutex_lock(&pl->state_mutex);
pl->phydev = phy;
@@ -2111,6 +2154,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
mutex_unlock(&pl->state_mutex);
mutex_unlock(&phy->lock);
+ mutex_unlock(&pl->phydev_mutex);
phylink_dbg(pl,
"phy: %s setting supported %*pb advertising %*pb\n",
@@ -2118,9 +2162,6 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
__ETHTOOL_LINK_MODE_MASK_NBITS, pl->supported,
__ETHTOOL_LINK_MODE_MASK_NBITS, phy->advertising);
- if (phy_interrupt_is_valid(phy))
- phy_request_interrupt(phy);
-
if (pl->config->mac_managed_pm)
phy->mac_managed_pm = true;
@@ -2137,6 +2178,9 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
ret = 0;
}
+ if (ret == 0 && phy_interrupt_is_valid(phy))
+ phy_request_interrupt(phy);
+
return ret;
}
@@ -2289,6 +2333,7 @@ void phylink_disconnect_phy(struct phylink *pl)
ASSERT_RTNL();
+ mutex_lock(&pl->phydev_mutex);
phy = pl->phydev;
if (phy) {
mutex_lock(&phy->lock);
@@ -2298,8 +2343,11 @@ void phylink_disconnect_phy(struct phylink *pl)
pl->mac_tx_clk_stop = false;
mutex_unlock(&pl->state_mutex);
mutex_unlock(&phy->lock);
- flush_work(&pl->resolve);
+ }
+ mutex_unlock(&pl->phydev_mutex);
+ if (phy) {
+ flush_work(&pl->resolve);
phy_disconnect(phy);
}
}
@@ -2696,6 +2744,39 @@ static phy_interface_t phylink_sfp_select_interface(struct phylink *pl,
return interface;
}
+static phy_interface_t phylink_sfp_select_interface_speed(struct phylink *pl,
+ u32 speed)
+{
+ phy_interface_t best_interface = PHY_INTERFACE_MODE_NA;
+ phy_interface_t interface;
+ u32 max_speed;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++) {
+ interface = phylink_sfp_interface_preference[i];
+ if (!test_bit(interface, pl->sfp_interfaces))
+ continue;
+
+ max_speed = phylink_interface_max_speed(interface);
+
+ /* The logic here is: if speed == max_speed, then we've found
+ * the best interface. Otherwise we find the interface that
+ * can just support the requested speed.
+ */
+ if (max_speed >= speed)
+ best_interface = interface;
+
+ if (max_speed <= speed)
+ break;
+ }
+
+ if (best_interface == PHY_INTERFACE_MODE_NA)
+ phylink_err(pl, "selection of interface failed, speed %u\n",
+ speed);
+
+ return best_interface;
+}
+
static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask);
@@ -2898,8 +2979,14 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
* link can be configured correctly.
*/
if (pl->sfp_bus) {
- config.interface = phylink_sfp_select_interface(pl,
+ if (kset->base.autoneg == AUTONEG_ENABLE)
+ config.interface =
+ phylink_sfp_select_interface(pl,
config.advertising);
+ else
+ config.interface =
+ phylink_sfp_select_interface_speed(pl,
+ config.speed);
if (config.interface == PHY_INTERFACE_MODE_NA)
return -EINVAL;
@@ -3523,6 +3610,8 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy)
struct phylink_link_state config;
int ret;
+ /* We're not using pl->sfp_interfaces, so clear it. */
+ phy_interface_zero(pl->sfp_interfaces);
linkmode_copy(support, phy->supported);
memset(&config, 0, sizeof(config));
@@ -3569,8 +3658,8 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy)
static int phylink_sfp_config_optical(struct phylink *pl)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
- DECLARE_PHY_INTERFACE_MASK(interfaces);
struct phylink_link_state config;
+ enum inband_type inband_type;
phy_interface_t interface;
int ret;
@@ -3583,9 +3672,9 @@ static int phylink_sfp_config_optical(struct phylink *pl)
/* Find the union of the supported interfaces by the PCS/MAC and
* the SFP module.
*/
- phy_interface_and(interfaces, pl->config->supported_interfaces,
+ phy_interface_and(pl->sfp_interfaces, pl->config->supported_interfaces,
pl->sfp_interfaces);
- if (phy_interface_empty(interfaces)) {
+ if (phy_interface_empty(pl->sfp_interfaces)) {
phylink_err(pl, "unsupported SFP module: no common interface modes\n");
return -EINVAL;
}
@@ -3601,14 +3690,14 @@ static int phylink_sfp_config_optical(struct phylink *pl)
* mask to only those link modes that can be supported.
*/
ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config,
- interfaces);
+ pl->sfp_interfaces);
if (ret) {
phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n",
__ETHTOOL_LINK_MODE_MASK_NBITS, support);
return ret;
}
- interface = phylink_choose_sfp_interface(pl, interfaces);
+ interface = phylink_choose_sfp_interface(pl, pl->sfp_interfaces);
if (interface == PHY_INTERFACE_MODE_NA) {
phylink_err(pl, "failed to select SFP interface\n");
return -EINVAL;
@@ -3617,6 +3706,23 @@ static int phylink_sfp_config_optical(struct phylink *pl)
phylink_dbg(pl, "optical SFP: chosen %s interface\n",
phy_modes(interface));
+ inband_type = phylink_get_inband_type(interface);
+ if (inband_type == INBAND_NONE) {
+ /* If this is the sole interface, and there is no inband
+ * support, clear the advertising mask and Autoneg bit in
+ * the support mask. Otherwise, just clear the Autoneg bit
+ * in the advertising mask.
+ */
+ if (phy_interface_weight(pl->sfp_interfaces) == 1) {
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+ pl->sfp_support);
+ linkmode_zero(config.advertising);
+ } else {
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+ config.advertising);
+ }
+ }
+
if (!phylink_validate_pcs_inband_autoneg(pl, interface,
config.advertising)) {
phylink_err(pl, "autoneg setting not compatible with PCS");
@@ -3644,23 +3750,31 @@ static int phylink_sfp_config_optical(struct phylink *pl)
static int phylink_sfp_module_insert(void *upstream,
const struct sfp_eeprom_id *id)
{
+ const struct sfp_module_caps *caps;
struct phylink *pl = upstream;
ASSERT_RTNL();
- linkmode_zero(pl->sfp_support);
- phy_interface_zero(pl->sfp_interfaces);
- sfp_parse_support(pl->sfp_bus, id, pl->sfp_support, pl->sfp_interfaces);
- pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, pl->sfp_support);
+ caps = sfp_get_module_caps(pl->sfp_bus);
+ phy_interface_copy(pl->sfp_interfaces, caps->interfaces);
+ linkmode_copy(pl->sfp_support, caps->link_modes);
+ pl->sfp_may_have_phy = caps->may_have_phy;
+ pl->sfp_port = caps->port;
/* If this module may have a PHY connecting later, defer until later */
- pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id);
if (pl->sfp_may_have_phy)
return 0;
return phylink_sfp_config_optical(pl);
}
+static void phylink_sfp_module_remove(void *upstream)
+{
+ struct phylink *pl = upstream;
+
+ phy_interface_zero(pl->sfp_interfaces);
+}
+
static int phylink_sfp_module_start(void *upstream)
{
struct phylink *pl = upstream;
@@ -3745,6 +3859,7 @@ static const struct sfp_upstream_ops sfp_phylink_ops = {
.attach = phylink_sfp_attach,
.detach = phylink_sfp_detach,
.module_insert = phylink_sfp_module_insert,
+ .module_remove = phylink_sfp_module_remove,
.module_start = phylink_sfp_module_start,
.module_stop = phylink_sfp_module_stop,
.link_up = phylink_sfp_link_up,
diff --git a/drivers/net/phy/qcom/Kconfig b/drivers/net/phy/qcom/Kconfig
index 570626cc8e14..06e8430c13b1 100644
--- a/drivers/net/phy/qcom/Kconfig
+++ b/drivers/net/phy/qcom/Kconfig
@@ -7,7 +7,7 @@ config AT803X_PHY
select QCOM_NET_PHYLIB
depends on REGULATOR
help
- Currently supports the AR8030, AR8031, AR8033, AR8035 model
+ Currently supports the AR8030, AR8031, AR8033, AR8035, IPQ5018 model
config QCA83XX_PHY
tristate "Qualcomm Atheros QCA833x PHYs"
@@ -24,6 +24,7 @@ config QCA808X_PHY
config QCA807X_PHY
tristate "Qualcomm QCA807x PHYs"
select QCOM_NET_PHYLIB
+ select PHY_PACKAGE
depends on OF_MDIO
help
Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII
diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c
index 8f26e395e39f..338acd11a9b6 100644
--- a/drivers/net/phy/qcom/at803x.c
+++ b/drivers/net/phy/qcom/at803x.c
@@ -19,6 +19,7 @@
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/phylink.h>
+#include <linux/reset.h>
#include <linux/sfp.h>
#include <dt-bindings/net/qca-ar803x.h>
@@ -93,6 +94,8 @@
#define ATH8035_PHY_ID 0x004dd072
#define AT8030_PHY_ID_MASK 0xffffffef
+#define IPQ5018_PHY_ID 0x004dd0c0
+
#define QCA9561_PHY_ID 0x004dd042
#define AT803X_PAGE_FIBER 0
@@ -105,6 +108,48 @@
/* disable hibernation mode */
#define AT803X_DISABLE_HIBERNATION_MODE BIT(2)
+#define IPQ5018_PHY_FIFO_CONTROL 0x19
+#define IPQ5018_PHY_FIFO_RESET GENMASK(1, 0)
+
+#define IPQ5018_PHY_DEBUG_EDAC 0x4380
+#define IPQ5018_PHY_MMD1_MDAC 0x8100
+#define IPQ5018_PHY_DAC_MASK GENMASK(15, 8)
+
+/* MDAC and EDAC values for short cable length */
+#define IPQ5018_PHY_DEBUG_EDAC_VAL 0x10
+#define IPQ5018_PHY_MMD1_MDAC_VAL 0x10
+
+#define IPQ5018_PHY_MMD1_MSE_THRESH1 0x1000
+#define IPQ5018_PHY_MMD1_MSE_THRESH2 0x1001
+#define IPQ5018_PHY_PCS_EEE_TX_TIMER 0x8008
+#define IPQ5018_PHY_PCS_EEE_RX_TIMER 0x8009
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL3 0x8074
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL4 0x8075
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL5 0x8076
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL6 0x8077
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL7 0x8078
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL9 0x807a
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL13 0x807e
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL14 0x807f
+
+#define IPQ5018_PHY_MMD1_MSE_THRESH1_VAL 0xf1
+#define IPQ5018_PHY_MMD1_MSE_THRESH2_VAL 0x1f6
+#define IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL 0x7880
+#define IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL 0xc8
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL3_VAL 0xc040
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL4_VAL 0xa060
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL5_VAL 0xc040
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL6_VAL 0xa060
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL7_VAL 0xc24c
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL9_VAL 0xc060
+#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL13_VAL 0xb060
+#define IPQ5018_PHY_PCS_NEAR_ECHO_THRESH_VAL 0x90b0
+
+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE 0x1
+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK GENMASK(7, 4)
+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT 0x50
+#define IPQ5018_PHY_DEBUG_ANA_DAC_FILTER 0xa080
+
MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
MODULE_AUTHOR("Matus Ujhelyi");
MODULE_LICENSE("GPL");
@@ -130,6 +175,11 @@ struct at803x_context {
u16 led_control;
};
+struct ipq5018_priv {
+ struct reset_control *rst;
+ bool set_short_cable_dac;
+};
+
static int at803x_write_page(struct phy_device *phydev, int page)
{
int mask;
@@ -721,10 +771,10 @@ static int at8031_register_regulators(struct phy_device *phydev)
static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{
- struct phy_device *phydev = upstream;
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
- DECLARE_PHY_INTERFACE_MASK(interfaces);
+ struct phy_device *phydev = upstream;
+ const struct sfp_module_caps *caps;
phy_interface_t iface;
linkmode_zero(phy_support);
@@ -734,12 +784,11 @@ static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
phylink_set(phy_support, Pause);
phylink_set(phy_support, Asym_Pause);
- linkmode_zero(sfp_support);
- sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
+ caps = sfp_get_module_caps(phydev->sfp_bus);
/* Some modules support 10G modes as well as others we support.
* Mask out non-supported modes so the correct interface is picked.
*/
- linkmode_and(sfp_support, phy_support, sfp_support);
+ linkmode_and(sfp_support, phy_support, caps->link_modes);
if (linkmode_empty(sfp_support)) {
dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
@@ -960,6 +1009,109 @@ static int at8035_probe(struct phy_device *phydev)
return at8035_parse_dt(phydev);
}
+static int ipq5018_cable_test_start(struct phy_device *phydev)
+{
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL3,
+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL3_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL4,
+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL4_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL5,
+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL5_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL6,
+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL6_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL7,
+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL7_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL9,
+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL9_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL13,
+ IPQ5018_PHY_PCS_CDT_THRESH_CTRL13_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL3,
+ IPQ5018_PHY_PCS_NEAR_ECHO_THRESH_VAL);
+
+ /* we do all the (time consuming) work later */
+ return 0;
+}
+
+static int ipq5018_config_init(struct phy_device *phydev)
+{
+ struct ipq5018_priv *priv = phydev->priv;
+ u16 val;
+
+ /*
+ * set LDO efuse: first temporarily store ANA_DAC_FILTER value from
+ * debug register as it will be reset once the ANA_LDO_EFUSE register
+ * is written to
+ */
+ val = at803x_debug_reg_read(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER);
+ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE,
+ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK,
+ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT);
+ at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, val);
+
+ /* set 8023AZ EEE TX and RX timer values */
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_TX_TIMER,
+ IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_RX_TIMER,
+ IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL);
+
+ /* set MSE threshold values */
+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH1,
+ IPQ5018_PHY_MMD1_MSE_THRESH1_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH2,
+ IPQ5018_PHY_MMD1_MSE_THRESH2_VAL);
+
+ /* PHY DAC values are optional and only set in a PHY to PHY link architecture */
+ if (priv->set_short_cable_dac) {
+ /* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */
+ phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC,
+ IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_MMD1_MDAC_VAL);
+
+ /* setting EDAC (Error-detection and Correction) in debug register */
+ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC,
+ IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_DEBUG_EDAC_VAL);
+ }
+
+ return 0;
+}
+
+static void ipq5018_link_change_notify(struct phy_device *phydev)
+{
+ /*
+ * Reset the FIFO buffer upon link disconnects to clear any residual data
+ * which may cause issues with the FIFO which it cannot recover from.
+ */
+ mdiobus_modify_changed(phydev->mdio.bus, phydev->mdio.addr,
+ IPQ5018_PHY_FIFO_CONTROL, IPQ5018_PHY_FIFO_RESET,
+ phydev->link ? IPQ5018_PHY_FIFO_RESET : 0);
+}
+
+static int ipq5018_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct ipq5018_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->set_short_cable_dac = of_property_read_bool(dev->of_node,
+ "qcom,dac-preset-short-cable");
+
+ priv->rst = devm_reset_control_array_get_exclusive(dev);
+ if (IS_ERR(priv->rst))
+ return dev_err_probe(dev, PTR_ERR(priv->rst),
+ "failed to acquire reset\n");
+
+ ret = reset_control_reset(priv->rst);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to reset\n");
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
static struct phy_driver at803x_driver[] = {
{
/* Qualcomm Atheros AR8035 */
@@ -1052,6 +1204,19 @@ static struct phy_driver at803x_driver[] = {
.soft_reset = genphy_soft_reset,
.config_aneg = at803x_config_aneg,
}, {
+ PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID),
+ .name = "Qualcomm Atheros IPQ5018 internal PHY",
+ .flags = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST,
+ .probe = ipq5018_probe,
+ .config_init = ipq5018_config_init,
+ .link_change_notify = ipq5018_link_change_notify,
+ .read_status = at803x_read_status,
+ .config_intr = at803x_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .cable_test_start = ipq5018_cable_test_start,
+ .cable_test_get_status = qca808x_cable_test_get_status,
+ .soft_reset = genphy_soft_reset,
+}, {
/* Qualcomm Atheros QCA9561 */
PHY_ID_MATCH_EXACT(QCA9561_PHY_ID),
.name = "Qualcomm Atheros QCA9561 built-in PHY",
@@ -1077,6 +1242,7 @@ static const struct mdio_device_id __maybe_unused atheros_tbl[] = {
{ PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) },
{ PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
{ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
+ { PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID) },
{ PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
{ }
};
diff --git a/drivers/net/phy/qcom/qca807x.c b/drivers/net/phy/qcom/qca807x.c
index 1af6b5ead74b..1be8295a95cb 100644
--- a/drivers/net/phy/qcom/qca807x.c
+++ b/drivers/net/phy/qcom/qca807x.c
@@ -124,6 +124,7 @@ struct qca807x_priv {
bool dac_full_amplitude;
bool dac_full_bias_current;
bool dac_disable_bias_current_tweak;
+ struct qcom_phy_hw_stats hw_stats;
};
static int qca807x_cable_test_start(struct phy_device *phydev)
@@ -377,7 +378,7 @@ static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset)
return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val);
}
-static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+static int qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
u16 reg;
@@ -386,18 +387,19 @@ static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int valu
reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
+ if (val < 0)
+ return val;
+
val &= ~QCA807X_GPIO_FORCE_MODE_MASK;
val |= QCA807X_GPIO_FORCE_EN;
val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value);
- phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val);
+ return phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val);
}
static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value)
{
- qca807x_gpio_set(gc, offset, value);
-
- return 0;
+ return qca807x_gpio_set(gc, offset, value);
}
static int qca807x_gpio(struct phy_device *phydev)
@@ -644,13 +646,12 @@ exit:
static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{
struct phy_device *phydev = upstream;
- __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
+ const struct sfp_module_caps *caps;
phy_interface_t iface;
int ret;
- DECLARE_PHY_INTERFACE_MASK(interfaces);
- sfp_parse_support(phydev->sfp_bus, id, support, interfaces);
- iface = sfp_select_interface(phydev->sfp_bus, support);
+ caps = sfp_get_module_caps(phydev->sfp_bus);
+ iface = sfp_select_interface(phydev->sfp_bus, caps->link_modes);
dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface));
@@ -767,6 +768,10 @@ static int qca807x_config_init(struct phy_device *phydev)
return ret;
}
+ ret = qcom_phy_counter_config(phydev);
+ if (ret)
+ return ret;
+
control_dac = phy_read_mmd(phydev, MDIO_MMD_AN,
QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH);
control_dac &= ~QCA807X_CONTROL_DAC_MASK;
@@ -781,6 +786,22 @@ static int qca807x_config_init(struct phy_device *phydev)
control_dac);
}
+static int qca807x_update_stats(struct phy_device *phydev)
+{
+ struct qca807x_priv *priv = phydev->priv;
+
+ return qcom_phy_update_stats(phydev, &priv->hw_stats);
+}
+
+static void qca807x_get_phy_stats(struct phy_device *phydev,
+ struct ethtool_eth_phy_stats *eth_stats,
+ struct ethtool_phy_stats *stats)
+{
+ struct qca807x_priv *priv = phydev->priv;
+
+ qcom_phy_get_stats(stats, priv->hw_stats);
+}
+
static struct phy_driver qca807x_drivers[] = {
{
PHY_ID_MATCH_EXACT(PHY_ID_QCA8072),
@@ -799,6 +820,10 @@ static struct phy_driver qca807x_drivers[] = {
.suspend = genphy_suspend,
.cable_test_start = qca807x_cable_test_start,
.cable_test_get_status = qca808x_cable_test_get_status,
+ .update_stats = qca807x_update_stats,
+ .get_phy_stats = qca807x_get_phy_stats,
+ .set_wol = at8031_set_wol,
+ .get_wol = at803x_get_wol,
},
{
PHY_ID_MATCH_EXACT(PHY_ID_QCA8075),
@@ -822,6 +847,10 @@ static struct phy_driver qca807x_drivers[] = {
.led_hw_is_supported = qca807x_led_hw_is_supported,
.led_hw_control_set = qca807x_led_hw_control_set,
.led_hw_control_get = qca807x_led_hw_control_get,
+ .update_stats = qca807x_update_stats,
+ .get_phy_stats = qca807x_get_phy_stats,
+ .set_wol = at8031_set_wol,
+ .get_wol = at803x_get_wol,
},
};
module_phy_driver(qca807x_drivers);
diff --git a/drivers/net/phy/qcom/qca808x.c b/drivers/net/phy/qcom/qca808x.c
index 6de16c0eaa08..8eb51b1a006c 100644
--- a/drivers/net/phy/qcom/qca808x.c
+++ b/drivers/net/phy/qcom/qca808x.c
@@ -93,6 +93,7 @@ MODULE_LICENSE("GPL");
struct qca808x_priv {
int led_polarity_mode;
+ struct qcom_phy_hw_stats hw_stats;
};
static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
@@ -243,6 +244,10 @@ static int qca808x_config_init(struct phy_device *phydev)
qca808x_fill_possible_interfaces(phydev);
+ ret = qcom_phy_counter_config(phydev);
+ if (ret)
+ return ret;
+
/* Configure adc threshold as 100mv for the link 10M */
return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
QCA808X_ADC_THRESHOLD_MASK,
@@ -622,6 +627,22 @@ static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
}
+static int qca808x_update_stats(struct phy_device *phydev)
+{
+ struct qca808x_priv *priv = phydev->priv;
+
+ return qcom_phy_update_stats(phydev, &priv->hw_stats);
+}
+
+static void qca808x_get_phy_stats(struct phy_device *phydev,
+ struct ethtool_eth_phy_stats *eth_stats,
+ struct ethtool_phy_stats *stats)
+{
+ struct qca808x_priv *priv = phydev->priv;
+
+ qcom_phy_get_stats(stats, priv->hw_stats);
+}
+
static struct phy_driver qca808x_driver[] = {
{
/* Qualcomm QCA8081 */
@@ -651,6 +672,8 @@ static struct phy_driver qca808x_driver[] = {
.led_hw_control_set = qca808x_led_hw_control_set,
.led_hw_control_get = qca808x_led_hw_control_get,
.led_polarity_set = qca808x_led_polarity_set,
+ .update_stats = qca808x_update_stats,
+ .get_phy_stats = qca808x_get_phy_stats,
}, };
module_phy_driver(qca808x_driver);
diff --git a/drivers/net/phy/qcom/qcom-phy-lib.c b/drivers/net/phy/qcom/qcom-phy-lib.c
index af7d0d8e81be..965c2bb99a9b 100644
--- a/drivers/net/phy/qcom/qcom-phy-lib.c
+++ b/drivers/net/phy/qcom/qcom-phy-lib.c
@@ -699,3 +699,78 @@ int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
return 0;
}
EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set);
+
+/* Enable CRC checking for both received and transmitted frames to ensure
+ * accurate counter recording. The hardware supports a 32-bit counter,
+ * configure the counter to clear after it is read to facilitate the
+ * implementation of a 64-bit software counter
+ */
+int qcom_phy_counter_config(struct phy_device *phydev)
+{
+ return phy_set_bits_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_CTRL,
+ QCA808X_MMD7_CNT_CTRL_CRC_CHECK_EN |
+ QCA808X_MMD7_CNT_CTRL_READ_CLEAR_EN);
+}
+EXPORT_SYMBOL_GPL(qcom_phy_counter_config);
+
+int qcom_phy_update_stats(struct phy_device *phydev,
+ struct qcom_phy_hw_stats *hw_stats)
+{
+ int ret;
+ u32 cnt;
+
+ /* PHY 32-bit counter for RX packets. */
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_PKT_15_0);
+ if (ret < 0)
+ return ret;
+
+ cnt = ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_PKT_31_16);
+ if (ret < 0)
+ return ret;
+
+ cnt |= ret << 16;
+ hw_stats->rx_pkts += cnt;
+
+ /* PHY 16-bit counter for RX CRC error packets. */
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_ERR_PKT);
+ if (ret < 0)
+ return ret;
+
+ hw_stats->rx_err_pkts += ret;
+
+ /* PHY 32-bit counter for TX packets. */
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_PKT_15_0);
+ if (ret < 0)
+ return ret;
+
+ cnt = ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_PKT_31_16);
+ if (ret < 0)
+ return ret;
+
+ cnt |= ret << 16;
+ hw_stats->tx_pkts += cnt;
+
+ /* PHY 16-bit counter for TX CRC error packets. */
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_ERR_PKT);
+ if (ret < 0)
+ return ret;
+
+ hw_stats->tx_err_pkts += ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_phy_update_stats);
+
+void qcom_phy_get_stats(struct ethtool_phy_stats *stats,
+ struct qcom_phy_hw_stats hw_stats)
+{
+ stats->tx_packets = hw_stats.tx_pkts;
+ stats->tx_errors = hw_stats.tx_err_pkts;
+ stats->rx_packets = hw_stats.rx_pkts;
+ stats->rx_errors = hw_stats.rx_err_pkts;
+}
+EXPORT_SYMBOL_GPL(qcom_phy_get_stats);
diff --git a/drivers/net/phy/qcom/qcom.h b/drivers/net/phy/qcom/qcom.h
index 7f7151c8baca..5071e7149a11 100644
--- a/drivers/net/phy/qcom/qcom.h
+++ b/drivers/net/phy/qcom/qcom.h
@@ -195,6 +195,17 @@
#define AT803X_MIN_DOWNSHIFT 2
#define AT803X_MAX_DOWNSHIFT 9
+#define QCA808X_MMD7_CNT_CTRL 0x8029
+#define QCA808X_MMD7_CNT_CTRL_READ_CLEAR_EN BIT(1)
+#define QCA808X_MMD7_CNT_CTRL_CRC_CHECK_EN BIT(0)
+
+#define QCA808X_MMD7_CNT_RX_PKT_31_16 0x802a
+#define QCA808X_MMD7_CNT_RX_PKT_15_0 0x802b
+#define QCA808X_MMD7_CNT_RX_ERR_PKT 0x802c
+#define QCA808X_MMD7_CNT_TX_PKT_31_16 0x802d
+#define QCA808X_MMD7_CNT_TX_PKT_15_0 0x802e
+#define QCA808X_MMD7_CNT_TX_ERR_PKT 0x802f
+
enum stat_access_type {
PHY,
MMD
@@ -212,6 +223,13 @@ struct at803x_ss_mask {
u8 speed_shift;
};
+struct qcom_phy_hw_stats {
+ u64 rx_pkts;
+ u64 rx_err_pkts;
+ u64 tx_pkts;
+ u64 tx_err_pkts;
+};
+
int at803x_debug_reg_read(struct phy_device *phydev, u16 reg);
int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
u16 clear, u16 set);
@@ -246,3 +264,8 @@ int qca808x_led_reg_brightness_set(struct phy_device *phydev,
int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
unsigned long *delay_on,
unsigned long *delay_off);
+int qcom_phy_counter_config(struct phy_device *phydev);
+int qcom_phy_update_stats(struct phy_device *phydev,
+ struct qcom_phy_hw_stats *hw_stats);
+void qcom_phy_get_stats(struct ethtool_phy_stats *stats,
+ struct qcom_phy_hw_stats hw_stats);
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index c3dcb6257430..82d8e1335215 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -10,6 +10,7 @@
#include <linux/bitops.h>
#include <linux/of.h>
#include <linux/phy.h>
+#include <linux/pm_wakeirq.h>
#include <linux/netdevice.h>
#include <linux/module.h>
#include <linux/delay.h>
@@ -31,6 +32,7 @@
#define RTL821x_INER 0x12
#define RTL8211B_INER_INIT 0x6400
#define RTL8211E_INER_LINK_STATUS BIT(10)
+#define RTL8211F_INER_PME BIT(7)
#define RTL8211F_INER_LINK_STATUS BIT(4)
#define RTL821x_INSR 0x13
@@ -96,17 +98,13 @@
#define RTL8211F_RXCR 0x15
#define RTL8211F_RX_DELAY BIT(3)
-/* RTL8211F WOL interrupt configuration */
-#define RTL8211F_INTBCR_PAGE 0xd40
-#define RTL8211F_INTBCR 0x16
-#define RTL8211F_INTBCR_INTB_PMEB BIT(5)
-
/* RTL8211F WOL settings */
-#define RTL8211F_WOL_SETTINGS_PAGE 0xd8a
+#define RTL8211F_WOL_PAGE 0xd8a
#define RTL8211F_WOL_SETTINGS_EVENTS 16
#define RTL8211F_WOL_EVENT_MAGIC BIT(12)
-#define RTL8211F_WOL_SETTINGS_STATUS 17
-#define RTL8211F_WOL_STATUS_RESET (BIT(15) | 0x1fff)
+#define RTL8211F_WOL_RST_RMSQ 17
+#define RTL8211F_WOL_RG_RSTB BIT(15)
+#define RTL8211F_WOL_RMSQ 0x1fff
/* RTL8211F Unique phyiscal and multicast address (WOL) */
#define RTL8211F_PHYSICAL_ADDR_PAGE 0xd8c
@@ -172,7 +170,8 @@ struct rtl821x_priv {
u16 phycr2;
bool has_phycr2;
struct clk *clk;
- u32 saved_wolopts;
+ /* rtl8211f */
+ u16 iner;
};
static int rtl821x_read_page(struct phy_device *phydev)
@@ -255,6 +254,34 @@ static int rtl821x_probe(struct phy_device *phydev)
return 0;
}
+static int rtl8211f_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ int ret;
+
+ ret = rtl821x_probe(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* Disable all PME events */
+ ret = phy_write_paged(phydev, RTL8211F_WOL_PAGE,
+ RTL8211F_WOL_SETTINGS_EVENTS, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Mark this PHY as wakeup capable and register the interrupt as a
+ * wakeup IRQ if the PHY is marked as a wakeup source in firmware,
+ * and the interrupt is valid.
+ */
+ if (device_property_read_bool(dev, "wakeup-source") &&
+ phy_interrupt_is_valid(phydev)) {
+ device_set_wakeup_capable(dev, true);
+ devm_pm_set_wake_irq(dev, phydev->irq);
+ }
+
+ return ret;
+}
+
static int rtl8201_ack_interrupt(struct phy_device *phydev)
{
int err;
@@ -352,6 +379,7 @@ static int rtl8211e_config_intr(struct phy_device *phydev)
static int rtl8211f_config_intr(struct phy_device *phydev)
{
+ struct rtl821x_priv *priv = phydev->priv;
u16 val;
int err;
@@ -362,8 +390,10 @@ static int rtl8211f_config_intr(struct phy_device *phydev)
val = RTL8211F_INER_LINK_STATUS;
err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val);
+ if (err == 0)
+ priv->iner = val;
} else {
- val = 0;
+ priv->iner = val = 0;
err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val);
if (err)
return err;
@@ -426,19 +456,38 @@ static irqreturn_t rtl8211f_handle_interrupt(struct phy_device *phydev)
return IRQ_NONE;
}
- if (!(irq_status & RTL8211F_INER_LINK_STATUS))
- return IRQ_NONE;
+ if (irq_status & RTL8211F_INER_LINK_STATUS) {
+ phy_trigger_machine(phydev);
+ return IRQ_HANDLED;
+ }
- phy_trigger_machine(phydev);
+ if (irq_status & RTL8211F_INER_PME) {
+ pm_wakeup_event(&phydev->mdio.dev, 0);
+ return IRQ_HANDLED;
+ }
- return IRQ_HANDLED;
+ return IRQ_NONE;
}
static void rtl8211f_get_wol(struct phy_device *dev, struct ethtool_wolinfo *wol)
{
+ int wol_events;
+
+ /* If the PHY is not capable of waking the system, then WoL can not
+ * be supported.
+ */
+ if (!device_can_wakeup(&dev->mdio.dev)) {
+ wol->supported = 0;
+ return;
+ }
+
wol->supported = WAKE_MAGIC;
- if (phy_read_paged(dev, RTL8211F_WOL_SETTINGS_PAGE, RTL8211F_WOL_SETTINGS_EVENTS)
- & RTL8211F_WOL_EVENT_MAGIC)
+
+ wol_events = phy_read_paged(dev, RTL8211F_WOL_PAGE, RTL8211F_WOL_SETTINGS_EVENTS);
+ if (wol_events < 0)
+ return;
+
+ if (wol_events & RTL8211F_WOL_EVENT_MAGIC)
wol->wolopts = WAKE_MAGIC;
}
@@ -447,6 +496,9 @@ static int rtl8211f_set_wol(struct phy_device *dev, struct ethtool_wolinfo *wol)
const u8 *mac_addr = dev->attached_dev->dev_addr;
int oldpage;
+ if (!device_can_wakeup(&dev->mdio.dev))
+ return -EOPNOTSUPP;
+
oldpage = phy_save_page(dev);
if (oldpage < 0)
goto err;
@@ -458,25 +510,23 @@ static int rtl8211f_set_wol(struct phy_device *dev, struct ethtool_wolinfo *wol)
__phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD1, mac_addr[3] << 8 | (mac_addr[2]));
__phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD2, mac_addr[5] << 8 | (mac_addr[4]));
- /* Enable magic packet matching and reset WOL status */
- rtl821x_write_page(dev, RTL8211F_WOL_SETTINGS_PAGE);
+ /* Enable magic packet matching */
+ rtl821x_write_page(dev, RTL8211F_WOL_PAGE);
__phy_write(dev, RTL8211F_WOL_SETTINGS_EVENTS, RTL8211F_WOL_EVENT_MAGIC);
- __phy_write(dev, RTL8211F_WOL_SETTINGS_STATUS, RTL8211F_WOL_STATUS_RESET);
-
- /* Enable the WOL interrupt */
- rtl821x_write_page(dev, RTL8211F_INTBCR_PAGE);
- __phy_set_bits(dev, RTL8211F_INTBCR, RTL8211F_INTBCR_INTB_PMEB);
+ /* Set the maximum packet size, and assert WoL reset */
+ __phy_write(dev, RTL8211F_WOL_RST_RMSQ, RTL8211F_WOL_RMSQ);
} else {
- /* Disable the WOL interrupt */
- rtl821x_write_page(dev, RTL8211F_INTBCR_PAGE);
- __phy_clear_bits(dev, RTL8211F_INTBCR, RTL8211F_INTBCR_INTB_PMEB);
-
- /* Disable magic packet matching and reset WOL status */
- rtl821x_write_page(dev, RTL8211F_WOL_SETTINGS_PAGE);
+ /* Disable magic packet matching */
+ rtl821x_write_page(dev, RTL8211F_WOL_PAGE);
__phy_write(dev, RTL8211F_WOL_SETTINGS_EVENTS, 0);
- __phy_write(dev, RTL8211F_WOL_SETTINGS_STATUS, RTL8211F_WOL_STATUS_RESET);
+
+ /* Place WoL in reset */
+ __phy_clear_bits(dev, RTL8211F_WOL_RST_RMSQ,
+ RTL8211F_WOL_RG_RSTB);
}
+ device_set_wakeup_enable(&dev->mdio.dev, !!(wol->wolopts & WAKE_MAGIC));
+
err:
return phy_restore_page(dev, oldpage, 0);
}
@@ -622,6 +672,52 @@ static int rtl821x_suspend(struct phy_device *phydev)
return ret;
}
+static int rtl8211f_suspend(struct phy_device *phydev)
+{
+ u16 wol_rst;
+ int ret;
+
+ ret = rtl821x_suspend(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* If a PME event is enabled, then configure the interrupt for
+ * PME events only, disabling link interrupt. We avoid switching
+ * to PMEB mode as we don't have a status bit for that.
+ */
+ if (device_may_wakeup(&phydev->mdio.dev)) {
+ ret = phy_write_paged(phydev, 0xa42, RTL821x_INER,
+ RTL8211F_INER_PME);
+ if (ret < 0)
+ goto err;
+
+ /* Read the INSR to clear any pending interrupt */
+ phy_read_paged(phydev, RTL8211F_INSR_PAGE, RTL8211F_INSR);
+
+ /* Reset the WoL to ensure that an event is picked up.
+ * Unless we do this, even if we receive another packet,
+ * we may not have a PME interrupt raised.
+ */
+ ret = phy_read_paged(phydev, RTL8211F_WOL_PAGE,
+ RTL8211F_WOL_RST_RMSQ);
+ if (ret < 0)
+ goto err;
+
+ wol_rst = ret & ~RTL8211F_WOL_RG_RSTB;
+ ret = phy_write_paged(phydev, RTL8211F_WOL_PAGE,
+ RTL8211F_WOL_RST_RMSQ, wol_rst);
+ if (ret < 0)
+ goto err;
+
+ wol_rst |= RTL8211F_WOL_RG_RSTB;
+ ret = phy_write_paged(phydev, RTL8211F_WOL_PAGE,
+ RTL8211F_WOL_RST_RMSQ, wol_rst);
+ }
+
+err:
+ return ret;
+}
+
static int rtl821x_resume(struct phy_device *phydev)
{
struct rtl821x_priv *priv = phydev->priv;
@@ -639,10 +735,29 @@ static int rtl821x_resume(struct phy_device *phydev)
return 0;
}
+static int rtl8211f_resume(struct phy_device *phydev)
+{
+ struct rtl821x_priv *priv = phydev->priv;
+ int ret;
+
+ ret = rtl821x_resume(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* If the device was programmed for a PME event, restore the interrupt
+ * enable so phylib can receive link state interrupts.
+ */
+ if (device_may_wakeup(&phydev->mdio.dev))
+ ret = phy_write_paged(phydev, 0xa42, RTL821x_INER, priv->iner);
+
+ return ret;
+}
+
static int rtl8211x_led_hw_is_supported(struct phy_device *phydev, u8 index,
unsigned long rules)
{
- const unsigned long mask = BIT(TRIGGER_NETDEV_LINK_10) |
+ const unsigned long mask = BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_LINK_10) |
BIT(TRIGGER_NETDEV_LINK_100) |
BIT(TRIGGER_NETDEV_LINK_1000) |
BIT(TRIGGER_NETDEV_RX) |
@@ -700,6 +815,12 @@ static int rtl8211f_led_hw_control_get(struct phy_device *phydev, u8 index,
if (val & RTL8211F_LEDCR_LINK_1000)
__set_bit(TRIGGER_NETDEV_LINK_1000, rules);
+ if ((val & RTL8211F_LEDCR_LINK_10) &&
+ (val & RTL8211F_LEDCR_LINK_100) &&
+ (val & RTL8211F_LEDCR_LINK_1000)) {
+ __set_bit(TRIGGER_NETDEV_LINK, rules);
+ }
+
if (val & RTL8211F_LEDCR_ACT_TXRX) {
__set_bit(TRIGGER_NETDEV_RX, rules);
__set_bit(TRIGGER_NETDEV_TX, rules);
@@ -717,14 +838,20 @@ static int rtl8211f_led_hw_control_set(struct phy_device *phydev, u8 index,
if (index >= RTL8211x_LED_COUNT)
return -EINVAL;
- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_10, &rules)) {
reg |= RTL8211F_LEDCR_LINK_10;
+ }
- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_100, &rules)) {
reg |= RTL8211F_LEDCR_LINK_100;
+ }
- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) {
reg |= RTL8211F_LEDCR_LINK_1000;
+ }
if (test_bit(TRIGGER_NETDEV_RX, &rules) ||
test_bit(TRIGGER_NETDEV_TX, &rules)) {
@@ -772,6 +899,12 @@ static int rtl8211e_led_hw_control_get(struct phy_device *phydev, u8 index,
if (cr2 & RTL8211E_LEDCR2_LINK_1000)
__set_bit(TRIGGER_NETDEV_LINK_1000, rules);
+ if ((cr2 & RTL8211E_LEDCR2_LINK_10) &&
+ (cr2 & RTL8211E_LEDCR2_LINK_100) &&
+ (cr2 & RTL8211E_LEDCR2_LINK_1000)) {
+ __set_bit(TRIGGER_NETDEV_LINK, rules);
+ }
+
return ret;
}
@@ -799,14 +932,20 @@ static int rtl8211e_led_hw_control_set(struct phy_device *phydev, u8 index,
if (ret < 0)
return ret;
- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_10, &rules)) {
cr2 |= RTL8211E_LEDCR2_LINK_10;
+ }
- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_100, &rules)) {
cr2 |= RTL8211E_LEDCR2_LINK_100;
+ }
- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) {
cr2 |= RTL8211E_LEDCR2_LINK_1000;
+ }
cr2 <<= RTL8211E_LEDCR2_SHIFT * index;
ret = rtl821x_modify_ext_page(phydev, RTL8211E_LEDCR_EXT_PAGE,
@@ -1032,7 +1171,7 @@ static int rtl822x_probe(struct phy_device *phydev)
return 0;
}
-static int rtl822xb_config_init(struct phy_device *phydev)
+static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1)
{
bool has_2500, has_sgmii;
u16 mode;
@@ -1067,15 +1206,18 @@ static int rtl822xb_config_init(struct phy_device *phydev)
/* the following sequence with magic numbers sets up the SerDes
* option mode
*/
- ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75f3, 0);
- if (ret < 0)
- return ret;
+
+ if (!gen1) {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75f3, 0);
+ if (ret < 0)
+ return ret;
+ }
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1,
RTL822X_VND1_SERDES_OPTION,
RTL822X_VND1_SERDES_OPTION_MODE_MASK,
mode);
- if (ret < 0)
+ if (gen1 || ret < 0)
return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6a04, 0x0503);
@@ -1089,6 +1231,16 @@ static int rtl822xb_config_init(struct phy_device *phydev)
return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
}
+static int rtl822x_config_init(struct phy_device *phydev)
+{
+ return rtl822x_set_serdes_option_mode(phydev, true);
+}
+
+static int rtl822xb_config_init(struct phy_device *phydev)
+{
+ return rtl822x_set_serdes_option_mode(phydev, false);
+}
+
static int rtl822xb_get_rate_matching(struct phy_device *phydev,
phy_interface_t iface)
{
@@ -1274,6 +1426,21 @@ static int rtl822x_c45_read_status(struct phy_device *phydev)
return 0;
}
+static int rtl822x_c45_soft_reset(struct phy_device *phydev)
+{
+ int ret, val;
+
+ ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1,
+ MDIO_CTRL1_RESET, MDIO_CTRL1_RESET);
+ if (ret < 0)
+ return ret;
+
+ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PMAPMD,
+ MDIO_CTRL1, val,
+ !(val & MDIO_CTRL1_RESET),
+ 5000, 100000, true);
+}
+
static int rtl822xb_c45_read_status(struct phy_device *phydev)
{
int ret;
@@ -1606,15 +1773,15 @@ static struct phy_driver realtek_drvs[] = {
}, {
PHY_ID_MATCH_EXACT(0x001cc916),
.name = "RTL8211F Gigabit Ethernet",
- .probe = rtl821x_probe,
+ .probe = rtl8211f_probe,
.config_init = &rtl8211f_config_init,
.read_status = rtlgen_read_status,
.config_intr = &rtl8211f_config_intr,
.handle_interrupt = rtl8211f_handle_interrupt,
.set_wol = rtl8211f_set_wol,
.get_wol = rtl8211f_get_wol,
- .suspend = rtl821x_suspend,
- .resume = rtl821x_resume,
+ .suspend = rtl8211f_suspend,
+ .resume = rtl8211f_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
.flags = PHY_ALWAYS_CALL_SUSPEND,
@@ -1669,13 +1836,13 @@ static struct phy_driver realtek_drvs[] = {
}, {
PHY_ID_MATCH_EXACT(0x001cc838),
.name = "RTL8226-CG 2.5Gbps PHY",
- .get_features = rtl822x_get_features,
- .config_aneg = rtl822x_config_aneg,
- .read_status = rtl822x_read_status,
- .suspend = genphy_suspend,
- .resume = rtlgen_resume,
- .read_page = rtl821x_read_page,
- .write_page = rtl821x_write_page,
+ .soft_reset = rtl822x_c45_soft_reset,
+ .get_features = rtl822x_c45_get_features,
+ .config_aneg = rtl822x_c45_config_aneg,
+ .config_init = rtl822x_config_init,
+ .read_status = rtl822xb_c45_read_status,
+ .suspend = genphy_c45_pma_suspend,
+ .resume = rtlgen_c45_resume,
}, {
PHY_ID_MATCH_EXACT(0x001cc848),
.name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c
index f13c00b5b449..b945d75966d5 100644
--- a/drivers/net/phy/sfp-bus.c
+++ b/drivers/net/phy/sfp-bus.c
@@ -22,7 +22,6 @@ struct sfp_bus {
const struct sfp_socket_ops *socket_ops;
struct device *sfp_dev;
struct sfp *sfp;
- const struct sfp_quirk *sfp_quirk;
const struct sfp_upstream_ops *upstream_ops;
void *upstream;
@@ -30,24 +29,18 @@ struct sfp_bus {
bool registered;
bool started;
+
+ struct sfp_module_caps caps;
};
-/**
- * sfp_parse_port() - Parse the EEPROM base ID, setting the port type
- * @bus: a pointer to the &struct sfp_bus structure for the sfp module
- * @id: a pointer to the module's &struct sfp_eeprom_id
- * @support: optional pointer to an array of unsigned long for the
- * ethtool support mask
- *
- * Parse the EEPROM identification given in @id, and return one of
- * %PORT_TP, %PORT_FIBRE or %PORT_OTHER. If @support is non-%NULL,
- * also set the ethtool %ETHTOOL_LINK_MODE_xxx_BIT corresponding with
- * the connector type.
- *
- * If the port type is not known, returns %PORT_OTHER.
- */
-int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
- unsigned long *support)
+const struct sfp_module_caps *sfp_get_module_caps(struct sfp_bus *bus)
+{
+ return &bus->caps;
+}
+EXPORT_SYMBOL_GPL(sfp_get_module_caps);
+
+static void sfp_module_parse_port(struct sfp_bus *bus,
+ const struct sfp_eeprom_id *id)
{
int port;
@@ -91,34 +84,26 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
break;
}
- if (support) {
- switch (port) {
- case PORT_FIBRE:
- phylink_set(support, FIBRE);
- break;
+ switch (port) {
+ case PORT_FIBRE:
+ phylink_set(bus->caps.link_modes, FIBRE);
+ break;
- case PORT_TP:
- phylink_set(support, TP);
- break;
- }
+ case PORT_TP:
+ phylink_set(bus->caps.link_modes, TP);
+ break;
}
- return port;
+ bus->caps.port = port;
}
-EXPORT_SYMBOL_GPL(sfp_parse_port);
-/**
- * sfp_may_have_phy() - indicate whether the module may have a PHY
- * @bus: a pointer to the &struct sfp_bus structure for the sfp module
- * @id: a pointer to the module's &struct sfp_eeprom_id
- *
- * Parse the EEPROM identification given in @id, and return whether
- * this module may have a PHY.
- */
-bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id)
+static void sfp_module_parse_may_have_phy(struct sfp_bus *bus,
+ const struct sfp_eeprom_id *id)
{
- if (id->base.e1000_base_t)
- return true;
+ if (id->base.e1000_base_t) {
+ bus->caps.may_have_phy = true;
+ return;
+ }
if (id->base.phys_id != SFF8024_ID_DWDM_SFP) {
switch (id->base.extended_cc) {
@@ -126,30 +111,20 @@ bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id)
case SFF8024_ECC_10GBASE_T_SR:
case SFF8024_ECC_5GBASE_T:
case SFF8024_ECC_2_5GBASE_T:
- return true;
+ bus->caps.may_have_phy = true;
+ return;
}
}
- return false;
+ bus->caps.may_have_phy = false;
}
-EXPORT_SYMBOL_GPL(sfp_may_have_phy);
-/**
- * sfp_parse_support() - Parse the eeprom id for supported link modes
- * @bus: a pointer to the &struct sfp_bus structure for the sfp module
- * @id: a pointer to the module's &struct sfp_eeprom_id
- * @support: pointer to an array of unsigned long for the ethtool support mask
- * @interfaces: pointer to an array of unsigned long for phy interface modes
- * mask
- *
- * Parse the EEPROM identification information and derive the supported
- * ethtool link modes for the module.
- */
-void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
- unsigned long *support, unsigned long *interfaces)
+static void sfp_module_parse_support(struct sfp_bus *bus,
+ const struct sfp_eeprom_id *id)
{
+ unsigned long *interfaces = bus->caps.interfaces;
+ unsigned long *modes = bus->caps.link_modes;
unsigned int br_min, br_nom, br_max;
- __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
/* Decode the bitrate information to MBd */
br_min = br_nom = br_max = 0;
@@ -338,13 +313,21 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
phylink_set(modes, Autoneg);
phylink_set(modes, Pause);
phylink_set(modes, Asym_Pause);
+}
- if (bus->sfp_quirk && bus->sfp_quirk->modes)
- bus->sfp_quirk->modes(id, modes, interfaces);
+static void sfp_init_module(struct sfp_bus *bus,
+ const struct sfp_eeprom_id *id,
+ const struct sfp_quirk *quirk)
+{
+ memset(&bus->caps, 0, sizeof(bus->caps));
+
+ sfp_module_parse_support(bus, id);
+ sfp_module_parse_port(bus, id);
+ sfp_module_parse_may_have_phy(bus, id);
- linkmode_or(support, support, modes);
+ if (quirk && quirk->support)
+ quirk->support(id, &bus->caps);
}
-EXPORT_SYMBOL_GPL(sfp_parse_support);
/**
* sfp_select_interface() - Select appropriate phy_interface_t mode
@@ -794,7 +777,7 @@ int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
int ret = 0;
- bus->sfp_quirk = quirk;
+ sfp_init_module(bus, id, quirk);
if (ops && ops->module_insert)
ret = ops->module_insert(bus->upstream, id);
@@ -809,8 +792,6 @@ void sfp_module_remove(struct sfp_bus *bus)
if (ops && ops->module_remove)
ops->module_remove(bus->upstream);
-
- bus->sfp_quirk = NULL;
}
EXPORT_SYMBOL_GPL(sfp_module_remove);
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index 347c1e0e94d9..0401fa6b24d2 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -17,7 +17,6 @@
#include <linux/workqueue.h>
#include "sfp.h"
-#include "swphy.h"
enum {
GPIO_MODDEF0,
@@ -221,6 +220,8 @@ static const enum gpiod_flags gpio_flags[] = {
*/
#define SFP_EEPROM_BLOCK_SIZE 16
+#define SFP_POLL_INTERVAL msecs_to_jiffies(100)
+
struct sff_data {
unsigned int gpios;
bool (*module_supported)(const struct sfp_eeprom_id *id);
@@ -299,6 +300,11 @@ struct sfp {
#endif
};
+static void sfp_schedule_poll(struct sfp *sfp)
+{
+ mod_delayed_work(system_percpu_wq, &sfp->poll, SFP_POLL_INTERVAL);
+}
+
static bool sff_module_supported(const struct sfp_eeprom_id *id)
{
return id->base.phys_id == SFF8024_ID_SFF_8472 &&
@@ -361,6 +367,11 @@ static void sfp_fixup_ignore_tx_fault(struct sfp *sfp)
sfp->state_ignore_mask |= SFP_F_TX_FAULT;
}
+static void sfp_fixup_ignore_hw(struct sfp *sfp, unsigned int mask)
+{
+ sfp->state_hw_mask &= ~mask;
+}
+
static void sfp_fixup_nokia(struct sfp *sfp)
{
sfp_fixup_long_startup(sfp);
@@ -409,7 +420,19 @@ static void sfp_fixup_halny_gsfp(struct sfp *sfp)
* these are possibly used for other purposes on this
* module, e.g. a serial port.
*/
- sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS);
+ sfp_fixup_ignore_hw(sfp, SFP_F_TX_FAULT | SFP_F_LOS);
+}
+
+static void sfp_fixup_potron(struct sfp *sfp)
+{
+ /*
+ * The TX_FAULT and LOS pins on this device are used for serial
+ * communication, so ignore them. Additionally, provide extra
+ * time for this device to fully start up.
+ */
+
+ sfp_fixup_long_startup(sfp);
+ sfp_fixup_ignore_hw(sfp, SFP_F_TX_FAULT | SFP_F_LOS);
}
static void sfp_fixup_rollball_cc(struct sfp *sfp)
@@ -423,45 +446,44 @@ static void sfp_fixup_rollball_cc(struct sfp *sfp)
}
static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
- unsigned long *modes,
- unsigned long *interfaces)
+ struct sfp_module_caps *caps)
{
- linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes);
- __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+ caps->link_modes);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, caps->interfaces);
}
static void sfp_quirk_disable_autoneg(const struct sfp_eeprom_id *id,
- unsigned long *modes,
- unsigned long *interfaces)
+ struct sfp_module_caps *caps)
{
- linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, modes);
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, caps->link_modes);
}
static void sfp_quirk_oem_2_5g(const struct sfp_eeprom_id *id,
- unsigned long *modes,
- unsigned long *interfaces)
+ struct sfp_module_caps *caps)
{
/* Copper 2.5G SFP */
- linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, modes);
- __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
- sfp_quirk_disable_autoneg(id, modes, interfaces);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ caps->link_modes);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, caps->interfaces);
+ sfp_quirk_disable_autoneg(id, caps);
}
static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
- unsigned long *modes,
- unsigned long *interfaces)
+ struct sfp_module_caps *caps)
{
/* Ubiquiti U-Fiber Instant module claims that support all transceiver
* types including 10G Ethernet which is not truth. So clear all claimed
* modes and set only one mode which module supports: 1000baseX_Full.
*/
- linkmode_zero(modes);
- linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes);
+ linkmode_zero(caps->link_modes);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+ caps->link_modes);
}
-#define SFP_QUIRK(_v, _p, _m, _f) \
- { .vendor = _v, .part = _p, .modes = _m, .fixup = _f, }
-#define SFP_QUIRK_M(_v, _p, _m) SFP_QUIRK(_v, _p, _m, NULL)
+#define SFP_QUIRK(_v, _p, _s, _f) \
+ { .vendor = _v, .part = _p, .support = _s, .fixup = _f, }
+#define SFP_QUIRK_S(_v, _p, _s) SFP_QUIRK(_v, _p, _s, NULL)
#define SFP_QUIRK_F(_v, _p, _f) SFP_QUIRK(_v, _p, NULL, _f)
static const struct sfp_quirk sfp_quirks[] = {
@@ -475,6 +497,9 @@ static const struct sfp_quirk sfp_quirks[] = {
SFP_QUIRK("ALCATELLUCENT", "3FE46541AA", sfp_quirk_2500basex,
sfp_fixup_nokia),
+ // FLYPRO SFP-10GT-CS-30M uses Rollball protocol to talk to the PHY.
+ SFP_QUIRK_F("FLYPRO", "SFP-10GT-CS-30M", sfp_fixup_rollball),
+
// Fiberstore SFP-10G-T doesn't identify as copper, uses the Rollball
// protocol to talk to the PHY and needs 4 sec wait before probing the
// PHY.
@@ -494,7 +519,7 @@ static const struct sfp_quirk sfp_quirks[] = {
// HG MXPD-483II-F 2.5G supports 2500Base-X, but incorrectly reports
// 2600MBd in their EERPOM
- SFP_QUIRK_M("HG GENUINE", "MXPD-483II", sfp_quirk_2500basex),
+ SFP_QUIRK_S("HG GENUINE", "MXPD-483II", sfp_quirk_2500basex),
// Huawei MA5671A can operate at 2500base-X, but report 1.2GBd NRZ in
// their EEPROM
@@ -503,22 +528,24 @@ static const struct sfp_quirk sfp_quirks[] = {
// Lantech 8330-262D-E can operate at 2500base-X, but incorrectly report
// 2500MBd NRZ in their EEPROM
- SFP_QUIRK_M("Lantech", "8330-262D-E", sfp_quirk_2500basex),
+ SFP_QUIRK_S("Lantech", "8330-262D-E", sfp_quirk_2500basex),
- SFP_QUIRK_M("UBNT", "UF-INSTANT", sfp_quirk_ubnt_uf_instant),
+ SFP_QUIRK_S("UBNT", "UF-INSTANT", sfp_quirk_ubnt_uf_instant),
// Walsun HXSX-ATR[CI]-1 don't identify as copper, and use the
// Rollball protocol to talk to the PHY.
SFP_QUIRK_F("Walsun", "HXSX-ATRC-1", sfp_fixup_fs_10gt),
SFP_QUIRK_F("Walsun", "HXSX-ATRI-1", sfp_fixup_fs_10gt),
+ SFP_QUIRK_F("YV", "SFP+ONU-XGSPON", sfp_fixup_potron),
+
// OEM SFP-GE-T is a 1000Base-T module with broken TX_FAULT indicator
SFP_QUIRK_F("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault),
SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc),
- SFP_QUIRK_M("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g),
- SFP_QUIRK_M("OEM", "SFP-2.5G-BX10-D", sfp_quirk_2500basex),
- SFP_QUIRK_M("OEM", "SFP-2.5G-BX10-U", sfp_quirk_2500basex),
+ SFP_QUIRK_S("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g),
+ SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-D", sfp_quirk_2500basex),
+ SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-U", sfp_quirk_2500basex),
SFP_QUIRK_F("OEM", "RTSFP-10", sfp_fixup_rollball_cc),
SFP_QUIRK_F("OEM", "RTSFP-10G", sfp_fixup_rollball_cc),
SFP_QUIRK_F("Turris", "RTSFP-2.5G", sfp_fixup_rollball),
@@ -566,8 +593,6 @@ static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id)
return NULL;
}
-static unsigned long poll_jiffies;
-
static unsigned int sfp_gpio_get_state(struct sfp *sfp)
{
unsigned int i, state, v;
@@ -890,7 +915,7 @@ static void sfp_soft_start_poll(struct sfp *sfp)
if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
!sfp->need_poll)
- mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
+ sfp_schedule_poll(sfp);
mutex_unlock(&sfp->st_mutex);
}
@@ -1661,7 +1686,7 @@ static void sfp_hwmon_probe(struct work_struct *work)
err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag));
if (err < 0) {
if (sfp->hwmon_tries--) {
- mod_delayed_work(system_wq, &sfp->hwmon_probe,
+ mod_delayed_work(system_percpu_wq, &sfp->hwmon_probe,
T_PROBE_RETRY_SLOW);
} else {
dev_warn(sfp->dev, "hwmon probe failed: %pe\n",
@@ -1688,7 +1713,7 @@ static void sfp_hwmon_probe(struct work_struct *work)
static int sfp_hwmon_insert(struct sfp *sfp)
{
if (sfp->have_a2 && sfp->id.ext.diagmon & SFP_DIAGMON_DDM) {
- mod_delayed_work(system_wq, &sfp->hwmon_probe, 1);
+ mod_delayed_work(system_percpu_wq, &sfp->hwmon_probe, 1);
sfp->hwmon_tries = R_PROBE_RETRY_SLOW;
}
@@ -2542,7 +2567,7 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event)
/* Force a poll to re-read the hardware signal state after
* sfp_sm_mod_probe() changed state_hw_mask.
*/
- mod_delayed_work(system_wq, &sfp->poll, 1);
+ mod_delayed_work(system_percpu_wq, &sfp->poll, 1);
err = sfp_hwmon_insert(sfp);
if (err)
@@ -2987,7 +3012,7 @@ static void sfp_poll(struct work_struct *work)
// it's unimportant if we race while reading this.
if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) ||
sfp->need_poll)
- mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
+ sfp_schedule_poll(sfp);
}
static struct sfp *sfp_alloc(struct device *dev)
@@ -3157,7 +3182,7 @@ static int sfp_probe(struct platform_device *pdev)
}
if (sfp->need_poll)
- mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
+ sfp_schedule_poll(sfp);
/* We could have an issue in cases no Tx disable pin is available or
* wired as modules using a laser as their light source will continue to
@@ -3224,19 +3249,7 @@ static struct platform_driver sfp_driver = {
},
};
-static int sfp_init(void)
-{
- poll_jiffies = msecs_to_jiffies(100);
-
- return platform_driver_register(&sfp_driver);
-}
-module_init(sfp_init);
-
-static void sfp_exit(void)
-{
- platform_driver_unregister(&sfp_driver);
-}
-module_exit(sfp_exit);
+module_platform_driver(sfp_driver);
MODULE_ALIAS("platform:sfp");
MODULE_AUTHOR("Russell King");
diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h
index 1fd097dccb9f..879dff7afe6a 100644
--- a/drivers/net/phy/sfp.h
+++ b/drivers/net/phy/sfp.h
@@ -9,8 +9,8 @@ struct sfp;
struct sfp_quirk {
const char *vendor;
const char *part;
- void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes,
- unsigned long *interfaces);
+ void (*support)(const struct sfp_eeprom_id *id,
+ struct sfp_module_caps *caps);
void (*fixup)(struct sfp *sfp);
};
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index b6489da5cfcd..48487149c225 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -785,6 +785,7 @@ static struct phy_driver smsc_phy_driver[] = {
/* PHY_BASIC_FEATURES */
+ .flags = PHY_RST_AFTER_CLK_EN,
.probe = smsc_phy_probe,
/* basic functions */
diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c
deleted file mode 100644
index 076a370be849..000000000000
--- a/drivers/net/phy/spi_ks8995.c
+++ /dev/null
@@ -1,506 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * SPI driver for Micrel/Kendin KS8995M and KSZ8864RMN ethernet switches
- *
- * Copyright (C) 2008 Gabor Juhos <juhosg at openwrt.org>
- *
- * This file was based on: drivers/spi/at25.c
- * Copyright (C) 2006 David Brownell
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/gpio/consumer.h>
-#include <linux/of.h>
-
-#include <linux/spi/spi.h>
-
-#define DRV_VERSION "0.1.1"
-#define DRV_DESC "Micrel KS8995 Ethernet switch SPI driver"
-
-/* ------------------------------------------------------------------------ */
-
-#define KS8995_REG_ID0 0x00 /* Chip ID0 */
-#define KS8995_REG_ID1 0x01 /* Chip ID1 */
-
-#define KS8995_REG_GC0 0x02 /* Global Control 0 */
-#define KS8995_REG_GC1 0x03 /* Global Control 1 */
-#define KS8995_REG_GC2 0x04 /* Global Control 2 */
-#define KS8995_REG_GC3 0x05 /* Global Control 3 */
-#define KS8995_REG_GC4 0x06 /* Global Control 4 */
-#define KS8995_REG_GC5 0x07 /* Global Control 5 */
-#define KS8995_REG_GC6 0x08 /* Global Control 6 */
-#define KS8995_REG_GC7 0x09 /* Global Control 7 */
-#define KS8995_REG_GC8 0x0a /* Global Control 8 */
-#define KS8995_REG_GC9 0x0b /* Global Control 9 */
-
-#define KS8995_REG_PC(p, r) ((0x10 * p) + r) /* Port Control */
-#define KS8995_REG_PS(p, r) ((0x10 * p) + r + 0xe) /* Port Status */
-
-#define KS8995_REG_TPC0 0x60 /* TOS Priority Control 0 */
-#define KS8995_REG_TPC1 0x61 /* TOS Priority Control 1 */
-#define KS8995_REG_TPC2 0x62 /* TOS Priority Control 2 */
-#define KS8995_REG_TPC3 0x63 /* TOS Priority Control 3 */
-#define KS8995_REG_TPC4 0x64 /* TOS Priority Control 4 */
-#define KS8995_REG_TPC5 0x65 /* TOS Priority Control 5 */
-#define KS8995_REG_TPC6 0x66 /* TOS Priority Control 6 */
-#define KS8995_REG_TPC7 0x67 /* TOS Priority Control 7 */
-
-#define KS8995_REG_MAC0 0x68 /* MAC address 0 */
-#define KS8995_REG_MAC1 0x69 /* MAC address 1 */
-#define KS8995_REG_MAC2 0x6a /* MAC address 2 */
-#define KS8995_REG_MAC3 0x6b /* MAC address 3 */
-#define KS8995_REG_MAC4 0x6c /* MAC address 4 */
-#define KS8995_REG_MAC5 0x6d /* MAC address 5 */
-
-#define KS8995_REG_IAC0 0x6e /* Indirect Access Control 0 */
-#define KS8995_REG_IAC1 0x6f /* Indirect Access Control 0 */
-#define KS8995_REG_IAD7 0x70 /* Indirect Access Data 7 */
-#define KS8995_REG_IAD6 0x71 /* Indirect Access Data 6 */
-#define KS8995_REG_IAD5 0x72 /* Indirect Access Data 5 */
-#define KS8995_REG_IAD4 0x73 /* Indirect Access Data 4 */
-#define KS8995_REG_IAD3 0x74 /* Indirect Access Data 3 */
-#define KS8995_REG_IAD2 0x75 /* Indirect Access Data 2 */
-#define KS8995_REG_IAD1 0x76 /* Indirect Access Data 1 */
-#define KS8995_REG_IAD0 0x77 /* Indirect Access Data 0 */
-
-#define KSZ8864_REG_ID1 0xfe /* Chip ID in bit 7 */
-
-#define KS8995_REGS_SIZE 0x80
-#define KSZ8864_REGS_SIZE 0x100
-#define KSZ8795_REGS_SIZE 0x100
-
-#define ID1_CHIPID_M 0xf
-#define ID1_CHIPID_S 4
-#define ID1_REVISION_M 0x7
-#define ID1_REVISION_S 1
-#define ID1_START_SW 1 /* start the switch */
-
-#define FAMILY_KS8995 0x95
-#define FAMILY_KSZ8795 0x87
-#define CHIPID_M 0
-#define KS8995_CHIP_ID 0x00
-#define KSZ8864_CHIP_ID 0x01
-#define KSZ8795_CHIP_ID 0x09
-
-#define KS8995_CMD_WRITE 0x02U
-#define KS8995_CMD_READ 0x03U
-
-#define KS8995_RESET_DELAY 10 /* usec */
-
-enum ks8995_chip_variant {
- ks8995,
- ksz8864,
- ksz8795,
- max_variant
-};
-
-struct ks8995_chip_params {
- char *name;
- int family_id;
- int chip_id;
- int regs_size;
- int addr_width;
- int addr_shift;
-};
-
-static const struct ks8995_chip_params ks8995_chip[] = {
- [ks8995] = {
- .name = "KS8995MA",
- .family_id = FAMILY_KS8995,
- .chip_id = KS8995_CHIP_ID,
- .regs_size = KS8995_REGS_SIZE,
- .addr_width = 8,
- .addr_shift = 0,
- },
- [ksz8864] = {
- .name = "KSZ8864RMN",
- .family_id = FAMILY_KS8995,
- .chip_id = KSZ8864_CHIP_ID,
- .regs_size = KSZ8864_REGS_SIZE,
- .addr_width = 8,
- .addr_shift = 0,
- },
- [ksz8795] = {
- .name = "KSZ8795CLX",
- .family_id = FAMILY_KSZ8795,
- .chip_id = KSZ8795_CHIP_ID,
- .regs_size = KSZ8795_REGS_SIZE,
- .addr_width = 12,
- .addr_shift = 1,
- },
-};
-
-struct ks8995_switch {
- struct spi_device *spi;
- struct mutex lock;
- struct gpio_desc *reset_gpio;
- struct bin_attribute regs_attr;
- const struct ks8995_chip_params *chip;
- int revision_id;
-};
-
-static const struct spi_device_id ks8995_id[] = {
- {"ks8995", ks8995},
- {"ksz8864", ksz8864},
- {"ksz8795", ksz8795},
- { }
-};
-MODULE_DEVICE_TABLE(spi, ks8995_id);
-
-static const struct of_device_id ks8895_spi_of_match[] = {
- { .compatible = "micrel,ks8995" },
- { .compatible = "micrel,ksz8864" },
- { .compatible = "micrel,ksz8795" },
- { },
-};
-MODULE_DEVICE_TABLE(of, ks8895_spi_of_match);
-
-static inline u8 get_chip_id(u8 val)
-{
- return (val >> ID1_CHIPID_S) & ID1_CHIPID_M;
-}
-
-static inline u8 get_chip_rev(u8 val)
-{
- return (val >> ID1_REVISION_S) & ID1_REVISION_M;
-}
-
-/* create_spi_cmd - create a chip specific SPI command header
- * @ks: pointer to switch instance
- * @cmd: SPI command for switch
- * @address: register address for command
- *
- * Different chip families use different bit pattern to address the switches
- * registers:
- *
- * KS8995: 8bit command + 8bit address
- * KSZ8795: 3bit command + 12bit address + 1bit TR (?)
- */
-static inline __be16 create_spi_cmd(struct ks8995_switch *ks, int cmd,
- unsigned address)
-{
- u16 result = cmd;
-
- /* make room for address (incl. address shift) */
- result <<= ks->chip->addr_width + ks->chip->addr_shift;
- /* add address */
- result |= address << ks->chip->addr_shift;
- /* SPI protocol needs big endian */
- return cpu_to_be16(result);
-}
-/* ------------------------------------------------------------------------ */
-static int ks8995_read(struct ks8995_switch *ks, char *buf,
- unsigned offset, size_t count)
-{
- __be16 cmd;
- struct spi_transfer t[2];
- struct spi_message m;
- int err;
-
- cmd = create_spi_cmd(ks, KS8995_CMD_READ, offset);
- spi_message_init(&m);
-
- memset(&t, 0, sizeof(t));
-
- t[0].tx_buf = &cmd;
- t[0].len = sizeof(cmd);
- spi_message_add_tail(&t[0], &m);
-
- t[1].rx_buf = buf;
- t[1].len = count;
- spi_message_add_tail(&t[1], &m);
-
- mutex_lock(&ks->lock);
- err = spi_sync(ks->spi, &m);
- mutex_unlock(&ks->lock);
-
- return err ? err : count;
-}
-
-static int ks8995_write(struct ks8995_switch *ks, char *buf,
- unsigned offset, size_t count)
-{
- __be16 cmd;
- struct spi_transfer t[2];
- struct spi_message m;
- int err;
-
- cmd = create_spi_cmd(ks, KS8995_CMD_WRITE, offset);
- spi_message_init(&m);
-
- memset(&t, 0, sizeof(t));
-
- t[0].tx_buf = &cmd;
- t[0].len = sizeof(cmd);
- spi_message_add_tail(&t[0], &m);
-
- t[1].tx_buf = buf;
- t[1].len = count;
- spi_message_add_tail(&t[1], &m);
-
- mutex_lock(&ks->lock);
- err = spi_sync(ks->spi, &m);
- mutex_unlock(&ks->lock);
-
- return err ? err : count;
-}
-
-static inline int ks8995_read_reg(struct ks8995_switch *ks, u8 addr, u8 *buf)
-{
- return ks8995_read(ks, buf, addr, 1) != 1;
-}
-
-static inline int ks8995_write_reg(struct ks8995_switch *ks, u8 addr, u8 val)
-{
- char buf = val;
-
- return ks8995_write(ks, &buf, addr, 1) != 1;
-}
-
-/* ------------------------------------------------------------------------ */
-
-static int ks8995_stop(struct ks8995_switch *ks)
-{
- return ks8995_write_reg(ks, KS8995_REG_ID1, 0);
-}
-
-static int ks8995_start(struct ks8995_switch *ks)
-{
- return ks8995_write_reg(ks, KS8995_REG_ID1, 1);
-}
-
-static int ks8995_reset(struct ks8995_switch *ks)
-{
- int err;
-
- err = ks8995_stop(ks);
- if (err)
- return err;
-
- udelay(KS8995_RESET_DELAY);
-
- return ks8995_start(ks);
-}
-
-static ssize_t ks8995_registers_read(struct file *filp, struct kobject *kobj,
- const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count)
-{
- struct device *dev;
- struct ks8995_switch *ks8995;
-
- dev = kobj_to_dev(kobj);
- ks8995 = dev_get_drvdata(dev);
-
- return ks8995_read(ks8995, buf, off, count);
-}
-
-static ssize_t ks8995_registers_write(struct file *filp, struct kobject *kobj,
- const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count)
-{
- struct device *dev;
- struct ks8995_switch *ks8995;
-
- dev = kobj_to_dev(kobj);
- ks8995 = dev_get_drvdata(dev);
-
- return ks8995_write(ks8995, buf, off, count);
-}
-
-/* ks8995_get_revision - get chip revision
- * @ks: pointer to switch instance
- *
- * Verify chip family and id and get chip revision.
- */
-static int ks8995_get_revision(struct ks8995_switch *ks)
-{
- int err;
- u8 id0, id1, ksz8864_id;
-
- /* read family id */
- err = ks8995_read_reg(ks, KS8995_REG_ID0, &id0);
- if (err) {
- err = -EIO;
- goto err_out;
- }
-
- /* verify family id */
- if (id0 != ks->chip->family_id) {
- dev_err(&ks->spi->dev, "chip family id mismatch: expected 0x%02x but 0x%02x read\n",
- ks->chip->family_id, id0);
- err = -ENODEV;
- goto err_out;
- }
-
- switch (ks->chip->family_id) {
- case FAMILY_KS8995:
- /* try reading chip id at CHIP ID1 */
- err = ks8995_read_reg(ks, KS8995_REG_ID1, &id1);
- if (err) {
- err = -EIO;
- goto err_out;
- }
-
- /* verify chip id */
- if ((get_chip_id(id1) == CHIPID_M) &&
- (get_chip_id(id1) == ks->chip->chip_id)) {
- /* KS8995MA */
- ks->revision_id = get_chip_rev(id1);
- } else if (get_chip_id(id1) != CHIPID_M) {
- /* KSZ8864RMN */
- err = ks8995_read_reg(ks, KS8995_REG_ID1, &ksz8864_id);
- if (err) {
- err = -EIO;
- goto err_out;
- }
-
- if ((ksz8864_id & 0x80) &&
- (ks->chip->chip_id == KSZ8864_CHIP_ID)) {
- ks->revision_id = get_chip_rev(id1);
- }
-
- } else {
- dev_err(&ks->spi->dev, "unsupported chip id for KS8995 family: 0x%02x\n",
- id1);
- err = -ENODEV;
- }
- break;
- case FAMILY_KSZ8795:
- /* try reading chip id at CHIP ID1 */
- err = ks8995_read_reg(ks, KS8995_REG_ID1, &id1);
- if (err) {
- err = -EIO;
- goto err_out;
- }
-
- if (get_chip_id(id1) == ks->chip->chip_id) {
- ks->revision_id = get_chip_rev(id1);
- } else {
- dev_err(&ks->spi->dev, "unsupported chip id for KSZ8795 family: 0x%02x\n",
- id1);
- err = -ENODEV;
- }
- break;
- default:
- dev_err(&ks->spi->dev, "unsupported family id: 0x%02x\n", id0);
- err = -ENODEV;
- break;
- }
-err_out:
- return err;
-}
-
-static const struct bin_attribute ks8995_registers_attr = {
- .attr = {
- .name = "registers",
- .mode = 0600,
- },
- .size = KS8995_REGS_SIZE,
- .read_new = ks8995_registers_read,
- .write_new = ks8995_registers_write,
-};
-
-/* ------------------------------------------------------------------------ */
-static int ks8995_probe(struct spi_device *spi)
-{
- struct ks8995_switch *ks;
- int err;
- int variant = spi_get_device_id(spi)->driver_data;
-
- if (variant >= max_variant) {
- dev_err(&spi->dev, "bad chip variant %d\n", variant);
- return -ENODEV;
- }
-
- ks = devm_kzalloc(&spi->dev, sizeof(*ks), GFP_KERNEL);
- if (!ks)
- return -ENOMEM;
-
- mutex_init(&ks->lock);
- ks->spi = spi;
- ks->chip = &ks8995_chip[variant];
-
- ks->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset",
- GPIOD_OUT_HIGH);
- err = PTR_ERR_OR_ZERO(ks->reset_gpio);
- if (err) {
- dev_err(&spi->dev,
- "failed to get reset gpio: %d\n", err);
- return err;
- }
-
- err = gpiod_set_consumer_name(ks->reset_gpio, "switch-reset");
- if (err)
- return err;
-
- /* de-assert switch reset */
- /* FIXME: this likely requires a delay */
- gpiod_set_value_cansleep(ks->reset_gpio, 0);
-
- spi_set_drvdata(spi, ks);
-
- spi->mode = SPI_MODE_0;
- spi->bits_per_word = 8;
- err = spi_setup(spi);
- if (err) {
- dev_err(&spi->dev, "spi_setup failed, err=%d\n", err);
- return err;
- }
-
- err = ks8995_get_revision(ks);
- if (err)
- return err;
-
- memcpy(&ks->regs_attr, &ks8995_registers_attr, sizeof(ks->regs_attr));
- ks->regs_attr.size = ks->chip->regs_size;
-
- err = ks8995_reset(ks);
- if (err)
- return err;
-
- sysfs_attr_init(&ks->regs_attr.attr);
- err = sysfs_create_bin_file(&spi->dev.kobj, &ks->regs_attr);
- if (err) {
- dev_err(&spi->dev, "unable to create sysfs file, err=%d\n",
- err);
- return err;
- }
-
- dev_info(&spi->dev, "%s device found, Chip ID:%x, Revision:%x\n",
- ks->chip->name, ks->chip->chip_id, ks->revision_id);
-
- return 0;
-}
-
-static void ks8995_remove(struct spi_device *spi)
-{
- struct ks8995_switch *ks = spi_get_drvdata(spi);
-
- sysfs_remove_bin_file(&spi->dev.kobj, &ks->regs_attr);
-
- /* assert reset */
- gpiod_set_value_cansleep(ks->reset_gpio, 1);
-}
-
-/* ------------------------------------------------------------------------ */
-static struct spi_driver ks8995_driver = {
- .driver = {
- .name = "spi-ks8995",
- .of_match_table = ks8895_spi_of_match,
- },
- .probe = ks8995_probe,
- .remove = ks8995_remove,
- .id_table = ks8995_id,
-};
-
-module_spi_driver(ks8995_driver);
-
-MODULE_DESCRIPTION(DRV_DESC);
-MODULE_VERSION(DRV_VERSION);
-MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>");
-MODULE_LICENSE("GPL v2");