net: usb: lan78xx: refactor PHY init to separate detection and MAC configuration
authorOleksij Rempel <o.rempel@pengutronix.de>
Mon, 5 May 2025 08:43:37 +0000 (10:43 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 7 May 2025 11:57:05 +0000 (12:57 +0100)
Split out PHY detection into lan78xx_get_phy() and MAC-side setup into
lan78xx_mac_prepare_for_phy(), making the main lan78xx_phy_init() cleaner
and easier to follow.

This improves separation of concerns and prepares the code for a future
transition to phylink. Fixed PHY registration and interface selection
are now handled in lan78xx_get_phy(), while MAC-side delay configuration
is done in lan78xx_mac_prepare_for_phy().

The fixed PHY fallback is preserved for setups like EVB-KSZ9897-1,
where LAN7801 connects directly to a KSZ switch without a standard PHY
or device tree support.

No functional changes intended.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Thangaraj Samynathan <thangaraj.s@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/usb/lan78xx.c

index 9c0658227bde54e43e5d1e4afcf2240a8552df82..7f1ecc415d53d8bb3f21e45f39ee45d4b3f79d8d 100644 (file)
@@ -2508,53 +2508,145 @@ static void lan78xx_remove_irq_domain(struct lan78xx_net *dev)
        dev->domain_data.irqdomain = NULL;
 }
 
-static struct phy_device *lan7801_phy_init(struct lan78xx_net *dev)
+/**
+ * lan78xx_register_fixed_phy() - Register a fallback fixed PHY
+ * @dev: LAN78xx device
+ *
+ * Registers a fixed PHY with 1 Gbps full duplex. This is used in special cases
+ * like EVB-KSZ9897-1, where LAN7801 acts as a USB-to-Ethernet interface to a
+ * switch without a visible PHY.
+ *
+ * Return: pointer to the registered fixed PHY, or ERR_PTR() on error.
+ */
+static struct phy_device *lan78xx_register_fixed_phy(struct lan78xx_net *dev)
 {
        struct fixed_phy_status fphy_status = {
                .link = 1,
                .speed = SPEED_1000,
                .duplex = DUPLEX_FULL,
        };
+
+       netdev_info(dev->net,
+                   "No PHY found on LAN7801 – registering fixed PHY (e.g. EVB-KSZ9897-1)\n");
+
+       return fixed_phy_register(PHY_POLL, &fphy_status, NULL);
+}
+
+/**
+ * lan78xx_get_phy() - Probe or register PHY device and set interface mode
+ * @dev: LAN78xx device structure
+ *
+ * This function attempts to find a PHY on the MDIO bus. If no PHY is found
+ * and the chip is LAN7801, it registers a fixed PHY as fallback. It also
+ * sets dev->interface based on chip ID and detected PHY type.
+ *
+ * Return: a valid PHY device pointer, or ERR_PTR() on failure.
+ */
+static struct phy_device *lan78xx_get_phy(struct lan78xx_net *dev)
+{
        struct phy_device *phydev;
-       int ret;
 
+       /* Attempt to locate a PHY on the MDIO bus */
        phydev = phy_find_first(dev->mdiobus);
-       if (!phydev) {
-               netdev_dbg(dev->net, "PHY Not Found!! Registering Fixed PHY\n");
-               phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
-               if (IS_ERR(phydev)) {
-                       netdev_err(dev->net, "No PHY/fixed_PHY found\n");
-                       return ERR_PTR(-ENODEV);
+
+       switch (dev->chipid) {
+       case ID_REV_CHIP_ID_7801_:
+               if (phydev) {
+                       /* External RGMII PHY detected */
+                       dev->interface = PHY_INTERFACE_MODE_RGMII_ID;
+                       phydev->is_internal = false;
+
+                       if (!phydev->drv)
+                               netdev_warn(dev->net,
+                                           "PHY driver not found – assuming RGMII delays are on PCB or strapped for the PHY\n");
+
+                       return phydev;
                }
-               netdev_dbg(dev->net, "Registered FIXED PHY\n");
+
                dev->interface = PHY_INTERFACE_MODE_RGMII;
+               /* No PHY found – fallback to fixed PHY (e.g. KSZ switch board) */
+               return lan78xx_register_fixed_phy(dev);
+
+       case ID_REV_CHIP_ID_7800_:
+       case ID_REV_CHIP_ID_7850_:
+               if (!phydev)
+                       return ERR_PTR(-ENODEV);
+
+               /* These use internal GMII-connected PHY */
+               dev->interface = PHY_INTERFACE_MODE_GMII;
+               phydev->is_internal = true;
+               return phydev;
+
+       default:
+               netdev_err(dev->net, "Unknown CHIP ID: 0x%08x\n", dev->chipid);
+               return ERR_PTR(-ENODEV);
+       }
+}
+
+/**
+ * lan78xx_mac_prepare_for_phy() - Preconfigure MAC-side interface settings
+ * @dev: LAN78xx device
+ *
+ * Configure MAC-side registers according to dev->interface, which should be
+ * set by lan78xx_get_phy().
+ *
+ * - For PHY_INTERFACE_MODE_RGMII:
+ *   Enable MAC-side TXC delay. This mode seems to be used in a special setup
+ *   without a real PHY, likely on EVB-KSZ9897-1. In that design, LAN7801 is
+ *   connected to the KSZ9897 switch, and the link timing is expected to be
+ *   hardwired (e.g. via strapping or board layout). No devicetree support is
+ *   assumed here.
+ *
+ * - For PHY_INTERFACE_MODE_RGMII_ID:
+ *   Disable MAC-side delay and rely on the PHY driver to provide delay.
+ *
+ * - For GMII, no MAC-specific config is needed.
+ *
+ * Return: 0 on success or a negative error code.
+ */
+static int lan78xx_mac_prepare_for_phy(struct lan78xx_net *dev)
+{
+       int ret;
+
+       switch (dev->interface) {
+       case PHY_INTERFACE_MODE_RGMII:
+               /* Enable MAC-side TX clock delay */
                ret = lan78xx_write_reg(dev, MAC_RGMII_ID,
                                        MAC_RGMII_ID_TXC_DELAY_EN_);
                if (ret < 0)
-                       return ERR_PTR(ret);
+                       return ret;
 
                ret = lan78xx_write_reg(dev, RGMII_TX_BYP_DLL, 0x3D00);
                if (ret < 0)
-                       return ERR_PTR(ret);
+                       return ret;
 
-               ret = lan78xx_update_reg(dev, HW_CFG, HW_CFG_CLK125_EN_ |
-                                        HW_CFG_REFCLK25_EN_,
+               ret = lan78xx_update_reg(dev, HW_CFG,
+                                        HW_CFG_CLK125_EN_ | HW_CFG_REFCLK25_EN_,
                                         HW_CFG_CLK125_EN_ | HW_CFG_REFCLK25_EN_);
                if (ret < 0)
-                       return ERR_PTR(ret);
-       } else {
-               dev->interface = PHY_INTERFACE_MODE_RGMII_ID;
-               /* The PHY driver is responsible to configure proper RGMII
-                * interface delays. Disable RGMII delays on MAC side.
-                */
+                       return ret;
+
+               break;
+
+       case PHY_INTERFACE_MODE_RGMII_ID:
+               /* Disable MAC-side TXC delay, PHY provides it */
                ret = lan78xx_write_reg(dev, MAC_RGMII_ID, 0);
                if (ret < 0)
-                       return ERR_PTR(ret);
+                       return ret;
 
-               phydev->is_internal = false;
+               break;
+
+       case PHY_INTERFACE_MODE_GMII:
+               /* No MAC-specific configuration required */
+               break;
+
+       default:
+               netdev_warn(dev->net, "Unsupported interface mode: %d\n",
+                           dev->interface);
+               break;
        }
 
-       return phydev;
+       return 0;
 }
 
 static int lan78xx_phy_init(struct lan78xx_net *dev)
@@ -2564,31 +2656,13 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
        u32 mii_adv;
        struct phy_device *phydev;
 
-       switch (dev->chipid) {
-       case ID_REV_CHIP_ID_7801_:
-               phydev = lan7801_phy_init(dev);
-               if (IS_ERR(phydev)) {
-                       netdev_err(dev->net, "lan7801: failed to init PHY: %pe\n",
-                                  phydev);
-                       return PTR_ERR(phydev);
-               }
-               break;
-
-       case ID_REV_CHIP_ID_7800_:
-       case ID_REV_CHIP_ID_7850_:
-               phydev = phy_find_first(dev->mdiobus);
-               if (!phydev) {
-                       netdev_err(dev->net, "no PHY found\n");
-                       return -ENODEV;
-               }
-               phydev->is_internal = true;
-               dev->interface = PHY_INTERFACE_MODE_GMII;
-               break;
+       phydev = lan78xx_get_phy(dev);
+       if (IS_ERR(phydev))
+               return PTR_ERR(phydev);
 
-       default:
-               netdev_err(dev->net, "Unknown CHIP ID found\n");
-               return -ENODEV;
-       }
+       ret = lan78xx_mac_prepare_for_phy(dev);
+       if (ret < 0)
+               goto free_phy;
 
        /* if phyirq is not set, use polling mode in phylib */
        if (dev->domain_data.phyirq > 0)
@@ -2662,6 +2736,14 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
        dev->fc_autoneg = phydev->autoneg;
 
        return 0;
+
+free_phy:
+       if (phy_is_pseudo_fixed_link(phydev)) {
+               fixed_phy_unregister(phydev);
+               phy_device_free(phydev);
+       }
+
+       return ret;
 }
 
 static int lan78xx_set_rx_max_frame_length(struct lan78xx_net *dev, int size)