diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/Kconfig | 12 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/phy/at803x.c | 100 | ||||
-rw-r--r-- | drivers/net/phy/broadcom.c | 76 | ||||
-rw-r--r-- | drivers/net/phy/intel-xway.c | 21 | ||||
-rw-r--r-- | drivers/net/phy/marvell-88x2222.c | 621 | ||||
-rw-r--r-- | drivers/net/phy/marvell.c | 647 | ||||
-rw-r--r-- | drivers/net/phy/marvell10g.c | 386 | ||||
-rw-r--r-- | drivers/net/phy/mdio-boardinfo.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/mscc/mscc_main.c | 217 | ||||
-rw-r--r-- | drivers/net/phy/nxp-c45-tja11xx.c | 621 | ||||
-rw-r--r-- | drivers/net/phy/phy-c45.c | 51 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 3 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 52 | ||||
-rw-r--r-- | drivers/net/phy/phylink.c | 5 | ||||
-rw-r--r-- | drivers/net/phy/sfp-bus.c | 20 | ||||
-rw-r--r-- | drivers/net/phy/sfp.c | 25 | ||||
-rw-r--r-- | drivers/net/phy/sfp.h | 3 | ||||
-rw-r--r-- | drivers/net/phy/smsc.c | 7 |
20 files changed, 2411 insertions, 462 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 698bea312adc..288bf405ebdb 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -201,6 +201,12 @@ config MARVELL_10G_PHY help Support for the Marvell Alaska MV88X3310 and compatible PHYs. +config MARVELL_88X2222_PHY + tristate "Marvell 88X2222 PHY" + help + Support for the Marvell 88X2222 Dual-port Multi-speed Ethernet + Transceiver. + config MICREL_PHY tristate "Micrel PHYs" help @@ -228,6 +234,12 @@ config NATIONAL_PHY help Currently supports the DP83865 PHY. +config NXP_C45_TJA11XX_PHY + tristate "NXP C45 TJA11XX PHYs" + help + Enable support for NXP C45 TJA11XX PHYs. + Currently supports only the TJA1103 PHY. + config NXP_TJA11XX_PHY tristate "NXP TJA11xx PHYs support" depends on HWMON diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index a13e402074cf..bcda7ed2455d 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o obj-$(CONFIG_MARVELL_PHY) += marvell.o +obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o obj-$(CONFIG_MICREL_PHY) += micrel.o @@ -70,6 +71,7 @@ obj-$(CONFIG_MICROCHIP_PHY) += microchip.o obj-$(CONFIG_MICROCHIP_T1_PHY) += microchip_t1.o obj-$(CONFIG_MICROSEMI_PHY) += mscc/ obj-$(CONFIG_NATIONAL_PHY) += national.o +obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja11xx.o obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_REALTEK_PHY) += realtek.o diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index c2aa4c92edde..32af52dd5aed 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -144,6 +144,9 @@ #define ATH8035_PHY_ID 0x004dd072 #define AT8030_PHY_ID_MASK 0xffffffef +#define AT803X_PAGE_FIBER 0 +#define AT803X_PAGE_COPPER 1 + MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver"); MODULE_AUTHOR("Matus Ujhelyi"); MODULE_LICENSE("GPL"); @@ -198,6 +201,35 @@ static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, return phy_write(phydev, AT803X_DEBUG_DATA, val); } +static int at803x_write_page(struct phy_device *phydev, int page) +{ + int mask; + int set; + + if (page == AT803X_PAGE_COPPER) { + set = AT803X_BT_BX_REG_SEL; + mask = 0; + } else { + set = 0; + mask = AT803X_BT_BX_REG_SEL; + } + + return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set); +} + +static int at803x_read_page(struct phy_device *phydev) +{ + int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG); + + if (ccr < 0) + return ccr; + + if (ccr & AT803X_BT_BX_REG_SEL) + return AT803X_PAGE_COPPER; + + return AT803X_PAGE_FIBER; +} + static int at803x_enable_rx_delay(struct phy_device *phydev) { return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0, @@ -522,10 +554,6 @@ static int at803x_parse_dt(struct phy_device *phydev) phydev_err(phydev, "failed to get VDDIO regulator\n"); return PTR_ERR(priv->vddio); } - - ret = regulator_enable(priv->vddio); - if (ret < 0) - return ret; } return 0; @@ -535,6 +563,7 @@ static int at803x_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; struct at803x_priv *priv; + int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -542,7 +571,35 @@ static int at803x_probe(struct phy_device *phydev) phydev->priv = priv; - return at803x_parse_dt(phydev); + ret = at803x_parse_dt(phydev); + if (ret) + return ret; + + if (priv->vddio) { + ret = regulator_enable(priv->vddio); + if (ret < 0) + return ret; + } + + /* Some bootloaders leave the fiber page selected. + * Switch to the copper page, as otherwise we read + * the PHY capabilities from the fiber side. + */ + if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) { + phy_lock_mdio_bus(phydev); + ret = at803x_write_page(phydev, AT803X_PAGE_COPPER); + phy_unlock_mdio_bus(phydev); + if (ret) + goto err; + } + + return 0; + +err: + if (priv->vddio) + regulator_disable(priv->vddio); + + return ret; } static void at803x_remove(struct phy_device *phydev) @@ -751,36 +808,6 @@ static void at803x_link_change_notify(struct phy_device *phydev) } } -static int at803x_aneg_done(struct phy_device *phydev) -{ - int ccr; - - int aneg_done = genphy_aneg_done(phydev); - if (aneg_done != BMSR_ANEGCOMPLETE) - return aneg_done; - - /* - * in SGMII mode, if copper side autoneg is successful, - * also check SGMII side autoneg result - */ - ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); - if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII) - return aneg_done; - - /* switch to SGMII/fiber page */ - phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL); - - /* check if the SGMII link is OK. */ - if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) { - phydev_warn(phydev, "803x_aneg_done: SGMII link is not ok\n"); - aneg_done = 0; - } - /* switch back to copper page */ - phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL); - - return aneg_done; -} - static int at803x_read_status(struct phy_device *phydev) { int ss, err, old_link = phydev->link; @@ -1196,9 +1223,10 @@ static struct phy_driver at803x_driver[] = { .get_wol = at803x_get_wol, .suspend = at803x_suspend, .resume = at803x_resume, + .read_page = at803x_read_page, + .write_page = at803x_write_page, /* PHY_GBIT_FEATURES */ .read_status = at803x_read_status, - .aneg_done = at803x_aneg_done, .config_intr = &at803x_config_intr, .handle_interrupt = at803x_handle_interrupt, .get_tunable = at803x_get_tunable, diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 82fe5f43f0e9..7bf3011b8e77 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -671,13 +671,13 @@ static irqreturn_t brcm_fet_handle_interrupt(struct phy_device *phydev) return IRQ_HANDLED; } -struct bcm53xx_phy_priv { +struct bcm54xx_phy_priv { u64 *stats; }; -static int bcm53xx_phy_probe(struct phy_device *phydev) +static int bcm54xx_phy_probe(struct phy_device *phydev) { - struct bcm53xx_phy_priv *priv; + struct bcm54xx_phy_priv *priv; priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -694,10 +694,10 @@ static int bcm53xx_phy_probe(struct phy_device *phydev) return 0; } -static void bcm53xx_phy_get_stats(struct phy_device *phydev, - struct ethtool_stats *stats, u64 *data) +static void bcm54xx_get_stats(struct phy_device *phydev, + struct ethtool_stats *stats, u64 *data) { - struct bcm53xx_phy_priv *priv = phydev->priv; + struct bcm54xx_phy_priv *priv = phydev->priv; bcm_phy_get_stats(phydev, priv->stats, stats, data); } @@ -708,6 +708,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5411", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -716,6 +720,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5421", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -724,6 +732,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM54210E", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -732,6 +744,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5461", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -740,6 +756,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM54612E", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -759,6 +779,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5464", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -769,6 +793,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5481", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_aneg = bcm5481_config_aneg, .config_intr = bcm_phy_config_intr, @@ -778,6 +806,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM54810", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_aneg = bcm5481_config_aneg, .config_intr = bcm_phy_config_intr, @@ -789,6 +821,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM54811", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54811_config_init, .config_aneg = bcm5481_config_aneg, .config_intr = bcm_phy_config_intr, @@ -800,6 +836,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5482", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -808,6 +848,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM50610", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -816,6 +860,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM50610M", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -824,6 +872,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM57780", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -851,8 +903,8 @@ static struct phy_driver broadcom_drivers[] = { /* PHY_GBIT_FEATURES */ .get_sset_count = bcm_phy_get_sset_count, .get_strings = bcm_phy_get_strings, - .get_stats = bcm53xx_phy_get_stats, - .probe = bcm53xx_phy_probe, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, }, { .phy_id = PHY_ID_BCM53125, .phy_id_mask = 0xfffffff0, @@ -861,8 +913,8 @@ static struct phy_driver broadcom_drivers[] = { /* PHY_GBIT_FEATURES */ .get_sset_count = bcm_phy_get_sset_count, .get_strings = bcm_phy_get_strings, - .get_stats = bcm53xx_phy_get_stats, - .probe = bcm53xx_phy_probe, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, @@ -871,6 +923,10 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM89610", /* PHY_GBIT_FEATURES */ + .get_sset_count = bcm_phy_get_sset_count, + .get_strings = bcm_phy_get_strings, + .get_stats = bcm54xx_get_stats, + .probe = bcm54xx_phy_probe, .config_init = bcm54xx_config_init, .config_intr = bcm_phy_config_intr, .handle_interrupt = bcm_phy_handle_interrupt, diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c index 6eac50d4b42f..d453ec016168 100644 --- a/drivers/net/phy/intel-xway.c +++ b/drivers/net/phy/intel-xway.c @@ -11,6 +11,18 @@ #define XWAY_MDIO_IMASK 0x19 /* interrupt mask */ #define XWAY_MDIO_ISTAT 0x1A /* interrupt status */ +#define XWAY_MDIO_LED 0x1B /* led control */ + +/* bit 15:12 are reserved */ +#define XWAY_MDIO_LED_LED3_EN BIT(11) /* Enable the integrated function of LED3 */ +#define XWAY_MDIO_LED_LED2_EN BIT(10) /* Enable the integrated function of LED2 */ +#define XWAY_MDIO_LED_LED1_EN BIT(9) /* Enable the integrated function of LED1 */ +#define XWAY_MDIO_LED_LED0_EN BIT(8) /* Enable the integrated function of LED0 */ +/* bit 7:4 are reserved */ +#define XWAY_MDIO_LED_LED3_DA BIT(3) /* Direct Access to LED3 */ +#define XWAY_MDIO_LED_LED2_DA BIT(2) /* Direct Access to LED2 */ +#define XWAY_MDIO_LED_LED1_DA BIT(1) /* Direct Access to LED1 */ +#define XWAY_MDIO_LED_LED0_DA BIT(0) /* Direct Access to LED0 */ #define XWAY_MDIO_INIT_WOL BIT(15) /* Wake-On-LAN */ #define XWAY_MDIO_INIT_MSRE BIT(14) @@ -159,6 +171,15 @@ static int xway_gphy_config_init(struct phy_device *phydev) /* Clear all pending interrupts */ phy_read(phydev, XWAY_MDIO_ISTAT); + /* Ensure that integrated led function is enabled for all leds */ + err = phy_write(phydev, XWAY_MDIO_LED, + XWAY_MDIO_LED_LED0_EN | + XWAY_MDIO_LED_LED1_EN | + XWAY_MDIO_LED_LED2_EN | + XWAY_MDIO_LED_LED3_EN); + if (err) + return err; + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCH, XWAY_MMD_LEDCH_NACS_NONE | XWAY_MMD_LEDCH_SBF_F02HZ | diff --git a/drivers/net/phy/marvell-88x2222.c b/drivers/net/phy/marvell-88x2222.c new file mode 100644 index 000000000000..d8b31d4d2a73 --- /dev/null +++ b/drivers/net/phy/marvell-88x2222.c @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Marvell 88x2222 dual-port multi-speed ethernet transceiver. + * + * Supports: + * XAUI on the host side. + * 1000Base-X or 10GBase-R on the line side. + * SGMII over 1000Base-X. + */ +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/mdio.h> +#include <linux/marvell_phy.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/sfp.h> +#include <linux/netdevice.h> + +/* Port PCS Configuration */ +#define MV_PCS_CONFIG 0xF002 +#define MV_PCS_HOST_XAUI 0x73 +#define MV_PCS_LINE_10GBR (0x71 << 8) +#define MV_PCS_LINE_1GBX_AN (0x7B << 8) +#define MV_PCS_LINE_SGMII_AN (0x7F << 8) + +/* Port Reset and Power Down */ +#define MV_PORT_RST 0xF003 +#define MV_LINE_RST_SW BIT(15) +#define MV_HOST_RST_SW BIT(7) +#define MV_PORT_RST_SW (MV_LINE_RST_SW | MV_HOST_RST_SW) + +/* PMD Receive Signal Detect */ +#define MV_RX_SIGNAL_DETECT 0x000A +#define MV_RX_SIGNAL_DETECT_GLOBAL BIT(0) + +/* 1000Base-X/SGMII Control Register */ +#define MV_1GBX_CTRL (0x2000 + MII_BMCR) + +/* 1000BASE-X/SGMII Status Register */ +#define MV_1GBX_STAT (0x2000 + MII_BMSR) + +/* 1000Base-X Auto-Negotiation Advertisement Register */ +#define MV_1GBX_ADVERTISE (0x2000 + MII_ADVERTISE) + +/* 1000Base-X PHY Specific Status Register */ +#define MV_1GBX_PHY_STAT 0xA003 +#define MV_1GBX_PHY_STAT_AN_RESOLVED BIT(11) +#define MV_1GBX_PHY_STAT_DUPLEX BIT(13) +#define MV_1GBX_PHY_STAT_SPEED100 BIT(14) +#define MV_1GBX_PHY_STAT_SPEED1000 BIT(15) + +#define AUTONEG_TIMEOUT 3 + +struct mv2222_data { + phy_interface_t line_interface; + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + bool sfp_link; +}; + +/* SFI PMA transmit enable */ +static int mv2222_tx_enable(struct phy_device *phydev) +{ + return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, + MDIO_PMD_TXDIS_GLOBAL); +} + +/* SFI PMA transmit disable */ +static int mv2222_tx_disable(struct phy_device *phydev) +{ + return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, + MDIO_PMD_TXDIS_GLOBAL); +} + +static int mv2222_soft_reset(struct phy_device *phydev) +{ + int val, ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PORT_RST, + MV_PORT_RST_SW); + if (ret < 0) + return ret; + + return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND2, MV_PORT_RST, + val, !(val & MV_PORT_RST_SW), + 5000, 1000000, true); +} + +static int mv2222_disable_aneg(struct phy_device *phydev) +{ + int ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL, + BMCR_ANENABLE | BMCR_ANRESTART); + if (ret < 0) + return ret; + + return mv2222_soft_reset(phydev); +} + +static int mv2222_enable_aneg(struct phy_device *phydev) +{ + int ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL, + BMCR_ANENABLE | BMCR_RESET); + if (ret < 0) + return ret; + + return mv2222_soft_reset(phydev); +} + +static int mv2222_set_sgmii_speed(struct phy_device *phydev) +{ + struct mv2222_data *priv = phydev->priv; + + switch (phydev->speed) { + default: + case SPEED_1000: + if ((linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + priv->supported))) + return phy_modify_mmd(phydev, MDIO_MMD_PCS, + MV_1GBX_CTRL, + BMCR_SPEED1000 | BMCR_SPEED100, + BMCR_SPEED1000); + + fallthrough; + case SPEED_100: + if ((linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, + priv->supported))) + return phy_modify_mmd(phydev, MDIO_MMD_PCS, + MV_1GBX_CTRL, + BMCR_SPEED1000 | BMCR_SPEED100, + BMCR_SPEED100); + fallthrough; + case SPEED_10: + if ((linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, + priv->supported))) + return phy_modify_mmd(phydev, MDIO_MMD_PCS, + MV_1GBX_CTRL, + BMCR_SPEED1000 | BMCR_SPEED100, + BMCR_SPEED10); + + return -EINVAL; + } +} + +static bool mv2222_is_10g_capable(struct phy_device *phydev) +{ + struct mv2222_data *priv = phydev->priv; + + return (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, + priv->supported)); +} + +static bool mv2222_is_1gbx_capable(struct phy_device *phydev) +{ + struct mv2222_data *priv = phydev->priv; + + return linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + priv->supported); +} + +static bool mv2222_is_sgmii_capable(struct phy_device *phydev) +{ + struct mv2222_data *priv = phydev->priv; + + return (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, + priv->supported) || + linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, + priv->supported)); +} + +static int mv2222_config_line(struct phy_device *phydev) +{ + struct mv2222_data *priv = phydev->priv; + + switch (priv->line_interface) { + case PHY_INTERFACE_MODE_10GBASER: + return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG, + MV_PCS_HOST_XAUI | MV_PCS_LINE_10GBR); + case PHY_INTERFACE_MODE_1000BASEX: + return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG, + MV_PCS_HOST_XAUI | MV_PCS_LINE_1GBX_AN); + case PHY_INTERFACE_MODE_SGMII: + return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG, + MV_PCS_HOST_XAUI | MV_PCS_LINE_SGMII_AN); + default: + return -EINVAL; + } +} + +/* Switch between 1G (1000Base-X/SGMII) and 10G (10GBase-R) modes */ +static int mv2222_swap_line_type(struct phy_device *phydev) +{ + struct mv2222_data *priv = phydev->priv; + bool changed = false; + int ret; + + switch (priv->line_interface) { + case PHY_INTERFACE_MODE_10GBASER: + if (mv2222_is_1gbx_capable(phydev)) { + priv->line_interface = PHY_INTERFACE_MODE_1000BASEX; + changed = true; + } + + if (mv2222_is_sgmii_capable(phydev)) { + priv->line_interface = PHY_INTERFACE_MODE_SGMII; + changed = true; + } + + break; + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + if (mv2222_is_10g_capable(phydev)) { + priv->line_interface = PHY_INTERFACE_MODE_10GBASER; + changed = true; + } + + break; + default: + return -EINVAL; + } + + if (changed) { + ret = mv2222_config_line(phydev); + if (ret < 0) + return ret; + } + + return 0; +} + +static int mv2222_setup_forced(struct phy_device *phydev) +{ + struct mv2222_data *priv = phydev->priv; + int ret; + + if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER) { + if (phydev->speed < SPEED_10000 && + phydev->speed != SPEED_UNKNOWN) { + ret = mv2222_swap_line_type(phydev); + if (ret < 0) + return ret; + } + } + + if (priv->line_interface == PHY_INTERFACE_MODE_SGMII) { + ret = mv2222_set_sgmii_speed(phydev); + if (ret < 0) + return ret; + } + + return mv2222_disable_aneg(phydev); +} + +static int mv2222_config_aneg(struct phy_device *phydev) +{ + struct mv2222_data *priv = phydev->priv; + int ret, adv; + + /* SFP is not present, do nothing */ + if (priv->line_interface == PHY_INTERFACE_MODE_NA) + return 0; + + if (phydev->autoneg == AUTONEG_DISABLE || + priv->line_interface == PHY_INTERFACE_MODE_10GBASER) + return mv2222_setup_forced(phydev); + + adv = linkmode_adv_to_mii_adv_x(priv->supported, + ETHTOOL_LINK_MODE_1000baseX_Full_BIT); + + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_ADVERTISE, + ADVERTISE_1000XFULL | + ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM, + adv); + if (ret < 0) + return ret; + + return mv2222_enable_aneg(phydev); +} + +static int mv2222_aneg_done(struct phy_device *phydev) +{ + int ret; + + if (mv2222_is_10g_capable(phydev)) { + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); + if (ret < 0) + return ret; + + if (ret & MDIO_STAT1_LSTATUS) + return 1; + } + + ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT); + if (ret < 0) + return ret; + + return (ret & BMSR_ANEGCOMPLETE); +} + +/* Returns negative on error, 0 if link is down, 1 if link is up */ +static int mv2222_read_status_10g(struct phy_device *phydev) +{ + static int timeout; + int val, link = 0; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); + if (val < 0) + return val; + + if (val & MDIO_STAT1_LSTATUS) { + link = 1; + + /* 10GBASE-R do not support auto-negotiation */ + phydev->autoneg = AUTONEG_DISABLE; + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + } else { + if (phydev->autoneg == AUTONEG_ENABLE) { + timeout++; + + if (timeout > AUTONEG_TIMEOUT) { + timeout = 0; + + val = mv2222_swap_line_type(phydev); + if (val < 0) + return val; + + return mv2222_config_aneg(phydev); + } + } + } + + return link; +} + +/* Returns negative on error, 0 if link is down, 1 if link is up */ +static int mv2222_read_status_1g(struct phy_device *phydev) +{ + static int timeout; + int val, link = 0; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT); + if (val < 0) + return val; + + if (phydev->autoneg == AUTONEG_ENABLE && + !(val & BMSR_ANEGCOMPLETE)) { + timeout++; + + if (timeout > AUTONEG_TIMEOUT) { + timeout = 0; + + val = mv2222_swap_line_type(phydev); + if (val < 0) + return val; + + return mv2222_config_aneg(phydev); + } + + return 0; + } + + if (!(val & BMSR_LSTATUS)) + return 0; + + link = 1; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_PHY_STAT); + if (val < 0) + return val; + + if (val & MV_1GBX_PHY_STAT_AN_RESOLVED) { + if (val & MV_1GBX_PHY_STAT_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + if (val & MV_1GBX_PHY_STAT_SPEED1000) + phydev->speed = SPEED_1000; + else if (val & MV_1GBX_PHY_STAT_SPEED100) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + } + + return link; +} + +static bool mv2222_link_is_operational(struct phy_device *phydev) +{ + struct mv2222_data *priv = phydev->priv; + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_RX_SIGNAL_DETECT); + if (val < 0 || !(val & MV_RX_SIGNAL_DETECT_GLOBAL)) + return false; + + if (phydev->sfp_bus && !priv->sfp_link) + return false; + + return true; +} + +static int mv2222_read_status(struct phy_device *phydev) +{ + struct mv2222_data *priv = phydev->priv; + int link; + + phydev->link = 0; + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + + if (!mv2222_link_is_operational(phydev)) + return 0; + + if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER) + link = mv2222_read_status_10g(phydev); + else + link = mv2222_read_status_1g(phydev); + + if (link < 0) + return link; + + phydev->link = link; + + return 0; +} + +static int mv2222_resume(struct phy_device *phydev) +{ + return mv2222_tx_enable(phydev); +} + +static int mv2222_suspend(struct phy_device *phydev) +{ + return mv2222_tx_disable(phydev); +} + +static int mv2222_get_features(struct phy_device *phydev) +{ + /* All supported linkmodes are set at probe */ + + return 0; +} + +static int mv2222_config_init(struct phy_device *phydev) +{ + if (phydev->interface != PHY_INTERFACE_MODE_XAUI) + return -EINVAL; + + return 0; +} + +static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +{ + struct phy_device *phydev = upstream; + phy_interface_t sfp_interface; + struct mv2222_data *priv; + struct device *dev; + int ret; + + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_supported) = { 0, }; + + priv = (struct mv2222_data *)phydev->priv; + dev = &phydev->mdio.dev; + + sfp_parse_support(phydev->sfp_bus, id, sfp_supported); + sfp_interface = sfp_select_interface(phydev->sfp_bus, sfp_supported); + + dev_info(dev, "%s SFP module inserted\n", phy_modes(sfp_interface)); + + if (sfp_interface != PHY_INTERFACE_MODE_10GBASER && + sfp_interface != PHY_INTERFACE_MODE_1000BASEX && + sfp_interface != PHY_INTERFACE_MODE_SGMII) { + dev_err(dev, "Incompatible SFP module inserted\n"); + + return -EINVAL; + } + + priv->line_interface = sfp_interface; + linkmode_and(priv->supported, phydev->supported, sfp_supported); + + ret = mv2222_config_line(phydev); + if (ret < 0) + return ret; + + if (mutex_trylock(&phydev->lock)) { + ret = mv2222_config_aneg(phydev); + mutex_unlock(&phydev->lock); + } + + return ret; +} + +static void mv2222_sfp_remove(void *upstream) +{ + struct phy_device *phydev = upstream; + struct mv2222_data *priv; + + priv = (struct mv2222_data *)phydev->priv; + + priv->line_interface = PHY_INTERFACE_MODE_NA; + linkmode_zero(priv->supported); +} + +static void mv2222_sfp_link_up(void *upstream) +{ + struct phy_device *phydev = upstream; + struct mv2222_data *priv; + + priv = phydev->priv; + priv->sfp_link = true; +} + +static void mv2222_sfp_link_down(void *upstream) +{ + struct phy_device *phydev = upstream; + struct mv2222_data *priv; + + priv = phydev->priv; + priv->sfp_link = false; +} + +static const struct sfp_upstream_ops sfp_phy_ops = { + .module_insert = mv2222_sfp_insert, + .module_remove = mv2222_sfp_remove, + .link_up = mv2222_sfp_link_up, + .link_down = mv2222_sfp_link_down, + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, +}; + +static int mv2222_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct mv2222_data *priv = NULL; + + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, }; + + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, supported); + + linkmode_copy(phydev->supported, supported); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->line_interface = PHY_INTERFACE_MODE_NA; + phydev->priv = priv; + + return phy_sfp_probe(phydev, &sfp_phy_ops); +} + +static struct phy_driver mv2222_drivers[] = { + { + .phy_id = MARVELL_PHY_ID_88X2222, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88X2222", + .get_features = mv2222_get_features, + .soft_reset = mv2222_soft_reset, + .config_init = mv2222_config_init, + .config_aneg = mv2222_config_aneg, + .aneg_done = mv2222_aneg_done, + .probe = mv2222_probe, + .suspend = mv2222_suspend, + .resume = mv2222_resume, + .read_status = mv2222_read_status, + }, +}; +module_phy_driver(mv2222_drivers); + +static struct mdio_device_id __maybe_unused mv2222_tbl[] = { + { MARVELL_PHY_ID_88X2222, MARVELL_PHY_ID_MASK }, + { } +}; +MODULE_DEVICE_TABLE(mdio, mv2222_tbl); + +MODULE_DESCRIPTION("Marvell 88x2222 ethernet transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index e26a5d663f8a..e6721c1c26c2 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -113,15 +113,26 @@ #define MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN BIT(9) #define MII_88E6390_MISC_TEST 0x1b -#define MII_88E6390_MISC_TEST_SAMPLE_1S 0 -#define MII_88E6390_MISC_TEST_SAMPLE_10MS BIT(14) -#define MII_88E6390_MISC_TEST_SAMPLE_DISABLE BIT(15) -#define MII_88E6390_MISC_TEST_SAMPLE_ENABLE 0 -#define MII_88E6390_MISC_TEST_SAMPLE_MASK (0x3 << 14) +#define MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE_SAMPLE_1S (0x0 << 14) +#define MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE (0x1 << 14) +#define MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE_ONESHOT (0x2 << 14) +#define MII_88E6390_MISC_TEST_TEMP_SENSOR_DISABLE (0x3 << 14) +#define MII_88E6390_MISC_TEST_TEMP_SENSOR_MASK (0x3 << 14) +#define MII_88E6393_MISC_TEST_SAMPLES_2048 (0x0 << 11) +#define MII_88E6393_MISC_TEST_SAMPLES_4096 (0x1 << 11) +#define MII_88E6393_MISC_TEST_SAMPLES_8192 (0x2 << 11) +#define MII_88E6393_MISC_TEST_SAMPLES_16384 (0x3 << 11) +#define MII_88E6393_MISC_TEST_SAMPLES_MASK (0x3 << 11) +#define MII_88E6393_MISC_TEST_RATE_2_3MS (0x5 << 8) +#define MII_88E6393_MISC_TEST_RATE_6_4MS (0x6 << 8) +#define MII_88E6393_MISC_TEST_RATE_11_9MS (0x7 << 8) +#define MII_88E6393_MISC_TEST_RATE_MASK (0x7 << 8) #define MII_88E6390_TEMP_SENSOR 0x1c -#define MII_88E6390_TEMP_SENSOR_MASK 0xff -#define MII_88E6390_TEMP_SENSOR_SAMPLES 10 +#define MII_88E6393_TEMP_SENSOR_THRESHOLD_MASK 0xff00 +#define MII_88E6393_TEMP_SENSOR_THRESHOLD_SHIFT 8 +#define MII_88E6390_TEMP_SENSOR_MASK 0xff +#define MII_88E6390_TEMP_SENSOR_SAMPLES 10 #define MII_88E1318S_PHY_MSCR1_REG 16 #define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6) @@ -967,22 +978,28 @@ static int m88e1111_get_downshift(struct phy_device *phydev, u8 *data) static int m88e1111_set_downshift(struct phy_device *phydev, u8 cnt) { - int val; + int val, err; if (cnt > MII_M1111_PHY_EXT_CR_DOWNSHIFT_MAX) return -E2BIG; - if (!cnt) - return phy_clear_bits(phydev, MII_M1111_PHY_EXT_CR, - MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN); + if (!cnt) { + err = phy_clear_bits(phydev, MII_M1111_PHY_EXT_CR, + MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN); + } else { + val = MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN; + val |= FIELD_PREP(MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, cnt - 1); - val = MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN; - val |= FIELD_PREP(MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, cnt - 1); + err = phy_modify(phydev, MII_M1111_PHY_EXT_CR, + MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN | + MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, + val); + } - return phy_modify(phydev, MII_M1111_PHY_EXT_CR, - MII_M1111_PHY_EXT_CR_DOWNSHIFT_EN | - MII_M1111_PHY_EXT_CR_DOWNSHIFT_MASK, - val); + if (err < 0) + return err; + + return genphy_soft_reset(phydev); } static int m88e1111_get_tunable(struct phy_device *phydev, @@ -1025,22 +1042,28 @@ static int m88e1011_get_downshift(struct phy_device *phydev, u8 *data) static int m88e1011_set_downshift(struct phy_device *phydev, u8 cnt) { - int val; + int val, err; if (cnt > MII_M1011_PHY_SCR_DOWNSHIFT_MAX) return -E2BIG; - if (!cnt) - return phy_clear_bits(phydev, MII_M1011_PHY_SCR, - MII_M1011_PHY_SCR_DOWNSHIFT_EN); + if (!cnt) { + err = phy_clear_bits(phydev, MII_M1011_PHY_SCR, + MII_M1011_PHY_SCR_DOWNSHIFT_EN); + } else { + val = MII_M1011_PHY_SCR_DOWNSHIFT_EN; + val |= FIELD_PREP(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, cnt - 1); - val = MII_M1011_PHY_SCR_DOWNSHIFT_EN; - val |= FIELD_PREP(MII_M1011_PHY_SCR_DOWNSHIFT_MASK, cnt - 1); + err = phy_modify(phydev, MII_M1011_PHY_SCR, + MII_M1011_PHY_SCR_DOWNSHIFT_EN | + MII_M1011_PHY_SCR_DOWNSHIFT_MASK, + val); + } - return phy_modify(phydev, MII_M1011_PHY_SCR, - MII_M1011_PHY_SCR_DOWNSHIFT_EN | - MII_M1011_PHY_SCR_DOWNSHIFT_MASK, - val); + if (err < 0) + return err; + + return genphy_soft_reset(phydev); } static int m88e1011_get_tunable(struct phy_device *phydev, @@ -1065,6 +1088,38 @@ static int m88e1011_set_tunable(struct phy_device *phydev, } } +static int m88e1112_config_init(struct phy_device *phydev) +{ + int err; + + err = m88e1011_set_downshift(phydev, 3); + if (err < 0) + return err; + + return m88e1111_config_init(phydev); +} + +static int m88e1111gbe_config_init(struct phy_device *phydev) +{ + int err; + + err = m88e1111_set_downshift(phydev, 3); + if (err < 0) + return err; + + return m88e1111_config_init(phydev); +} + +static int marvell_1011gbe_config_init(struct phy_device *phydev) +{ + int err; + + err = m88e1011_set_downshift(phydev, 3); + if (err < 0) + return err; + + return marvell_config_init(phydev); +} static int m88e1116r_config_init(struct phy_device *phydev) { int err; @@ -1145,6 +1200,9 @@ static int m88e1510_config_init(struct phy_device *phydev) if (err < 0) return err; } + err = m88e1011_set_downshift(phydev, 3); + if (err < 0) + return err; return m88e1318_config_init(phydev); } @@ -1297,6 +1355,9 @@ static int m88e1145_config_init(struct phy_device *phydev) if (err < 0) return err; } + err = m88e1111_set_downshift(phydev, 3); + if (err < 0) + return err; err = marvell_of_reg_init(phydev); if (err < 0) @@ -2216,6 +2277,20 @@ static int marvell_vct7_cable_test_get_status(struct phy_device *phydev, } #ifdef CONFIG_HWMON +struct marvell_hwmon_ops { + int (*config)(struct phy_device *phydev); + int (*get_temp)(struct phy_device *phydev, long *temp); + int (*get_temp_critical)(struct phy_device *phydev, long *temp); + int (*set_temp_critical)(struct phy_device *phydev, long temp); + int (*get_temp_alarm)(struct phy_device *phydev, long *alarm); +}; + +static const struct marvell_hwmon_ops * +to_marvell_hwmon_ops(const struct phy_device *phydev) +{ + return phydev->drv->driver_data; +} + static int m88e1121_get_temp(struct phy_device *phydev, long *temp) { int oldpage; @@ -2259,75 +2334,6 @@ error: return phy_restore_page(phydev, oldpage, ret); } -static int m88e1121_hwmon_read(struct device *dev, - enum hwmon_sensor_types type, - u32 attr, int channel, long *temp) -{ - struct phy_device *phydev = dev_get_drvdata(dev); - int err; - - switch (attr) { - case hwmon_temp_input: - err = m88e1121_get_temp(phydev, temp); - break; - default: - return -EOPNOTSUPP; - } - - return err; -} - -static umode_t m88e1121_hwmon_is_visible(const void *data, - enum hwmon_sensor_types type, - u32 attr, int channel) -{ - if (type != hwmon_temp) - return 0; - - switch (attr) { - case hwmon_temp_input: - return 0444; - default: - return 0; - } -} - -static u32 m88e1121_hwmon_chip_config[] = { - HWMON_C_REGISTER_TZ, - 0 -}; - -static const struct hwmon_channel_info m88e1121_hwmon_chip = { - .type = hwmon_chip, - .config = m88e1121_hwmon_chip_config, -}; - -static u32 m88e1121_hwmon_temp_config[] = { - HWMON_T_INPUT, - 0 -}; - -static const struct hwmon_channel_info m88e1121_hwmon_temp = { - .type = hwmon_temp, - .config = m88e1121_hwmon_temp_config, -}; - -static const struct hwmon_channel_info *m88e1121_hwmon_info[] = { - &m88e1121_hwmon_chip, - &m88e1121_hwmon_temp, - NULL -}; - -static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = { - .is_visible = m88e1121_hwmon_is_visible, - .read = m88e1121_hwmon_read, -}; - -static const struct hwmon_chip_info m88e1121_hwmon_chip_info = { - .ops = &m88e1121_hwmon_hwmon_ops, - .info = m88e1121_hwmon_info, -}; - static int m88e1510_get_temp(struct phy_device *phydev, long *temp) { int ret; @@ -2390,92 +2396,6 @@ static int m88e1510_get_temp_alarm(struct phy_device *phydev, long *alarm) return 0; } -static int m88e1510_hwmon_read(struct device *dev, - enum hwmon_sensor_types type, - u32 attr, int channel, long *temp) -{ - struct phy_device *phydev = dev_get_drvdata(dev); - int err; - - switch (attr) { - case hwmon_temp_input: - err = m88e1510_get_temp(phydev, temp); - break; - case hwmon_temp_crit: - err = m88e1510_get_temp_critical(phydev, temp); - break; - case hwmon_temp_max_alarm: - err = m88e1510_get_temp_alarm(phydev, temp); - break; - default: - return -EOPNOTSUPP; - } - - return err; -} - -static int m88e1510_hwmon_write(struct device *dev, - enum hwmon_sensor_types type, - u32 attr, int channel, long temp) -{ - struct phy_device *phydev = dev_get_drvdata(dev); - int err; - - switch (attr) { - case hwmon_temp_crit: - err = m88e1510_set_temp_critical(phydev, temp); - break; - default: - return -EOPNOTSUPP; - } - return err; -} - -static umode_t m88e1510_hwmon_is_visible(const void *data, - enum hwmon_sensor_types type, - u32 attr, int channel) -{ - if (type != hwmon_temp) - return 0; - - switch (attr) { - case hwmon_temp_input: - case hwmon_temp_max_alarm: - return 0444; - case hwmon_temp_crit: - return 0644; - default: - return 0; - } -} - -static u32 m88e1510_hwmon_temp_config[] = { - HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM, - 0 -}; - -static const struct hwmon_channel_info m88e1510_hwmon_temp = { - .type = hwmon_temp, - .config = m88e1510_hwmon_temp_config, -}; - -static const struct hwmon_channel_info *m88e1510_hwmon_info[] = { - &m88e1121_hwmon_chip, - &m88e1510_hwmon_temp, - NULL -}; - -static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = { - .is_visible = m88e1510_hwmon_is_visible, - .read = m88e1510_hwmon_read, - .write = m88e1510_hwmon_write, -}; - -static const struct hwmon_chip_info m88e1510_hwmon_chip_info = { - .ops = &m88e1510_hwmon_hwmon_ops, - .info = m88e1510_hwmon_info, -}; - static int m88e6390_get_temp(struct phy_device *phydev, long *temp) { int sum = 0; @@ -2494,9 +2414,8 @@ static int m88e6390_get_temp(struct phy_device *phydev, long *temp) if (ret < 0) goto error; - ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK; - ret |= MII_88E6390_MISC_TEST_SAMPLE_ENABLE | - MII_88E6390_MISC_TEST_SAMPLE_1S; + ret &= ~MII_88E6390_MISC_TEST_TEMP_SENSOR_MASK; + ret |= MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE_SAMPLE_1S; ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret); if (ret < 0) @@ -2523,8 +2442,8 @@ static int m88e6390_get_temp(struct phy_device *phydev, long *temp) if (ret < 0) goto error; - ret = ret & ~MII_88E6390_MISC_TEST_SAMPLE_MASK; - ret |= MII_88E6390_MISC_TEST_SAMPLE_DISABLE; + ret = ret & ~MII_88E6390_MISC_TEST_TEMP_SENSOR_MASK; + ret |= MII_88E6390_MISC_TEST_TEMP_SENSOR_DISABLE; ret = __phy_write(phydev, MII_88E6390_MISC_TEST, ret); @@ -2534,63 +2453,169 @@ error: return ret; } -static int m88e6390_hwmon_read(struct device *dev, - enum hwmon_sensor_types type, - u32 attr, int channel, long *temp) +static int m88e6393_get_temp(struct phy_device *phydev, long *temp) +{ + int err; + + err = m88e1510_get_temp(phydev, temp); + + /* 88E1510 measures T + 25, while the PHY on 88E6393X switch + * T + 75, so we have to subtract another 50 + */ + *temp -= 50000; + + return err; +} + +static int m88e6393_get_temp_critical(struct phy_device *phydev, long *temp) +{ + int ret; + + *temp = 0; + + ret = phy_read_paged(phydev, MII_MARVELL_MISC_TEST_PAGE, + MII_88E6390_TEMP_SENSOR); + if (ret < 0) + return ret; + + *temp = (((ret & MII_88E6393_TEMP_SENSOR_THRESHOLD_MASK) >> + MII_88E6393_TEMP_SENSOR_THRESHOLD_SHIFT) - 75) * 1000; + + return 0; +} + +static int m88e6393_set_temp_critical(struct phy_device *phydev, long temp) +{ + temp = (temp / 1000) + 75; + + return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE, + MII_88E6390_TEMP_SENSOR, + MII_88E6393_TEMP_SENSOR_THRESHOLD_MASK, + temp << MII_88E6393_TEMP_SENSOR_THRESHOLD_SHIFT); +} + +static int m88e6393_hwmon_config(struct phy_device *phydev) { - struct phy_device *phydev = dev_get_drvdata(dev); int err; + err = m88e6393_set_temp_critical(phydev, 100000); + if (err) + return err; + + return phy_modify_paged(phydev, MII_MARVELL_MISC_TEST_PAGE, + MII_88E6390_MISC_TEST, + MII_88E6390_MISC_TEST_TEMP_SENSOR_MASK | + MII_88E6393_MISC_TEST_SAMPLES_MASK | + MII_88E6393_MISC_TEST_RATE_MASK, + MII_88E6390_MISC_TEST_TEMP_SENSOR_ENABLE | + MII_88E6393_MISC_TEST_SAMPLES_2048 | + MII_88E6393_MISC_TEST_RATE_2_3MS); +} + +static int marvell_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + struct phy_device *phydev = dev_get_drvdata(dev); + const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev); + int err = -EOPNOTSUPP; + switch (attr) { case hwmon_temp_input: - err = m88e6390_get_temp(phydev, temp); + if (ops->get_temp) + err = ops->get_temp(phydev, temp); + break; + case hwmon_temp_crit: + if (ops->get_temp_critical) + err = ops->get_temp_critical(phydev, temp); + break; + case hwmon_temp_max_alarm: + if (ops->get_temp_alarm) + err = ops->get_temp_alarm(phydev, temp); break; - default: - return -EOPNOTSUPP; } return err; } -static umode_t m88e6390_hwmon_is_visible(const void *data, - enum hwmon_sensor_types type, - u32 attr, int channel) +static int marvell_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long temp) { + struct phy_device *phydev = dev_get_drvdata(dev); + const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev); + int err = -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_crit: + if (ops->set_temp_critical) + err = ops->set_temp_critical(phydev, temp); + break; + } + + return err; +} + +static umode_t marvell_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct phy_device *phydev = data; + const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev); + if (type != hwmon_temp) return 0; switch (attr) { case hwmon_temp_input: - return 0444; + return ops->get_temp ? 0444 : 0; + case hwmon_temp_max_alarm: + return ops->get_temp_alarm ? 0444 : 0; + case hwmon_temp_crit: + return (ops->get_temp_critical ? 0444 : 0) | + (ops->set_temp_critical ? 0200 : 0); default: return 0; } } -static u32 m88e6390_hwmon_temp_config[] = { - HWMON_T_INPUT, +static u32 marvell_hwmon_chip_config[] = { + HWMON_C_REGISTER_TZ, + 0 +}; + +static const struct hwmon_channel_info marvell_hwmon_chip = { + .type = hwmon_chip, + .config = marvell_hwmon_chip_config, +}; + +/* we can define HWMON_T_CRIT and HWMON_T_MAX_ALARM even though these are not + * defined for all PHYs, because the hwmon code checks whether the attributes + * exists via the .is_visible method + */ +static u32 marvell_hwmon_temp_config[] = { + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM, 0 }; -static const struct hwmon_channel_info m88e6390_hwmon_temp = { +static const struct hwmon_channel_info marvell_hwmon_temp = { .type = hwmon_temp, - .config = m88e6390_hwmon_temp_config, + .config = marvell_hwmon_temp_config, }; -static const struct hwmon_channel_info *m88e6390_hwmon_info[] = { - &m88e1121_hwmon_chip, - &m88e6390_hwmon_temp, +static const struct hwmon_channel_info *marvell_hwmon_info[] = { + &marvell_hwmon_chip, + &marvell_hwmon_temp, NULL }; -static const struct hwmon_ops m88e6390_hwmon_hwmon_ops = { - .is_visible = m88e6390_hwmon_is_visible, - .read = m88e6390_hwmon_read, +static const struct hwmon_ops marvell_hwmon_hwmon_ops = { + .is_visible = marvell_hwmon_is_visible, + .read = marvell_hwmon_read, + .write = marvell_hwmon_write, }; -static const struct hwmon_chip_info m88e6390_hwmon_chip_info = { - .ops = &m88e6390_hwmon_hwmon_ops, - .info = m88e6390_hwmon_info, +static const struct hwmon_chip_info marvell_hwmon_chip_info = { + .ops = &marvell_hwmon_hwmon_ops, + .info = marvell_hwmon_info, }; static int marvell_hwmon_name(struct phy_device *phydev) @@ -2613,49 +2638,61 @@ static int marvell_hwmon_name(struct phy_device *phydev) return 0; } -static int marvell_hwmon_probe(struct phy_device *phydev, - const struct hwmon_chip_info *chip) +static int marvell_hwmon_probe(struct phy_device *phydev) { + const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev); struct marvell_priv *priv = phydev->priv; struct device *dev = &phydev->mdio.dev; int err; + if (!ops) + return 0; + err = marvell_hwmon_name(phydev); if (err) return err; priv->hwmon_dev = devm_hwmon_device_register_with_info( - dev, priv->hwmon_name, phydev, chip, NULL); + dev, priv->hwmon_name, phydev, &marvell_hwmon_chip_info, NULL); + if (IS_ERR(priv->hwmon_dev)) + return PTR_ERR(priv->hwmon_dev); - return PTR_ERR_OR_ZERO(priv->hwmon_dev); -} + if (ops->config) + err = ops->config(phydev); -static int m88e1121_hwmon_probe(struct phy_device *phydev) -{ - return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info); + return err; } -static int m88e1510_hwmon_probe(struct phy_device *phydev) -{ - return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info); -} +static const struct marvell_hwmon_ops m88e1121_hwmon_ops = { + .get_temp = m88e1121_get_temp, +}; + +static const struct marvell_hwmon_ops m88e1510_hwmon_ops = { + .get_temp = m88e1510_get_temp, + .get_temp_critical = m88e1510_get_temp_critical, + .set_temp_critical = m88e1510_set_temp_critical, + .get_temp_alarm = m88e1510_get_temp_alarm, +}; + +static const struct marvell_hwmon_ops m88e6390_hwmon_ops = { + .get_temp = m88e6390_get_temp, +}; + +static const struct marvell_hwmon_ops m88e6393_hwmon_ops = { + .config = m88e6393_hwmon_config, + .get_temp = m88e6393_get_temp, + .get_temp_critical = m88e6393_get_temp_critical, + .set_temp_critical = m88e6393_set_temp_critical, + .get_temp_alarm = m88e1510_get_temp_alarm, +}; + +#define DEF_MARVELL_HWMON_OPS(s) (&(s)) -static int m88e6390_hwmon_probe(struct phy_device *phydev) -{ - return marvell_hwmon_probe(phydev, &m88e6390_hwmon_chip_info); -} #else -static int m88e1121_hwmon_probe(struct phy_device *phydev) -{ - return 0; -} -static int m88e1510_hwmon_probe(struct phy_device *phydev) -{ - return 0; -} +#define DEF_MARVELL_HWMON_OPS(s) NULL -static int m88e6390_hwmon_probe(struct phy_device *phydev) +static int marvell_hwmon_probe(struct phy_device *phydev) { return 0; } @@ -2671,40 +2708,7 @@ static int marvell_probe(struct phy_device *phydev) phydev->priv = priv; - return 0; -} - -static int m88e1121_probe(struct phy_device *phydev) -{ - int err; - - err = marvell_probe(phydev); - if (err) - return err; - - return m88e1121_hwmon_probe(phydev); -} - -static int m88e1510_probe(struct phy_device *phydev) -{ - int err; - - err = marvell_probe(phydev); - if (err) - return err; - - return m88e1510_hwmon_probe(phydev); -} - -static int m88e6390_probe(struct phy_device *phydev) -{ - int err; - - err = marvell_probe(phydev); - if (err) - return err; - - return m88e6390_hwmon_probe(phydev); + return marvell_hwmon_probe(phydev); } static struct phy_driver marvell_drivers[] = { @@ -2732,7 +2736,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1112", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = m88e1111_config_init, + .config_init = m88e1112_config_init, .config_aneg = marvell_config_aneg, .config_intr = marvell_config_intr, .handle_interrupt = marvell_handle_interrupt, @@ -2752,7 +2756,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1111", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = m88e1111_config_init, + .config_init = m88e1111gbe_config_init, .config_aneg = m88e1111_config_aneg, .read_status = marvell_read_status, .config_intr = marvell_config_intr, @@ -2773,7 +2777,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1111 (Finisar)", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = m88e1111_config_init, + .config_init = m88e1111gbe_config_init, .config_aneg = m88e1111_config_aneg, .read_status = marvell_read_status, .config_intr = marvell_config_intr, @@ -2810,9 +2814,10 @@ static struct phy_driver marvell_drivers[] = { .phy_id = MARVELL_PHY_ID_88E1121R, .phy_id_mask = MARVELL_PHY_ID_MASK, .name = "Marvell 88E1121R", + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1121_hwmon_ops), /* PHY_GBIT_FEATURES */ - .probe = m88e1121_probe, - .config_init = marvell_config_init, + .probe = marvell_probe, + .config_init = marvell_1011gbe_config_init, .config_aneg = m88e1121_config_aneg, .read_status = marvell_read_status, .config_intr = marvell_config_intr, @@ -2892,7 +2897,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1240", /* PHY_GBIT_FEATURES */ .probe = marvell_probe, - .config_init = m88e1111_config_init, + .config_init = m88e1112_config_init, .config_aneg = marvell_config_aneg, .config_intr = marvell_config_intr, .handle_interrupt = marvell_handle_interrupt, @@ -2903,6 +2908,8 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, + .get_tunable = m88e1011_get_tunable, + .set_tunable = m88e1011_set_tunable, }, { .phy_id = MARVELL_PHY_ID_88E1116R, @@ -2927,9 +2934,10 @@ static struct phy_driver marvell_drivers[] = { .phy_id = MARVELL_PHY_ID_88E1510, .phy_id_mask = MARVELL_PHY_ID_MASK, .name = "Marvell 88E1510", + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), .features = PHY_GBIT_FIBRE_FEATURES, .flags = PHY_POLL_CABLE_TEST, - .probe = m88e1510_probe, + .probe = marvell_probe, .config_init = m88e1510_config_init, .config_aneg = m88e1510_config_aneg, .read_status = marvell_read_status, @@ -2955,10 +2963,11 @@ static struct phy_driver marvell_drivers[] = { .phy_id = MARVELL_PHY_ID_88E1540, .phy_id_mask = MARVELL_PHY_ID_MASK, .name = "Marvell 88E1540", + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), /* PHY_GBIT_FEATURES */ .flags = PHY_POLL_CABLE_TEST, - .probe = m88e1510_probe, - .config_init = marvell_config_init, + .probe = marvell_probe, + .config_init = marvell_1011gbe_config_init, .config_aneg = m88e1510_config_aneg, .read_status = marvell_read_status, .config_intr = marvell_config_intr, @@ -2980,10 +2989,11 @@ static struct phy_driver marvell_drivers[] = { .phy_id = MARVELL_PHY_ID_88E1545, .phy_id_mask = MARVELL_PHY_ID_MASK, .name = "Marvell 88E1545", - .probe = m88e1510_probe, + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), + .probe = marvell_probe, /* PHY_GBIT_FEATURES */ .flags = PHY_POLL_CABLE_TEST, - .config_init = marvell_config_init, + .config_init = marvell_1011gbe_config_init, .config_aneg = m88e1510_config_aneg, .read_status = marvell_read_status, .config_intr = marvell_config_intr, @@ -3021,13 +3031,14 @@ static struct phy_driver marvell_drivers[] = { .get_stats = marvell_get_stats, }, { - .phy_id = MARVELL_PHY_ID_88E6390, + .phy_id = MARVELL_PHY_ID_88E6341_FAMILY, .phy_id_mask = MARVELL_PHY_ID_MASK, - .name = "Marvell 88E6390", + .name = "Marvell 88E6341 Family", + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), /* PHY_GBIT_FEATURES */ .flags = PHY_POLL_CABLE_TEST, - .probe = m88e6390_probe, - .config_init = marvell_config_init, + .probe = marvell_probe, + .config_init = marvell_1011gbe_config_init, .config_aneg = m88e6390_config_aneg, .read_status = marvell_read_status, .config_intr = marvell_config_intr, @@ -3046,12 +3057,65 @@ static struct phy_driver marvell_drivers[] = { .cable_test_get_status = marvell_vct7_cable_test_get_status, }, { + .phy_id = MARVELL_PHY_ID_88E6390_FAMILY, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E6390 Family", + .driver_data = DEF_MARVELL_HWMON_OPS(m88e6390_hwmon_ops), + /* PHY_GBIT_FEATURES */ + .flags = PHY_POLL_CABLE_TEST, + .probe = marvell_probe, + .config_init = marvell_1011gbe_config_init, + .config_aneg = m88e6390_config_aneg, + .read_status = marvell_read_status, + .config_intr = marvell_config_intr, + .handle_interrupt = marvell_handle_interrupt, + .resume = genphy_resume, + .suspend = genphy_suspend, + .read_page = marvell_read_page, + .write_page = marvell_write_page, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, + .get_tunable = m88e1540_get_tunable, + .set_tunable = m88e1540_set_tunable, + .cable_test_start = marvell_vct7_cable_test_start, + .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, + .cable_test_get_status = marvell_vct7_cable_test_get_status, + }, + { + .phy_id = MARVELL_PHY_ID_88E6393_FAMILY, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E6393 Family", + .driver_data = DEF_MARVELL_HWMON_OPS(m88e6393_hwmon_ops), + /* PHY_GBIT_FEATURES */ + .flags = PHY_POLL_CABLE_TEST, + .probe = marvell_probe, + .config_init = marvell_1011gbe_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, + .config_intr = marvell_config_intr, + .handle_interrupt = marvell_handle_interrupt, + .resume = genphy_resume, + .suspend = genphy_suspend, + .read_page = marvell_read_page, + .write_page = marvell_write_page, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, + .get_tunable = m88e1540_get_tunable, + .set_tunable = m88e1540_set_tunable, + .cable_test_start = marvell_vct7_cable_test_start, + .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, + .cable_test_get_status = marvell_vct7_cable_test_get_status, + }, + { .phy_id = MARVELL_PHY_ID_88E1340S, .phy_id_mask = MARVELL_PHY_ID_MASK, .name = "Marvell 88E1340S", - .probe = m88e1510_probe, + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), + .probe = marvell_probe, /* PHY_GBIT_FEATURES */ - .config_init = marvell_config_init, + .config_init = marvell_1011gbe_config_init, .config_aneg = m88e1510_config_aneg, .read_status = marvell_read_status, .config_intr = marvell_config_intr, @@ -3070,9 +3134,10 @@ static struct phy_driver marvell_drivers[] = { .phy_id = MARVELL_PHY_ID_88E1548P, .phy_id_mask = MARVELL_PHY_ID_MASK, .name = "Marvell 88E1548P", - .probe = m88e1510_probe, + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), + .probe = marvell_probe, .features = PHY_GBIT_FIBRE_FEATURES, - .config_init = marvell_config_init, + .config_init = marvell_1011gbe_config_init, .config_aneg = m88e1510_config_aneg, .read_status = marvell_read_status, .config_intr = marvell_config_intr, @@ -3107,7 +3172,9 @@ static struct mdio_device_id __maybe_unused marvell_tbl[] = { { MARVELL_PHY_ID_88E1540, MARVELL_PHY_ID_MASK }, { MARVELL_PHY_ID_88E1545, MARVELL_PHY_ID_MASK }, { MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK }, - { MARVELL_PHY_ID_88E6390, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E6341_FAMILY, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E6390_FAMILY, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88E6393_FAMILY, MARVELL_PHY_ID_MASK }, { MARVELL_PHY_ID_88E1340S, MARVELL_PHY_ID_MASK }, { MARVELL_PHY_ID_88E1548P, MARVELL_PHY_ID_MASK }, { } diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index b1bb9b8e1e4e..bbbc6ac8fa82 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -35,6 +35,15 @@ enum { MV_PMA_FW_VER0 = 0xc011, MV_PMA_FW_VER1 = 0xc012, + MV_PMA_21X0_PORT_CTRL = 0xc04a, + MV_PMA_21X0_PORT_CTRL_SWRST = BIT(15), + MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK = 0x7, + MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII = 0x0, + MV_PMA_2180_PORT_CTRL_MACTYPE_DXGMII = 0x1, + MV_PMA_2180_PORT_CTRL_MACTYPE_QXGMII = 0x2, + MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER = 0x4, + MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER_NO_SGMII_AN = 0x5, + MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH = 0x6, MV_PMA_BOOT = 0xc050, MV_PMA_BOOT_FATAL = BIT(0), @@ -78,10 +87,18 @@ enum { /* Vendor2 MMD registers */ MV_V2_PORT_CTRL = 0xf001, - MV_V2_PORT_CTRL_SWRST = BIT(15), - MV_V2_PORT_CTRL_PWRDOWN = BIT(11), - MV_V2_PORT_MAC_TYPE_MASK = 0x7, - MV_V2_PORT_MAC_TYPE_RATE_MATCH = 0x6, + MV_V2_PORT_CTRL_PWRDOWN = BIT(11), + MV_V2_33X0_PORT_CTRL_SWRST = BIT(15), + MV_V2_33X0_PORT_CTRL_MACTYPE_MASK = 0x7, + MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI = 0x0, + MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH = 0x1, + MV_V2_3340_PORT_CTRL_MACTYPE_RXAUI_NO_SGMII_AN = 0x1, + MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH = 0x2, + MV_V2_3310_PORT_CTRL_MACTYPE_XAUI = 0x3, + MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER = 0x4, + MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN = 0x5, + MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH = 0x6, + MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII = 0x7, /* Temperature control/read registers (88X3310 only) */ MV_V2_TEMP_CTRL = 0xf08a, MV_V2_TEMP_CTRL_MASK = 0xc000, @@ -91,14 +108,32 @@ enum { MV_V2_TEMP_UNKNOWN = 0x9600, /* unknown function */ }; +struct mv3310_chip { + void (*init_supported_interfaces)(unsigned long *mask); + int (*get_mactype)(struct phy_device *phydev); + int (*init_interface)(struct phy_device *phydev, int mactype); + +#ifdef CONFIG_HWMON + int (*hwmon_read_temp_reg)(struct phy_device *phydev); +#endif +}; + struct mv3310_priv { + DECLARE_BITMAP(supported_interfaces, PHY_INTERFACE_MODE_MAX); + u32 firmware_ver; bool rate_match; + phy_interface_t const_interface; struct device *hwmon_dev; char *hwmon_name; }; +static const struct mv3310_chip *to_mv3310_chip(struct phy_device *phydev) +{ + return phydev->drv->driver_data; +} + #ifdef CONFIG_HWMON static umode_t mv3310_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, @@ -121,18 +156,11 @@ static int mv2110_hwmon_read_temp_reg(struct phy_device *phydev) return phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_TEMP); } -static int mv10g_hwmon_read_temp_reg(struct phy_device *phydev) -{ - if (phydev->drv->phy_id == MARVELL_PHY_ID_88X3310) - return mv3310_hwmon_read_temp_reg(phydev); - else /* MARVELL_PHY_ID_88E2110 */ - return mv2110_hwmon_read_temp_reg(phydev); -} - static int mv3310_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *value) { struct phy_device *phydev = dev_get_drvdata(dev); + const struct mv3310_chip *chip = to_mv3310_chip(phydev); int temp; if (type == hwmon_chip && attr == hwmon_chip_update_interval) { @@ -141,7 +169,7 @@ static int mv3310_hwmon_read(struct device *dev, enum hwmon_sensor_types type, } if (type == hwmon_temp && attr == hwmon_temp_input) { - temp = mv10g_hwmon_read_temp_reg(phydev); + temp = chip->hwmon_read_temp_reg(phydev); if (temp < 0) return temp; @@ -268,7 +296,7 @@ static int mv3310_power_up(struct phy_device *phydev) return ret; return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL, - MV_V2_PORT_CTRL_SWRST); + MV_V2_33X0_PORT_CTRL_SWRST); } static int mv3310_reset(struct phy_device *phydev, u32 unit) @@ -363,6 +391,7 @@ static const struct sfp_upstream_ops mv3310_sfp_ops = { static int mv3310_probe(struct phy_device *phydev) { + const struct mv3310_chip *chip = to_mv3310_chip(phydev); struct mv3310_priv *priv; u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; int ret; @@ -412,6 +441,8 @@ static int mv3310_probe(struct phy_device *phydev) if (ret) return ret; + chip->init_supported_interfaces(priv->supported_interfaces); + return phy_sfp_probe(phydev, &mv3310_sfp_ops); } @@ -453,18 +484,102 @@ static bool mv3310_has_pma_ngbaset_quirk(struct phy_device *phydev) MV_PHY_ALASKA_NBT_QUIRK_MASK) == MV_PHY_ALASKA_NBT_QUIRK_REV; } +static int mv2110_get_mactype(struct phy_device *phydev) +{ + int mactype; + + mactype = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_21X0_PORT_CTRL); + if (mactype < 0) + return mactype; + + return mactype & MV_PMA_21X0_PORT_CTRL_MACTYPE_MASK; +} + +static int mv3310_get_mactype(struct phy_device *phydev) +{ + int mactype; + + mactype = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL); + if (mactype < 0) + return mactype; + + return mactype & MV_V2_33X0_PORT_CTRL_MACTYPE_MASK; +} + +static int mv2110_init_interface(struct phy_device *phydev, int mactype) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + + priv->rate_match = false; + + if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH) + priv->rate_match = true; + + if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII) + priv->const_interface = PHY_INTERFACE_MODE_USXGMII; + else if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH) + priv->const_interface = PHY_INTERFACE_MODE_10GBASER; + else if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER || + mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER_NO_SGMII_AN) + priv->const_interface = PHY_INTERFACE_MODE_NA; + else + return -EINVAL; + + return 0; +} + +static int mv3310_init_interface(struct phy_device *phydev, int mactype) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + + priv->rate_match = false; + + if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH || + mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH || + mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH) + priv->rate_match = true; + + if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII) + priv->const_interface = PHY_INTERFACE_MODE_USXGMII; + else if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH || + mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN || + mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER) + priv->const_interface = PHY_INTERFACE_MODE_10GBASER; + else if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH || + mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI) + priv->const_interface = PHY_INTERFACE_MODE_RXAUI; + else if (mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH || + mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI) + priv->const_interface = PHY_INTERFACE_MODE_XAUI; + else + return -EINVAL; + + return 0; +} + +static int mv3340_init_interface(struct phy_device *phydev, int mactype) +{ + struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); + int err = 0; + + priv->rate_match = false; + + if (mactype == MV_V2_3340_PORT_CTRL_MACTYPE_RXAUI_NO_SGMII_AN) + priv->const_interface = PHY_INTERFACE_MODE_RXAUI; + else + err = mv3310_init_interface(phydev, mactype); + + return err; +} + static int mv3310_config_init(struct phy_device *phydev) { struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); - int err; - int val; + const struct mv3310_chip *chip = to_mv3310_chip(phydev); + int err, mactype; /* Check that the PHY interface type is compatible */ - if (phydev->interface != PHY_INTERFACE_MODE_SGMII && - phydev->interface != PHY_INTERFACE_MODE_2500BASEX && - phydev->interface != PHY_INTERFACE_MODE_XAUI && - phydev->interface != PHY_INTERFACE_MODE_RXAUI && - phydev->interface != PHY_INTERFACE_MODE_10GBASER) + if (!test_bit(phydev->interface, priv->supported_interfaces)) return -ENODEV; phydev->mdix_ctrl = ETH_TP_MDI_AUTO; @@ -474,11 +589,15 @@ static int mv3310_config_init(struct phy_device *phydev) if (err) return err; - val = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL); - if (val < 0) - return val; - priv->rate_match = ((val & MV_V2_PORT_MAC_TYPE_MASK) == - MV_V2_PORT_MAC_TYPE_RATE_MATCH); + mactype = chip->get_mactype(phydev); + if (mactype < 0) + return mactype; + + err = chip->init_interface(phydev, mactype); + if (err) { + phydev_err(phydev, "MACTYPE configuration invalid\n"); + return err; + } /* Enable EDPD mode - saving 600mW */ return mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); @@ -588,40 +707,44 @@ static void mv3310_update_interface(struct phy_device *phydev) { struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev); - /* In "XFI with Rate Matching" mode the PHY interface is fixed at - * 10Gb. The PHY adapts the rate to actual wire speed with help of + if (!phydev->link) + return; + + /* In all of the "* with Rate Matching" modes the PHY interface is fixed + * at 10Gb. The PHY adapts the rate to actual wire speed with help of * internal 16KB buffer. + * + * In USXGMII mode the PHY interface mode is also fixed. */ - if (priv->rate_match) { - phydev->interface = PHY_INTERFACE_MODE_10GBASER; + if (priv->rate_match || + priv->const_interface == PHY_INTERFACE_MODE_USXGMII) { + phydev->interface = priv->const_interface; return; } - if ((phydev->interface == PHY_INTERFACE_MODE_SGMII || - phydev->interface == PHY_INTERFACE_MODE_2500BASEX || - phydev->interface == PHY_INTERFACE_MODE_10GBASER) && - phydev->link) { - /* The PHY automatically switches its serdes interface (and - * active PHYXS instance) between Cisco SGMII, 10GBase-R and - * 2500BaseX modes according to the speed. Florian suggests - * setting phydev->interface to communicate this to the MAC. - * Only do this if we are already in one of the above modes. - */ - switch (phydev->speed) { - case SPEED_10000: - phydev->interface = PHY_INTERFACE_MODE_10GBASER; - break; - case SPEED_2500: - phydev->interface = PHY_INTERFACE_MODE_2500BASEX; - break; - case SPEED_1000: - case SPEED_100: - case SPEED_10: - phydev->interface = PHY_INTERFACE_MODE_SGMII; - break; - default: - break; - } + /* The PHY automatically switches its serdes interface (and active PHYXS + * instance) between Cisco SGMII, 2500BaseX, 5GBase-R and 10GBase-R / + * xaui / rxaui modes according to the speed. + * Florian suggests setting phydev->interface to communicate this to the + * MAC. Only do this if we are already in one of the above modes. + */ + switch (phydev->speed) { + case SPEED_10000: + phydev->interface = priv->const_interface; + break; + case SPEED_5000: + phydev->interface = PHY_INTERFACE_MODE_5GBASER; + break; + case SPEED_2500: + phydev->interface = PHY_INTERFACE_MODE_2500BASEX; + break; + case SPEED_1000: + case SPEED_100: + case SPEED_10: + phydev->interface = PHY_INTERFACE_MODE_SGMII; + break; + default: + break; } } @@ -765,11 +888,133 @@ static int mv3310_set_tunable(struct phy_device *phydev, } } +static void mv3310_init_supported_interfaces(unsigned long *mask) +{ + __set_bit(PHY_INTERFACE_MODE_SGMII, mask); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, mask); + __set_bit(PHY_INTERFACE_MODE_5GBASER, mask); + __set_bit(PHY_INTERFACE_MODE_XAUI, mask); + __set_bit(PHY_INTERFACE_MODE_RXAUI, mask); + __set_bit(PHY_INTERFACE_MODE_10GBASER, mask); + __set_bit(PHY_INTERFACE_MODE_USXGMII, mask); +} + +static void mv3340_init_supported_interfaces(unsigned long *mask) +{ + __set_bit(PHY_INTERFACE_MODE_SGMII, mask); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, mask); + __set_bit(PHY_INTERFACE_MODE_5GBASER, mask); + __set_bit(PHY_INTERFACE_MODE_RXAUI, mask); + __set_bit(PHY_INTERFACE_MODE_10GBASER, mask); + __set_bit(PHY_INTERFACE_MODE_USXGMII, mask); +} + +static void mv2110_init_supported_interfaces(unsigned long *mask) +{ + __set_bit(PHY_INTERFACE_MODE_SGMII, mask); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, mask); + __set_bit(PHY_INTERFACE_MODE_5GBASER, mask); + __set_bit(PHY_INTERFACE_MODE_10GBASER, mask); + __set_bit(PHY_INTERFACE_MODE_USXGMII, mask); +} + +static void mv2111_init_supported_interfaces(unsigned long *mask) +{ + __set_bit(PHY_INTERFACE_MODE_SGMII, mask); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, mask); + __set_bit(PHY_INTERFACE_MODE_10GBASER, mask); + __set_bit(PHY_INTERFACE_MODE_USXGMII, mask); +} + +static const struct mv3310_chip mv3310_type = { + .init_supported_interfaces = mv3310_init_supported_interfaces, + .get_mactype = mv3310_get_mactype, + .init_interface = mv3310_init_interface, + +#ifdef CONFIG_HWMON + .hwmon_read_temp_reg = mv3310_hwmon_read_temp_reg, +#endif +}; + +static const struct mv3310_chip mv3340_type = { + .init_supported_interfaces = mv3340_init_supported_interfaces, + .get_mactype = mv3310_get_mactype, + .init_interface = mv3340_init_interface, + +#ifdef CONFIG_HWMON + .hwmon_read_temp_reg = mv3310_hwmon_read_temp_reg, +#endif +}; + +static const struct mv3310_chip mv2110_type = { + .init_supported_interfaces = mv2110_init_supported_interfaces, + .get_mactype = mv2110_get_mactype, + .init_interface = mv2110_init_interface, + +#ifdef CONFIG_HWMON + .hwmon_read_temp_reg = mv2110_hwmon_read_temp_reg, +#endif +}; + +static const struct mv3310_chip mv2111_type = { + .init_supported_interfaces = mv2111_init_supported_interfaces, + .get_mactype = mv2110_get_mactype, + .init_interface = mv2110_init_interface, + +#ifdef CONFIG_HWMON + .hwmon_read_temp_reg = mv2110_hwmon_read_temp_reg, +#endif +}; + +static int mv211x_match_phy_device(struct phy_device *phydev, bool has_5g) +{ + int val; + + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88E2110) + return 0; + + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_SPEED); + if (val < 0) + return val; + + return !!(val & MDIO_PCS_SPEED_5G) == has_5g; +} + +static int mv2110_match_phy_device(struct phy_device *phydev) +{ + return mv211x_match_phy_device(phydev, true); +} + +static int mv2111_match_phy_device(struct phy_device *phydev) +{ + return mv211x_match_phy_device(phydev, false); +} + static struct phy_driver mv3310_drivers[] = { { .phy_id = MARVELL_PHY_ID_88X3310, - .phy_id_mask = MARVELL_PHY_ID_MASK, + .phy_id_mask = MARVELL_PHY_ID_88X33X0_MASK, .name = "mv88x3310", + .driver_data = &mv3310_type, + .get_features = mv3310_get_features, + .config_init = mv3310_config_init, + .probe = mv3310_probe, + .suspend = mv3310_suspend, + .resume = mv3310_resume, + .config_aneg = mv3310_config_aneg, + .aneg_done = mv3310_aneg_done, + .read_status = mv3310_read_status, + .get_tunable = mv3310_get_tunable, + .set_tunable = mv3310_set_tunable, + .remove = mv3310_remove, + .set_loopback = genphy_c45_loopback, + }, + { + .phy_id = MARVELL_PHY_ID_88X3340, + .phy_id_mask = MARVELL_PHY_ID_88X33X0_MASK, + .name = "mv88x3340", + .driver_data = &mv3340_type, .get_features = mv3310_get_features, .config_init = mv3310_config_init, .probe = mv3310_probe, @@ -781,11 +1026,32 @@ static struct phy_driver mv3310_drivers[] = { .get_tunable = mv3310_get_tunable, .set_tunable = mv3310_set_tunable, .remove = mv3310_remove, + .set_loopback = genphy_c45_loopback, + }, + { + .phy_id = MARVELL_PHY_ID_88E2110, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .match_phy_device = mv2110_match_phy_device, + .name = "mv88e2110", + .driver_data = &mv2110_type, + .probe = mv3310_probe, + .suspend = mv3310_suspend, + .resume = mv3310_resume, + .config_init = mv3310_config_init, + .config_aneg = mv3310_config_aneg, + .aneg_done = mv3310_aneg_done, + .read_status = mv3310_read_status, + .get_tunable = mv3310_get_tunable, + .set_tunable = mv3310_set_tunable, + .remove = mv3310_remove, + .set_loopback = genphy_c45_loopback, }, { .phy_id = MARVELL_PHY_ID_88E2110, .phy_id_mask = MARVELL_PHY_ID_MASK, - .name = "mv88x2110", + .match_phy_device = mv2111_match_phy_device, + .name = "mv88e2111", + .driver_data = &mv2111_type, .probe = mv3310_probe, .suspend = mv3310_suspend, .resume = mv3310_resume, @@ -796,16 +1062,18 @@ static struct phy_driver mv3310_drivers[] = { .get_tunable = mv3310_get_tunable, .set_tunable = mv3310_set_tunable, .remove = mv3310_remove, + .set_loopback = genphy_c45_loopback, }, }; module_phy_driver(mv3310_drivers); static struct mdio_device_id __maybe_unused mv3310_tbl[] = { - { MARVELL_PHY_ID_88X3310, MARVELL_PHY_ID_MASK }, + { MARVELL_PHY_ID_88X3310, MARVELL_PHY_ID_88X33X0_MASK }, + { MARVELL_PHY_ID_88X3340, MARVELL_PHY_ID_88X33X0_MASK }, { MARVELL_PHY_ID_88E2110, MARVELL_PHY_ID_MASK }, { }, }; MODULE_DEVICE_TABLE(mdio, mv3310_tbl); -MODULE_DESCRIPTION("Marvell Alaska X 10Gigabit Ethernet PHY driver (MV88X3310)"); +MODULE_DESCRIPTION("Marvell Alaska X/M multi-gigabit Ethernet PHY driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/mdio-boardinfo.c b/drivers/net/phy/mdio-boardinfo.c index 033df435f76c..2de679a68115 100644 --- a/drivers/net/phy/mdio-boardinfo.c +++ b/drivers/net/phy/mdio-boardinfo.c @@ -50,7 +50,7 @@ void mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus, EXPORT_SYMBOL(mdiobus_setup_mdiodev_from_board_info); /** - * mdio_register_board_info - register MDIO devices for a given board + * mdiobus_register_board_info - register MDIO devices for a given board * @info: array of devices descriptors * @n: number of descriptors provided * Context: can sleep diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 823518554079..dadf75ff3ab9 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -510,7 +510,7 @@ static int mdiobus_create_device(struct mii_bus *bus, * on a given bus, and attach them to the bus. Drivers should use * mdiobus_register() rather than __mdiobus_register() unless they * need to pass a specific owner module. MDIO devices which are not - * PHYs will not be brought up by this function. They are expected to + * PHYs will not be brought up by this function. They are expected * to be explicitly listed in DT and instantiated by of_mdiobus_register(). * * Returns 0 on success or < 0 on error. diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c index 3a7705228ed5..6e32da28e138 100644 --- a/drivers/net/phy/mscc/mscc_main.c +++ b/drivers/net/phy/mscc/mscc_main.c @@ -1362,6 +1362,12 @@ static int vsc8584_config_pre_init(struct phy_device *phydev) u16 crc, reg; int ret; + ret = vsc8584_pll5g_reset(phydev); + if (ret < 0) { + dev_err(dev, "failed LCPLL reset, ret: %d\n", ret); + return ret; + } + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); /* all writes below are broadcasted to all PHYs in the same package */ @@ -1466,6 +1472,24 @@ static int vsc8584_config_pre_init(struct phy_device *phydev) if (ret) goto out; + /* Write patch vector 0, to skip IB cal polling */ + phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED_GPIO); + reg = MSCC_ROM_TRAP_SERDES_6G_CFG; /* ROM address to trap, for patch vector 0 */ + ret = phy_base_write(phydev, MSCC_TRAP_ROM_ADDR(1), reg); + if (ret) + goto out; + + reg = MSCC_RAM_TRAP_SERDES_6G_CFG; /* RAM address to jump to, when patch vector 0 enabled */ + ret = phy_base_write(phydev, MSCC_PATCH_RAM_ADDR(1), reg); + if (ret) + goto out; + + reg = phy_base_read(phydev, MSCC_INT_MEM_CNTL); + reg |= PATCH_VEC_ZERO_EN; /* bit 8, enable patch vector 0 */ + ret = phy_base_write(phydev, MSCC_INT_MEM_CNTL, reg); + if (ret) + goto out; + vsc8584_micro_deassert_reset(phydev, true); out: @@ -1531,62 +1555,81 @@ static void vsc85xx_coma_mode_release(struct phy_device *phydev) vsc85xx_phy_write_page(phydev, MSCC_PHY_PAGE_STANDARD); } -static int vsc8584_config_init(struct phy_device *phydev) +static int vsc8584_config_host_serdes(struct phy_device *phydev) { struct vsc8531_private *vsc8531 = phydev->priv; - int ret, i; + int ret; u16 val; - phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_EXTENDED_GPIO); + if (ret) + return ret; - phy_lock_mdio_bus(phydev); + val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK); + val &= ~MAC_CFG_MASK; + if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) { + val |= MAC_CFG_QSGMII; + } else if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + val |= MAC_CFG_SGMII; + } else { + ret = -EINVAL; + return ret; + } - /* Some parts of the init sequence are identical for every PHY in the - * package. Some parts are modifying the GPIO register bank which is a - * set of registers that are affecting all PHYs, a few resetting the - * microprocessor common to all PHYs. The CRC check responsible of the - * checking the firmware within the 8051 microprocessor can only be - * accessed via the PHY whose internal address in the package is 0. - * All PHYs' interrupts mask register has to be zeroed before enabling - * any PHY's interrupt in this register. - * For all these reasons, we need to do the init sequence once and only - * once whatever is the first PHY in the package that is initialized and - * do the correct init sequence for all PHYs that are package-critical - * in this pre-init function. - */ - if (phy_package_init_once(phydev)) { - /* The following switch statement assumes that the lowest - * nibble of the phy_id_mask is always 0. This works because - * the lowest nibble of the PHY_ID's below are also 0. - */ - WARN_ON(phydev->drv->phy_id_mask & 0xf); + ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); + if (ret) + return ret; - switch (phydev->phy_id & phydev->drv->phy_id_mask) { - case PHY_ID_VSC8504: - case PHY_ID_VSC8552: - case PHY_ID_VSC8572: - case PHY_ID_VSC8574: - ret = vsc8574_config_pre_init(phydev); - break; - case PHY_ID_VSC856X: - case PHY_ID_VSC8575: - case PHY_ID_VSC8582: - case PHY_ID_VSC8584: - ret = vsc8584_config_pre_init(phydev); - break; - default: - ret = -EINVAL; - break; - } + ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, + MSCC_PHY_PAGE_STANDARD); + if (ret) + return ret; - if (ret) - goto err; - } + val = PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_RST_CONF_PORT | + PROC_CMD_READ_MOD_WRITE_PORT; + if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) + val |= PROC_CMD_QSGMII_MAC; + else + val |= PROC_CMD_SGMII_MAC; + + ret = vsc8584_cmd(phydev, val); + if (ret) + return ret; + + usleep_range(10000, 20000); + + /* Disable SerDes for 100Base-FX */ + ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF | + PROC_CMD_FIBER_PORT(vsc8531->addr) | + PROC_CMD_FIBER_DISABLE | + PROC_CMD_READ_MOD_WRITE_PORT | + PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_100BASE_FX); + if (ret) + return ret; + + /* Disable SerDes for 1000Base-X */ + ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF | + PROC_CMD_FIBER_PORT(vsc8531->addr) | + PROC_CMD_FIBER_DISABLE | + PROC_CMD_READ_MOD_WRITE_PORT | + PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_1000BASE_X); + if (ret) + return ret; + + return vsc85xx_sd6g_config_v2(phydev); +} + +static int vsc8574_config_host_serdes(struct phy_device *phydev) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + int ret; + u16 val; ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED_GPIO); if (ret) - goto err; + return ret; val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK); val &= ~MAC_CFG_MASK; @@ -1598,17 +1641,17 @@ static int vsc8584_config_init(struct phy_device *phydev) val |= MAC_CFG_RGMII; } else { ret = -EINVAL; - goto err; + return ret; } ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); if (ret) - goto err; + return ret; ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); if (ret) - goto err; + return ret; if (!phy_interface_is_rgmii(phydev)) { val = PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_RST_CONF_PORT | @@ -1620,7 +1663,7 @@ static int vsc8584_config_init(struct phy_device *phydev) ret = vsc8584_cmd(phydev, val); if (ret) - goto err; + return ret; usleep_range(10000, 20000); } @@ -1632,16 +1675,78 @@ static int vsc8584_config_init(struct phy_device *phydev) PROC_CMD_READ_MOD_WRITE_PORT | PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_100BASE_FX); if (ret) - goto err; + return ret; /* Disable SerDes for 1000Base-X */ - ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF | - PROC_CMD_FIBER_PORT(vsc8531->addr) | - PROC_CMD_FIBER_DISABLE | - PROC_CMD_READ_MOD_WRITE_PORT | - PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_1000BASE_X); - if (ret) - goto err; + return vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF | + PROC_CMD_FIBER_PORT(vsc8531->addr) | + PROC_CMD_FIBER_DISABLE | + PROC_CMD_READ_MOD_WRITE_PORT | + PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_1000BASE_X); +} + +static int vsc8584_config_init(struct phy_device *phydev) +{ + struct vsc8531_private *vsc8531 = phydev->priv; + int ret, i; + u16 val; + + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + + phy_lock_mdio_bus(phydev); + + /* Some parts of the init sequence are identical for every PHY in the + * package. Some parts are modifying the GPIO register bank which is a + * set of registers that are affecting all PHYs, a few resetting the + * microprocessor common to all PHYs. The CRC check responsible of the + * checking the firmware within the 8051 microprocessor can only be + * accessed via the PHY whose internal address in the package is 0. + * All PHYs' interrupts mask register has to be zeroed before enabling + * any PHY's interrupt in this register. + * For all these reasons, we need to do the init sequence once and only + * once whatever is the first PHY in the package that is initialized and + * do the correct init sequence for all PHYs that are package-critical + * in this pre-init function. + */ + if (phy_package_init_once(phydev)) { + /* The following switch statement assumes that the lowest + * nibble of the phy_id_mask is always 0. This works because + * the lowest nibble of the PHY_ID's below are also 0. + */ + WARN_ON(phydev->drv->phy_id_mask & 0xf); + + switch (phydev->phy_id & phydev->drv->phy_id_mask) { + case PHY_ID_VSC8504: + case PHY_ID_VSC8552: + case PHY_ID_VSC8572: + case PHY_ID_VSC8574: + ret = vsc8574_config_pre_init(phydev); + if (ret) + goto err; + ret = vsc8574_config_host_serdes(phydev); + if (ret) + goto err; + break; + case PHY_ID_VSC856X: + case PHY_ID_VSC8575: + case PHY_ID_VSC8582: + case PHY_ID_VSC8584: + ret = vsc8584_config_pre_init(phydev); + if (ret) + goto err; + ret = vsc8584_config_host_serdes(phydev); + if (ret) + goto err; + vsc85xx_coma_mode_release(phydev); + break; + default: + ret = -EINVAL; + break; + } + + if (ret) + goto err; + } phy_unlock_mdio_bus(phydev); diff --git a/drivers/net/phy/nxp-c45-tja11xx.c b/drivers/net/phy/nxp-c45-tja11xx.c new file mode 100644 index 000000000000..26b9c0d7cb9d --- /dev/null +++ b/drivers/net/phy/nxp-c45-tja11xx.c @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: GPL-2.0 +/* NXP C45 PHY driver + * Copyright (C) 2021 NXP + * Author: Radu Pirea <radu-nicolae.pirea@oss.nxp.com> + */ + +#include <linux/delay.h> +#include <linux/ethtool.h> +#include <linux/ethtool_netlink.h> +#include <linux/kernel.h> +#include <linux/mii.h> +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/processor.h> +#include <linux/property.h> + +#define PHY_ID_TJA_1103 0x001BB010 + +#define PMAPMD_B100T1_PMAPMD_CTL 0x0834 +#define B100T1_PMAPMD_CONFIG_EN BIT(15) +#define B100T1_PMAPMD_MASTER BIT(14) +#define MASTER_MODE (B100T1_PMAPMD_CONFIG_EN | \ + B100T1_PMAPMD_MASTER) +#define SLAVE_MODE (B100T1_PMAPMD_CONFIG_EN) + +#define VEND1_DEVICE_CONTROL 0x0040 +#define DEVICE_CONTROL_RESET BIT(15) +#define DEVICE_CONTROL_CONFIG_GLOBAL_EN BIT(14) +#define DEVICE_CONTROL_CONFIG_ALL_EN BIT(13) + +#define VEND1_PHY_IRQ_ACK 0x80A0 +#define VEND1_PHY_IRQ_EN 0x80A1 +#define VEND1_PHY_IRQ_STATUS 0x80A2 +#define PHY_IRQ_LINK_EVENT BIT(1) + +#define VEND1_PHY_CONTROL 0x8100 +#define PHY_CONFIG_EN BIT(14) +#define PHY_START_OP BIT(0) + +#define VEND1_PHY_CONFIG 0x8108 +#define PHY_CONFIG_AUTO BIT(0) + +#define VEND1_SIGNAL_QUALITY 0x8320 +#define SQI_VALID BIT(14) +#define SQI_MASK GENMASK(2, 0) +#define MAX_SQI SQI_MASK + +#define VEND1_CABLE_TEST 0x8330 +#define CABLE_TEST_ENABLE BIT(15) +#define CABLE_TEST_START BIT(14) +#define CABLE_TEST_VALID BIT(13) +#define CABLE_TEST_OK 0x00 +#define CABLE_TEST_SHORTED 0x01 +#define CABLE_TEST_OPEN 0x02 +#define CABLE_TEST_UNKNOWN 0x07 + +#define VEND1_PORT_CONTROL 0x8040 +#define PORT_CONTROL_EN BIT(14) + +#define VEND1_PORT_INFRA_CONTROL 0xAC00 +#define PORT_INFRA_CONTROL_EN BIT(14) + +#define VEND1_RXID 0xAFCC +#define VEND1_TXID 0xAFCD +#define ID_ENABLE BIT(15) + +#define VEND1_ABILITIES 0xAFC4 +#define RGMII_ID_ABILITY BIT(15) +#define RGMII_ABILITY BIT(14) +#define RMII_ABILITY BIT(10) +#define REVMII_ABILITY BIT(9) +#define MII_ABILITY BIT(8) +#define SGMII_ABILITY BIT(0) + +#define VEND1_MII_BASIC_CONFIG 0xAFC6 +#define MII_BASIC_CONFIG_REV BIT(8) +#define MII_BASIC_CONFIG_SGMII 0x9 +#define MII_BASIC_CONFIG_RGMII 0x7 +#define MII_BASIC_CONFIG_RMII 0x5 +#define MII_BASIC_CONFIG_MII 0x4 + +#define VEND1_SYMBOL_ERROR_COUNTER 0x8350 +#define VEND1_LINK_DROP_COUNTER 0x8352 +#define VEND1_LINK_LOSSES_AND_FAILURES 0x8353 +#define VEND1_R_GOOD_FRAME_CNT 0xA950 +#define VEND1_R_BAD_FRAME_CNT 0xA952 +#define VEND1_R_RXER_FRAME_CNT 0xA954 +#define VEND1_RX_PREAMBLE_COUNT 0xAFCE +#define VEND1_TX_PREAMBLE_COUNT 0xAFCF +#define VEND1_RX_IPG_LENGTH 0xAFD0 +#define VEND1_TX_IPG_LENGTH 0xAFD1 +#define COUNTER_EN BIT(15) + +#define RGMII_PERIOD_PS 8000U +#define PS_PER_DEGREE div_u64(RGMII_PERIOD_PS, 360) +#define MIN_ID_PS 1644U +#define MAX_ID_PS 2260U +#define DEFAULT_ID_PS 2000U + +struct nxp_c45_phy { + u32 tx_delay; + u32 rx_delay; +}; + +struct nxp_c45_phy_stats { + const char *name; + u8 mmd; + u16 reg; + u8 off; + u16 mask; +}; + +static const struct nxp_c45_phy_stats nxp_c45_hw_stats[] = { + { "phy_symbol_error_cnt", MDIO_MMD_VEND1, + VEND1_SYMBOL_ERROR_COUNTER, 0, GENMASK(15, 0) }, + { "phy_link_status_drop_cnt", MDIO_MMD_VEND1, + VEND1_LINK_DROP_COUNTER, 8, GENMASK(13, 8) }, + { "phy_link_availability_drop_cnt", MDIO_MMD_VEND1, + VEND1_LINK_DROP_COUNTER, 0, GENMASK(5, 0) }, + { "phy_link_loss_cnt", MDIO_MMD_VEND1, + VEND1_LINK_LOSSES_AND_FAILURES, 10, GENMASK(15, 10) }, + { "phy_link_failure_cnt", MDIO_MMD_VEND1, + VEND1_LINK_LOSSES_AND_FAILURES, 0, GENMASK(9, 0) }, + { "r_good_frame_cnt", MDIO_MMD_VEND1, + VEND1_R_GOOD_FRAME_CNT, 0, GENMASK(15, 0) }, + { "r_bad_frame_cnt", MDIO_MMD_VEND1, + VEND1_R_BAD_FRAME_CNT, 0, GENMASK(15, 0) }, + { "r_rxer_frame_cnt", MDIO_MMD_VEND1, + VEND1_R_RXER_FRAME_CNT, 0, GENMASK(15, 0) }, + { "rx_preamble_count", MDIO_MMD_VEND1, + VEND1_RX_PREAMBLE_COUNT, 0, GENMASK(5, 0) }, + { "tx_preamble_count", MDIO_MMD_VEND1, + VEND1_TX_PREAMBLE_COUNT, 0, GENMASK(5, 0) }, + { "rx_ipg_length", MDIO_MMD_VEND1, + VEND1_RX_IPG_LENGTH, 0, GENMASK(8, 0) }, + { "tx_ipg_length", MDIO_MMD_VEND1, + VEND1_TX_IPG_LENGTH, 0, GENMASK(8, 0) }, +}; + +static int nxp_c45_get_sset_count(struct phy_device *phydev) +{ + return ARRAY_SIZE(nxp_c45_hw_stats); +} + +static void nxp_c45_get_strings(struct phy_device *phydev, u8 *data) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(nxp_c45_hw_stats); i++) { + strncpy(data + i * ETH_GSTRING_LEN, + nxp_c45_hw_stats[i].name, ETH_GSTRING_LEN); + } +} + +static void nxp_c45_get_stats(struct phy_device *phydev, + struct ethtool_stats *stats, u64 *data) +{ + size_t i; + int ret; + + for (i = 0; i < ARRAY_SIZE(nxp_c45_hw_stats); i++) { + ret = phy_read_mmd(phydev, nxp_c45_hw_stats[i].mmd, + nxp_c45_hw_stats[i].reg); + if (ret < 0) { + data[i] = U64_MAX; + } else { + data[i] = ret & nxp_c45_hw_stats[i].mask; + data[i] >>= nxp_c45_hw_stats[i].off; + } + } +} + +static int nxp_c45_config_enable(struct phy_device *phydev) +{ + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_DEVICE_CONTROL, + DEVICE_CONTROL_CONFIG_GLOBAL_EN | + DEVICE_CONTROL_CONFIG_ALL_EN); + usleep_range(400, 450); + + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PORT_CONTROL, + PORT_CONTROL_EN); + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_CONTROL, + PHY_CONFIG_EN); + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PORT_INFRA_CONTROL, + PORT_INFRA_CONTROL_EN); + + return 0; +} + +static int nxp_c45_start_op(struct phy_device *phydev) +{ + return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_CONTROL, + PHY_START_OP); +} + +static int nxp_c45_config_intr(struct phy_device *phydev) +{ + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + VEND1_PHY_IRQ_EN, PHY_IRQ_LINK_EVENT); + else + return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + VEND1_PHY_IRQ_EN, PHY_IRQ_LINK_EVENT); +} + +static irqreturn_t nxp_c45_handle_interrupt(struct phy_device *phydev) +{ + irqreturn_t ret = IRQ_NONE; + int irq; + + irq = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_IRQ_STATUS); + if (irq & PHY_IRQ_LINK_EVENT) { + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_IRQ_ACK, + PHY_IRQ_LINK_EVENT); + phy_trigger_machine(phydev); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int nxp_c45_soft_reset(struct phy_device *phydev) +{ + int ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_DEVICE_CONTROL, + DEVICE_CONTROL_RESET); + if (ret) + return ret; + + return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + VEND1_DEVICE_CONTROL, ret, + !(ret & DEVICE_CONTROL_RESET), 20000, + 240000, false); +} + +static int nxp_c45_cable_test_start(struct phy_device *phydev) +{ + return phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_CABLE_TEST, + CABLE_TEST_ENABLE | CABLE_TEST_START); +} + +static int nxp_c45_cable_test_get_status(struct phy_device *phydev, + bool *finished) +{ + int ret; + u8 cable_test_result; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_CABLE_TEST); + if (!(ret & CABLE_TEST_VALID)) { + *finished = false; + return 0; + } + + *finished = true; + cable_test_result = ret & GENMASK(2, 0); + + switch (cable_test_result) { + case CABLE_TEST_OK: + ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, + ETHTOOL_A_CABLE_RESULT_CODE_OK); + break; + case CABLE_TEST_SHORTED: + ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, + ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT); + break; + case CABLE_TEST_OPEN: + ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, + ETHTOOL_A_CABLE_RESULT_CODE_OPEN); + break; + default: + ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, + ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC); + } + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_CABLE_TEST, + CABLE_TEST_ENABLE); + + return nxp_c45_start_op(phydev); +} + +static int nxp_c45_setup_master_slave(struct phy_device *phydev) +{ + switch (phydev->master_slave_set) { + case MASTER_SLAVE_CFG_MASTER_FORCE: + case MASTER_SLAVE_CFG_MASTER_PREFERRED: + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_B100T1_PMAPMD_CTL, + MASTER_MODE); + break; + case MASTER_SLAVE_CFG_SLAVE_PREFERRED: + case MASTER_SLAVE_CFG_SLAVE_FORCE: + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_B100T1_PMAPMD_CTL, + SLAVE_MODE); + break; + case MASTER_SLAVE_CFG_UNKNOWN: + case MASTER_SLAVE_CFG_UNSUPPORTED: + return 0; + default: + phydev_warn(phydev, "Unsupported Master/Slave mode\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int nxp_c45_read_master_slave(struct phy_device *phydev) +{ + int reg; + + phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN; + phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; + + reg = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_B100T1_PMAPMD_CTL); + if (reg < 0) + return reg; + + if (reg & B100T1_PMAPMD_MASTER) { + phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE; + phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER; + } else { + phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE; + phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE; + } + + return 0; +} + +static int nxp_c45_config_aneg(struct phy_device *phydev) +{ + return nxp_c45_setup_master_slave(phydev); +} + +static int nxp_c45_read_status(struct phy_device *phydev) +{ + int ret; + + ret = genphy_c45_read_status(phydev); + if (ret) + return ret; + + ret = nxp_c45_read_master_slave(phydev); + if (ret) + return ret; + + return 0; +} + +static int nxp_c45_get_sqi(struct phy_device *phydev) +{ + int reg; + + reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_SIGNAL_QUALITY); + if (!(reg & SQI_VALID)) + return -EINVAL; + + reg &= SQI_MASK; + + return reg; +} + +static int nxp_c45_get_sqi_max(struct phy_device *phydev) +{ + return MAX_SQI; +} + +static int nxp_c45_check_delay(struct phy_device *phydev, u32 delay) +{ + if (delay < MIN_ID_PS) { + phydev_err(phydev, "delay value smaller than %u\n", MIN_ID_PS); + return -EINVAL; + } + + if (delay > MAX_ID_PS) { + phydev_err(phydev, "delay value higher than %u\n", MAX_ID_PS); + return -EINVAL; + } + + return 0; +} + +static u64 nxp_c45_get_phase_shift(u64 phase_offset_raw) +{ + /* The delay in degree phase is 73.8 + phase_offset_raw * 0.9. + * To avoid floating point operations we'll multiply by 10 + * and get 1 decimal point precision. + */ + phase_offset_raw *= 10; + phase_offset_raw -= 738; + return div_u64(phase_offset_raw, 9); +} + +static void nxp_c45_disable_delays(struct phy_device *phydev) +{ + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_TXID, ID_ENABLE); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_RXID, ID_ENABLE); +} + +static void nxp_c45_set_delays(struct phy_device *phydev) +{ + struct nxp_c45_phy *priv = phydev->priv; + u64 tx_delay = priv->tx_delay; + u64 rx_delay = priv->rx_delay; + u64 degree; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { + degree = div_u64(tx_delay, PS_PER_DEGREE); + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_TXID, + ID_ENABLE | nxp_c45_get_phase_shift(degree)); + } else { + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_TXID, + ID_ENABLE); + } + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { + degree = div_u64(rx_delay, PS_PER_DEGREE); + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_RXID, + ID_ENABLE | nxp_c45_get_phase_shift(degree)); + } else { + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_RXID, + ID_ENABLE); + } +} + +static int nxp_c45_get_delays(struct phy_device *phydev) +{ + struct nxp_c45_phy *priv = phydev->priv; + int ret; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { + ret = device_property_read_u32(&phydev->mdio.dev, + "tx-internal-delay-ps", + &priv->tx_delay); + if (ret) + priv->tx_delay = DEFAULT_ID_PS; + + ret = nxp_c45_check_delay(phydev, priv->tx_delay); + if (ret) { + phydev_err(phydev, + "tx-internal-delay-ps invalid value\n"); + return ret; + } + } + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { + ret = device_property_read_u32(&phydev->mdio.dev, + "rx-internal-delay-ps", + &priv->rx_delay); + if (ret) + priv->rx_delay = DEFAULT_ID_PS; + + ret = nxp_c45_check_delay(phydev, priv->rx_delay); + if (ret) { + phydev_err(phydev, + "rx-internal-delay-ps invalid value\n"); + return ret; + } + } + + return 0; +} + +static int nxp_c45_set_phy_mode(struct phy_device *phydev) +{ + int ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_ABILITIES); + phydev_dbg(phydev, "Clause 45 managed PHY abilities 0x%x\n", ret); + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + if (!(ret & RGMII_ABILITY)) { + phydev_err(phydev, "rgmii mode not supported\n"); + return -EINVAL; + } + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, + MII_BASIC_CONFIG_RGMII); + nxp_c45_disable_delays(phydev); + break; + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + if (!(ret & RGMII_ID_ABILITY)) { + phydev_err(phydev, "rgmii-id, rgmii-txid, rgmii-rxid modes are not supported\n"); + return -EINVAL; + } + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, + MII_BASIC_CONFIG_RGMII); + ret = nxp_c45_get_delays(phydev); + if (ret) + return ret; + + nxp_c45_set_delays(phydev); + break; + case PHY_INTERFACE_MODE_MII: + if (!(ret & MII_ABILITY)) { + phydev_err(phydev, "mii mode not supported\n"); + return -EINVAL; + } + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, + MII_BASIC_CONFIG_MII); + break; + case PHY_INTERFACE_MODE_REVMII: + if (!(ret & REVMII_ABILITY)) { + phydev_err(phydev, "rev-mii mode not supported\n"); + return -EINVAL; + } + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, + MII_BASIC_CONFIG_MII | MII_BASIC_CONFIG_REV); + break; + case PHY_INTERFACE_MODE_RMII: + if (!(ret & RMII_ABILITY)) { + phydev_err(phydev, "rmii mode not supported\n"); + return -EINVAL; + } + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, + MII_BASIC_CONFIG_RMII); + break; + case PHY_INTERFACE_MODE_SGMII: + if (!(ret & SGMII_ABILITY)) { + phydev_err(phydev, "sgmii mode not supported\n"); + return -EINVAL; + } + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, + MII_BASIC_CONFIG_SGMII); + break; + case PHY_INTERFACE_MODE_INTERNAL: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nxp_c45_config_init(struct phy_device *phydev) +{ + int ret; + + ret = nxp_c45_config_enable(phydev); + if (ret) { + phydev_err(phydev, "Failed to enable config\n"); + return ret; + } + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_CONFIG, + PHY_CONFIG_AUTO); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_LINK_DROP_COUNTER, + COUNTER_EN); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_RX_PREAMBLE_COUNT, + COUNTER_EN); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_TX_PREAMBLE_COUNT, + COUNTER_EN); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_RX_IPG_LENGTH, + COUNTER_EN); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_TX_IPG_LENGTH, + COUNTER_EN); + + ret = nxp_c45_set_phy_mode(phydev); + if (ret) + return ret; + + phydev->autoneg = AUTONEG_DISABLE; + + return nxp_c45_start_op(phydev); +} + +static int nxp_c45_probe(struct phy_device *phydev) +{ + struct nxp_c45_phy *priv; + + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + return 0; +} + +static struct phy_driver nxp_c45_driver[] = { + { + PHY_ID_MATCH_MODEL(PHY_ID_TJA_1103), + .name = "NXP C45 TJA1103", + .features = PHY_BASIC_T1_FEATURES, + .probe = nxp_c45_probe, + .soft_reset = nxp_c45_soft_reset, + .config_aneg = nxp_c45_config_aneg, + .config_init = nxp_c45_config_init, + .config_intr = nxp_c45_config_intr, + .handle_interrupt = nxp_c45_handle_interrupt, + .read_status = nxp_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = genphy_c45_pma_resume, + .get_sset_count = nxp_c45_get_sset_count, + .get_strings = nxp_c45_get_strings, + .get_stats = nxp_c45_get_stats, + .cable_test_start = nxp_c45_cable_test_start, + .cable_test_get_status = nxp_c45_cable_test_get_status, + .set_loopback = genphy_c45_loopback, + .get_sqi = nxp_c45_get_sqi, + .get_sqi_max = nxp_c45_get_sqi_max, + }, +}; + +module_phy_driver(nxp_c45_driver); + +static struct mdio_device_id __maybe_unused nxp_c45_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_TJA_1103) }, + { /*sentinel*/ }, +}; + +MODULE_DEVICE_TABLE(mdio, nxp_c45_tbl); + +MODULE_AUTHOR("Radu Pirea <radu-nicolae.pirea@oss.nxp.com>"); +MODULE_DESCRIPTION("NXP C45 PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index 077f2929c45e..f4816b7d31b3 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -9,6 +9,49 @@ #include <linux/phy.h> /** + * genphy_c45_pma_can_sleep - checks if the PMA have sleep support + * @phydev: target phy_device struct + */ +static bool genphy_c45_pma_can_sleep(struct phy_device *phydev) +{ + int stat1; + + stat1 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT1); + if (stat1 < 0) + return false; + + return !!(stat1 & MDIO_STAT1_LPOWERABLE); +} + +/** + * genphy_c45_pma_resume - wakes up the PMA module + * @phydev: target phy_device struct + */ +int genphy_c45_pma_resume(struct phy_device *phydev) +{ + if (!genphy_c45_pma_can_sleep(phydev)) + return -EOPNOTSUPP; + + return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, + MDIO_CTRL1_LPOWER); +} +EXPORT_SYMBOL_GPL(genphy_c45_pma_resume); + +/** + * genphy_c45_pma_suspend - suspends the PMA module + * @phydev: target phy_device struct + */ +int genphy_c45_pma_suspend(struct phy_device *phydev) +{ + if (!genphy_c45_pma_can_sleep(phydev)) + return -EOPNOTSUPP; + + return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, + MDIO_CTRL1_LPOWER); +} +EXPORT_SYMBOL_GPL(genphy_c45_pma_suspend); + +/** * genphy_c45_pma_setup_forced - configures a forced speed * @phydev: target phy_device struct */ @@ -560,6 +603,14 @@ int gen10g_config_aneg(struct phy_device *phydev) } EXPORT_SYMBOL_GPL(gen10g_config_aneg); +int genphy_c45_loopback(struct phy_device *phydev, bool enable) +{ + return phy_modify_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, + MDIO_PCS_CTRL1_LOOPBACK, + enable ? MDIO_PCS_CTRL1_LOOPBACK : 0); +} +EXPORT_SYMBOL_GPL(genphy_c45_loopback); + struct phy_driver genphy_c45_driver = { .phy_id = 0xffffffff, .phy_id_mask = 0xffffffff, diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index fc2e7cb5b2e5..1f0512e39c65 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -701,7 +701,7 @@ out: } EXPORT_SYMBOL(phy_start_cable_test_tdr); -static int phy_config_aneg(struct phy_device *phydev) +int phy_config_aneg(struct phy_device *phydev) { if (phydev->drv->config_aneg) return phydev->drv->config_aneg(phydev); @@ -714,6 +714,7 @@ static int phy_config_aneg(struct phy_device *phydev) return genphy_config_aneg(phydev); } +EXPORT_SYMBOL(phy_config_aneg); /** * phy_check_link_status - check link status and set state accordingly diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index cc38e326405a..0a2d8bedf73d 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -273,6 +273,9 @@ static __maybe_unused int mdio_bus_phy_suspend(struct device *dev) { struct phy_device *phydev = to_phy_device(dev); + if (phydev->mac_managed_pm) + return 0; + /* We must stop the state machine manually, otherwise it stops out of * control, possibly with the phydev->lock held. Upon resume, netdev * may call phy routines that try to grab the same lock, and that may @@ -294,6 +297,9 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev) struct phy_device *phydev = to_phy_device(dev); int ret; + if (phydev->mac_managed_pm) + return 0; + if (!phydev->suspended_by_mdio_bus) goto no_resume; @@ -512,10 +518,21 @@ phy_has_fixups_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(phy_has_fixups); +static ssize_t phy_dev_flags_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct phy_device *phydev = to_phy_device(dev); + + return sprintf(buf, "0x%08x\n", phydev->dev_flags); +} +static DEVICE_ATTR_RO(phy_dev_flags); + static struct attribute *phy_dev_attrs[] = { &dev_attr_phy_id.attr, &dev_attr_phy_interface.attr, &dev_attr_phy_has_fixups.attr, + &dev_attr_phy_dev_flags.attr, NULL, }; ATTRIBUTE_GROUPS(phy_dev); @@ -1760,6 +1777,9 @@ int phy_loopback(struct phy_device *phydev, bool enable) struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); int ret = 0; + if (!phydrv) + return -ENODEV; + mutex_lock(&phydev->lock); if (enable && phydev->loopback_enabled) { @@ -1772,10 +1792,10 @@ int phy_loopback(struct phy_device *phydev, bool enable) goto out; } - if (phydev->drv && phydrv->set_loopback) + if (phydrv->set_loopback) ret = phydrv->set_loopback(phydev, enable); else - ret = -EOPNOTSUPP; + ret = genphy_loopback(phydev, enable); if (ret) goto out; @@ -2545,8 +2565,32 @@ EXPORT_SYMBOL(genphy_resume); int genphy_loopback(struct phy_device *phydev, bool enable) { - return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, - enable ? BMCR_LOOPBACK : 0); + if (enable) { + u16 val, ctl = BMCR_LOOPBACK; + int ret; + + if (phydev->speed == SPEED_1000) + ctl |= BMCR_SPEED1000; + else if (phydev->speed == SPEED_100) + ctl |= BMCR_SPEED100; + + if (phydev->duplex == DUPLEX_FULL) + ctl |= BMCR_FULLDPLX; + + phy_modify(phydev, MII_BMCR, ~0, ctl); + + ret = phy_read_poll_timeout(phydev, MII_BMSR, val, + val & BMSR_LSTATUS, + 5000, 500000, true); + if (ret) + return ret; + } else { + phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 0); + + phy_config_aneg(phydev); + } + + return 0; } EXPORT_SYMBOL(genphy_loopback); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index dc2800beacc3..96d8e88b4e46 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -271,8 +271,9 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) pl->cfg_link_an_mode = MLO_AN_FIXED; fwnode_handle_put(dn); - if (fwnode_property_read_string(fwnode, "managed", &managed) == 0 && - strcmp(managed, "in-band-status") == 0) { + if ((fwnode_property_read_string(fwnode, "managed", &managed) == 0 && + strcmp(managed, "in-band-status") == 0) || + pl->config->ovr_an_inband) { if (pl->cfg_link_an_mode == MLO_AN_FIXED) { phylink_err(pl, "can't use both fixed-link and in-band-status\n"); diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 2e11176c6b94..e61de66e973b 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -556,6 +556,26 @@ int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee, EXPORT_SYMBOL_GPL(sfp_get_module_eeprom); /** + * sfp_get_module_eeprom_by_page() - Read a page from the SFP module EEPROM + * @bus: a pointer to the &struct sfp_bus structure for the sfp module + * @page: a &struct ethtool_module_eeprom + * @extack: extack for reporting problems + * + * Read an EEPROM page as specified by the supplied @page. See the + * documentation for &struct ethtool_module_eeprom for the page to be read. + * + * Returns 0 on success or a negative errno number. More error + * information might be provided via extack + */ +int sfp_get_module_eeprom_by_page(struct sfp_bus *bus, + const struct ethtool_module_eeprom *page, + struct netlink_ext_ack *extack) +{ + return bus->socket_ops->module_eeprom_by_page(bus->sfp, page, extack); +} +EXPORT_SYMBOL_GPL(sfp_get_module_eeprom_by_page); + +/** * sfp_upstream_start() - Inform the SFP that the network device is up * @bus: a pointer to the &struct sfp_bus structure for the sfp module * diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 7998acc689b7..37f722c763d7 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -2330,6 +2330,30 @@ static int sfp_module_eeprom(struct sfp *sfp, struct ethtool_eeprom *ee, return 0; } +static int sfp_module_eeprom_by_page(struct sfp *sfp, + const struct ethtool_module_eeprom *page, + struct netlink_ext_ack *extack) +{ + if (page->bank) { + NL_SET_ERR_MSG(extack, "Banks not supported"); + return -EOPNOTSUPP; + } + + if (page->page) { + NL_SET_ERR_MSG(extack, "Only page 0 supported"); + return -EOPNOTSUPP; + } + + if (page->i2c_address != 0x50 && + page->i2c_address != 0x51) { + NL_SET_ERR_MSG(extack, "Only address 0x50 and 0x51 supported"); + return -EOPNOTSUPP; + } + + return sfp_read(sfp, page->i2c_address == 0x51, page->offset, + page->data, page->length); +}; + static const struct sfp_socket_ops sfp_module_ops = { .attach = sfp_attach, .detach = sfp_detach, @@ -2337,6 +2361,7 @@ static const struct sfp_socket_ops sfp_module_ops = { .stop = sfp_stop, .module_info = sfp_module_info, .module_eeprom = sfp_module_eeprom, + .module_eeprom_by_page = sfp_module_eeprom_by_page, }; static void sfp_timeout(struct work_struct *work) diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h index b83f70526270..27226535c72b 100644 --- a/drivers/net/phy/sfp.h +++ b/drivers/net/phy/sfp.h @@ -14,6 +14,9 @@ struct sfp_socket_ops { int (*module_info)(struct sfp *sfp, struct ethtool_modinfo *modinfo); int (*module_eeprom)(struct sfp *sfp, struct ethtool_eeprom *ee, u8 *data); + int (*module_eeprom_by_page)(struct sfp *sfp, + const struct ethtool_module_eeprom *page, + struct netlink_ext_ack *extack); }; int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev); diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index ddb78fb4d6dc..d8cac02a79b9 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -185,10 +185,13 @@ static int lan87xx_config_aneg(struct phy_device *phydev) return genphy_config_aneg(phydev); } -static int lan87xx_config_aneg_ext(struct phy_device *phydev) +static int lan95xx_config_aneg_ext(struct phy_device *phydev) { int rc; + if (phydev->phy_id != 0x0007c0f0) /* not (LAN9500A or LAN9505A) */ + return lan87xx_config_aneg(phydev); + /* Extend Manual AutoMDIX timer */ rc = phy_read(phydev, PHY_EDPD_CONFIG); if (rc < 0) @@ -441,7 +444,7 @@ static struct phy_driver smsc_phy_driver[] = { .read_status = lan87xx_read_status, .config_init = smsc_phy_config_init, .soft_reset = smsc_phy_reset, - .config_aneg = lan87xx_config_aneg_ext, + .config_aneg = lan95xx_config_aneg_ext, /* IRQ related */ .config_intr = smsc_phy_config_intr, |