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.

Merge branch 'net-phy-microchip-add-downshift-support-for-lan88xx'

Nicolai Buchwitz says:

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

Add standard ETHTOOL_PHY_DOWNSHIFT tunable support for the Microchip
LAN88xx PHY, following the same pattern used by Marvell and other PHY
drivers.

Ethernet cables with faulty or missing pairs (specifically C and D)
can successfully auto-negotiate 1000BASE-T but fail to establish a
stable link. The LAN88xx PHY supports automatic downshift to
100BASE-TX after a configurable number of failed attempts (2-5).

Patch 1 adds the get/set tunable implementation.
Patch 2 enables downshift by default with a count of 2. The setting is
stored in the driver's private data so that user changes via ethtool are
preserved across suspend/resume cycles.

Based on an earlier downstream implementation by Phil Elwell.

Tested on Raspberry Pi 3B+ (LAN7515/LAN88xx).
====================

Link: https://patch.msgid.link/20260401123848.696766-1-nb@tipi-net.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+83 -1
+78 -1
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> ··· 26 25 int chip_id; 27 26 int chip_rev; 28 27 __u32 wolopts; 28 + u8 downshift_cnt; 29 29 }; 30 30 31 31 static int lan88xx_read_page(struct phy_device *phydev) ··· 195 193 phydev_warn(phydev, "Failed to Set Register[0x1686]\n"); 196 194 } 197 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 + struct lan88xx_priv *priv = phydev->priv; 250 + int ret; 251 + 252 + switch (tuna->id) { 253 + case ETHTOOL_PHY_DOWNSHIFT: 254 + ret = lan88xx_set_downshift(phydev, *(const u8 *)data); 255 + if (!ret) 256 + priv->downshift_cnt = *(const u8 *)data; 257 + return ret; 258 + default: 259 + return -EOPNOTSUPP; 260 + } 261 + } 262 + 198 263 static int lan88xx_probe(struct phy_device *phydev) 199 264 { 200 265 struct device *dev = &phydev->mdio.dev; ··· 274 205 return -ENOMEM; 275 206 276 207 priv->wolopts = 0; 208 + priv->downshift_cnt = 2; 277 209 278 210 len = of_property_read_variable_u32_array(dev->of_node, 279 211 "microchip,led-modes", ··· 354 284 355 285 static int lan88xx_config_init(struct phy_device *phydev) 356 286 { 357 - int val; 287 + struct lan88xx_priv *priv = phydev->priv; 288 + int val, err; 358 289 359 290 /*Zerodetect delay enable */ 360 291 val = phy_read_mmd(phydev, MDIO_MMD_PCS, ··· 367 296 368 297 /* Config DSP registers */ 369 298 lan88xx_config_TR_regs(phydev); 299 + 300 + err = lan88xx_set_downshift(phydev, priv->downshift_cnt); 301 + if (err < 0) 302 + return err; 370 303 371 304 return 0; 372 305 } ··· 574 499 .set_wol = lan88xx_set_wol, 575 500 .read_page = lan88xx_read_page, 576 501 .write_page = lan88xx_write_page, 502 + .get_tunable = lan88xx_get_tunable, 503 + .set_tunable = lan88xx_set_tunable, 577 504 }, 578 505 { 579 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)