diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/Kconfig | 4 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/phy/aquantia/aquantia.h | 28 | ||||
-rw-r--r-- | drivers/net/phy/aquantia/aquantia_main.c | 531 | ||||
-rw-r--r-- | drivers/net/phy/as21xxx.c | 7 | ||||
-rw-r--r-- | drivers/net/phy/fixed_phy.c | 26 | ||||
-rw-r--r-- | drivers/net/phy/mediatek/mtk-2p5ge.c | 104 | ||||
-rw-r--r-- | drivers/net/phy/micrel.c | 786 | ||||
-rw-r--r-- | drivers/net/phy/motorcomm.c | 117 | ||||
-rw-r--r-- | drivers/net/phy/mscc/mscc.h | 3 | ||||
-rw-r--r-- | drivers/net/phy/mscc/mscc_main.c | 40 | ||||
-rw-r--r-- | drivers/net/phy/mxl-86110.c | 392 | ||||
-rw-r--r-- | drivers/net/phy/realtek/realtek_main.c | 263 | ||||
-rw-r--r-- | drivers/net/phy/spi_ks8995.c | 506 |
14 files changed, 1700 insertions, 1108 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 28acc6392cfc..a7fb1d7cae94 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -465,7 +465,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 b4795aaf9c1c..402a33d559de 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -72,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/aquantia/aquantia.h b/drivers/net/phy/aquantia/aquantia.h index 0c78bfabace5..492052cf1e6e 100644 --- a/drivers/net/phy/aquantia/aquantia.h +++ b/drivers/net/phy/aquantia/aquantia.h @@ -174,10 +174,38 @@ 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]; 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..8516690e34db 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -26,10 +26,12 @@ #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 @@ -465,7 +467,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,7 +506,7 @@ static int aqr105_read_rate(struct phy_device *phydev) return 0; } -static int aqr105_read_status(struct phy_device *phydev) +static int aqr_gen1_read_status(struct phy_device *phydev) { int ret; int val; @@ -562,124 +564,32 @@ static int aqr105_read_status(struct phy_device *phydev) } /* Read rate from vendor register */ - return aqr105_read_rate(phydev); + return aqr_gen1_read_rate(phydev); } -static int aqr107_read_rate(struct phy_device *phydev) +static int aqr_gen2_read_status(struct phy_device *phydev) { - u32 config_reg; - int val; - - 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; -} - -static int aqr107_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) @@ -810,7 +720,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; @@ -859,20 +769,137 @@ 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 = 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; + } + } + + 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 +947,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 +971,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 +989,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 +1001,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 +1029,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 +1045,7 @@ 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 aqr113c_fill_interface_modes(phydev); + return aqr_gen1_wait_processor_intensive_op(phydev); } static int aqr107_probe(struct phy_device *phydev) @@ -1144,13 +1087,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 +1107,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, @@ -1188,16 +1131,16 @@ static struct phy_driver aqr_driver[] = { 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, @@ -1213,16 +1156,16 @@ static struct phy_driver aqr_driver[] = { 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, @@ -1238,16 +1181,16 @@ static struct phy_driver aqr_driver[] = { 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, @@ -1271,15 +1214,16 @@ static struct phy_driver aqr_driver[] = { 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, @@ -1294,15 +1238,35 @@ static struct phy_driver aqr_driver[] = { 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 = 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, +}, +{ + 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 = 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, @@ -1312,16 +1276,16 @@ static struct phy_driver aqr_driver[] = { 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, @@ -1336,16 +1300,16 @@ static struct phy_driver aqr_driver[] = { 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, @@ -1360,16 +1324,16 @@ static struct phy_driver aqr_driver[] = { 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, @@ -1382,19 +1346,44 @@ static struct phy_driver aqr_driver[] = { .led_polarity_set = aqr_phy_led_polarity_set, }, { + 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, +}, +{ 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, @@ -1410,16 +1399,16 @@ static struct phy_driver aqr_driver[] = { 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, @@ -1446,9 +1435,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/fixed_phy.c b/drivers/net/phy/fixed_phy.c index 033656d574b8..7f4e1a155a0f 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> @@ -40,7 +39,6 @@ struct fixed_phy { struct gpio_desc *link_gpiod; }; -static struct faux_device *fdev; static struct fixed_mdio_bus platform_fmb = { .phys = LIST_HEAD_INIT(platform_fmb.phys), }; @@ -77,8 +75,6 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) 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. */ @@ -88,9 +84,8 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) /* Check the GPIO for change in status */ fixed_phy_update(fp); - state = fp->status; - return swphy_read_reg(reg_num, &state); + return swphy_read_reg(reg_num, &fp->status); } } @@ -160,9 +155,9 @@ static int fixed_phy_add_gpiod(unsigned int irq, int phy_addr, 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_gpiod(PHY_POLL, 0, status, NULL); } EXPORT_SYMBOL_GPL(fixed_phy_add); @@ -309,6 +304,7 @@ 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); @@ -317,20 +313,13 @@ 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; - } + 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; @@ -343,8 +332,6 @@ static int __init fixed_mdio_bus_init(void) err_mdiobus_alloc: mdiobus_free(fmb->mii_bus); -err_mdiobus_reg: - faux_device_destroy(fdev); return ret; } module_init(fixed_mdio_bus_init); @@ -356,7 +343,6 @@ static void __exit fixed_mdio_bus_exit(void) mdiobus_unregister(fmb->mii_bus); mdiobus_free(fmb->mii_bus); - faux_device_destroy(fdev); list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { list_del(&fp->node); 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 605b0315b4cb..04bd744920b0 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -448,6 +448,17 @@ struct kszphy_priv { 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; +}; + static const struct kszphy_type lan8814_type = { .led_mode_reg = ~LAN8814_LED_CTRL_1, .cable_diag_reg = LAN8814_CABLE_DIAG, @@ -2790,6 +2801,52 @@ static int ksz886x_cable_test_get_status(struct phy_device *phydev, return ret; } +/** + * 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 @@ -2840,6 +2897,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; @@ -2850,35 +2928,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) @@ -2912,11 +3001,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, @@ -2928,7 +3017,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; @@ -2967,21 +3055,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) @@ -3103,29 +3198,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, @@ -3164,14 +3271,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); } @@ -3269,9 +3380,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); @@ -3289,9 +3402,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); @@ -3299,8 +3414,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; @@ -3309,12 +3424,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_); } } @@ -3356,8 +3473,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; @@ -3366,17 +3485,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); } @@ -3384,73 +3503,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, @@ -3565,61 +3683,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, @@ -3749,7 +3870,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); } @@ -3822,7 +3944,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); } @@ -3859,31 +3982,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; @@ -3908,19 +4040,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; } @@ -4002,7 +4132,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; @@ -4030,7 +4161,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); @@ -4056,35 +4187,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); @@ -4169,12 +4306,14 @@ 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; } @@ -4183,36 +4322,38 @@ 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_COMMON_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); @@ -4243,29 +4384,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) @@ -4288,7 +4424,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)); @@ -5643,10 +5779,191 @@ static int ksz9131_resume(struct phy_device *phydev) return kszphy_resume(phydev); } +#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 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; + + 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; + + /* 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); + } 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 irqreturn_t lan8842_handle_interrupt(struct phy_device *phydev) +{ + 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) { + phy_trigger_machine(phydev); + ret = IRQ_HANDLED; + } + + 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; +} + +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, @@ -5687,8 +6004,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, @@ -5703,8 +6019,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, @@ -5747,9 +6062,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, @@ -5768,9 +6082,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, @@ -5798,8 +6111,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, @@ -5819,8 +6131,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, @@ -5838,8 +6149,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, @@ -5854,8 +6164,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, @@ -5872,8 +6181,22 @@ 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, + .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, @@ -5894,8 +6217,7 @@ static struct phy_driver ksphy_driver[] = { .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, @@ -5904,8 +6226,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 */ @@ -5925,8 +6246,7 @@ 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 */ @@ -5953,22 +6273,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 2bfe314ef881..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 diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c index 24c75903f535..ef0ef1570d39 100644 --- a/drivers/net/phy/mscc/mscc_main.c +++ b/drivers/net/phy/mscc/mscc_main.c @@ -2202,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; @@ -2414,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, @@ -2437,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, @@ -2557,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, @@ -2579,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, @@ -2605,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, @@ -2631,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, @@ -2655,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, @@ -2679,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, @@ -2704,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/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/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index dd0d675149ad..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,21 +456,34 @@ 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; - wol_events = phy_read_paged(dev, RTL8211F_WOL_SETTINGS_PAGE, RTL8211F_WOL_SETTINGS_EVENTS); + wol_events = phy_read_paged(dev, RTL8211F_WOL_PAGE, RTL8211F_WOL_SETTINGS_EVENTS); if (wol_events < 0) return; @@ -453,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; @@ -464,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); } @@ -628,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; @@ -645,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) | @@ -706,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); @@ -723,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)) { @@ -778,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; } @@ -805,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, @@ -1038,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; @@ -1073,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); @@ -1095,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) { @@ -1280,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; @@ -1612,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, @@ -1675,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/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c deleted file mode 100644 index d135b061d810..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 = ks8995_registers_read, - .write = 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"); |