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

net: lan78xx: fix accessing the LAN7800's internal phy specific registers from the MAC driver

Move the LAN7800 internal phy (phy ID 0x0007c132) specific register
accesses to the phy driver (microchip.c).

Fix the error reported by Enguerrand de Ribaucourt in December 2022,
"Some operations during the cable switch workaround modify the register
LAN88XX_INT_MASK of the PHY. However, this register is specific to the
LAN8835 PHY. For instance, if a DP8322I PHY is connected to the LAN7801,
that register (0x19), corresponds to the LED and MAC address
configuration, resulting in unapropriate behavior."

I did not test with the DP8322I PHY, but I tested with an EVB-LAN7800
with the internal PHY.

Fixes: 14437e3fa284 ("lan78xx: workaround of forced 100 Full/Half duplex mode error")
Signed-off-by: Yuiko Oshino <yuiko.oshino@microchip.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://lore.kernel.org/r/20230301154307.30438-1-yuiko.oshino@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Yuiko Oshino and committed by
Jakub Kicinski
e57cf363 5c1ebbfa

+33 -26
+32
drivers/net/phy/microchip.c
··· 342 342 return genphy_config_aneg(phydev); 343 343 } 344 344 345 + static void lan88xx_link_change_notify(struct phy_device *phydev) 346 + { 347 + int temp; 348 + 349 + /* At forced 100 F/H mode, chip may fail to set mode correctly 350 + * when cable is switched between long(~50+m) and short one. 351 + * As workaround, set to 10 before setting to 100 352 + * at forced 100 F/H mode. 353 + */ 354 + if (!phydev->autoneg && phydev->speed == 100) { 355 + /* disable phy interrupt */ 356 + temp = phy_read(phydev, LAN88XX_INT_MASK); 357 + temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_; 358 + phy_write(phydev, LAN88XX_INT_MASK, temp); 359 + 360 + temp = phy_read(phydev, MII_BMCR); 361 + temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000); 362 + phy_write(phydev, MII_BMCR, temp); /* set to 10 first */ 363 + temp |= BMCR_SPEED100; 364 + phy_write(phydev, MII_BMCR, temp); /* set to 100 later */ 365 + 366 + /* clear pending interrupt generated while workaround */ 367 + temp = phy_read(phydev, LAN88XX_INT_STS); 368 + 369 + /* enable phy interrupt back */ 370 + temp = phy_read(phydev, LAN88XX_INT_MASK); 371 + temp |= LAN88XX_INT_MASK_MDINTPIN_EN_; 372 + phy_write(phydev, LAN88XX_INT_MASK, temp); 373 + } 374 + } 375 + 345 376 static struct phy_driver microchip_phy_driver[] = { 346 377 { 347 378 .phy_id = 0x0007c132, ··· 390 359 391 360 .config_init = lan88xx_config_init, 392 361 .config_aneg = lan88xx_config_aneg, 362 + .link_change_notify = lan88xx_link_change_notify, 393 363 394 364 .config_intr = lan88xx_phy_config_intr, 395 365 .handle_interrupt = lan88xx_handle_interrupt,
+1 -26
drivers/net/usb/lan78xx.c
··· 2115 2115 static void lan78xx_link_status_change(struct net_device *net) 2116 2116 { 2117 2117 struct phy_device *phydev = net->phydev; 2118 - int temp; 2119 2118 2120 - /* At forced 100 F/H mode, chip may fail to set mode correctly 2121 - * when cable is switched between long(~50+m) and short one. 2122 - * As workaround, set to 10 before setting to 100 2123 - * at forced 100 F/H mode. 2124 - */ 2125 - if (!phydev->autoneg && (phydev->speed == 100)) { 2126 - /* disable phy interrupt */ 2127 - temp = phy_read(phydev, LAN88XX_INT_MASK); 2128 - temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_; 2129 - phy_write(phydev, LAN88XX_INT_MASK, temp); 2130 - 2131 - temp = phy_read(phydev, MII_BMCR); 2132 - temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000); 2133 - phy_write(phydev, MII_BMCR, temp); /* set to 10 first */ 2134 - temp |= BMCR_SPEED100; 2135 - phy_write(phydev, MII_BMCR, temp); /* set to 100 later */ 2136 - 2137 - /* clear pending interrupt generated while workaround */ 2138 - temp = phy_read(phydev, LAN88XX_INT_STS); 2139 - 2140 - /* enable phy interrupt back */ 2141 - temp = phy_read(phydev, LAN88XX_INT_MASK); 2142 - temp |= LAN88XX_INT_MASK_MDINTPIN_EN_; 2143 - phy_write(phydev, LAN88XX_INT_MASK, temp); 2144 - } 2119 + phy_print_status(phydev); 2145 2120 } 2146 2121 2147 2122 static int irq_map(struct irq_domain *d, unsigned int irq,