Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net: phy: microchip: add downshift tunable support for LAN88xx

Implement the standard ETHTOOL_PHY_DOWNSHIFT tunable for the LAN88xx
PHY. This allows runtime configuration of the auto-downshift feature
via ethtool:

ethtool --set-phy-tunable eth0 downshift on count 3

The LAN88xx PHY supports downshifting from 1000BASE-T to 100BASE-TX
after 2-5 failed auto-negotiation attempts. Valid count values are
2, 3, 4 and 5.

This is based on an earlier downstream implementation by Phil Elwell.

Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Link: https://patch.msgid.link/20260401123848.696766-2-nb@tipi-net.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Nicolai Buchwitz and committed by
Jakub Kicinski
e417ac73 86f5dd4e

+69
+64
drivers/net/phy/microchip.c
··· 2 2 /* 3 3 * Copyright (C) 2015 Microchip Technology 4 4 */ 5 + #include <linux/bitfield.h> 5 6 #include <linux/kernel.h> 6 7 #include <linux/module.h> 7 8 #include <linux/mii.h> ··· 192 191 err = lan88xx_TR_reg_set(phydev, 0x1686, 0x000004); 193 192 if (err < 0) 194 193 phydev_warn(phydev, "Failed to Set Register[0x1686]\n"); 194 + } 195 + 196 + static int lan88xx_get_downshift(struct phy_device *phydev, u8 *data) 197 + { 198 + int val; 199 + 200 + val = phy_read_paged(phydev, 1, LAN78XX_PHY_CTRL3); 201 + if (val < 0) 202 + return val; 203 + 204 + if (!(val & LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT)) { 205 + *data = DOWNSHIFT_DEV_DISABLE; 206 + return 0; 207 + } 208 + 209 + *data = FIELD_GET(LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK, val) + 2; 210 + 211 + return 0; 212 + } 213 + 214 + static int lan88xx_set_downshift(struct phy_device *phydev, u8 cnt) 215 + { 216 + u32 mask = LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK | 217 + LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT; 218 + 219 + if (cnt == DOWNSHIFT_DEV_DISABLE) 220 + return phy_modify_paged(phydev, 1, LAN78XX_PHY_CTRL3, 221 + LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT, 0); 222 + 223 + if (cnt == DOWNSHIFT_DEV_DEFAULT_COUNT) 224 + cnt = 2; 225 + 226 + if (cnt < 2 || cnt > 5) 227 + return -EINVAL; 228 + 229 + return phy_modify_paged(phydev, 1, LAN78XX_PHY_CTRL3, mask, 230 + FIELD_PREP(LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK, 231 + cnt - 2) | 232 + LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT); 233 + } 234 + 235 + static int lan88xx_get_tunable(struct phy_device *phydev, 236 + struct ethtool_tunable *tuna, void *data) 237 + { 238 + switch (tuna->id) { 239 + case ETHTOOL_PHY_DOWNSHIFT: 240 + return lan88xx_get_downshift(phydev, data); 241 + default: 242 + return -EOPNOTSUPP; 243 + } 244 + } 245 + 246 + static int lan88xx_set_tunable(struct phy_device *phydev, 247 + struct ethtool_tunable *tuna, const void *data) 248 + { 249 + switch (tuna->id) { 250 + case ETHTOOL_PHY_DOWNSHIFT: 251 + return lan88xx_set_downshift(phydev, *(const u8 *)data); 252 + default: 253 + return -EOPNOTSUPP; 254 + } 195 255 } 196 256 197 257 static int lan88xx_probe(struct phy_device *phydev) ··· 561 499 .set_wol = lan88xx_set_wol, 562 500 .read_page = lan88xx_read_page, 563 501 .write_page = lan88xx_write_page, 502 + .get_tunable = lan88xx_get_tunable, 503 + .set_tunable = lan88xx_set_tunable, 564 504 }, 565 505 { 566 506 PHY_ID_MATCH_MODEL(PHY_ID_LAN937X_TX),
+5
include/linux/microchipphy.h
··· 61 61 /* Registers specific to the LAN7800/LAN7850 embedded phy */ 62 62 #define LAN78XX_PHY_LED_MODE_SELECT (0x1D) 63 63 64 + /* PHY Control 3 register (page 1) */ 65 + #define LAN78XX_PHY_CTRL3 (0x14) 66 + #define LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT BIT(4) 67 + #define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK GENMASK(3, 2) 68 + 64 69 /* DSP registers */ 65 70 #define PHY_ARDENNES_MMD_DEV_3_PHY_CFG (0x806A) 66 71 #define PHY_ARDENNES_MMD_DEV_3_PHY_CFG_ZD_DLY_EN_ (0x2000)