diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/adin.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/adin1100.c | 7 | ||||
-rw-r--r-- | drivers/net/phy/at803x.c | 25 | ||||
-rw-r--r-- | drivers/net/phy/bcm-phy-lib.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/broadcom.c | 39 | ||||
-rw-r--r-- | drivers/net/phy/marvell.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/micrel.c | 193 | ||||
-rw-r--r-- | drivers/net/phy/mscc/mscc_main.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/nxp-tja11xx.c | 83 | ||||
-rw-r--r-- | drivers/net/phy/phy-core.c | 53 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/phylink.c | 9 | ||||
-rw-r--r-- | drivers/net/phy/realtek.c | 44 | ||||
-rw-r--r-- | drivers/net/phy/sfp-bus.c | 100 | ||||
-rw-r--r-- | drivers/net/phy/sfp.c | 294 | ||||
-rw-r--r-- | drivers/net/phy/sfp.h | 10 | ||||
-rw-r--r-- | drivers/net/phy/smsc.c | 30 | ||||
-rw-r--r-- | drivers/net/phy/spi_ks8995.c | 69 |
18 files changed, 639 insertions, 327 deletions
diff --git a/drivers/net/phy/adin.c b/drivers/net/phy/adin.c index ee374a85544a..134637584a83 100644 --- a/drivers/net/phy/adin.c +++ b/drivers/net/phy/adin.c @@ -749,7 +749,7 @@ static void adin_get_strings(struct phy_device *phydev, u8 *data) int i; for (i = 0; i < ARRAY_SIZE(adin_hw_stats); i++) { - strlcpy(&data[i * ETH_GSTRING_LEN], + strscpy(&data[i * ETH_GSTRING_LEN], adin_hw_stats[i].string, ETH_GSTRING_LEN); } } diff --git a/drivers/net/phy/adin1100.c b/drivers/net/phy/adin1100.c index b6d139501199..7619d6185801 100644 --- a/drivers/net/phy/adin1100.c +++ b/drivers/net/phy/adin1100.c @@ -15,6 +15,8 @@ #include <linux/property.h> #define PHY_ID_ADIN1100 0x0283bc81 +#define PHY_ID_ADIN1110 0x0283bc91 +#define PHY_ID_ADIN2111 0x0283bca1 #define ADIN_FORCED_MODE 0x8000 #define ADIN_FORCED_MODE_EN BIT(0) @@ -265,7 +267,8 @@ static int adin_probe(struct phy_device *phydev) static struct phy_driver adin_driver[] = { { - PHY_ID_MATCH_MODEL(PHY_ID_ADIN1100), + .phy_id = PHY_ID_ADIN1100, + .phy_id_mask = 0xffffffcf, .name = "ADIN1100", .get_features = adin_get_features, .soft_reset = adin_soft_reset, @@ -284,6 +287,8 @@ module_phy_driver(adin_driver); static struct mdio_device_id __maybe_unused adin_tbl[] = { { PHY_ID_MATCH_MODEL(PHY_ID_ADIN1100) }, + { PHY_ID_MATCH_MODEL(PHY_ID_ADIN1110) }, + { PHY_ID_MATCH_MODEL(PHY_ID_ADIN2111) }, { } }; diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 59fe356942b5..11ebd59bf2eb 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -115,6 +115,7 @@ #define AT803X_DEBUG_REG_HIB_CTRL 0x0b #define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10) #define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) +#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) #define AT803X_DEBUG_REG_3C 0x3C @@ -192,6 +193,9 @@ #define AT803X_KEEP_PLL_ENABLED BIT(0) #define AT803X_DISABLE_SMARTEEE BIT(1) +/* disable hibernation mode */ +#define AT803X_DISABLE_HIBERNATION_MODE BIT(2) + /* ADC threshold */ #define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 #define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) @@ -730,6 +734,9 @@ static int at803x_parse_dt(struct phy_device *phydev) if (of_property_read_bool(node, "qca,disable-smarteee")) priv->flags |= AT803X_DISABLE_SMARTEEE; + if (of_property_read_bool(node, "qca,disable-hibernation-mode")) + priv->flags |= AT803X_DISABLE_HIBERNATION_MODE; + if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) { if (!tw || tw > 255) { phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n"); @@ -999,6 +1006,20 @@ static int at8031_pll_config(struct phy_device *phydev) AT803X_DEBUG_PLL_ON, 0); } +static int at803x_hibernation_mode_config(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + + /* The default after hardware reset is hibernation mode enabled. After + * software reset, the value is retained. + */ + if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE)) + return 0; + + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, + AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0); +} + static int at803x_config_init(struct phy_device *phydev) { struct at803x_priv *priv = phydev->priv; @@ -1051,6 +1072,10 @@ static int at803x_config_init(struct phy_device *phydev) if (ret < 0) return ret; + ret = at803x_hibernation_mode_config(phydev); + if (ret < 0) + return ret; + /* Ar803x extended next page bit is enabled by default. Cisco * multigig switches read this bit and attempt to negotiate 10Gbps * rates even if the next page bit is disabled. This is incorrect diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index 287cccf8f7f4..b2c0baa51f39 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -519,7 +519,7 @@ void bcm_phy_get_strings(struct phy_device *phydev, u8 *data) unsigned int i; for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) - strlcpy(data + i * ETH_GSTRING_LEN, + strscpy(data + i * ETH_GSTRING_LEN, bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN); } EXPORT_SYMBOL_GPL(bcm_phy_get_strings); diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 31fbcdddc9ad..ad71c88c87e7 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -766,6 +766,41 @@ static irqreturn_t brcm_fet_handle_interrupt(struct phy_device *phydev) return IRQ_HANDLED; } +static int brcm_fet_suspend(struct phy_device *phydev) +{ + int reg, err, err2, brcmtest; + + /* We cannot use a read/modify/write here otherwise the PHY continues + * to drive LEDs which defeats the purpose of low power mode. + */ + err = phy_write(phydev, MII_BMCR, BMCR_PDOWN); + if (err < 0) + return err; + + /* Enable shadow register access */ + brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST); + if (brcmtest < 0) + return brcmtest; + + reg = brcmtest | MII_BRCM_FET_BT_SRE; + + err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg); + if (err < 0) + return err; + + /* Set standby mode */ + err = phy_modify(phydev, MII_BRCM_FET_SHDW_AUXMODE4, + MII_BRCM_FET_SHDW_AM4_STANDBY, + MII_BRCM_FET_SHDW_AM4_STANDBY); + + /* Disable shadow register access */ + err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest); + if (!err) + err = err2; + + return err; +} + static int bcm54xx_phy_probe(struct phy_device *phydev) { struct bcm54xx_phy_priv *priv; @@ -1033,6 +1068,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = brcm_fet_config_init, .config_intr = brcm_fet_config_intr, .handle_interrupt = brcm_fet_handle_interrupt, + .suspend = brcm_fet_suspend, + .resume = brcm_fet_config_init, }, { .phy_id = PHY_ID_BCM5241, .phy_id_mask = 0xfffffff0, @@ -1041,6 +1078,8 @@ static struct phy_driver broadcom_drivers[] = { .config_init = brcm_fet_config_init, .config_intr = brcm_fet_config_intr, .handle_interrupt = brcm_fet_handle_interrupt, + .suspend = brcm_fet_suspend, + .resume = brcm_fet_config_init, }, { .phy_id = PHY_ID_BCM5395, .phy_id_mask = 0xfffffff0, diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index a714150f5e8c..a3e810705ce2 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1952,7 +1952,7 @@ static void marvell_get_strings(struct phy_device *phydev, u8 *data) int i; for (i = 0; i < count; i++) { - strlcpy(data + i * ETH_GSTRING_LEN, + strscpy(data + i * ETH_GSTRING_LEN, marvell_hw_stats[i].string, ETH_GSTRING_LEN); } } diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 38234d7e14c5..6a8c740dce65 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -92,6 +92,15 @@ #define KSZ9x31_LMD_VCT_DATA_HI_PULSE_MASK GENMASK(1, 0) #define KSZ9x31_LMD_VCT_DATA_MASK GENMASK(7, 0) +#define KSZPHY_WIRE_PAIR_MASK 0x3 + +#define LAN8814_CABLE_DIAG 0x12 +#define LAN8814_CABLE_DIAG_STAT_MASK GENMASK(9, 8) +#define LAN8814_CABLE_DIAG_VCT_DATA_MASK GENMASK(7, 0) +#define LAN8814_PAIR_BIT_SHIFT 12 + +#define LAN8814_WIRE_PAIR_MASK 0xF + /* Lan8814 general Interrupt control/status reg in GPHY specific block. */ #define LAN8814_INTC 0x18 #define LAN8814_INTS 0x1B @@ -257,6 +266,8 @@ static struct kszphy_hw_stat kszphy_hw_stats[] = { struct kszphy_type { u32 led_mode_reg; u16 interrupt_level_mask; + u16 cable_diag_reg; + unsigned long pair_mask; bool has_broadcast_disable; bool has_nand_tree_disable; bool has_rmii_ref_clk_sel; @@ -313,6 +324,13 @@ struct kszphy_priv { static const struct kszphy_type lan8814_type = { .led_mode_reg = ~LAN8814_LED_CTRL_1, + .cable_diag_reg = LAN8814_CABLE_DIAG, + .pair_mask = LAN8814_WIRE_PAIR_MASK, +}; + +static const struct kszphy_type ksz886x_type = { + .cable_diag_reg = KSZ8081_LMD, + .pair_mask = KSZPHY_WIRE_PAIR_MASK, }; static const struct kszphy_type ksz8021_type = { @@ -1650,7 +1668,7 @@ static void kszphy_get_strings(struct phy_device *phydev, u8 *data) int i; for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) { - strlcpy(data + i * ETH_GSTRING_LEN, + strscpy(data + i * ETH_GSTRING_LEN, kszphy_hw_stats[i].string, ETH_GSTRING_LEN); } } @@ -1796,6 +1814,17 @@ static int kszphy_probe(struct phy_device *phydev) return 0; } +static int lan8814_cable_test_start(struct phy_device *phydev) +{ + /* If autoneg is enabled, we won't be able to test cross pair + * short. In this case, the PHY will "detect" a link and + * confuse the internal state machine - disable auto neg here. + * Set the speed to 1000mbit and full duplex. + */ + return phy_modify(phydev, MII_BMCR, BMCR_ANENABLE | BMCR_SPEED100, + BMCR_SPEED1000 | BMCR_FULLDPLX); +} + static int ksz886x_cable_test_start(struct phy_device *phydev) { if (phydev->dev_flags & MICREL_KSZ8_P1_ERRATA) @@ -1809,9 +1838,9 @@ static int ksz886x_cable_test_start(struct phy_device *phydev) return phy_clear_bits(phydev, MII_BMCR, BMCR_ANENABLE | BMCR_SPEED100); } -static int ksz886x_cable_test_result_trans(u16 status) +static int ksz886x_cable_test_result_trans(u16 status, u16 mask) { - switch (FIELD_GET(KSZ8081_LMD_STAT_MASK, status)) { + switch (FIELD_GET(mask, status)) { case KSZ8081_LMD_STAT_NORMAL: return ETHTOOL_A_CABLE_RESULT_CODE_OK; case KSZ8081_LMD_STAT_SHORT: @@ -1825,15 +1854,15 @@ static int ksz886x_cable_test_result_trans(u16 status) } } -static bool ksz886x_cable_test_failed(u16 status) +static bool ksz886x_cable_test_failed(u16 status, u16 mask) { - return FIELD_GET(KSZ8081_LMD_STAT_MASK, status) == + return FIELD_GET(mask, status) == KSZ8081_LMD_STAT_FAIL; } -static bool ksz886x_cable_test_fault_length_valid(u16 status) +static bool ksz886x_cable_test_fault_length_valid(u16 status, u16 mask) { - switch (FIELD_GET(KSZ8081_LMD_STAT_MASK, status)) { + switch (FIELD_GET(mask, status)) { case KSZ8081_LMD_STAT_OPEN: fallthrough; case KSZ8081_LMD_STAT_SHORT: @@ -1842,29 +1871,79 @@ static bool ksz886x_cable_test_fault_length_valid(u16 status) return false; } -static int ksz886x_cable_test_fault_length(u16 status) +static int ksz886x_cable_test_fault_length(struct phy_device *phydev, u16 status, u16 data_mask) { int dt; /* According to the data sheet the distance to the fault is - * DELTA_TIME * 0.4 meters. + * DELTA_TIME * 0.4 meters for ksz phys. + * (DELTA_TIME - 22) * 0.8 for lan8814 phy. */ - dt = FIELD_GET(KSZ8081_LMD_DELTA_TIME_MASK, status); + dt = FIELD_GET(data_mask, status); - return (dt * 400) / 10; + if ((phydev->phy_id & MICREL_PHY_ID_MASK) == PHY_ID_LAN8814) + return ((dt - 22) * 800) / 10; + else + return (dt * 400) / 10; } static int ksz886x_cable_test_wait_for_completion(struct phy_device *phydev) { + const struct kszphy_type *type = phydev->drv->driver_data; int val, ret; - ret = phy_read_poll_timeout(phydev, KSZ8081_LMD, val, + ret = phy_read_poll_timeout(phydev, type->cable_diag_reg, val, !(val & KSZ8081_LMD_ENABLE_TEST), 30000, 100000, true); return ret < 0 ? ret : 0; } +static int lan8814_cable_test_one_pair(struct phy_device *phydev, int pair) +{ + static const int ethtool_pair[] = { ETHTOOL_A_CABLE_PAIR_A, + ETHTOOL_A_CABLE_PAIR_B, + ETHTOOL_A_CABLE_PAIR_C, + ETHTOOL_A_CABLE_PAIR_D, + }; + u32 fault_length; + int ret; + int val; + + val = KSZ8081_LMD_ENABLE_TEST; + val = val | (pair << LAN8814_PAIR_BIT_SHIFT); + + ret = phy_write(phydev, LAN8814_CABLE_DIAG, val); + if (ret < 0) + return ret; + + ret = ksz886x_cable_test_wait_for_completion(phydev); + if (ret) + return ret; + + val = phy_read(phydev, LAN8814_CABLE_DIAG); + if (val < 0) + return val; + + if (ksz886x_cable_test_failed(val, LAN8814_CABLE_DIAG_STAT_MASK)) + return -EAGAIN; + + ret = ethnl_cable_test_result(phydev, ethtool_pair[pair], + ksz886x_cable_test_result_trans(val, + LAN8814_CABLE_DIAG_STAT_MASK + )); + if (ret) + return ret; + + if (!ksz886x_cable_test_fault_length_valid(val, LAN8814_CABLE_DIAG_STAT_MASK)) + return 0; + + fault_length = ksz886x_cable_test_fault_length(phydev, val, + LAN8814_CABLE_DIAG_VCT_DATA_MASK); + + return ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], fault_length); +} + static int ksz886x_cable_test_one_pair(struct phy_device *phydev, int pair) { static const int ethtool_pair[] = { @@ -1872,6 +1951,7 @@ static int ksz886x_cable_test_one_pair(struct phy_device *phydev, int pair) ETHTOOL_A_CABLE_PAIR_B, }; int ret, val, mdix; + u32 fault_length; /* There is no way to choice the pair, like we do one ksz9031. * We can workaround this limitation by using the MDI-X functionality. @@ -1910,25 +1990,27 @@ static int ksz886x_cable_test_one_pair(struct phy_device *phydev, int pair) if (val < 0) return val; - if (ksz886x_cable_test_failed(val)) + if (ksz886x_cable_test_failed(val, KSZ8081_LMD_STAT_MASK)) return -EAGAIN; ret = ethnl_cable_test_result(phydev, ethtool_pair[pair], - ksz886x_cable_test_result_trans(val)); + ksz886x_cable_test_result_trans(val, KSZ8081_LMD_STAT_MASK)); if (ret) return ret; - if (!ksz886x_cable_test_fault_length_valid(val)) + if (!ksz886x_cable_test_fault_length_valid(val, KSZ8081_LMD_STAT_MASK)) return 0; - return ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], - ksz886x_cable_test_fault_length(val)); + fault_length = ksz886x_cable_test_fault_length(phydev, val, KSZ8081_LMD_DELTA_TIME_MASK); + + return ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], fault_length); } static int ksz886x_cable_test_get_status(struct phy_device *phydev, bool *finished) { - unsigned long pair_mask = 0x3; + const struct kszphy_type *type = phydev->drv->driver_data; + unsigned long pair_mask = type->pair_mask; int retries = 20; int pair, ret; @@ -1937,7 +2019,10 @@ static int ksz886x_cable_test_get_status(struct phy_device *phydev, /* Try harder if link partner is active */ while (pair_mask && retries--) { for_each_set_bit(pair, &pair_mask, 4) { - ret = ksz886x_cable_test_one_pair(phydev, pair); + if (type->cable_diag_reg == LAN8814_CABLE_DIAG) + ret = lan8814_cable_test_one_pair(phydev, pair); + else + ret = ksz886x_cable_test_one_pair(phydev, pair); if (ret == -EAGAIN) continue; if (ret < 0) @@ -2676,6 +2761,66 @@ static int lan8804_config_init(struct phy_device *phydev) return 0; } +static irqreturn_t lan8804_handle_interrupt(struct phy_device *phydev) +{ + int status; + + status = phy_read(phydev, LAN8814_INTS); + if (status < 0) { + phy_error(phydev); + return IRQ_NONE; + } + + if (status > 0) + phy_trigger_machine(phydev); + + return IRQ_HANDLED; +} + +#define LAN8804_OUTPUT_CONTROL 25 +#define LAN8804_OUTPUT_CONTROL_INTR_BUFFER BIT(14) +#define LAN8804_CONTROL 31 +#define LAN8804_CONTROL_INTR_POLARITY BIT(14) + +static int lan8804_config_intr(struct phy_device *phydev) +{ + int err; + + /* This is an internal PHY of lan966x and is not possible to change the + * polarity on the GIC found in lan966x, therefore change the polarity + * of the interrupt in the PHY from being active low instead of active + * high. + */ + phy_write(phydev, LAN8804_CONTROL, LAN8804_CONTROL_INTR_POLARITY); + + /* By default interrupt buffer is open-drain in which case the interrupt + * can be active only low. Therefore change the interrupt buffer to be + * push-pull to be able to change interrupt polarity + */ + phy_write(phydev, LAN8804_OUTPUT_CONTROL, + LAN8804_OUTPUT_CONTROL_INTR_BUFFER); + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + err = phy_read(phydev, LAN8814_INTS); + if (err < 0) + return err; + + err = phy_write(phydev, LAN8814_INTC, LAN8814_INT_LINK); + if (err) + return err; + } else { + err = phy_write(phydev, LAN8814_INTC, 0); + if (err) + return err; + + err = phy_read(phydev, LAN8814_INTS); + if (err < 0) + return err; + } + + return 0; +} + static irqreturn_t lan8814_handle_interrupt(struct phy_device *phydev) { int irq_status, tsu_irq_status; @@ -2735,9 +2880,9 @@ static int lan8814_config_intr(struct phy_device *phydev) if (err) return err; - err = phy_write(phydev, LAN8814_INTC, LAN8814_INT_LINK); + err = phy_write(phydev, LAN8814_INTC, LAN8814_INT_LINK); } else { - err = phy_write(phydev, LAN8814_INTC, 0); + err = phy_write(phydev, LAN8814_INTC, 0); if (err) return err; @@ -3117,6 +3262,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_LAN8814, .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Microchip INDY Gigabit Quad PHY", + .flags = PHY_POLL_CABLE_TEST, .config_init = lan8814_config_init, .driver_data = &lan8814_type, .probe = lan8814_probe, @@ -3129,6 +3275,8 @@ static struct phy_driver ksphy_driver[] = { .resume = kszphy_resume, .config_intr = lan8814_config_intr, .handle_interrupt = lan8814_handle_interrupt, + .cable_test_start = lan8814_cable_test_start, + .cable_test_get_status = ksz886x_cable_test_get_status, }, { .phy_id = PHY_ID_LAN8804, .phy_id_mask = MICREL_PHY_ID_MASK, @@ -3143,6 +3291,8 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = kszphy_resume, + .config_intr = lan8804_config_intr, + .handle_interrupt = lan8804_handle_interrupt, }, { .phy_id = PHY_ID_KSZ9131, .phy_id_mask = MICREL_PHY_ID_MASK, @@ -3175,6 +3325,7 @@ static struct phy_driver ksphy_driver[] = { .phy_id = PHY_ID_KSZ886X, .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ8851 Ethernet MAC or KSZ886X Switch", + .driver_data = &ksz886x_type, /* PHY_BASIC_FEATURES */ .flags = PHY_POLL_CABLE_TEST, .config_init = kszphy_config_init, diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c index 7e3017e7a1c0..8a13b1ad9a33 100644 --- a/drivers/net/phy/mscc/mscc_main.c +++ b/drivers/net/phy/mscc/mscc_main.c @@ -136,7 +136,7 @@ static void vsc85xx_get_strings(struct phy_device *phydev, u8 *data) return; for (i = 0; i < priv->nstats; i++) - strlcpy(data + i * ETH_GSTRING_LEN, priv->hw_stats[i].string, + strscpy(data + i * ETH_GSTRING_LEN, priv->hw_stats[i].string, ETH_GSTRING_LEN); } diff --git a/drivers/net/phy/nxp-tja11xx.c b/drivers/net/phy/nxp-tja11xx.c index 2a8195c50d14..ec91e671f8aa 100644 --- a/drivers/net/phy/nxp-tja11xx.c +++ b/drivers/net/phy/nxp-tja11xx.c @@ -10,6 +10,7 @@ #include <linux/mdio.h> #include <linux/mii.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/phy.h> #include <linux/hwmon.h> #include <linux/bitfield.h> @@ -34,6 +35,11 @@ #define MII_CFG1 18 #define MII_CFG1_MASTER_SLAVE BIT(15) #define MII_CFG1_AUTO_OP BIT(14) +#define MII_CFG1_INTERFACE_MODE_MASK GENMASK(9, 8) +#define MII_CFG1_MII_MODE (0x0 << 8) +#define MII_CFG1_RMII_MODE_REFCLK_IN BIT(8) +#define MII_CFG1_RMII_MODE_REFCLK_OUT BIT(9) +#define MII_CFG1_REVMII_MODE GENMASK(9, 8) #define MII_CFG1_SLEEP_CONFIRM BIT(6) #define MII_CFG1_LED_MODE_MASK GENMASK(5, 4) #define MII_CFG1_LED_MODE_LINKUP 0 @@ -72,11 +78,15 @@ #define MII_COMMCFG 27 #define MII_COMMCFG_AUTO_OP BIT(15) +/* Configure REF_CLK as input in RMII mode */ +#define TJA110X_RMII_MODE_REFCLK_IN BIT(0) + struct tja11xx_priv { char *hwmon_name; struct device *hwmon_dev; struct phy_device *phydev; struct work_struct phy_register_work; + u32 flags; }; struct tja11xx_phy_stats { @@ -251,8 +261,34 @@ do_test: return __genphy_config_aneg(phydev, changed); } +static int tja11xx_get_interface_mode(struct phy_device *phydev) +{ + struct tja11xx_priv *priv = phydev->priv; + int mii_mode; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_MII: + mii_mode = MII_CFG1_MII_MODE; + break; + case PHY_INTERFACE_MODE_REVMII: + mii_mode = MII_CFG1_REVMII_MODE; + break; + case PHY_INTERFACE_MODE_RMII: + if (priv->flags & TJA110X_RMII_MODE_REFCLK_IN) + mii_mode = MII_CFG1_RMII_MODE_REFCLK_IN; + else + mii_mode = MII_CFG1_RMII_MODE_REFCLK_OUT; + break; + default: + return -EINVAL; + } + + return mii_mode; +} + static int tja11xx_config_init(struct phy_device *phydev) { + u16 reg_mask, reg_val; int ret; ret = tja11xx_enable_reg_write(phydev); @@ -265,15 +301,32 @@ static int tja11xx_config_init(struct phy_device *phydev) switch (phydev->phy_id & PHY_ID_MASK) { case PHY_ID_TJA1100: - ret = phy_modify(phydev, MII_CFG1, - MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_MASK | - MII_CFG1_LED_ENABLE, - MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_LINKUP | - MII_CFG1_LED_ENABLE); + reg_mask = MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_MASK | + MII_CFG1_LED_ENABLE; + reg_val = MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_LINKUP | + MII_CFG1_LED_ENABLE; + + reg_mask |= MII_CFG1_INTERFACE_MODE_MASK; + ret = tja11xx_get_interface_mode(phydev); + if (ret < 0) + return ret; + + reg_val |= (ret & 0xffff); + ret = phy_modify(phydev, MII_CFG1, reg_mask, reg_val); if (ret) return ret; break; case PHY_ID_TJA1101: + reg_mask = MII_CFG1_INTERFACE_MODE_MASK; + ret = tja11xx_get_interface_mode(phydev); + if (ret < 0) + return ret; + + reg_val = ret & 0xffff; + ret = phy_modify(phydev, MII_CFG1, reg_mask, reg_val); + if (ret) + return ret; + fallthrough; case PHY_ID_TJA1102: ret = phy_set_bits(phydev, MII_COMMCFG, MII_COMMCFG_AUTO_OP); if (ret) @@ -458,16 +511,36 @@ static int tja11xx_hwmon_register(struct phy_device *phydev, return PTR_ERR_OR_ZERO(priv->hwmon_dev); } +static int tja11xx_parse_dt(struct phy_device *phydev) +{ + struct device_node *node = phydev->mdio.dev.of_node; + struct tja11xx_priv *priv = phydev->priv; + + if (!IS_ENABLED(CONFIG_OF_MDIO)) + return 0; + + if (of_property_read_bool(node, "nxp,rmii-refclk-in")) + priv->flags |= TJA110X_RMII_MODE_REFCLK_IN; + + return 0; +} + static int tja11xx_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; struct tja11xx_priv *priv; + int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->phydev = phydev; + phydev->priv = priv; + + ret = tja11xx_parse_dt(phydev); + if (ret) + return ret; return tja11xx_hwmon_register(phydev, priv); } diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 1f2531a1a876..2a2924bc8f76 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -74,6 +74,59 @@ const char *phy_duplex_to_str(unsigned int duplex) } EXPORT_SYMBOL_GPL(phy_duplex_to_str); +/** + * phy_interface_num_ports - Return the number of links that can be carried by + * a given MAC-PHY physical link. Returns 0 if this is + * unknown, the number of links else. + * + * @interface: The interface mode we want to get the number of ports + */ +int phy_interface_num_ports(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_NA: + return 0; + case PHY_INTERFACE_MODE_INTERNAL: + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_GMII: + case PHY_INTERFACE_MODE_TBI: + case PHY_INTERFACE_MODE_REVMII: + case PHY_INTERFACE_MODE_RMII: + case PHY_INTERFACE_MODE_REVRMII: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RTBI: + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_XLGMII: + case PHY_INTERFACE_MODE_MOCA: + case PHY_INTERFACE_MODE_TRGMII: + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_SMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_5GBASER: + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_25GBASER: + case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_100BASEX: + case PHY_INTERFACE_MODE_RXAUI: + case PHY_INTERFACE_MODE_XAUI: + case PHY_INTERFACE_MODE_1000BASEKX: + return 1; + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: + return 4; + case PHY_INTERFACE_MODE_MAX: + WARN_ONCE(1, "PHY_INTERFACE_MODE_MAX isn't a valid interface mode"); + return 0; + } + return 0; +} +EXPORT_SYMBOL_GPL(phy_interface_num_ports); + /* A mapping of all SUPPORTED settings to speed/duplex. This table * must be grouped by speed and sorted in descending match priority * - iow, descending speed. diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 12ff276b80ae..2198f1302642 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -370,7 +370,7 @@ int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, if (!fixup) return -ENOMEM; - strlcpy(fixup->bus_id, bus_id, sizeof(fixup->bus_id)); + strscpy(fixup->bus_id, bus_id, sizeof(fixup->bus_id)); fixup->phy_uid = phy_uid; fixup->phy_uid_mask = phy_uid_mask; fixup->run = run; diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 9bd69328dc4d..e9d62f9598f9 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -321,6 +321,7 @@ void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface, case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_GMII: caps |= MAC_1000HD | MAC_1000FD; @@ -344,6 +345,7 @@ void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface, case PHY_INTERFACE_MODE_1000BASEX: caps |= MAC_1000HD; fallthrough; + case PHY_INTERFACE_MODE_1000BASEKX: case PHY_INTERFACE_MODE_TRGMII: caps |= MAC_1000FD; break; @@ -632,6 +634,12 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) switch (pl->link_config.interface) { case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RTBI: phylink_set(pl->supported, 10baseT_Half); phylink_set(pl->supported, 10baseT_Full); phylink_set(pl->supported, 100baseT_Half); @@ -2929,6 +2937,7 @@ void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: phylink_decode_sgmii_word(state, lpa); break; diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index a5671ab896b3..3d99fd6664d7 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -70,6 +70,7 @@ #define RTLGEN_SPEED_MASK 0x0630 #define RTL_GENERIC_PHYID 0x001cc800 +#define RTL_8211FVD_PHYID 0x001cc878 MODULE_DESCRIPTION("Realtek PHY driver"); MODULE_AUTHOR("Johnson Leung"); @@ -78,6 +79,7 @@ MODULE_LICENSE("GPL"); struct rtl821x_priv { u16 phycr1; u16 phycr2; + bool has_phycr2; }; static int rtl821x_read_page(struct phy_device *phydev) @@ -94,6 +96,7 @@ static int rtl821x_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; struct rtl821x_priv *priv; + u32 phy_id = phydev->drv->phy_id; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -108,13 +111,16 @@ static int rtl821x_probe(struct phy_device *phydev) if (of_property_read_bool(dev->of_node, "realtek,aldps-enable")) priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF; - ret = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR2); - if (ret < 0) - return ret; + priv->has_phycr2 = !(phy_id == RTL_8211FVD_PHYID); + if (priv->has_phycr2) { + ret = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR2); + if (ret < 0) + return ret; - priv->phycr2 = ret & RTL8211F_CLKOUT_EN; - if (of_property_read_bool(dev->of_node, "realtek,clkout-disable")) - priv->phycr2 &= ~RTL8211F_CLKOUT_EN; + priv->phycr2 = ret & RTL8211F_CLKOUT_EN; + if (of_property_read_bool(dev->of_node, "realtek,clkout-disable")) + priv->phycr2 &= ~RTL8211F_CLKOUT_EN; + } phydev->priv = priv; @@ -400,12 +406,14 @@ static int rtl8211f_config_init(struct phy_device *phydev) val_rxdly ? "enabled" : "disabled"); } - ret = phy_modify_paged(phydev, 0xa43, RTL8211F_PHYCR2, - RTL8211F_CLKOUT_EN, priv->phycr2); - if (ret < 0) { - dev_err(dev, "clkout configuration failed: %pe\n", - ERR_PTR(ret)); - return ret; + if (priv->has_phycr2) { + ret = phy_modify_paged(phydev, 0xa43, RTL8211F_PHYCR2, + RTL8211F_CLKOUT_EN, priv->phycr2); + if (ret < 0) { + dev_err(dev, "clkout configuration failed: %pe\n", + ERR_PTR(ret)); + return ret; + } } return genphy_soft_reset(phydev); @@ -924,6 +932,18 @@ static struct phy_driver realtek_drvs[] = { .read_page = rtl821x_read_page, .write_page = rtl821x_write_page, }, { + PHY_ID_MATCH_EXACT(RTL_8211FVD_PHYID), + .name = "RTL8211F-VD Gigabit Ethernet", + .probe = rtl821x_probe, + .config_init = &rtl8211f_config_init, + .read_status = rtlgen_read_status, + .config_intr = &rtl8211f_config_intr, + .handle_interrupt = rtl8211f_handle_interrupt, + .suspend = genphy_suspend, + .resume = rtl821x_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + }, { .name = "Generic FE-GE Realtek PHY", .match_phy_device = rtlgen_match_phy_device, .read_status = rtlgen_read_status, diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 15aa5ac1ff49..0a9099c77694 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -10,12 +10,6 @@ #include "sfp.h" -struct sfp_quirk { - const char *vendor; - const char *part; - void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); -}; - /** * struct sfp_bus - internal representation of a sfp bus */ @@ -38,93 +32,6 @@ struct sfp_bus { bool started; }; -static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, - unsigned long *modes) -{ - phylink_set(modes, 2500baseX_Full); -} - -static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, - unsigned long *modes) -{ - /* Ubiquiti U-Fiber Instant module claims that support all transceiver - * types including 10G Ethernet which is not truth. So clear all claimed - * modes and set only one mode which module supports: 1000baseX_Full. - */ - phylink_zero(modes); - phylink_set(modes, 1000baseX_Full); -} - -static const struct sfp_quirk sfp_quirks[] = { - { - // Alcatel Lucent G-010S-P can operate at 2500base-X, but - // incorrectly report 2500MBd NRZ in their EEPROM - .vendor = "ALCATELLUCENT", - .part = "G010SP", - .modes = sfp_quirk_2500basex, - }, { - // Alcatel Lucent G-010S-A can operate at 2500base-X, but - // report 3.2GBd NRZ in their EEPROM - .vendor = "ALCATELLUCENT", - .part = "3FE46541AA", - .modes = sfp_quirk_2500basex, - }, { - // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd - // NRZ in their EEPROM - .vendor = "HUAWEI", - .part = "MA5671A", - .modes = sfp_quirk_2500basex, - }, { - // Lantech 8330-262D-E can operate at 2500base-X, but - // incorrectly report 2500MBd NRZ in their EEPROM - .vendor = "Lantech", - .part = "8330-262D-E", - .modes = sfp_quirk_2500basex, - }, { - .vendor = "UBNT", - .part = "UF-INSTANT", - .modes = sfp_quirk_ubnt_uf_instant, - }, -}; - -static size_t sfp_strlen(const char *str, size_t maxlen) -{ - size_t size, i; - - /* Trailing characters should be filled with space chars */ - for (i = 0, size = 0; i < maxlen; i++) - if (str[i] != ' ') - size = i + 1; - - return size; -} - -static bool sfp_match(const char *qs, const char *str, size_t len) -{ - if (!qs) - return true; - if (strlen(qs) != len) - return false; - return !strncmp(qs, str, len); -} - -static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) -{ - const struct sfp_quirk *q; - unsigned int i; - size_t vs, ps; - - vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); - ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); - - for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) - if (sfp_match(q->vendor, id->base.vendor_name, vs) && - sfp_match(q->part, id->base.vendor_pn, ps)) - return q; - - return NULL; -} - /** * sfp_parse_port() - Parse the EEPROM base ID, setting the port type * @bus: a pointer to the &struct sfp_bus structure for the sfp module @@ -376,7 +283,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, phylink_set(modes, 2500baseX_Full); } - if (bus->sfp_quirk) + if (bus->sfp_quirk && bus->sfp_quirk->modes) bus->sfp_quirk->modes(id, modes); linkmode_or(support, support, modes); @@ -786,12 +693,13 @@ void sfp_link_down(struct sfp_bus *bus) } EXPORT_SYMBOL_GPL(sfp_link_down); -int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id) +int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + const struct sfp_quirk *quirk) { const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); int ret = 0; - bus->sfp_quirk = sfp_lookup_quirk(id); + bus->sfp_quirk = quirk; if (ops && ops->module_insert) ret = ops->module_insert(bus->upstream, id); diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 63f90fe9a4d2..cb1dbd0d9701 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -234,6 +234,7 @@ struct sfp { bool need_poll; struct mutex st_mutex; /* Protects state */ + unsigned int state_hw_mask; unsigned int state_soft_mask; unsigned int state; struct delayed_work poll; @@ -252,6 +253,8 @@ struct sfp { unsigned int module_t_start_up; bool tx_fault_ignore; + const struct sfp_quirk *quirk; + #if IS_ENABLED(CONFIG_HWMON) struct sfp_diag diag; struct delayed_work hwmon_probe; @@ -308,6 +311,120 @@ static const struct of_device_id sfp_of_match[] = { }; MODULE_DEVICE_TABLE(of, sfp_of_match); +static void sfp_fixup_long_startup(struct sfp *sfp) +{ + sfp->module_t_start_up = T_START_UP_BAD_GPON; +} + +static void sfp_fixup_ignore_tx_fault(struct sfp *sfp) +{ + sfp->tx_fault_ignore = true; +} + +static void sfp_fixup_halny_gsfp(struct sfp *sfp) +{ + /* Ignore the TX_FAULT and LOS signals on this module. + * these are possibly used for other purposes on this + * module, e.g. a serial port. + */ + sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS); +} + +static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, + unsigned long *modes) +{ + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes); +} + +static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, + unsigned long *modes) +{ + /* Ubiquiti U-Fiber Instant module claims that support all transceiver + * types including 10G Ethernet which is not truth. So clear all claimed + * modes and set only one mode which module supports: 1000baseX_Full. + */ + linkmode_zero(modes); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes); +} + +static const struct sfp_quirk sfp_quirks[] = { + { + // Alcatel Lucent G-010S-P can operate at 2500base-X, but + // incorrectly report 2500MBd NRZ in their EEPROM + .vendor = "ALCATELLUCENT", + .part = "G010SP", + .modes = sfp_quirk_2500basex, + }, { + // Alcatel Lucent G-010S-A can operate at 2500base-X, but + // report 3.2GBd NRZ in their EEPROM + .vendor = "ALCATELLUCENT", + .part = "3FE46541AA", + .modes = sfp_quirk_2500basex, + .fixup = sfp_fixup_long_startup, + }, { + .vendor = "HALNy", + .part = "HL-GSFP", + .fixup = sfp_fixup_halny_gsfp, + }, { + // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd + // NRZ in their EEPROM + .vendor = "HUAWEI", + .part = "MA5671A", + .modes = sfp_quirk_2500basex, + .fixup = sfp_fixup_ignore_tx_fault, + }, { + // Lantech 8330-262D-E can operate at 2500base-X, but + // incorrectly report 2500MBd NRZ in their EEPROM + .vendor = "Lantech", + .part = "8330-262D-E", + .modes = sfp_quirk_2500basex, + }, { + .vendor = "UBNT", + .part = "UF-INSTANT", + .modes = sfp_quirk_ubnt_uf_instant, + } +}; + +static size_t sfp_strlen(const char *str, size_t maxlen) +{ + size_t size, i; + + /* Trailing characters should be filled with space chars, but + * some manufacturers can't read SFF-8472 and use NUL. + */ + for (i = 0, size = 0; i < maxlen; i++) + if (str[i] != ' ' && str[i] != '\0') + size = i + 1; + + return size; +} + +static bool sfp_match(const char *qs, const char *str, size_t len) +{ + if (!qs) + return true; + if (strlen(qs) != len) + return false; + return !strncmp(qs, str, len); +} + +static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) +{ + const struct sfp_quirk *q; + unsigned int i; + size_t vs, ps; + + vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); + ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); + + for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) + if (sfp_match(q->vendor, id->base.vendor_name, vs) && + sfp_match(q->part, id->base.vendor_pn, ps)) + return q; + + return NULL; +} + static unsigned long poll_jiffies; static unsigned int sfp_gpio_get_state(struct sfp *sfp) @@ -499,17 +616,18 @@ static void sfp_soft_set_state(struct sfp *sfp, unsigned int state) static void sfp_soft_start_poll(struct sfp *sfp) { const struct sfp_eeprom_id *id = &sfp->id; + unsigned int mask = 0; sfp->state_soft_mask = 0; - if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE && - !sfp->gpio[GPIO_TX_DISABLE]) - sfp->state_soft_mask |= SFP_F_TX_DISABLE; - if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT && - !sfp->gpio[GPIO_TX_FAULT]) - sfp->state_soft_mask |= SFP_F_TX_FAULT; - if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS && - !sfp->gpio[GPIO_LOS]) - sfp->state_soft_mask |= SFP_F_LOS; + if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE) + mask |= SFP_F_TX_DISABLE; + if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT) + mask |= SFP_F_TX_FAULT; + if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS) + mask |= SFP_F_LOS; + + // Poll the soft state for hardware pins we want to ignore + sfp->state_soft_mask = ~sfp->state_hw_mask & mask; if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && !sfp->need_poll) @@ -523,10 +641,11 @@ static void sfp_soft_stop_poll(struct sfp *sfp) static unsigned int sfp_get_state(struct sfp *sfp) { - unsigned int state = sfp->get_state(sfp); + unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT); + unsigned int state; - if (state & SFP_F_PRESENT && - sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT)) + state = sfp->get_state(sfp) & sfp->state_hw_mask; + if (state & SFP_F_PRESENT && soft) state |= sfp_soft_get_state(sfp); return state; @@ -1195,90 +1314,45 @@ static const struct hwmon_ops sfp_hwmon_ops = { .read_string = sfp_hwmon_read_string, }; -static u32 sfp_hwmon_chip_config[] = { - HWMON_C_REGISTER_TZ, - 0, -}; - -static const struct hwmon_channel_info sfp_hwmon_chip = { - .type = hwmon_chip, - .config = sfp_hwmon_chip_config, -}; - -static u32 sfp_hwmon_temp_config[] = { - HWMON_T_INPUT | - HWMON_T_MAX | HWMON_T_MIN | - HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | - HWMON_T_CRIT | HWMON_T_LCRIT | - HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM | - HWMON_T_LABEL, - 0, -}; - -static const struct hwmon_channel_info sfp_hwmon_temp_channel_info = { - .type = hwmon_temp, - .config = sfp_hwmon_temp_config, -}; - -static u32 sfp_hwmon_vcc_config[] = { - HWMON_I_INPUT | - HWMON_I_MAX | HWMON_I_MIN | - HWMON_I_MAX_ALARM | HWMON_I_MIN_ALARM | - HWMON_I_CRIT | HWMON_I_LCRIT | - HWMON_I_CRIT_ALARM | HWMON_I_LCRIT_ALARM | - HWMON_I_LABEL, - 0, -}; - -static const struct hwmon_channel_info sfp_hwmon_vcc_channel_info = { - .type = hwmon_in, - .config = sfp_hwmon_vcc_config, -}; - -static u32 sfp_hwmon_bias_config[] = { - HWMON_C_INPUT | - HWMON_C_MAX | HWMON_C_MIN | - HWMON_C_MAX_ALARM | HWMON_C_MIN_ALARM | - HWMON_C_CRIT | HWMON_C_LCRIT | - HWMON_C_CRIT_ALARM | HWMON_C_LCRIT_ALARM | - HWMON_C_LABEL, - 0, -}; - -static const struct hwmon_channel_info sfp_hwmon_bias_channel_info = { - .type = hwmon_curr, - .config = sfp_hwmon_bias_config, -}; - -static u32 sfp_hwmon_power_config[] = { - /* Transmit power */ - HWMON_P_INPUT | - HWMON_P_MAX | HWMON_P_MIN | - HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM | - HWMON_P_CRIT | HWMON_P_LCRIT | - HWMON_P_CRIT_ALARM | HWMON_P_LCRIT_ALARM | - HWMON_P_LABEL, - /* Receive power */ - HWMON_P_INPUT | - HWMON_P_MAX | HWMON_P_MIN | - HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM | - HWMON_P_CRIT | HWMON_P_LCRIT | - HWMON_P_CRIT_ALARM | HWMON_P_LCRIT_ALARM | - HWMON_P_LABEL, - 0, -}; - -static const struct hwmon_channel_info sfp_hwmon_power_channel_info = { - .type = hwmon_power, - .config = sfp_hwmon_power_config, -}; - static const struct hwmon_channel_info *sfp_hwmon_info[] = { - &sfp_hwmon_chip, - &sfp_hwmon_vcc_channel_info, - &sfp_hwmon_temp_channel_info, - &sfp_hwmon_bias_channel_info, - &sfp_hwmon_power_channel_info, + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | + HWMON_I_MAX | HWMON_I_MIN | + HWMON_I_MAX_ALARM | HWMON_I_MIN_ALARM | + HWMON_I_CRIT | HWMON_I_LCRIT | + HWMON_I_CRIT_ALARM | HWMON_I_LCRIT_ALARM | + HWMON_I_LABEL), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | + HWMON_T_MAX | HWMON_T_MIN | + HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | + HWMON_T_CRIT | HWMON_T_LCRIT | + HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM | + HWMON_T_LABEL), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | + HWMON_C_MAX | HWMON_C_MIN | + HWMON_C_MAX_ALARM | HWMON_C_MIN_ALARM | + HWMON_C_CRIT | HWMON_C_LCRIT | + HWMON_C_CRIT_ALARM | HWMON_C_LCRIT_ALARM | + HWMON_C_LABEL), + HWMON_CHANNEL_INFO(power, + /* Transmit power */ + HWMON_P_INPUT | + HWMON_P_MAX | HWMON_P_MIN | + HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM | + HWMON_P_CRIT | HWMON_P_LCRIT | + HWMON_P_CRIT_ALARM | HWMON_P_LCRIT_ALARM | + HWMON_P_LABEL, + /* Receive power */ + HWMON_P_INPUT | + HWMON_P_MAX | HWMON_P_MIN | + HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM | + HWMON_P_CRIT | HWMON_P_LCRIT | + HWMON_P_CRIT_ALARM | HWMON_P_LCRIT_ALARM | + HWMON_P_LABEL), NULL, }; @@ -1947,17 +2021,22 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) if (ret < 0) return ret; - if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) && - !memcmp(id.base.vendor_pn, "3FE46541AA ", 16)) - sfp->module_t_start_up = T_START_UP_BAD_GPON; - else - sfp->module_t_start_up = T_START_UP; + /* Initialise state bits to use from hardware */ + sfp->state_hw_mask = SFP_F_PRESENT; + if (sfp->gpio[GPIO_TX_DISABLE]) + sfp->state_hw_mask |= SFP_F_TX_DISABLE; + if (sfp->gpio[GPIO_TX_FAULT]) + sfp->state_hw_mask |= SFP_F_TX_FAULT; + if (sfp->gpio[GPIO_LOS]) + sfp->state_hw_mask |= SFP_F_LOS; - if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) && - !memcmp(id.base.vendor_pn, "MA5671A ", 16)) - sfp->tx_fault_ignore = true; - else - sfp->tx_fault_ignore = false; + sfp->module_t_start_up = T_START_UP; + + sfp->tx_fault_ignore = false; + + sfp->quirk = sfp_lookup_quirk(&id); + if (sfp->quirk && sfp->quirk->fixup) + sfp->quirk->fixup(sfp); return 0; } @@ -2071,7 +2150,8 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event) break; /* Report the module insertion to the upstream device */ - err = sfp_module_insert(sfp->sfp_bus, &sfp->id); + err = sfp_module_insert(sfp->sfp_bus, &sfp->id, + sfp->quirk); if (err < 0) { sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); break; @@ -2573,6 +2653,8 @@ static int sfp_probe(struct platform_device *pdev) return PTR_ERR(sfp->gpio[i]); } + sfp->state_hw_mask = SFP_F_PRESENT; + sfp->get_state = sfp_gpio_get_state; sfp->set_state = sfp_gpio_set_state; diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h index 27226535c72b..7ad06deae76c 100644 --- a/drivers/net/phy/sfp.h +++ b/drivers/net/phy/sfp.h @@ -6,6 +6,13 @@ struct sfp; +struct sfp_quirk { + const char *vendor; + const char *part; + void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); + void (*fixup)(struct sfp *sfp); +}; + struct sfp_socket_ops { void (*attach)(struct sfp *sfp); void (*detach)(struct sfp *sfp); @@ -23,7 +30,8 @@ int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev); void sfp_remove_phy(struct sfp_bus *bus); void sfp_link_up(struct sfp_bus *bus); void sfp_link_down(struct sfp_bus *bus); -int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id); +int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + const struct sfp_quirk *quirk); void sfp_module_remove(struct sfp_bus *bus); int sfp_module_start(struct sfp_bus *bus); void sfp_module_stop(struct sfp_bus *bus); diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 69423b8965b3..ac7481ce2fc1 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -46,7 +46,6 @@ static struct smsc_hw_stat smsc_hw_stats[] = { struct smsc_phy_priv { u16 intmask; bool energy_enable; - struct clk *refclk; }; static int smsc_phy_ack_interrupt(struct phy_device *phydev) @@ -285,20 +284,12 @@ static void smsc_get_stats(struct phy_device *phydev, data[i] = smsc_get_stat(phydev, i); } -static void smsc_phy_remove(struct phy_device *phydev) -{ - struct smsc_phy_priv *priv = phydev->priv; - - clk_disable_unprepare(priv->refclk); - clk_put(priv->refclk); -} - static int smsc_phy_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; struct device_node *of_node = dev->of_node; struct smsc_phy_priv *priv; - int ret; + struct clk *refclk; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -312,22 +303,12 @@ static int smsc_phy_probe(struct phy_device *phydev) phydev->priv = priv; /* Make clk optional to keep DTB backward compatibility. */ - priv->refclk = clk_get_optional(dev, NULL); - if (IS_ERR(priv->refclk)) - return dev_err_probe(dev, PTR_ERR(priv->refclk), + refclk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(refclk)) + return dev_err_probe(dev, PTR_ERR(refclk), "Failed to request clock\n"); - ret = clk_prepare_enable(priv->refclk); - if (ret) - return ret; - - ret = clk_set_rate(priv->refclk, 50 * 1000 * 1000); - if (ret) { - clk_disable_unprepare(priv->refclk); - return ret; - } - - return 0; + return clk_set_rate(refclk, 50 * 1000 * 1000); } static struct phy_driver smsc_phy_driver[] = { @@ -429,7 +410,6 @@ static struct phy_driver smsc_phy_driver[] = { /* PHY_BASIC_FEATURES */ .probe = smsc_phy_probe, - .remove = smsc_phy_remove, /* basic functions */ .read_status = lan87xx_read_status, diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c index ff37f8ba6758..d4202d40d47a 100644 --- a/drivers/net/phy/spi_ks8995.c +++ b/drivers/net/phy/spi_ks8995.c @@ -17,7 +17,6 @@ #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/spi/spi.h> @@ -137,15 +136,10 @@ static const struct ks8995_chip_params ks8995_chip[] = { }, }; -struct ks8995_pdata { - int reset_gpio; - enum of_gpio_flags reset_gpio_flags; -}; - struct ks8995_switch { struct spi_device *spi; struct mutex lock; - struct ks8995_pdata *pdata; + struct gpio_desc *reset_gpio; struct bin_attribute regs_attr; const struct ks8995_chip_params *chip; int revision_id; @@ -401,24 +395,6 @@ err_out: return err; } -/* ks8995_parse_dt - setup platform data from devicetree - * @ks: pointer to switch instance - * - * Parses supported DT properties and sets up platform data - * accordingly. - */ -static void ks8995_parse_dt(struct ks8995_switch *ks) -{ - struct device_node *np = ks->spi->dev.of_node; - struct ks8995_pdata *pdata = ks->pdata; - - if (!np) - return; - - pdata->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, - &pdata->reset_gpio_flags); -} - static const struct bin_attribute ks8995_registers_attr = { .attr = { .name = "registers", @@ -449,38 +425,22 @@ static int ks8995_probe(struct spi_device *spi) ks->spi = spi; ks->chip = &ks8995_chip[variant]; - if (ks->spi->dev.of_node) { - ks->pdata = devm_kzalloc(&spi->dev, sizeof(*ks->pdata), - GFP_KERNEL); - if (!ks->pdata) - return -ENOMEM; - - ks->pdata->reset_gpio = -1; - - ks8995_parse_dt(ks); + ks->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", + GPIOD_OUT_HIGH); + err = PTR_ERR_OR_ZERO(ks->reset_gpio); + if (err) { + dev_err(&spi->dev, + "failed to get reset gpio: %d\n", err); + return err; } - if (!ks->pdata) - ks->pdata = spi->dev.platform_data; + err = gpiod_set_consumer_name(ks->reset_gpio, "switch-reset"); + if (err) + return err; /* de-assert switch reset */ - if (ks->pdata && gpio_is_valid(ks->pdata->reset_gpio)) { - unsigned long flags; - - flags = (ks->pdata->reset_gpio_flags == OF_GPIO_ACTIVE_LOW ? - GPIOF_ACTIVE_LOW : 0); - - err = devm_gpio_request_one(&spi->dev, - ks->pdata->reset_gpio, - flags, "switch-reset"); - if (err) { - dev_err(&spi->dev, - "failed to get reset-gpios: %d\n", err); - return -EIO; - } - - gpiod_set_value(gpio_to_desc(ks->pdata->reset_gpio), 0); - } + /* FIXME: this likely requires a delay */ + gpiod_set_value_cansleep(ks->reset_gpio, 0); spi_set_drvdata(spi, ks); @@ -524,8 +484,7 @@ static void ks8995_remove(struct spi_device *spi) sysfs_remove_bin_file(&spi->dev.kobj, &ks->regs_attr); /* assert reset */ - if (ks->pdata && gpio_is_valid(ks->pdata->reset_gpio)) - gpiod_set_value(gpio_to_desc(ks->pdata->reset_gpio), 1); + gpiod_set_value_cansleep(ks->reset_gpio, 1); } /* ------------------------------------------------------------------------ */ |