* [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK
@ 2025-04-10 11:52 Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 01/12] net: usb: lan78xx: Improve error handling in PHY initialization Oleksij Rempel
` (11 more replies)
0 siblings, 12 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:52 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
This patch series refactors the LAN78xx USB Ethernet driver to use the
PHYLINK framework for MAC and PHY management.
In the process, various improvements were made to structure the code and
separate concerns such as:
- Refactoring PHY detection and MAC-side interface setup
- Consolidating USB and flow control configuration into helpers
- Dropping legacy EEE and pause handling in favor of PHYLINK equivalents
- Simplifying the suspend/resume flow with proper phylink_suspend/resume
- Removing unused fields and outdated workarounds
changes v6:
- Move unrelated logic out of the main PHYLINK conversion patch:
- EEE handling, USB config, flow control, LED config, etc., now split
into separate commits
- Remove obsolete members from struct lan78xx_net earlier, in the patch
where they're no longer needed
changes v5:
- merge ethtool pause configuration patch with PHYlink patch
- merge some other small cleanup to a single patch
changes v4:
- split "Improve error handling in PHY initialization" patch and move
some parts before PHYlink porting to address some of compile warning
as early as possible.
- add cleanup patch to remove unused struct members
Oleksij Rempel (12):
net: usb: lan78xx: Improve error handling in PHY initialization
net: usb: lan78xx: remove explicit check for missing PHY driver
net: usb: lan78xx: refactor PHY init to separate detection and MAC
configuration
net: usb: lan78xx: move LED DT configuration to helper
net: usb: lan78xx: Extract PHY interrupt acknowledgment to helper
net: usb: lan78xx: Refactor USB link power configuration into helper
net: usb: lan78xx: Extract flow control configuration to helper
net: usb: lan78xx: Convert to PHYLINK for improved PHY and MAC
management
net: usb: lan78xx: Use ethtool_op_get_link to reflect current link
status
net: usb: lan78xx: port link settings to phylink API
net: usb: lan78xx: Integrate EEE support with phylink LPI API
net: usb: lan78xx: remove unused struct members
drivers/net/usb/Kconfig | 3 +-
drivers/net/usb/lan78xx.c | 1114 +++++++++++++++++++++----------------
2 files changed, 646 insertions(+), 471 deletions(-)
--
2.39.5
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH net-next v6 01/12] net: usb: lan78xx: Improve error handling in PHY initialization
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
@ 2025-04-10 11:52 ` Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 02/12] net: usb: lan78xx: remove explicit check for missing PHY driver Oleksij Rempel
` (10 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:52 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
Ensure that return values from `lan78xx_write_reg()`,
`lan78xx_read_reg()`, and `phy_find_first()` are properly checked and
propagated. Use `ERR_PTR(ret)` for error reporting in
`lan7801_phy_init()` and replace `-EIO` with `-ENODEV` where appropriate
to provide more accurate error codes.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- confirmed with maintainers that fixed_phy_register() leak is acceptable
as soon as it is fixed within the patch set
changes v5:
- make sure lan7801_phy_init() caller is testing against IS_ERR
instead of NULL.
changes v4:
- split the patch and move part of it before PHYlink migration
---
drivers/net/usb/lan78xx.c | 47 ++++++++++++++++++++++++++-------------
1 file changed, 31 insertions(+), 16 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 137adf6d5b08..13b5da18850a 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -2510,14 +2510,13 @@ static void lan78xx_remove_irq_domain(struct lan78xx_net *dev)
static struct phy_device *lan7801_phy_init(struct lan78xx_net *dev)
{
- u32 buf;
- int ret;
struct fixed_phy_status fphy_status = {
.link = 1,
.speed = SPEED_1000,
.duplex = DUPLEX_FULL,
};
struct phy_device *phydev;
+ int ret;
phydev = phy_find_first(dev->mdiobus);
if (!phydev) {
@@ -2525,30 +2524,40 @@ static struct phy_device *lan7801_phy_init(struct lan78xx_net *dev)
phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
if (IS_ERR(phydev)) {
netdev_err(dev->net, "No PHY/fixed_PHY found\n");
- return NULL;
+ return ERR_PTR(-ENODEV);
}
netdev_dbg(dev->net, "Registered FIXED PHY\n");
dev->interface = PHY_INTERFACE_MODE_RGMII;
ret = lan78xx_write_reg(dev, MAC_RGMII_ID,
MAC_RGMII_ID_TXC_DELAY_EN_);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
ret = lan78xx_write_reg(dev, RGMII_TX_BYP_DLL, 0x3D00);
- ret = lan78xx_read_reg(dev, HW_CFG, &buf);
- buf |= HW_CFG_CLK125_EN_;
- buf |= HW_CFG_REFCLK25_EN_;
- ret = lan78xx_write_reg(dev, HW_CFG, buf);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ 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 {
if (!phydev->drv) {
netdev_err(dev->net, "no PHY driver found\n");
- return NULL;
+ return ERR_PTR(-EINVAL);
}
dev->interface = PHY_INTERFACE_MODE_RGMII_ID;
/* The PHY driver is responsible to configure proper RGMII
* interface delays. Disable RGMII delays on MAC side.
*/
- lan78xx_write_reg(dev, MAC_RGMII_ID, 0);
+ ret = lan78xx_write_reg(dev, MAC_RGMII_ID, 0);
+ if (ret < 0)
+ return ERR_PTR(ret);
phydev->is_internal = false;
}
+
return phydev;
}
@@ -2562,9 +2571,10 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
switch (dev->chipid) {
case ID_REV_CHIP_ID_7801_:
phydev = lan7801_phy_init(dev);
- if (!phydev) {
- netdev_err(dev->net, "lan7801: PHY Init Failed");
- return -EIO;
+ if (IS_ERR(phydev)) {
+ netdev_err(dev->net, "lan7801: failed to init PHY: %pe\n",
+ phydev);
+ return PTR_ERR(phydev);
}
break;
@@ -2573,7 +2583,7 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
phydev = phy_find_first(dev->mdiobus);
if (!phydev) {
netdev_err(dev->net, "no PHY found\n");
- return -EIO;
+ return -ENODEV;
}
phydev->is_internal = true;
dev->interface = PHY_INTERFACE_MODE_GMII;
@@ -2581,7 +2591,7 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
default:
netdev_err(dev->net, "Unknown CHIP ID found\n");
- return -EIO;
+ return -ENODEV;
}
/* if phyirq is not set, use polling mode in phylib */
@@ -2633,7 +2643,10 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
sizeof(u32));
if (len >= 0) {
/* Ensure the appropriate LEDs are enabled */
- lan78xx_read_reg(dev, HW_CFG, ®);
+ ret = lan78xx_read_reg(dev, HW_CFG, ®);
+ if (ret < 0)
+ return ret;
+
reg &= ~(HW_CFG_LED0_EN_ |
HW_CFG_LED1_EN_ |
HW_CFG_LED2_EN_ |
@@ -2642,7 +2655,9 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
(len > 1) * HW_CFG_LED1_EN_ |
(len > 2) * HW_CFG_LED2_EN_ |
(len > 3) * HW_CFG_LED3_EN_;
- lan78xx_write_reg(dev, HW_CFG, reg);
+ ret = lan78xx_write_reg(dev, HW_CFG, reg);
+ if (ret < 0)
+ return ret;
}
}
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 02/12] net: usb: lan78xx: remove explicit check for missing PHY driver
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 01/12] net: usb: lan78xx: Improve error handling in PHY initialization Oleksij Rempel
@ 2025-04-10 11:52 ` Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 03/12] net: usb: lan78xx: refactor PHY init to separate detection and MAC configuration Oleksij Rempel
` (9 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:52 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
RGMII timing correctness relies on the PHY providing internal delays.
This is typically ensured via PHY driver, strap pins, or PCB layout.
Explicitly checking for a PHY driver here is unnecessary and non-standard.
This logic applies to all MACs, not just LAN78xx, and should be left to
phylib, phylink, or platform configuration.
Drop the check and rely on standard subsystem behavior.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- this patch is added in v6
---
drivers/net/usb/lan78xx.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 13b5da18850a..2876a119159c 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -2543,10 +2543,6 @@ static struct phy_device *lan7801_phy_init(struct lan78xx_net *dev)
if (ret < 0)
return ERR_PTR(ret);
} else {
- if (!phydev->drv) {
- netdev_err(dev->net, "no PHY driver found\n");
- return ERR_PTR(-EINVAL);
- }
dev->interface = PHY_INTERFACE_MODE_RGMII_ID;
/* The PHY driver is responsible to configure proper RGMII
* interface delays. Disable RGMII delays on MAC side.
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 03/12] net: usb: lan78xx: refactor PHY init to separate detection and MAC configuration
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 01/12] net: usb: lan78xx: Improve error handling in PHY initialization Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 02/12] net: usb: lan78xx: remove explicit check for missing PHY driver Oleksij Rempel
@ 2025-04-10 11:52 ` Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 04/12] net: usb: lan78xx: move LED DT configuration to helper Oleksij Rempel
` (8 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:52 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
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>
---
changes v6:
- this patch is added in v6
---
drivers/net/usb/lan78xx.c | 174 ++++++++++++++++++++++++++++----------
1 file changed, 128 insertions(+), 46 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 2876a119159c..a54d246244b8 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -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)
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 04/12] net: usb: lan78xx: move LED DT configuration to helper
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
` (2 preceding siblings ...)
2025-04-10 11:52 ` [PATCH net-next v6 03/12] net: usb: lan78xx: refactor PHY init to separate detection and MAC configuration Oleksij Rempel
@ 2025-04-10 11:52 ` Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 05/12] net: usb: lan78xx: Extract PHY interrupt acknowledgment " Oleksij Rempel
` (7 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:52 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
Extract the LED enable logic based on the "microchip,led-modes"
property into a new helper function lan78xx_configure_leds_from_dt().
This simplifies lan78xx_phy_init() and improves modularity.
No functional changes intended.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- this patch is added in v6
---
drivers/net/usb/lan78xx.c | 72 +++++++++++++++++++++++++--------------
1 file changed, 46 insertions(+), 26 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index a54d246244b8..6965013ebc59 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -2649,6 +2649,49 @@ static int lan78xx_mac_prepare_for_phy(struct lan78xx_net *dev)
return 0;
}
+/**
+ * lan78xx_configure_leds_from_dt() - Configure LED enables based on DT
+ * @dev: LAN78xx device
+ * @phydev: PHY device (must be valid)
+ *
+ * Reads "microchip,led-modes" property from the PHY's DT node and enables
+ * the corresponding number of LEDs by writing to HW_CFG.
+ *
+ * This helper preserves the original logic, enabling up to 4 LEDs.
+ * If the property is not present, this function does nothing.
+ *
+ * Return: 0 on success or a negative error code.
+ */
+static int lan78xx_configure_leds_from_dt(struct lan78xx_net *dev,
+ struct phy_device *phydev)
+{
+ struct device_node *np = phydev->mdio.dev.of_node;
+ u32 reg;
+ int len, ret;
+
+ if (!np)
+ return 0;
+
+ len = of_property_count_elems_of_size(np, "microchip,led-modes",
+ sizeof(u32));
+ if (len < 0)
+ return 0;
+
+ ret = lan78xx_read_reg(dev, HW_CFG, ®);
+ if (ret < 0)
+ return ret;
+
+ reg &= ~(HW_CFG_LED0_EN_ | HW_CFG_LED1_EN_ |
+ HW_CFG_LED2_EN_ | HW_CFG_LED3_EN_);
+
+ reg |= (len > 0) * HW_CFG_LED0_EN_ |
+ (len > 1) * HW_CFG_LED1_EN_ |
+ (len > 2) * HW_CFG_LED2_EN_ |
+ (len > 3) * HW_CFG_LED3_EN_;
+
+ return lan78xx_write_reg(dev, HW_CFG, reg);
+}
+
static int lan78xx_phy_init(struct lan78xx_net *dev)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, };
@@ -2704,32 +2747,9 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
phy_support_eee(phydev);
- if (phydev->mdio.dev.of_node) {
- u32 reg;
- int len;
-
- len = of_property_count_elems_of_size(phydev->mdio.dev.of_node,
- "microchip,led-modes",
- sizeof(u32));
- if (len >= 0) {
- /* Ensure the appropriate LEDs are enabled */
- ret = lan78xx_read_reg(dev, HW_CFG, ®);
- if (ret < 0)
- return ret;
-
- reg &= ~(HW_CFG_LED0_EN_ |
- HW_CFG_LED1_EN_ |
- HW_CFG_LED2_EN_ |
- HW_CFG_LED3_EN_);
- reg |= (len > 0) * HW_CFG_LED0_EN_ |
- (len > 1) * HW_CFG_LED1_EN_ |
- (len > 2) * HW_CFG_LED2_EN_ |
- (len > 3) * HW_CFG_LED3_EN_;
- ret = lan78xx_write_reg(dev, HW_CFG, reg);
- if (ret < 0)
- return ret;
- }
- }
+ ret = lan78xx_configure_leds_from_dt(dev, phydev);
+ if (ret)
+ goto free_phy;
genphy_config_aneg(phydev);
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 05/12] net: usb: lan78xx: Extract PHY interrupt acknowledgment to helper
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
` (3 preceding siblings ...)
2025-04-10 11:52 ` [PATCH net-next v6 04/12] net: usb: lan78xx: move LED DT configuration to helper Oleksij Rempel
@ 2025-04-10 11:52 ` Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 06/12] net: usb: lan78xx: Refactor USB link power configuration into helper Oleksij Rempel
` (6 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:52 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
Move the PHY interrupt acknowledgment logic from lan78xx_link_reset()
to a new helper function lan78xx_phy_int_ack(). This simplifies the
code and prepares for reusing the acknowledgment logic independently
from the full link reset process, such as when using phylink.
No functional change intended.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- this patch is added in v6
---
drivers/net/usb/lan78xx.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 6965013ebc59..b79cb8632671 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -1636,6 +1636,20 @@ static int lan78xx_mac_reset(struct lan78xx_net *dev)
return ret;
}
+/**
+ * lan78xx_phy_int_ack - Acknowledge PHY interrupt
+ * @dev: pointer to the LAN78xx device structure
+ *
+ * This function acknowledges the PHY interrupt by setting the
+ * INT_STS_PHY_INT_ bit in the interrupt status register (INT_STS).
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+static int lan78xx_phy_int_ack(struct lan78xx_net *dev)
+{
+ return lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_);
+}
+
static int lan78xx_link_reset(struct lan78xx_net *dev)
{
struct phy_device *phydev = dev->net->phydev;
@@ -1644,7 +1658,7 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
u32 buf;
/* clear LAN78xx interrupt status */
- ret = lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_);
+ ret = lan78xx_phy_int_ack(dev);
if (unlikely(ret < 0))
return ret;
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 06/12] net: usb: lan78xx: Refactor USB link power configuration into helper
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
` (4 preceding siblings ...)
2025-04-10 11:52 ` [PATCH net-next v6 05/12] net: usb: lan78xx: Extract PHY interrupt acknowledgment " Oleksij Rempel
@ 2025-04-10 11:52 ` Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 07/12] net: usb: lan78xx: Extract flow control configuration to helper Oleksij Rempel
` (5 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:52 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
Move the USB link power configuration logic from lan78xx_link_reset()
to a new helper function lan78xx_configure_usb(). This simplifies the
main link reset path and isolates USB-specific logic.
The new function handles U1/U2 enablement based on Ethernet link speed,
but only for SuperSpeed-capable devices (LAN7800 and LAN7801). LAN7850,
a High-Speed-only device, is explicitly excluded. A warning is logged
if SuperSpeed is reported unexpectedly for LAN7850.
Add a forward declaration for lan78xx_configure_usb() as preparation for
the upcoming phylink conversion, where it will also be used from the
mac_link_up() callback.
Open questions remain:
- Why is the 1000 Mbps configuration split into two steps (U2 disable,
then U1 enable), unlike the single-step config used for 10/100 Mbps?
- U1/U2 behavior appears to depend on proper EEPROM configuration.
There are known devices in the field without EEPROM. Should the driver
enforce safe defaults in such cases?
Due to lack of USB subsystem expertise, no changes were made to this logic
beyond structural refactoring.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- this patch is added in v6
---
drivers/net/usb/lan78xx.c | 90 +++++++++++++++++++++++++--------------
1 file changed, 59 insertions(+), 31 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index b79cb8632671..8d29f9932a42 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -1650,12 +1650,13 @@ static int lan78xx_phy_int_ack(struct lan78xx_net *dev)
return lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_);
}
+static int lan78xx_configure_usb(struct lan78xx_net *dev, int speed);
+
static int lan78xx_link_reset(struct lan78xx_net *dev)
{
struct phy_device *phydev = dev->net->phydev;
struct ethtool_link_ksettings ecmd;
int ladv, radv, ret, link;
- u32 buf;
/* clear LAN78xx interrupt status */
ret = lan78xx_phy_int_ack(dev);
@@ -1681,36 +1682,9 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
phy_ethtool_ksettings_get(phydev, &ecmd);
- if (dev->udev->speed == USB_SPEED_SUPER) {
- if (ecmd.base.speed == 1000) {
- /* disable U2 */
- ret = lan78xx_read_reg(dev, USB_CFG1, &buf);
- if (ret < 0)
- return ret;
- buf &= ~USB_CFG1_DEV_U2_INIT_EN_;
- ret = lan78xx_write_reg(dev, USB_CFG1, buf);
- if (ret < 0)
- return ret;
- /* enable U1 */
- ret = lan78xx_read_reg(dev, USB_CFG1, &buf);
- if (ret < 0)
- return ret;
- buf |= USB_CFG1_DEV_U1_INIT_EN_;
- ret = lan78xx_write_reg(dev, USB_CFG1, buf);
- if (ret < 0)
- return ret;
- } else {
- /* enable U1 & U2 */
- ret = lan78xx_read_reg(dev, USB_CFG1, &buf);
- if (ret < 0)
- return ret;
- buf |= USB_CFG1_DEV_U2_INIT_EN_;
- buf |= USB_CFG1_DEV_U1_INIT_EN_;
- ret = lan78xx_write_reg(dev, USB_CFG1, buf);
- if (ret < 0)
- return ret;
- }
- }
+ ret = lan78xx_configure_usb(dev, ecmd.base.speed);
+ if (ret < 0)
+ return ret;
ladv = phy_read(phydev, MII_ADVERTISE);
if (ladv < 0)
@@ -2522,6 +2496,60 @@ static void lan78xx_remove_irq_domain(struct lan78xx_net *dev)
dev->domain_data.irqdomain = NULL;
}
+/**
+ * lan78xx_configure_usb - Configure USB link power settings
+ * @dev: pointer to the LAN78xx device structure
+ * @speed: negotiated Ethernet link speed (in Mbps)
+ *
+ * This function configures U1/U2 link power management for SuperSpeed
+ * USB devices based on the current Ethernet link speed. It uses the
+ * USB_CFG1 register to enable or disable U1 and U2 low-power states.
+ *
+ * Note: Only LAN7800 and LAN7801 support SuperSpeed (USB 3.x).
+ * LAN7850 is a High-Speed-only (USB 2.0) device and is skipped.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+static int lan78xx_configure_usb(struct lan78xx_net *dev, int speed)
+{
+ u32 mask, val;
+ int ret;
+
+ /* Only configure USB settings for SuperSpeed devices */
+ if (dev->udev->speed != USB_SPEED_SUPER)
+ return 0;
+
+ /* LAN7850 does not support USB 3.x */
+ if (dev->chipid == ID_REV_CHIP_ID_7850_) {
+ netdev_warn_once(dev->net, "Unexpected SuperSpeed for LAN7850 (USB 2.0 only)\n");
+ return 0;
+ }
+
+ switch (speed) {
+ case SPEED_1000:
+ /* Disable U2, enable U1 */
+ ret = lan78xx_update_reg(dev, USB_CFG1,
+ USB_CFG1_DEV_U2_INIT_EN_, 0);
+ if (ret < 0)
+ return ret;
+
+ return lan78xx_update_reg(dev, USB_CFG1,
+ USB_CFG1_DEV_U1_INIT_EN_,
+ USB_CFG1_DEV_U1_INIT_EN_);
+
+ case SPEED_100:
+ case SPEED_10:
+ /* Enable both U1 and U2 */
+ mask = USB_CFG1_DEV_U1_INIT_EN_ | USB_CFG1_DEV_U2_INIT_EN_;
+ val = mask;
+ return lan78xx_update_reg(dev, USB_CFG1, mask, val);
+
+ default:
+ netdev_warn(dev->net, "Unsupported link speed: %d\n", speed);
+ return -EINVAL;
+ }
+}
+
/**
* lan78xx_register_fixed_phy() - Register a fallback fixed PHY
* @dev: LAN78xx device
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 07/12] net: usb: lan78xx: Extract flow control configuration to helper
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
` (5 preceding siblings ...)
2025-04-10 11:52 ` [PATCH net-next v6 06/12] net: usb: lan78xx: Refactor USB link power configuration into helper Oleksij Rempel
@ 2025-04-10 11:52 ` Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 08/12] net: usb: lan78xx: Convert to PHYLINK for improved PHY and MAC management Oleksij Rempel
` (4 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:52 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
Move flow control register configuration from
lan78xx_update_flowcontrol() into a new helper function
lan78xx_configure_flowcontrol(). This separates hardware-specific
programming from policy logic and simplifies the upcoming phylink
integration.
The values used in this initial version of
lan78xx_configure_flowcontrol() are taken over as-is from the original
implementation to avoid functional changes. While they may not be
optimal for all USB and link speed combinations, they are known to work
reliably. Optimization of pause time and thresholds based on runtime
conditions can be done in a separate follow-up patch.
The forward declaration of lan78xx_configure_flowcontrol() will also be
removed later during the phylink conversion.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- this patch is added in v6
---
drivers/net/usb/lan78xx.c | 105 +++++++++++++++++++++++++++++++-------
1 file changed, 87 insertions(+), 18 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 8d29f9932a42..19ac1c280300 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -1554,10 +1554,12 @@ static void lan78xx_set_multicast(struct net_device *netdev)
schedule_work(&pdata->set_multicast);
}
+static int lan78xx_configure_flowcontrol(struct lan78xx_net *dev,
+ bool tx_pause, bool rx_pause);
+
static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex,
u16 lcladv, u16 rmtadv)
{
- u32 flow = 0, fct_flow = 0;
u8 cap;
if (dev->fc_autoneg)
@@ -1565,27 +1567,13 @@ static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex,
else
cap = dev->fc_request_control;
- if (cap & FLOW_CTRL_TX)
- flow |= (FLOW_CR_TX_FCEN_ | 0xFFFF);
-
- if (cap & FLOW_CTRL_RX)
- flow |= FLOW_CR_RX_FCEN_;
-
- if (dev->udev->speed == USB_SPEED_SUPER)
- fct_flow = FLOW_CTRL_THRESHOLD(FLOW_ON_SS, FLOW_OFF_SS);
- else if (dev->udev->speed == USB_SPEED_HIGH)
- fct_flow = FLOW_CTRL_THRESHOLD(FLOW_ON_HS, FLOW_OFF_HS);
-
netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s",
(cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
(cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
- lan78xx_write_reg(dev, FCT_FLOW, fct_flow);
-
- /* threshold value should be set before enabling flow */
- lan78xx_write_reg(dev, FLOW, flow);
-
- return 0;
+ return lan78xx_configure_flowcontrol(dev,
+ cap & FLOW_CTRL_TX,
+ cap & FLOW_CTRL_RX);
}
static void lan78xx_rx_urb_submit_all(struct lan78xx_net *dev);
@@ -2550,6 +2538,87 @@ static int lan78xx_configure_usb(struct lan78xx_net *dev, int speed)
}
}
+/**
+ * lan78xx_configure_flowcontrol - Set MAC and FIFO flow control configuration
+ * @dev: pointer to the LAN78xx device structure
+ * @tx_pause: enable transmission of pause frames
+ * @rx_pause: enable reception of pause frames
+ *
+ * This function configures the LAN78xx flow control settings by writing
+ * to the FLOW and FCT_FLOW registers. The pause time is set to the
+ * maximum allowed value (65535 quanta). FIFO thresholds are selected
+ * based on USB speed.
+ *
+ * The Pause Time field is measured in units of 512-bit times (quanta):
+ * - At 1 Gbps: 1 quanta = 512 ns → max ~33.6 ms pause
+ * - At 100 Mbps: 1 quanta = 5.12 µs → max ~335 ms pause
+ * - At 10 Mbps: 1 quanta = 51.2 µs → max ~3.3 s pause
+ *
+ * Flow control thresholds (FCT_FLOW) are used to trigger pause/resume:
+ * - RXUSED is the number of bytes used in the RX FIFO
+ * - Flow is turned ON when RXUSED ≥ FLOW_ON threshold
+ * - Flow is turned OFF when RXUSED ≤ FLOW_OFF threshold
+ * - Both thresholds are encoded in units of 512 bytes (rounded up)
+ *
+ * Thresholds differ by USB speed because available USB bandwidth
+ * affects how fast packets can be drained from the RX FIFO:
+ * - USB 3.x (SuperSpeed):
+ * FLOW_ON = 9216 bytes → 18 units
+ * FLOW_OFF = 4096 bytes → 8 units
+ * - USB 2.0 (High-Speed):
+ * FLOW_ON = 8704 bytes → 17 units
+ * FLOW_OFF = 1024 bytes → 2 units
+ *
+ * Note: The FCT_FLOW register must be configured before enabling TX pause
+ * (i.e., before setting FLOW_CR_TX_FCEN_), as required by the hardware.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+static int lan78xx_configure_flowcontrol(struct lan78xx_net *dev,
+ bool tx_pause, bool rx_pause)
+{
+ /* Use maximum pause time: 65535 quanta (512-bit times) */
+ const u32 pause_time_quanta = 65535;
+ u32 fct_flow = 0;
+ u32 flow = 0;
+ int ret;
+
+ /* Prepare MAC flow control bits */
+ if (tx_pause)
+ flow |= FLOW_CR_TX_FCEN_ | pause_time_quanta;
+
+ if (rx_pause)
+ flow |= FLOW_CR_RX_FCEN_;
+
+ /* Select RX FIFO thresholds based on USB speed
+ *
+ * FCT_FLOW layout:
+ * bits [6:0] FLOW_ON threshold (RXUSED ≥ ON → assert pause)
+ * bits [14:8] FLOW_OFF threshold (RXUSED ≤ OFF → deassert pause)
+ * thresholds are expressed in units of 512 bytes
+ */
+ switch (dev->udev->speed) {
+ case USB_SPEED_SUPER:
+ fct_flow = FLOW_CTRL_THRESHOLD(FLOW_ON_SS, FLOW_OFF_SS);
+ break;
+ case USB_SPEED_HIGH:
+ fct_flow = FLOW_CTRL_THRESHOLD(FLOW_ON_HS, FLOW_OFF_HS);
+ break;
+ default:
+ netdev_warn(dev->net, "Unsupported USB speed: %d\n",
+ dev->udev->speed);
+ return -EINVAL;
+ }
+
+ /* Step 1: Write FIFO thresholds before enabling pause frames */
+ ret = lan78xx_write_reg(dev, FCT_FLOW, fct_flow);
+ if (ret < 0)
+ return ret;
+
+ /* Step 2: Enable MAC pause functionality */
+ return lan78xx_write_reg(dev, FLOW, flow);
+}
+
/**
* lan78xx_register_fixed_phy() - Register a fallback fixed PHY
* @dev: LAN78xx device
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 08/12] net: usb: lan78xx: Convert to PHYLINK for improved PHY and MAC management
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
` (6 preceding siblings ...)
2025-04-10 11:52 ` [PATCH net-next v6 07/12] net: usb: lan78xx: Extract flow control configuration to helper Oleksij Rempel
@ 2025-04-10 11:52 ` Oleksij Rempel
2025-04-11 5:33 ` Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 09/12] net: usb: lan78xx: Use ethtool_op_get_link to reflect current link status Oleksij Rempel
` (3 subsequent siblings)
11 siblings, 1 reply; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:52 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
Convert the LAN78xx USB Ethernet driver to use the PHYLINK framework for
managing PHY and MAC interactions. This improves consistency with other
network drivers, simplifies pause frame handling, and enables cleaner
suspend/resume support.
Key changes:
- Replace all PHYLIB-based logic with PHYLINK equivalents:
- Replace phy_connect()/phy_disconnect() with phylink_connect_phy()
- Replace phy_start()/phy_stop() with phylink_start()/phylink_stop()
- Replace pauseparam handling with phylink_ethtool_get/set_pauseparam()
- Introduce lan78xx_phylink_setup() to configure PHYLINK
- Add phylink MAC operations:
- lan78xx_mac_config()
- lan78xx_mac_link_up()
- lan78xx_mac_link_down()
- Remove legacy link state handling:
- lan78xx_link_status_change()
- lan78xx_link_reset()
- Handle fixed-link fallback for LAN7801 using phylink_set_fixed_link()
- Replace deprecated flow control handling with phylink-managed logic
Power management:
- Switch suspend/resume paths to use phylink_suspend()/phylink_resume()
- Ensure proper use of rtnl_lock() where required
- Note: full runtime testing of power management is currently limited
due to hardware setup constraints
Note: Conversion of EEE (Energy Efficient Ethernet) handling to the
PHYLINK-managed API will be done in a follow-up patch. For now, the
legacy EEE enable logic is preserved in mac_link_up().
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- move functional interface changes and refactoring to separate patches
- switch suspend/resume to use phylink_suspend/resume with correct locking
- avoid setting phydev->interface manually; rely on phylink negotiation
- remove legacy pause/aneg setup and dead code (e.g. lan78xx_link_status_change)
- EEE conversion postponed to follow-up patch
- note that power management testing is limited due to HW constraints
changes v5:
- merge ethtool pause interface changes to this patch
changes v4:
- add PHYLINK dependency
- remove PHYLIB and FIXED_PHY, both are replaced by PHYLINK
changes v3:
- lan78xx_phy_init: drop phy_suspend()
- lan78xx_phylink_setup: use phy_interface_set_rgmii()
changes v2:
- lan78xx_mac_config: remove unused rgmii_id
- lan78xx_mac_config: PHY_INTERFACE_MODE_RGMII* variants
- lan78xx_mac_config: remove auto-speed and duplex configuration
- lan78xx_phylink_setup: set link_interface to PHY_INTERFACE_MODE_RGMII_ID
instead of PHY_INTERFACE_MODE_NA.
- lan78xx_phy_init: use phylink_set_fixed_link() instead of allocating
fixed PHY.
- lan78xx_configure_usb: move function values to separate variables
20220427_lukas_polling_be_gone_on_lan95xx.cover
---
drivers/net/usb/Kconfig | 3 +-
drivers/net/usb/lan78xx.c | 551 ++++++++++++++++++--------------------
2 files changed, 258 insertions(+), 296 deletions(-)
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 3c360d4f0635..71168e47a9b1 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -115,9 +115,8 @@ config USB_RTL8152
config USB_LAN78XX
tristate "Microchip LAN78XX Based USB Ethernet Adapters"
select MII
- select PHYLIB
+ select PHYLINK
select MICROCHIP_PHY
- select FIXED_PHY
select CRC32
help
This option adds support for Microchip LAN78XX based USB 2
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 19ac1c280300..03d8102026f1 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -6,6 +6,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
+#include <linux/phylink.h>
#include <linux/usb.h>
#include <linux/crc32.h>
#include <linux/signal.h>
@@ -384,7 +385,7 @@ struct skb_data { /* skb->cb is one of these */
#define EVENT_RX_HALT 1
#define EVENT_RX_MEMORY 2
#define EVENT_STS_SPLIT 3
-#define EVENT_LINK_RESET 4
+#define EVENT_PHY_INT_ACK 4
#define EVENT_RX_PAUSED 5
#define EVENT_DEV_WAKING 6
#define EVENT_DEV_ASLEEP 7
@@ -455,7 +456,6 @@ struct lan78xx_net {
unsigned long data[5];
- int link_on;
u8 mdix_ctrl;
u32 chipid;
@@ -463,13 +463,13 @@ struct lan78xx_net {
struct mii_bus *mdiobus;
phy_interface_t interface;
- int fc_autoneg;
- u8 fc_request_control;
-
int delta;
struct statstage stats;
struct irq_domain_data domain_data;
+
+ struct phylink *phylink;
+ struct phylink_config phylink_config;
};
/* use ethtool to change the level for any given device */
@@ -1554,28 +1554,6 @@ static void lan78xx_set_multicast(struct net_device *netdev)
schedule_work(&pdata->set_multicast);
}
-static int lan78xx_configure_flowcontrol(struct lan78xx_net *dev,
- bool tx_pause, bool rx_pause);
-
-static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex,
- u16 lcladv, u16 rmtadv)
-{
- u8 cap;
-
- if (dev->fc_autoneg)
- cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
- else
- cap = dev->fc_request_control;
-
- netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s",
- (cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
- (cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
-
- return lan78xx_configure_flowcontrol(dev,
- cap & FLOW_CTRL_TX,
- cap & FLOW_CTRL_RX);
-}
-
static void lan78xx_rx_urb_submit_all(struct lan78xx_net *dev);
static int lan78xx_mac_reset(struct lan78xx_net *dev)
@@ -1638,75 +1616,6 @@ static int lan78xx_phy_int_ack(struct lan78xx_net *dev)
return lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_);
}
-static int lan78xx_configure_usb(struct lan78xx_net *dev, int speed);
-
-static int lan78xx_link_reset(struct lan78xx_net *dev)
-{
- struct phy_device *phydev = dev->net->phydev;
- struct ethtool_link_ksettings ecmd;
- int ladv, radv, ret, link;
-
- /* clear LAN78xx interrupt status */
- ret = lan78xx_phy_int_ack(dev);
- if (unlikely(ret < 0))
- return ret;
-
- mutex_lock(&phydev->lock);
- phy_read_status(phydev);
- link = phydev->link;
- mutex_unlock(&phydev->lock);
-
- if (!link && dev->link_on) {
- dev->link_on = false;
-
- /* reset MAC */
- ret = lan78xx_mac_reset(dev);
- if (ret < 0)
- return ret;
-
- del_timer(&dev->stat_monitor);
- } else if (link && !dev->link_on) {
- dev->link_on = true;
-
- phy_ethtool_ksettings_get(phydev, &ecmd);
-
- ret = lan78xx_configure_usb(dev, ecmd.base.speed);
- if (ret < 0)
- return ret;
-
- ladv = phy_read(phydev, MII_ADVERTISE);
- if (ladv < 0)
- return ladv;
-
- radv = phy_read(phydev, MII_LPA);
- if (radv < 0)
- return radv;
-
- netif_dbg(dev, link, dev->net,
- "speed: %u duplex: %d anadv: 0x%04x anlpa: 0x%04x",
- ecmd.base.speed, ecmd.base.duplex, ladv, radv);
-
- ret = lan78xx_update_flowcontrol(dev, ecmd.base.duplex, ladv,
- radv);
- if (ret < 0)
- return ret;
-
- if (!timer_pending(&dev->stat_monitor)) {
- dev->delta = 1;
- mod_timer(&dev->stat_monitor,
- jiffies + STAT_UPDATE_TIMER);
- }
-
- lan78xx_rx_urb_submit_all(dev);
-
- local_bh_disable();
- napi_schedule(&dev->napi);
- local_bh_enable();
- }
-
- return 0;
-}
-
/* some work can't be done in tasklets, so we use keventd
*
* NOTE: annoying asymmetry: if it's active, schedule_work() fails,
@@ -1733,7 +1642,7 @@ static void lan78xx_status(struct lan78xx_net *dev, struct urb *urb)
if (intdata & INT_ENP_PHY_INT) {
netif_dbg(dev, link, dev->net, "PHY INTR: 0x%08x\n", intdata);
- lan78xx_defer_kevent(dev, EVENT_LINK_RESET);
+ lan78xx_defer_kevent(dev, EVENT_PHY_INT_ACK);
if (dev->domain_data.phyirq > 0)
generic_handle_irq_safe(dev->domain_data.phyirq);
@@ -2015,63 +1924,16 @@ static void lan78xx_get_pause(struct net_device *net,
struct ethtool_pauseparam *pause)
{
struct lan78xx_net *dev = netdev_priv(net);
- struct phy_device *phydev = net->phydev;
- struct ethtool_link_ksettings ecmd;
-
- phy_ethtool_ksettings_get(phydev, &ecmd);
-
- pause->autoneg = dev->fc_autoneg;
-
- if (dev->fc_request_control & FLOW_CTRL_TX)
- pause->tx_pause = 1;
- if (dev->fc_request_control & FLOW_CTRL_RX)
- pause->rx_pause = 1;
+ phylink_ethtool_get_pauseparam(dev->phylink, pause);
}
static int lan78xx_set_pause(struct net_device *net,
struct ethtool_pauseparam *pause)
{
struct lan78xx_net *dev = netdev_priv(net);
- struct phy_device *phydev = net->phydev;
- struct ethtool_link_ksettings ecmd;
- int ret;
-
- phy_ethtool_ksettings_get(phydev, &ecmd);
-
- if (pause->autoneg && !ecmd.base.autoneg) {
- ret = -EINVAL;
- goto exit;
- }
-
- dev->fc_request_control = 0;
- if (pause->rx_pause)
- dev->fc_request_control |= FLOW_CTRL_RX;
-
- if (pause->tx_pause)
- dev->fc_request_control |= FLOW_CTRL_TX;
-
- if (ecmd.base.autoneg) {
- __ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, };
- u32 mii_adv;
-
- linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
- ecmd.link_modes.advertising);
- linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
- ecmd.link_modes.advertising);
- mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control);
- mii_adv_to_linkmode_adv_t(fc, mii_adv);
- linkmode_or(ecmd.link_modes.advertising, fc,
- ecmd.link_modes.advertising);
- phy_ethtool_ksettings_set(phydev, &ecmd);
- }
-
- dev->fc_autoneg = pause->autoneg;
-
- ret = 0;
-exit:
- return ret;
+ return phylink_ethtool_set_pauseparam(dev->phylink, pause);
}
static int lan78xx_get_regs_len(struct net_device *netdev)
@@ -2332,26 +2194,6 @@ static void lan78xx_remove_mdio(struct lan78xx_net *dev)
mdiobus_free(dev->mdiobus);
}
-static void lan78xx_link_status_change(struct net_device *net)
-{
- struct lan78xx_net *dev = netdev_priv(net);
- struct phy_device *phydev = net->phydev;
- u32 data;
- int ret;
-
- ret = lan78xx_read_reg(dev, MAC_CR, &data);
- if (ret < 0)
- return;
-
- if (phydev->enable_tx_lpi)
- data |= MAC_CR_EEE_EN_;
- else
- data &= ~MAC_CR_EEE_EN_;
- lan78xx_write_reg(dev, MAC_CR, data);
-
- phy_print_status(phydev);
-}
-
static int irq_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
@@ -2484,6 +2326,75 @@ static void lan78xx_remove_irq_domain(struct lan78xx_net *dev)
dev->domain_data.irqdomain = NULL;
}
+static void lan78xx_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct net_device *net = to_net_dev(config->dev);
+ struct lan78xx_net *dev = netdev_priv(net);
+ u32 mac_cr = 0;
+ int ret;
+
+ /* Check if the mode is supported */
+ if (mode != MLO_AN_FIXED && mode != MLO_AN_PHY) {
+ netdev_err(net, "Unsupported negotiation mode: %u\n", mode);
+ return;
+ }
+
+ switch (state->interface) {
+ case PHY_INTERFACE_MODE_GMII:
+ mac_cr |= MAC_CR_GMII_EN_;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ break;
+ default:
+ netdev_warn(net, "Unsupported interface mode: %d\n",
+ state->interface);
+ return;
+ }
+
+ ret = lan78xx_update_reg(dev, MAC_CR, MAC_CR_GMII_EN_, mac_cr);
+ if (ret < 0)
+ netdev_err(net, "Failed to config MAC with error %pe\n",
+ ERR_PTR(ret));
+}
+
+static void lan78xx_mac_link_down(struct phylink_config *config,
+ unsigned int mode, phy_interface_t interface)
+{
+ struct net_device *net = to_net_dev(config->dev);
+ struct lan78xx_net *dev = netdev_priv(net);
+ int ret;
+
+ /* MAC reset will not de-assert TXEN/RXEN, we need to stop them
+ * manually before reset. TX and RX should be disabled before running
+ * link_up sequence.
+ */
+ ret = lan78xx_stop_tx_path(dev);
+ if (ret < 0)
+ goto link_down_fail;
+
+ ret = lan78xx_stop_rx_path(dev);
+ if (ret < 0)
+ goto link_down_fail;
+
+ /* MAC reset seems to not affect MAC configuration, no idea if it is
+ * really needed, but it was done in previous driver version. So, leave
+ * it here.
+ */
+ ret = lan78xx_mac_reset(dev);
+ if (ret < 0)
+ goto link_down_fail;
+
+ return;
+
+link_down_fail:
+ netdev_err(dev->net, "Failed to set MAC down with error %pe\n",
+ ERR_PTR(ret));
+}
+
/**
* lan78xx_configure_usb - Configure USB link power settings
* @dev: pointer to the LAN78xx device structure
@@ -2619,28 +2530,100 @@ static int lan78xx_configure_flowcontrol(struct lan78xx_net *dev,
return lan78xx_write_reg(dev, FLOW, flow);
}
+static void lan78xx_mac_link_up(struct phylink_config *config,
+ struct phy_device *phy,
+ unsigned int mode, phy_interface_t interface,
+ int speed, int duplex,
+ bool tx_pause, bool rx_pause)
+{
+ struct net_device *net = to_net_dev(config->dev);
+ struct lan78xx_net *dev = netdev_priv(net);
+ u32 mac_cr = 0;
+ int ret;
+
+ switch (speed) {
+ case SPEED_1000:
+ mac_cr |= MAC_CR_SPEED_1000_;
+ break;
+ case SPEED_100:
+ mac_cr |= MAC_CR_SPEED_100_;
+ break;
+ case SPEED_10:
+ mac_cr |= MAC_CR_SPEED_10_;
+ break;
+ default:
+ netdev_err(dev->net, "Unsupported speed %d\n", speed);
+ return;
+ }
+
+ if (duplex == DUPLEX_FULL)
+ mac_cr |= MAC_CR_FULL_DUPLEX_;
+
+ /* make sure TXEN and RXEN are disabled before reconfiguring MAC */
+ ret = lan78xx_update_reg(dev, MAC_CR, MAC_CR_SPEED_MASK_ |
+ MAC_CR_FULL_DUPLEX_ | MAC_CR_EEE_EN_, mac_cr);
+ if (ret < 0)
+ goto link_up_fail;
+
+ ret = lan78xx_configure_flowcontrol(dev, tx_pause, rx_pause);
+ if (ret < 0)
+ goto link_up_fail;
+
+ ret = lan78xx_configure_usb(dev, speed);
+ if (ret < 0)
+ goto link_up_fail;
+
+ lan78xx_rx_urb_submit_all(dev);
+
+ ret = lan78xx_flush_rx_fifo(dev);
+ if (ret < 0)
+ goto link_up_fail;
+
+ ret = lan78xx_flush_tx_fifo(dev);
+ if (ret < 0)
+ goto link_up_fail;
+
+ ret = lan78xx_start_tx_path(dev);
+ if (ret < 0)
+ goto link_up_fail;
+
+ ret = lan78xx_start_rx_path(dev);
+ if (ret < 0)
+ goto link_up_fail;
+
+ return;
+link_up_fail:
+ netdev_err(dev->net, "Failed to set MAC up with error %pe\n",
+ ERR_PTR(ret));
+}
+
+static const struct phylink_mac_ops lan78xx_phylink_mac_ops = {
+ .mac_config = lan78xx_mac_config,
+ .mac_link_down = lan78xx_mac_link_down,
+ .mac_link_up = lan78xx_mac_link_up,
+};
+
/**
- * lan78xx_register_fixed_phy() - Register a fallback fixed PHY
+ * lan78xx_set_fixed_link() - Set fixed link configuration for LAN7801
* @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.
+ * Use fixed link configuration 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)
+static int lan78xx_set_fixed_link(struct lan78xx_net *dev)
{
- struct fixed_phy_status fphy_status = {
- .link = 1,
+ struct phylink_link_state state = {
.speed = SPEED_1000,
.duplex = DUPLEX_FULL,
};
netdev_info(dev->net,
- "No PHY found on LAN7801 – registering fixed PHY (e.g. EVB-KSZ9897-1)\n");
+ "No PHY found on LAN7801 – using fixed link instead (e.g. EVB-KSZ9897-1)\n");
- return fixed_phy_register(PHY_POLL, &fphy_status, NULL);
+ return phylink_set_fixed_link(dev->phylink, &state);
}
/**
@@ -2676,7 +2659,7 @@ static struct phy_device *lan78xx_get_phy(struct lan78xx_net *dev)
dev->interface = PHY_INTERFACE_MODE_RGMII;
/* No PHY found – fallback to fixed PHY (e.g. KSZ switch board) */
- return lan78xx_register_fixed_phy(dev);
+ return NULL;
case ID_REV_CHIP_ID_7800_:
case ID_REV_CHIP_ID_7850_:
@@ -2803,20 +2786,63 @@ static int lan78xx_configure_leds_from_dt(struct lan78xx_net *dev,
return lan78xx_write_reg(dev, HW_CFG, reg);
}
+static int lan78xx_phylink_setup(struct lan78xx_net *dev)
+{
+ struct phylink_config *pc = &dev->phylink_config;
+ struct phylink *phylink;
+
+ pc->dev = &dev->net->dev;
+ pc->type = PHYLINK_NETDEV;
+ pc->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10 |
+ MAC_100 | MAC_1000FD;
+ pc->mac_managed_pm = true;
+
+ if (dev->chipid == ID_REV_CHIP_ID_7801_)
+ phy_interface_set_rgmii(pc->supported_interfaces);
+ else
+ __set_bit(PHY_INTERFACE_MODE_GMII, pc->supported_interfaces);
+
+ phylink = phylink_create(pc, dev->net->dev.fwnode,
+ dev->interface, &lan78xx_phylink_mac_ops);
+ if (IS_ERR(phylink))
+ return PTR_ERR(phylink);
+
+ dev->phylink = phylink;
+
+ return 0;
+}
+
static int lan78xx_phy_init(struct lan78xx_net *dev)
{
- __ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, };
- int ret;
- u32 mii_adv;
struct phy_device *phydev;
+ int ret;
phydev = lan78xx_get_phy(dev);
+ /* phydev can be NULL if no PHY is found and the chip is LAN7801,
+ * which will use a fixed link later.
+ * If an error occurs, return the error code immediately.
+ */
if (IS_ERR(phydev))
return PTR_ERR(phydev);
+ ret = lan78xx_phylink_setup(dev);
+ if (ret < 0)
+ return ret;
+
+ /* If no PHY is found, set up a fixed link. It is very specific to
+ * the LAN7801 and 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.
+ */
+ if (!phydev) {
+ ret = lan78xx_set_fixed_link(dev);
+ if (ret < 0)
+ return ret;
+ }
+
ret = lan78xx_mac_prepare_for_phy(dev);
if (ret < 0)
- goto free_phy;
+ return ret;
/* if phyirq is not set, use polling mode in phylib */
if (dev->domain_data.phyirq > 0)
@@ -2825,56 +2851,25 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
phydev->irq = PHY_POLL;
netdev_dbg(dev->net, "phydev->irq = %d\n", phydev->irq);
- /* set to AUTOMDIX */
- phydev->mdix = ETH_TP_MDI_AUTO;
-
- ret = phy_connect_direct(dev->net, phydev,
- lan78xx_link_status_change,
- dev->interface);
+ ret = phylink_connect_phy(dev->phylink, phydev);
if (ret) {
- netdev_err(dev->net, "can't attach PHY to %s\n",
- dev->mdiobus->id);
- if (dev->chipid == ID_REV_CHIP_ID_7801_) {
- if (phy_is_pseudo_fixed_link(phydev)) {
- fixed_phy_unregister(phydev);
- phy_device_free(phydev);
- }
- }
- return -EIO;
+ netdev_err(dev->net, "can't attach PHY to %s, error %pe\n",
+ dev->mdiobus->id, ERR_PTR(ret));
+ return ret;
}
- /* MAC doesn't support 1000T Half */
- phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
-
- /* support both flow controls */
- dev->fc_request_control = (FLOW_CTRL_RX | FLOW_CTRL_TX);
- linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
- phydev->advertising);
- linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
- phydev->advertising);
- mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control);
- mii_adv_to_linkmode_adv_t(fc, mii_adv);
- linkmode_or(phydev->advertising, fc, phydev->advertising);
-
phy_support_eee(phydev);
- ret = lan78xx_configure_leds_from_dt(dev, phydev);
- if (ret)
- goto free_phy;
-
- genphy_config_aneg(phydev);
-
- dev->fc_autoneg = phydev->autoneg;
-
- return 0;
+ return lan78xx_configure_leds_from_dt(dev, phydev);
+}
-free_phy:
- if (phy_is_pseudo_fixed_link(phydev)) {
- fixed_phy_unregister(phydev);
- phy_device_free(phydev);
+static void lan78xx_phy_uninit(struct lan78xx_net *dev)
+{
+ if (dev->phylink) {
+ phylink_disconnect_phy(dev->phylink);
+ phylink_destroy(dev->phylink);
+ dev->phylink = NULL;
}
-
- return ret;
}
static int lan78xx_set_rx_max_frame_length(struct lan78xx_net *dev, int size)
@@ -3213,7 +3208,6 @@ static int lan78xx_reset(struct lan78xx_net *dev)
unsigned long timeout;
int ret;
u32 buf;
- u8 sig;
ret = lan78xx_read_reg(dev, HW_CFG, &buf);
if (ret < 0)
@@ -3370,22 +3364,12 @@ static int lan78xx_reset(struct lan78xx_net *dev)
if (ret < 0)
return ret;
+ buf &= ~(MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_);
+
/* LAN7801 only has RGMII mode */
- if (dev->chipid == ID_REV_CHIP_ID_7801_) {
+ if (dev->chipid == ID_REV_CHIP_ID_7801_)
buf &= ~MAC_CR_GMII_EN_;
- /* Enable Auto Duplex and Auto speed */
- buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
- }
- if (dev->chipid == ID_REV_CHIP_ID_7800_ ||
- dev->chipid == ID_REV_CHIP_ID_7850_) {
- ret = lan78xx_read_raw_eeprom(dev, 0, 1, &sig);
- if (!ret && sig != EEPROM_INDICATOR) {
- /* Implies there is no external eeprom. Set mac speed */
- netdev_info(dev->net, "No External EEPROM. Setting MAC Speed\n");
- buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
- }
- }
ret = lan78xx_write_reg(dev, MAC_CR, buf);
if (ret < 0)
return ret;
@@ -3435,9 +3419,11 @@ static int lan78xx_open(struct net_device *net)
mutex_lock(&dev->dev_mutex);
- phy_start(net->phydev);
+ lan78xx_init_stats(dev);
+
+ napi_enable(&dev->napi);
- netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
+ set_bit(EVENT_DEV_OPEN, &dev->flags);
/* for Link Check */
if (dev->urb_intr) {
@@ -3449,31 +3435,9 @@ static int lan78xx_open(struct net_device *net)
}
}
- ret = lan78xx_flush_rx_fifo(dev);
- if (ret < 0)
- goto done;
- ret = lan78xx_flush_tx_fifo(dev);
- if (ret < 0)
- goto done;
-
- ret = lan78xx_start_tx_path(dev);
- if (ret < 0)
- goto done;
- ret = lan78xx_start_rx_path(dev);
- if (ret < 0)
- goto done;
-
- lan78xx_init_stats(dev);
-
- set_bit(EVENT_DEV_OPEN, &dev->flags);
+ phylink_start(dev->phylink);
netif_start_queue(net);
-
- dev->link_on = false;
-
- napi_enable(&dev->napi);
-
- lan78xx_defer_kevent(dev, EVENT_LINK_RESET);
done:
mutex_unlock(&dev->dev_mutex);
@@ -3541,12 +3505,7 @@ static int lan78xx_stop(struct net_device *net)
net->stats.rx_packets, net->stats.tx_packets,
net->stats.rx_errors, net->stats.tx_errors);
- /* ignore errors that occur stopping the Tx and Rx data paths */
- lan78xx_stop_tx_path(dev);
- lan78xx_stop_rx_path(dev);
-
- if (net->phydev)
- phy_stop(net->phydev);
+ phylink_stop(dev->phylink);
usb_kill_urb(dev->urb_intr);
@@ -3556,7 +3515,7 @@ static int lan78xx_stop(struct net_device *net)
*/
clear_bit(EVENT_TX_HALT, &dev->flags);
clear_bit(EVENT_RX_HALT, &dev->flags);
- clear_bit(EVENT_LINK_RESET, &dev->flags);
+ clear_bit(EVENT_PHY_INT_ACK, &dev->flags);
clear_bit(EVENT_STAT_UPDATE, &dev->flags);
cancel_delayed_work_sync(&dev->wq);
@@ -4480,14 +4439,14 @@ static void lan78xx_delayedwork(struct work_struct *work)
}
}
- if (test_bit(EVENT_LINK_RESET, &dev->flags)) {
+ if (test_bit(EVENT_PHY_INT_ACK, &dev->flags)) {
int ret = 0;
- clear_bit(EVENT_LINK_RESET, &dev->flags);
- if (lan78xx_link_reset(dev) < 0) {
- netdev_info(dev->net, "link reset failed (%d)\n",
- ret);
- }
+ clear_bit(EVENT_PHY_INT_ACK, &dev->flags);
+ ret = lan78xx_phy_int_ack(dev);
+ if (ret)
+ netdev_info(dev->net, "PHY INT ack failed (%pe)\n",
+ ERR_PTR(ret));
}
if (test_bit(EVENT_STAT_UPDATE, &dev->flags)) {
@@ -4561,32 +4520,29 @@ static void lan78xx_disconnect(struct usb_interface *intf)
struct lan78xx_net *dev;
struct usb_device *udev;
struct net_device *net;
- struct phy_device *phydev;
dev = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
if (!dev)
return;
- netif_napi_del(&dev->napi);
-
udev = interface_to_usbdev(intf);
net = dev->net;
+ rtnl_lock();
+ phylink_stop(dev->phylink);
+ phylink_disconnect_phy(dev->phylink);
+ rtnl_unlock();
+
+ netif_napi_del(&dev->napi);
+
unregister_netdev(net);
timer_shutdown_sync(&dev->stat_monitor);
set_bit(EVENT_DEV_DISCONNECT, &dev->flags);
cancel_delayed_work_sync(&dev->wq);
- phydev = net->phydev;
-
- phy_disconnect(net->phydev);
-
- if (phy_is_pseudo_fixed_link(phydev)) {
- fixed_phy_unregister(phydev);
- phy_device_free(phydev);
- }
+ phylink_destroy(dev->phylink);
usb_scuttle_anchored_urbs(&dev->deferred);
@@ -4670,7 +4626,6 @@ static int lan78xx_probe(struct usb_interface *intf,
goto out1;
}
- /* netdev_printk() needs this */
SET_NETDEV_DEV(netdev, &intf->dev);
dev = netdev_priv(netdev);
@@ -4784,12 +4739,12 @@ static int lan78xx_probe(struct usb_interface *intf,
ret = lan78xx_phy_init(dev);
if (ret < 0)
- goto free_urbs;
+ goto phy_uninit;
ret = register_netdev(netdev);
if (ret != 0) {
netif_err(dev, probe, netdev, "couldn't register the device\n");
- goto out8;
+ goto phy_uninit;
}
usb_set_intfdata(intf, dev);
@@ -4804,8 +4759,8 @@ static int lan78xx_probe(struct usb_interface *intf,
return 0;
-out8:
- phy_disconnect(netdev->phydev);
+phy_uninit:
+ lan78xx_phy_uninit(dev);
free_urbs:
usb_free_urb(dev->urb_intr);
out5:
@@ -5140,6 +5095,10 @@ static int lan78xx_suspend(struct usb_interface *intf, pm_message_t message)
spin_unlock_irq(&dev->txq.lock);
}
+ rtnl_lock();
+ phylink_suspend(dev->phylink, false);
+ rtnl_unlock();
+
/* stop RX */
ret = lan78xx_stop_rx_path(dev);
if (ret < 0)
@@ -5367,11 +5326,15 @@ static int lan78xx_reset_resume(struct usb_interface *intf)
if (ret < 0)
return ret;
- phy_start(dev->net->phydev);
-
ret = lan78xx_resume(intf);
+ if (ret < 0)
+ return ret;
- return ret;
+ rtnl_lock();
+ phylink_resume(dev->phylink);
+ rtnl_unlock();
+
+ return 0;
}
static const struct usb_device_id products[] = {
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 09/12] net: usb: lan78xx: Use ethtool_op_get_link to reflect current link status
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
` (7 preceding siblings ...)
2025-04-10 11:52 ` [PATCH net-next v6 08/12] net: usb: lan78xx: Convert to PHYLINK for improved PHY and MAC management Oleksij Rempel
@ 2025-04-10 11:52 ` Oleksij Rempel
2025-04-10 11:53 ` [PATCH net-next v6 10/12] net: usb: lan78xx: port link settings to phylink API Oleksij Rempel
` (2 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:52 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, Maxime Chevallier, kernel, linux-kernel, netdev,
UNGLinuxDriver, Phil Elwell, Simon Horman
Replace the custom lan78xx_get_link implementation with the standard
ethtool_op_get_link helper, which uses netif_carrier_ok to reflect
the current link status accurately.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
---
drivers/net/usb/lan78xx.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 03d8102026f1..ee01f021fa3a 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -1839,18 +1839,6 @@ static int lan78xx_set_eee(struct net_device *net, struct ethtool_keee *edata)
return ret;
}
-static u32 lan78xx_get_link(struct net_device *net)
-{
- u32 link;
-
- mutex_lock(&net->phydev->lock);
- phy_read_status(net->phydev);
- link = net->phydev->link;
- mutex_unlock(&net->phydev->lock);
-
- return link;
-}
-
static void lan78xx_get_drvinfo(struct net_device *net,
struct ethtool_drvinfo *info)
{
@@ -1970,7 +1958,7 @@ lan78xx_get_regs(struct net_device *netdev, struct ethtool_regs *regs,
}
static const struct ethtool_ops lan78xx_ethtool_ops = {
- .get_link = lan78xx_get_link,
+ .get_link = ethtool_op_get_link,
.nway_reset = phy_ethtool_nway_reset,
.get_drvinfo = lan78xx_get_drvinfo,
.get_msglevel = lan78xx_get_msglevel,
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 10/12] net: usb: lan78xx: port link settings to phylink API
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
` (8 preceding siblings ...)
2025-04-10 11:52 ` [PATCH net-next v6 09/12] net: usb: lan78xx: Use ethtool_op_get_link to reflect current link status Oleksij Rempel
@ 2025-04-10 11:53 ` Oleksij Rempel
2025-04-10 11:53 ` [PATCH net-next v6 11/12] net: usb: lan78xx: Integrate EEE support with phylink LPI API Oleksij Rempel
2025-04-10 11:53 ` [PATCH net-next v6 12/12] net: usb: lan78xx: remove unused struct members Oleksij Rempel
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:53 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, Maxime Chevallier, kernel, linux-kernel, netdev,
UNGLinuxDriver, Phil Elwell, Simon Horman
Refactor lan78xx_get_link_ksettings and lan78xx_set_link_ksettings to
use the phylink API (phylink_ethtool_ksettings_get and
phylink_ethtool_ksettings_set) instead of directly interfacing with the
PHY. This change simplifies the code and ensures better integration with
the phylink framework for link management.
Additionally, the explicit calls to usb_autopm_get_interface() and
usb_autopm_put_interface() have been removed. These were originally
needed to manage USB power management during register accesses. However,
lan78xx_mdiobus_read() and lan78xx_mdiobus_write() already handle USB
auto power management internally, ensuring that the interface remains
active when necessary. Since there are no other direct register accesses
in these functions that require explicit power management handling, the
extra calls have become redundant and are no longer needed.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
---
changes v4:
- add explanation why we do not care about usb_autopm in this functions
---
drivers/net/usb/lan78xx.c | 34 ++--------------------------------
1 file changed, 2 insertions(+), 32 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index ee01f021fa3a..680b8efa086a 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -1866,46 +1866,16 @@ static int lan78xx_get_link_ksettings(struct net_device *net,
struct ethtool_link_ksettings *cmd)
{
struct lan78xx_net *dev = netdev_priv(net);
- struct phy_device *phydev = net->phydev;
- int ret;
-
- ret = usb_autopm_get_interface(dev->intf);
- if (ret < 0)
- return ret;
- phy_ethtool_ksettings_get(phydev, cmd);
-
- usb_autopm_put_interface(dev->intf);
-
- return ret;
+ return phylink_ethtool_ksettings_get(dev->phylink, cmd);
}
static int lan78xx_set_link_ksettings(struct net_device *net,
const struct ethtool_link_ksettings *cmd)
{
struct lan78xx_net *dev = netdev_priv(net);
- struct phy_device *phydev = net->phydev;
- int ret = 0;
- int temp;
-
- ret = usb_autopm_get_interface(dev->intf);
- if (ret < 0)
- return ret;
-
- /* change speed & duplex */
- ret = phy_ethtool_ksettings_set(phydev, cmd);
- if (!cmd->base.autoneg) {
- /* force link down */
- temp = phy_read(phydev, MII_BMCR);
- phy_write(phydev, MII_BMCR, temp | BMCR_LOOPBACK);
- mdelay(1);
- phy_write(phydev, MII_BMCR, temp);
- }
-
- usb_autopm_put_interface(dev->intf);
-
- return ret;
+ return phylink_ethtool_ksettings_set(dev->phylink, cmd);
}
static void lan78xx_get_pause(struct net_device *net,
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 11/12] net: usb: lan78xx: Integrate EEE support with phylink LPI API
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
` (9 preceding siblings ...)
2025-04-10 11:53 ` [PATCH net-next v6 10/12] net: usb: lan78xx: port link settings to phylink API Oleksij Rempel
@ 2025-04-10 11:53 ` Oleksij Rempel
2025-04-10 11:53 ` [PATCH net-next v6 12/12] net: usb: lan78xx: remove unused struct members Oleksij Rempel
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:53 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
Refactor Energy-Efficient Ethernet (EEE) support in the LAN78xx driver to
fully integrate with the phylink Low Power Idle (LPI) API. This includes:
- Replacing direct calls to `phy_ethtool_get_eee` and `phy_ethtool_set_eee`
with `phylink_ethtool_get_eee` and `phylink_ethtool_set_eee`.
- Implementing `.mac_enable_tx_lpi` and `.mac_disable_tx_lpi` to control
LPI transitions via phylink.
- Configuring `lpi_timer_default` to align with recommended values from
LAN7800 documentation.
- ensure EEE is disabled on controller reset
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- clarify in lan78xx_mac_eee_enable() comment that MAC_CR_EEE_EN can be
modified without disabling TX/RX.
I can't recall where the requirement to disable TX/RX came from;
may have confused it with nearby MAC_CR bits that require this
kind of configuration
changes v5:
- remove redundant error prints
changes v2:
- use latest PHYlink TX_LPI API
---
drivers/net/usb/lan78xx.c | 123 ++++++++++++++++++++++++--------------
1 file changed, 79 insertions(+), 44 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 680b8efa086a..053c7a31d65c 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -1789,54 +1789,15 @@ static int lan78xx_set_wol(struct net_device *netdev,
static int lan78xx_get_eee(struct net_device *net, struct ethtool_keee *edata)
{
struct lan78xx_net *dev = netdev_priv(net);
- struct phy_device *phydev = net->phydev;
- int ret;
- u32 buf;
-
- ret = usb_autopm_get_interface(dev->intf);
- if (ret < 0)
- return ret;
-
- ret = phy_ethtool_get_eee(phydev, edata);
- if (ret < 0)
- goto exit;
- ret = lan78xx_read_reg(dev, MAC_CR, &buf);
- if (buf & MAC_CR_EEE_EN_) {
- /* EEE_TX_LPI_REQ_DLY & tx_lpi_timer are same uSec unit */
- ret = lan78xx_read_reg(dev, EEE_TX_LPI_REQ_DLY, &buf);
- edata->tx_lpi_timer = buf;
- } else {
- edata->tx_lpi_timer = 0;
- }
-
- ret = 0;
-exit:
- usb_autopm_put_interface(dev->intf);
-
- return ret;
+ return phylink_ethtool_get_eee(dev->phylink, edata);
}
static int lan78xx_set_eee(struct net_device *net, struct ethtool_keee *edata)
{
struct lan78xx_net *dev = netdev_priv(net);
- int ret;
- u32 buf;
-
- ret = usb_autopm_get_interface(dev->intf);
- if (ret < 0)
- return ret;
- ret = phy_ethtool_set_eee(net->phydev, edata);
- if (ret < 0)
- goto out;
-
- buf = (u32)edata->tx_lpi_timer;
- ret = lan78xx_write_reg(dev, EEE_TX_LPI_REQ_DLY, buf);
-out:
- usb_autopm_put_interface(dev->intf);
-
- return ret;
+ return phylink_ethtool_set_eee(dev->phylink, edata);
}
static void lan78xx_get_drvinfo(struct net_device *net,
@@ -2555,10 +2516,62 @@ static void lan78xx_mac_link_up(struct phylink_config *config,
ERR_PTR(ret));
}
+/**
+ * lan78xx_mac_eee_enable - Enable or disable MAC-side EEE support
+ * @dev: LAN78xx device
+ * @enable: true to enable EEE, false to disable
+ *
+ * This function sets or clears the MAC_CR_EEE_EN_ bit to control Energy
+ * Efficient Ethernet (EEE) operation. According to current understanding
+ * of the LAN7800 documentation, this bit can be modified while TX and RX
+ * are enabled. No explicit requirement was found to disable data paths
+ * before changing this bit.
+ *
+ * Return: 0 on success or a negative error code
+ */
+static int lan78xx_mac_eee_enable(struct lan78xx_net *dev, bool enable)
+{
+ u32 mac_cr = 0;
+
+ if (enable)
+ mac_cr |= MAC_CR_EEE_EN_;
+
+ return lan78xx_update_reg(dev, MAC_CR, MAC_CR_EEE_EN_, mac_cr);
+}
+
+static void lan78xx_mac_disable_tx_lpi(struct phylink_config *config)
+{
+ struct net_device *net = to_net_dev(config->dev);
+ struct lan78xx_net *dev = netdev_priv(net);
+
+ lan78xx_mac_eee_enable(dev, false);
+}
+
+static int lan78xx_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
+ bool tx_clk_stop)
+{
+ struct net_device *net = to_net_dev(config->dev);
+ struct lan78xx_net *dev = netdev_priv(net);
+ int ret;
+
+ /* Software should only change this field when Energy Efficient
+ * Ethernet Enable (EEEEN) is cleared. We ensure that by clearing
+ * EEEEN during probe, and phylink itself guarantees that
+ * mac_disable_tx_lpi() will have been previously called.
+ */
+ ret = lan78xx_write_reg(dev, EEE_TX_LPI_REQ_DLY, timer);
+ if (ret < 0)
+ return ret;
+
+ return lan78xx_mac_eee_enable(dev, true);
+}
+
static const struct phylink_mac_ops lan78xx_phylink_mac_ops = {
.mac_config = lan78xx_mac_config,
.mac_link_down = lan78xx_mac_link_down,
.mac_link_up = lan78xx_mac_link_up,
+ .mac_disable_tx_lpi = lan78xx_mac_disable_tx_lpi,
+ .mac_enable_tx_lpi = lan78xx_mac_enable_tx_lpi,
};
/**
@@ -2754,12 +2767,36 @@ static int lan78xx_phylink_setup(struct lan78xx_net *dev)
pc->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10 |
MAC_100 | MAC_1000FD;
pc->mac_managed_pm = true;
+ pc->lpi_capabilities = MAC_100FD | MAC_1000FD;
+ /*
+ * Default TX LPI (Low Power Idle) request delay count is set to 50us.
+ *
+ * Source: LAN7800 Documentation, DS00001992H, Section 15.1.57, Page 204.
+ *
+ * Reasoning:
+ * According to the application note in the LAN7800 documentation, a
+ * zero delay may negatively impact the TX data path’s ability to
+ * support Gigabit operation. A value of 50us is recommended as a
+ * reasonable default when the part operates at Gigabit speeds,
+ * balancing stability and power efficiency in EEE mode. This delay can
+ * be increased based on performance testing, as EEE is designed for
+ * scenarios with mostly idle links and occasional bursts of full
+ * bandwidth transmission. The goal is to ensure reliable Gigabit
+ * performance without overly aggressive power optimization during
+ * inactive periods.
+ */
+ pc->lpi_timer_default = 50;
+ pc->eee_enabled_default = true;
if (dev->chipid == ID_REV_CHIP_ID_7801_)
phy_interface_set_rgmii(pc->supported_interfaces);
else
__set_bit(PHY_INTERFACE_MODE_GMII, pc->supported_interfaces);
+ memcpy(dev->phylink_config.lpi_interfaces,
+ dev->phylink_config.supported_interfaces,
+ sizeof(dev->phylink_config.lpi_interfaces));
+
phylink = phylink_create(pc, dev->net->dev.fwnode,
dev->interface, &lan78xx_phylink_mac_ops);
if (IS_ERR(phylink))
@@ -2816,8 +2853,6 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
return ret;
}
- phy_support_eee(phydev);
-
return lan78xx_configure_leds_from_dt(dev, phydev);
}
@@ -3322,7 +3357,7 @@ static int lan78xx_reset(struct lan78xx_net *dev)
if (ret < 0)
return ret;
- buf &= ~(MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_);
+ buf &= ~(MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_ | MAC_CR_EEE_EN_);
/* LAN7801 only has RGMII mode */
if (dev->chipid == ID_REV_CHIP_ID_7801_)
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next v6 12/12] net: usb: lan78xx: remove unused struct members
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
` (10 preceding siblings ...)
2025-04-10 11:53 ` [PATCH net-next v6 11/12] net: usb: lan78xx: Integrate EEE support with phylink LPI API Oleksij Rempel
@ 2025-04-10 11:53 ` Oleksij Rempel
11 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-10 11:53 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: Oleksij Rempel, kernel, linux-kernel, netdev, UNGLinuxDriver,
Phil Elwell, Maxime Chevallier, Simon Horman
Remove unused members from struct lan78xx_net, including:
driver_priv
suspend_count
mdix_ctrl
These fields are no longer used in the driver and can be safely removed
as part of a cleanup.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v6:
- drop only those fields not already removed in previous patches
- align patch structure with review feedback from Russell King
---
drivers/net/usb/lan78xx.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 053c7a31d65c..8868f4a19760 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -414,7 +414,6 @@ struct lan78xx_net {
struct net_device *net;
struct usb_device *udev;
struct usb_interface *intf;
- void *driver_priv;
unsigned int tx_pend_data_len;
size_t n_tx_urbs;
@@ -449,15 +448,12 @@ struct lan78xx_net {
unsigned long flags;
wait_queue_head_t *wait;
- unsigned char suspend_count;
unsigned int maxpacket;
struct timer_list stat_monitor;
unsigned long data[5];
- u8 mdix_ctrl;
-
u32 chipid;
u32 chiprev;
struct mii_bus *mdiobus;
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH net-next v6 08/12] net: usb: lan78xx: Convert to PHYLINK for improved PHY and MAC management
2025-04-10 11:52 ` [PATCH net-next v6 08/12] net: usb: lan78xx: Convert to PHYLINK for improved PHY and MAC management Oleksij Rempel
@ 2025-04-11 5:33 ` Oleksij Rempel
0 siblings, 0 replies; 14+ messages in thread
From: Oleksij Rempel @ 2025-04-11 5:33 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Woojung Huh, Andrew Lunn, Russell King, Thangaraj Samynathan,
Rengarajan Sundararajan
Cc: kernel, linux-kernel, netdev, UNGLinuxDriver, Phil Elwell,
Maxime Chevallier, Simon Horman
This patch fails to apply on very latest net-next. I'll send new version
soon.
On Thu, Apr 10, 2025 at 01:52:58PM +0200, Oleksij Rempel wrote:
> Convert the LAN78xx USB Ethernet driver to use the PHYLINK framework for
> managing PHY and MAC interactions. This improves consistency with other
> network drivers, simplifies pause frame handling, and enables cleaner
> suspend/resume support.
>
> Key changes:
> - Replace all PHYLIB-based logic with PHYLINK equivalents:
> - Replace phy_connect()/phy_disconnect() with phylink_connect_phy()
> - Replace phy_start()/phy_stop() with phylink_start()/phylink_stop()
> - Replace pauseparam handling with phylink_ethtool_get/set_pauseparam()
> - Introduce lan78xx_phylink_setup() to configure PHYLINK
> - Add phylink MAC operations:
> - lan78xx_mac_config()
> - lan78xx_mac_link_up()
> - lan78xx_mac_link_down()
> - Remove legacy link state handling:
> - lan78xx_link_status_change()
> - lan78xx_link_reset()
> - Handle fixed-link fallback for LAN7801 using phylink_set_fixed_link()
> - Replace deprecated flow control handling with phylink-managed logic
>
> Power management:
> - Switch suspend/resume paths to use phylink_suspend()/phylink_resume()
> - Ensure proper use of rtnl_lock() where required
> - Note: full runtime testing of power management is currently limited
> due to hardware setup constraints
>
> Note: Conversion of EEE (Energy Efficient Ethernet) handling to the
> PHYLINK-managed API will be done in a follow-up patch. For now, the
> legacy EEE enable logic is preserved in mac_link_up().
>
> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> ---
> changes v6:
> - move functional interface changes and refactoring to separate patches
> - switch suspend/resume to use phylink_suspend/resume with correct locking
> - avoid setting phydev->interface manually; rely on phylink negotiation
> - remove legacy pause/aneg setup and dead code (e.g. lan78xx_link_status_change)
> - EEE conversion postponed to follow-up patch
> - note that power management testing is limited due to HW constraints
> changes v5:
> - merge ethtool pause interface changes to this patch
> changes v4:
> - add PHYLINK dependency
> - remove PHYLIB and FIXED_PHY, both are replaced by PHYLINK
> changes v3:
> - lan78xx_phy_init: drop phy_suspend()
> - lan78xx_phylink_setup: use phy_interface_set_rgmii()
> changes v2:
> - lan78xx_mac_config: remove unused rgmii_id
> - lan78xx_mac_config: PHY_INTERFACE_MODE_RGMII* variants
> - lan78xx_mac_config: remove auto-speed and duplex configuration
> - lan78xx_phylink_setup: set link_interface to PHY_INTERFACE_MODE_RGMII_ID
> instead of PHY_INTERFACE_MODE_NA.
> - lan78xx_phy_init: use phylink_set_fixed_link() instead of allocating
> fixed PHY.
> - lan78xx_configure_usb: move function values to separate variables
>
> 20220427_lukas_polling_be_gone_on_lan95xx.cover
> ---
> drivers/net/usb/Kconfig | 3 +-
> drivers/net/usb/lan78xx.c | 551 ++++++++++++++++++--------------------
> 2 files changed, 258 insertions(+), 296 deletions(-)
>
> diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
> index 3c360d4f0635..71168e47a9b1 100644
> --- a/drivers/net/usb/Kconfig
> +++ b/drivers/net/usb/Kconfig
> @@ -115,9 +115,8 @@ config USB_RTL8152
> config USB_LAN78XX
> tristate "Microchip LAN78XX Based USB Ethernet Adapters"
> select MII
> - select PHYLIB
> + select PHYLINK
> select MICROCHIP_PHY
> - select FIXED_PHY
> select CRC32
> help
> This option adds support for Microchip LAN78XX based USB 2
> diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
> index 19ac1c280300..03d8102026f1 100644
> --- a/drivers/net/usb/lan78xx.c
> +++ b/drivers/net/usb/lan78xx.c
> @@ -6,6 +6,7 @@
> #include <linux/netdevice.h>
> #include <linux/etherdevice.h>
> #include <linux/ethtool.h>
> +#include <linux/phylink.h>
> #include <linux/usb.h>
> #include <linux/crc32.h>
> #include <linux/signal.h>
> @@ -384,7 +385,7 @@ struct skb_data { /* skb->cb is one of these */
> #define EVENT_RX_HALT 1
> #define EVENT_RX_MEMORY 2
> #define EVENT_STS_SPLIT 3
> -#define EVENT_LINK_RESET 4
> +#define EVENT_PHY_INT_ACK 4
> #define EVENT_RX_PAUSED 5
> #define EVENT_DEV_WAKING 6
> #define EVENT_DEV_ASLEEP 7
> @@ -455,7 +456,6 @@ struct lan78xx_net {
>
> unsigned long data[5];
>
> - int link_on;
> u8 mdix_ctrl;
>
> u32 chipid;
> @@ -463,13 +463,13 @@ struct lan78xx_net {
> struct mii_bus *mdiobus;
> phy_interface_t interface;
>
> - int fc_autoneg;
> - u8 fc_request_control;
> -
> int delta;
> struct statstage stats;
>
> struct irq_domain_data domain_data;
> +
> + struct phylink *phylink;
> + struct phylink_config phylink_config;
> };
>
> /* use ethtool to change the level for any given device */
> @@ -1554,28 +1554,6 @@ static void lan78xx_set_multicast(struct net_device *netdev)
> schedule_work(&pdata->set_multicast);
> }
>
> -static int lan78xx_configure_flowcontrol(struct lan78xx_net *dev,
> - bool tx_pause, bool rx_pause);
> -
> -static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex,
> - u16 lcladv, u16 rmtadv)
> -{
> - u8 cap;
> -
> - if (dev->fc_autoneg)
> - cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
> - else
> - cap = dev->fc_request_control;
> -
> - netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s",
> - (cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
> - (cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
> -
> - return lan78xx_configure_flowcontrol(dev,
> - cap & FLOW_CTRL_TX,
> - cap & FLOW_CTRL_RX);
> -}
> -
> static void lan78xx_rx_urb_submit_all(struct lan78xx_net *dev);
>
> static int lan78xx_mac_reset(struct lan78xx_net *dev)
> @@ -1638,75 +1616,6 @@ static int lan78xx_phy_int_ack(struct lan78xx_net *dev)
> return lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_);
> }
>
> -static int lan78xx_configure_usb(struct lan78xx_net *dev, int speed);
> -
> -static int lan78xx_link_reset(struct lan78xx_net *dev)
> -{
> - struct phy_device *phydev = dev->net->phydev;
> - struct ethtool_link_ksettings ecmd;
> - int ladv, radv, ret, link;
> -
> - /* clear LAN78xx interrupt status */
> - ret = lan78xx_phy_int_ack(dev);
> - if (unlikely(ret < 0))
> - return ret;
> -
> - mutex_lock(&phydev->lock);
> - phy_read_status(phydev);
> - link = phydev->link;
> - mutex_unlock(&phydev->lock);
> -
> - if (!link && dev->link_on) {
> - dev->link_on = false;
> -
> - /* reset MAC */
> - ret = lan78xx_mac_reset(dev);
> - if (ret < 0)
> - return ret;
> -
> - del_timer(&dev->stat_monitor);
> - } else if (link && !dev->link_on) {
> - dev->link_on = true;
> -
> - phy_ethtool_ksettings_get(phydev, &ecmd);
> -
> - ret = lan78xx_configure_usb(dev, ecmd.base.speed);
> - if (ret < 0)
> - return ret;
> -
> - ladv = phy_read(phydev, MII_ADVERTISE);
> - if (ladv < 0)
> - return ladv;
> -
> - radv = phy_read(phydev, MII_LPA);
> - if (radv < 0)
> - return radv;
> -
> - netif_dbg(dev, link, dev->net,
> - "speed: %u duplex: %d anadv: 0x%04x anlpa: 0x%04x",
> - ecmd.base.speed, ecmd.base.duplex, ladv, radv);
> -
> - ret = lan78xx_update_flowcontrol(dev, ecmd.base.duplex, ladv,
> - radv);
> - if (ret < 0)
> - return ret;
> -
> - if (!timer_pending(&dev->stat_monitor)) {
> - dev->delta = 1;
> - mod_timer(&dev->stat_monitor,
> - jiffies + STAT_UPDATE_TIMER);
> - }
> -
> - lan78xx_rx_urb_submit_all(dev);
> -
> - local_bh_disable();
> - napi_schedule(&dev->napi);
> - local_bh_enable();
> - }
> -
> - return 0;
> -}
> -
> /* some work can't be done in tasklets, so we use keventd
> *
> * NOTE: annoying asymmetry: if it's active, schedule_work() fails,
> @@ -1733,7 +1642,7 @@ static void lan78xx_status(struct lan78xx_net *dev, struct urb *urb)
>
> if (intdata & INT_ENP_PHY_INT) {
> netif_dbg(dev, link, dev->net, "PHY INTR: 0x%08x\n", intdata);
> - lan78xx_defer_kevent(dev, EVENT_LINK_RESET);
> + lan78xx_defer_kevent(dev, EVENT_PHY_INT_ACK);
>
> if (dev->domain_data.phyirq > 0)
> generic_handle_irq_safe(dev->domain_data.phyirq);
> @@ -2015,63 +1924,16 @@ static void lan78xx_get_pause(struct net_device *net,
> struct ethtool_pauseparam *pause)
> {
> struct lan78xx_net *dev = netdev_priv(net);
> - struct phy_device *phydev = net->phydev;
> - struct ethtool_link_ksettings ecmd;
> -
> - phy_ethtool_ksettings_get(phydev, &ecmd);
> -
> - pause->autoneg = dev->fc_autoneg;
> -
> - if (dev->fc_request_control & FLOW_CTRL_TX)
> - pause->tx_pause = 1;
>
> - if (dev->fc_request_control & FLOW_CTRL_RX)
> - pause->rx_pause = 1;
> + phylink_ethtool_get_pauseparam(dev->phylink, pause);
> }
>
> static int lan78xx_set_pause(struct net_device *net,
> struct ethtool_pauseparam *pause)
> {
> struct lan78xx_net *dev = netdev_priv(net);
> - struct phy_device *phydev = net->phydev;
> - struct ethtool_link_ksettings ecmd;
> - int ret;
> -
> - phy_ethtool_ksettings_get(phydev, &ecmd);
> -
> - if (pause->autoneg && !ecmd.base.autoneg) {
> - ret = -EINVAL;
> - goto exit;
> - }
> -
> - dev->fc_request_control = 0;
> - if (pause->rx_pause)
> - dev->fc_request_control |= FLOW_CTRL_RX;
> -
> - if (pause->tx_pause)
> - dev->fc_request_control |= FLOW_CTRL_TX;
> -
> - if (ecmd.base.autoneg) {
> - __ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, };
> - u32 mii_adv;
> -
> - linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
> - ecmd.link_modes.advertising);
> - linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
> - ecmd.link_modes.advertising);
> - mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control);
> - mii_adv_to_linkmode_adv_t(fc, mii_adv);
> - linkmode_or(ecmd.link_modes.advertising, fc,
> - ecmd.link_modes.advertising);
>
> - phy_ethtool_ksettings_set(phydev, &ecmd);
> - }
> -
> - dev->fc_autoneg = pause->autoneg;
> -
> - ret = 0;
> -exit:
> - return ret;
> + return phylink_ethtool_set_pauseparam(dev->phylink, pause);
> }
>
> static int lan78xx_get_regs_len(struct net_device *netdev)
> @@ -2332,26 +2194,6 @@ static void lan78xx_remove_mdio(struct lan78xx_net *dev)
> mdiobus_free(dev->mdiobus);
> }
>
> -static void lan78xx_link_status_change(struct net_device *net)
> -{
> - struct lan78xx_net *dev = netdev_priv(net);
> - struct phy_device *phydev = net->phydev;
> - u32 data;
> - int ret;
> -
> - ret = lan78xx_read_reg(dev, MAC_CR, &data);
> - if (ret < 0)
> - return;
> -
> - if (phydev->enable_tx_lpi)
> - data |= MAC_CR_EEE_EN_;
> - else
> - data &= ~MAC_CR_EEE_EN_;
> - lan78xx_write_reg(dev, MAC_CR, data);
> -
> - phy_print_status(phydev);
> -}
> -
> static int irq_map(struct irq_domain *d, unsigned int irq,
> irq_hw_number_t hwirq)
> {
> @@ -2484,6 +2326,75 @@ static void lan78xx_remove_irq_domain(struct lan78xx_net *dev)
> dev->domain_data.irqdomain = NULL;
> }
>
> +static void lan78xx_mac_config(struct phylink_config *config, unsigned int mode,
> + const struct phylink_link_state *state)
> +{
> + struct net_device *net = to_net_dev(config->dev);
> + struct lan78xx_net *dev = netdev_priv(net);
> + u32 mac_cr = 0;
> + int ret;
> +
> + /* Check if the mode is supported */
> + if (mode != MLO_AN_FIXED && mode != MLO_AN_PHY) {
> + netdev_err(net, "Unsupported negotiation mode: %u\n", mode);
> + return;
> + }
> +
> + switch (state->interface) {
> + case PHY_INTERFACE_MODE_GMII:
> + mac_cr |= MAC_CR_GMII_EN_;
> + break;
> + case PHY_INTERFACE_MODE_RGMII:
> + case PHY_INTERFACE_MODE_RGMII_ID:
> + case PHY_INTERFACE_MODE_RGMII_TXID:
> + case PHY_INTERFACE_MODE_RGMII_RXID:
> + break;
> + default:
> + netdev_warn(net, "Unsupported interface mode: %d\n",
> + state->interface);
> + return;
> + }
> +
> + ret = lan78xx_update_reg(dev, MAC_CR, MAC_CR_GMII_EN_, mac_cr);
> + if (ret < 0)
> + netdev_err(net, "Failed to config MAC with error %pe\n",
> + ERR_PTR(ret));
> +}
> +
> +static void lan78xx_mac_link_down(struct phylink_config *config,
> + unsigned int mode, phy_interface_t interface)
> +{
> + struct net_device *net = to_net_dev(config->dev);
> + struct lan78xx_net *dev = netdev_priv(net);
> + int ret;
> +
> + /* MAC reset will not de-assert TXEN/RXEN, we need to stop them
> + * manually before reset. TX and RX should be disabled before running
> + * link_up sequence.
> + */
> + ret = lan78xx_stop_tx_path(dev);
> + if (ret < 0)
> + goto link_down_fail;
> +
> + ret = lan78xx_stop_rx_path(dev);
> + if (ret < 0)
> + goto link_down_fail;
> +
> + /* MAC reset seems to not affect MAC configuration, no idea if it is
> + * really needed, but it was done in previous driver version. So, leave
> + * it here.
> + */
> + ret = lan78xx_mac_reset(dev);
> + if (ret < 0)
> + goto link_down_fail;
> +
> + return;
> +
> +link_down_fail:
> + netdev_err(dev->net, "Failed to set MAC down with error %pe\n",
> + ERR_PTR(ret));
> +}
> +
> /**
> * lan78xx_configure_usb - Configure USB link power settings
> * @dev: pointer to the LAN78xx device structure
> @@ -2619,28 +2530,100 @@ static int lan78xx_configure_flowcontrol(struct lan78xx_net *dev,
> return lan78xx_write_reg(dev, FLOW, flow);
> }
>
> +static void lan78xx_mac_link_up(struct phylink_config *config,
> + struct phy_device *phy,
> + unsigned int mode, phy_interface_t interface,
> + int speed, int duplex,
> + bool tx_pause, bool rx_pause)
> +{
> + struct net_device *net = to_net_dev(config->dev);
> + struct lan78xx_net *dev = netdev_priv(net);
> + u32 mac_cr = 0;
> + int ret;
> +
> + switch (speed) {
> + case SPEED_1000:
> + mac_cr |= MAC_CR_SPEED_1000_;
> + break;
> + case SPEED_100:
> + mac_cr |= MAC_CR_SPEED_100_;
> + break;
> + case SPEED_10:
> + mac_cr |= MAC_CR_SPEED_10_;
> + break;
> + default:
> + netdev_err(dev->net, "Unsupported speed %d\n", speed);
> + return;
> + }
> +
> + if (duplex == DUPLEX_FULL)
> + mac_cr |= MAC_CR_FULL_DUPLEX_;
> +
> + /* make sure TXEN and RXEN are disabled before reconfiguring MAC */
> + ret = lan78xx_update_reg(dev, MAC_CR, MAC_CR_SPEED_MASK_ |
> + MAC_CR_FULL_DUPLEX_ | MAC_CR_EEE_EN_, mac_cr);
> + if (ret < 0)
> + goto link_up_fail;
> +
> + ret = lan78xx_configure_flowcontrol(dev, tx_pause, rx_pause);
> + if (ret < 0)
> + goto link_up_fail;
> +
> + ret = lan78xx_configure_usb(dev, speed);
> + if (ret < 0)
> + goto link_up_fail;
> +
> + lan78xx_rx_urb_submit_all(dev);
> +
> + ret = lan78xx_flush_rx_fifo(dev);
> + if (ret < 0)
> + goto link_up_fail;
> +
> + ret = lan78xx_flush_tx_fifo(dev);
> + if (ret < 0)
> + goto link_up_fail;
> +
> + ret = lan78xx_start_tx_path(dev);
> + if (ret < 0)
> + goto link_up_fail;
> +
> + ret = lan78xx_start_rx_path(dev);
> + if (ret < 0)
> + goto link_up_fail;
> +
> + return;
> +link_up_fail:
> + netdev_err(dev->net, "Failed to set MAC up with error %pe\n",
> + ERR_PTR(ret));
> +}
> +
> +static const struct phylink_mac_ops lan78xx_phylink_mac_ops = {
> + .mac_config = lan78xx_mac_config,
> + .mac_link_down = lan78xx_mac_link_down,
> + .mac_link_up = lan78xx_mac_link_up,
> +};
> +
> /**
> - * lan78xx_register_fixed_phy() - Register a fallback fixed PHY
> + * lan78xx_set_fixed_link() - Set fixed link configuration for LAN7801
> * @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.
> + * Use fixed link configuration 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)
> +static int lan78xx_set_fixed_link(struct lan78xx_net *dev)
> {
> - struct fixed_phy_status fphy_status = {
> - .link = 1,
> + struct phylink_link_state state = {
> .speed = SPEED_1000,
> .duplex = DUPLEX_FULL,
> };
>
> netdev_info(dev->net,
> - "No PHY found on LAN7801 – registering fixed PHY (e.g. EVB-KSZ9897-1)\n");
> + "No PHY found on LAN7801 – using fixed link instead (e.g. EVB-KSZ9897-1)\n");
>
> - return fixed_phy_register(PHY_POLL, &fphy_status, NULL);
> + return phylink_set_fixed_link(dev->phylink, &state);
> }
>
> /**
> @@ -2676,7 +2659,7 @@ static struct phy_device *lan78xx_get_phy(struct lan78xx_net *dev)
>
> dev->interface = PHY_INTERFACE_MODE_RGMII;
> /* No PHY found – fallback to fixed PHY (e.g. KSZ switch board) */
> - return lan78xx_register_fixed_phy(dev);
> + return NULL;
>
> case ID_REV_CHIP_ID_7800_:
> case ID_REV_CHIP_ID_7850_:
> @@ -2803,20 +2786,63 @@ static int lan78xx_configure_leds_from_dt(struct lan78xx_net *dev,
> return lan78xx_write_reg(dev, HW_CFG, reg);
> }
>
> +static int lan78xx_phylink_setup(struct lan78xx_net *dev)
> +{
> + struct phylink_config *pc = &dev->phylink_config;
> + struct phylink *phylink;
> +
> + pc->dev = &dev->net->dev;
> + pc->type = PHYLINK_NETDEV;
> + pc->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10 |
> + MAC_100 | MAC_1000FD;
> + pc->mac_managed_pm = true;
> +
> + if (dev->chipid == ID_REV_CHIP_ID_7801_)
> + phy_interface_set_rgmii(pc->supported_interfaces);
> + else
> + __set_bit(PHY_INTERFACE_MODE_GMII, pc->supported_interfaces);
> +
> + phylink = phylink_create(pc, dev->net->dev.fwnode,
> + dev->interface, &lan78xx_phylink_mac_ops);
> + if (IS_ERR(phylink))
> + return PTR_ERR(phylink);
> +
> + dev->phylink = phylink;
> +
> + return 0;
> +}
> +
> static int lan78xx_phy_init(struct lan78xx_net *dev)
> {
> - __ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, };
> - int ret;
> - u32 mii_adv;
> struct phy_device *phydev;
> + int ret;
>
> phydev = lan78xx_get_phy(dev);
> + /* phydev can be NULL if no PHY is found and the chip is LAN7801,
> + * which will use a fixed link later.
> + * If an error occurs, return the error code immediately.
> + */
> if (IS_ERR(phydev))
> return PTR_ERR(phydev);
>
> + ret = lan78xx_phylink_setup(dev);
> + if (ret < 0)
> + return ret;
> +
> + /* If no PHY is found, set up a fixed link. It is very specific to
> + * the LAN7801 and 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.
> + */
> + if (!phydev) {
> + ret = lan78xx_set_fixed_link(dev);
> + if (ret < 0)
> + return ret;
> + }
> +
> ret = lan78xx_mac_prepare_for_phy(dev);
> if (ret < 0)
> - goto free_phy;
> + return ret;
>
> /* if phyirq is not set, use polling mode in phylib */
> if (dev->domain_data.phyirq > 0)
> @@ -2825,56 +2851,25 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
> phydev->irq = PHY_POLL;
> netdev_dbg(dev->net, "phydev->irq = %d\n", phydev->irq);
>
> - /* set to AUTOMDIX */
> - phydev->mdix = ETH_TP_MDI_AUTO;
> -
> - ret = phy_connect_direct(dev->net, phydev,
> - lan78xx_link_status_change,
> - dev->interface);
> + ret = phylink_connect_phy(dev->phylink, phydev);
> if (ret) {
> - netdev_err(dev->net, "can't attach PHY to %s\n",
> - dev->mdiobus->id);
> - if (dev->chipid == ID_REV_CHIP_ID_7801_) {
> - if (phy_is_pseudo_fixed_link(phydev)) {
> - fixed_phy_unregister(phydev);
> - phy_device_free(phydev);
> - }
> - }
> - return -EIO;
> + netdev_err(dev->net, "can't attach PHY to %s, error %pe\n",
> + dev->mdiobus->id, ERR_PTR(ret));
> + return ret;
> }
>
> - /* MAC doesn't support 1000T Half */
> - phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
> -
> - /* support both flow controls */
> - dev->fc_request_control = (FLOW_CTRL_RX | FLOW_CTRL_TX);
> - linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
> - phydev->advertising);
> - linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
> - phydev->advertising);
> - mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control);
> - mii_adv_to_linkmode_adv_t(fc, mii_adv);
> - linkmode_or(phydev->advertising, fc, phydev->advertising);
> -
> phy_support_eee(phydev);
>
> - ret = lan78xx_configure_leds_from_dt(dev, phydev);
> - if (ret)
> - goto free_phy;
> -
> - genphy_config_aneg(phydev);
> -
> - dev->fc_autoneg = phydev->autoneg;
> -
> - return 0;
> + return lan78xx_configure_leds_from_dt(dev, phydev);
> +}
>
> -free_phy:
> - if (phy_is_pseudo_fixed_link(phydev)) {
> - fixed_phy_unregister(phydev);
> - phy_device_free(phydev);
> +static void lan78xx_phy_uninit(struct lan78xx_net *dev)
> +{
> + if (dev->phylink) {
> + phylink_disconnect_phy(dev->phylink);
> + phylink_destroy(dev->phylink);
> + dev->phylink = NULL;
> }
> -
> - return ret;
> }
>
> static int lan78xx_set_rx_max_frame_length(struct lan78xx_net *dev, int size)
> @@ -3213,7 +3208,6 @@ static int lan78xx_reset(struct lan78xx_net *dev)
> unsigned long timeout;
> int ret;
> u32 buf;
> - u8 sig;
>
> ret = lan78xx_read_reg(dev, HW_CFG, &buf);
> if (ret < 0)
> @@ -3370,22 +3364,12 @@ static int lan78xx_reset(struct lan78xx_net *dev)
> if (ret < 0)
> return ret;
>
> + buf &= ~(MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_);
> +
> /* LAN7801 only has RGMII mode */
> - if (dev->chipid == ID_REV_CHIP_ID_7801_) {
> + if (dev->chipid == ID_REV_CHIP_ID_7801_)
> buf &= ~MAC_CR_GMII_EN_;
> - /* Enable Auto Duplex and Auto speed */
> - buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
> - }
>
> - if (dev->chipid == ID_REV_CHIP_ID_7800_ ||
> - dev->chipid == ID_REV_CHIP_ID_7850_) {
> - ret = lan78xx_read_raw_eeprom(dev, 0, 1, &sig);
> - if (!ret && sig != EEPROM_INDICATOR) {
> - /* Implies there is no external eeprom. Set mac speed */
> - netdev_info(dev->net, "No External EEPROM. Setting MAC Speed\n");
> - buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
> - }
> - }
> ret = lan78xx_write_reg(dev, MAC_CR, buf);
> if (ret < 0)
> return ret;
> @@ -3435,9 +3419,11 @@ static int lan78xx_open(struct net_device *net)
>
> mutex_lock(&dev->dev_mutex);
>
> - phy_start(net->phydev);
> + lan78xx_init_stats(dev);
> +
> + napi_enable(&dev->napi);
>
> - netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
> + set_bit(EVENT_DEV_OPEN, &dev->flags);
>
> /* for Link Check */
> if (dev->urb_intr) {
> @@ -3449,31 +3435,9 @@ static int lan78xx_open(struct net_device *net)
> }
> }
>
> - ret = lan78xx_flush_rx_fifo(dev);
> - if (ret < 0)
> - goto done;
> - ret = lan78xx_flush_tx_fifo(dev);
> - if (ret < 0)
> - goto done;
> -
> - ret = lan78xx_start_tx_path(dev);
> - if (ret < 0)
> - goto done;
> - ret = lan78xx_start_rx_path(dev);
> - if (ret < 0)
> - goto done;
> -
> - lan78xx_init_stats(dev);
> -
> - set_bit(EVENT_DEV_OPEN, &dev->flags);
> + phylink_start(dev->phylink);
>
> netif_start_queue(net);
> -
> - dev->link_on = false;
> -
> - napi_enable(&dev->napi);
> -
> - lan78xx_defer_kevent(dev, EVENT_LINK_RESET);
> done:
> mutex_unlock(&dev->dev_mutex);
>
> @@ -3541,12 +3505,7 @@ static int lan78xx_stop(struct net_device *net)
> net->stats.rx_packets, net->stats.tx_packets,
> net->stats.rx_errors, net->stats.tx_errors);
>
> - /* ignore errors that occur stopping the Tx and Rx data paths */
> - lan78xx_stop_tx_path(dev);
> - lan78xx_stop_rx_path(dev);
> -
> - if (net->phydev)
> - phy_stop(net->phydev);
> + phylink_stop(dev->phylink);
>
> usb_kill_urb(dev->urb_intr);
>
> @@ -3556,7 +3515,7 @@ static int lan78xx_stop(struct net_device *net)
> */
> clear_bit(EVENT_TX_HALT, &dev->flags);
> clear_bit(EVENT_RX_HALT, &dev->flags);
> - clear_bit(EVENT_LINK_RESET, &dev->flags);
> + clear_bit(EVENT_PHY_INT_ACK, &dev->flags);
> clear_bit(EVENT_STAT_UPDATE, &dev->flags);
>
> cancel_delayed_work_sync(&dev->wq);
> @@ -4480,14 +4439,14 @@ static void lan78xx_delayedwork(struct work_struct *work)
> }
> }
>
> - if (test_bit(EVENT_LINK_RESET, &dev->flags)) {
> + if (test_bit(EVENT_PHY_INT_ACK, &dev->flags)) {
> int ret = 0;
>
> - clear_bit(EVENT_LINK_RESET, &dev->flags);
> - if (lan78xx_link_reset(dev) < 0) {
> - netdev_info(dev->net, "link reset failed (%d)\n",
> - ret);
> - }
> + clear_bit(EVENT_PHY_INT_ACK, &dev->flags);
> + ret = lan78xx_phy_int_ack(dev);
> + if (ret)
> + netdev_info(dev->net, "PHY INT ack failed (%pe)\n",
> + ERR_PTR(ret));
> }
>
> if (test_bit(EVENT_STAT_UPDATE, &dev->flags)) {
> @@ -4561,32 +4520,29 @@ static void lan78xx_disconnect(struct usb_interface *intf)
> struct lan78xx_net *dev;
> struct usb_device *udev;
> struct net_device *net;
> - struct phy_device *phydev;
>
> dev = usb_get_intfdata(intf);
> usb_set_intfdata(intf, NULL);
> if (!dev)
> return;
>
> - netif_napi_del(&dev->napi);
> -
> udev = interface_to_usbdev(intf);
> net = dev->net;
>
> + rtnl_lock();
> + phylink_stop(dev->phylink);
> + phylink_disconnect_phy(dev->phylink);
> + rtnl_unlock();
> +
> + netif_napi_del(&dev->napi);
> +
> unregister_netdev(net);
>
> timer_shutdown_sync(&dev->stat_monitor);
> set_bit(EVENT_DEV_DISCONNECT, &dev->flags);
> cancel_delayed_work_sync(&dev->wq);
>
> - phydev = net->phydev;
> -
> - phy_disconnect(net->phydev);
> -
> - if (phy_is_pseudo_fixed_link(phydev)) {
> - fixed_phy_unregister(phydev);
> - phy_device_free(phydev);
> - }
> + phylink_destroy(dev->phylink);
>
> usb_scuttle_anchored_urbs(&dev->deferred);
>
> @@ -4670,7 +4626,6 @@ static int lan78xx_probe(struct usb_interface *intf,
> goto out1;
> }
>
> - /* netdev_printk() needs this */
> SET_NETDEV_DEV(netdev, &intf->dev);
>
> dev = netdev_priv(netdev);
> @@ -4784,12 +4739,12 @@ static int lan78xx_probe(struct usb_interface *intf,
>
> ret = lan78xx_phy_init(dev);
> if (ret < 0)
> - goto free_urbs;
> + goto phy_uninit;
>
> ret = register_netdev(netdev);
> if (ret != 0) {
> netif_err(dev, probe, netdev, "couldn't register the device\n");
> - goto out8;
> + goto phy_uninit;
> }
>
> usb_set_intfdata(intf, dev);
> @@ -4804,8 +4759,8 @@ static int lan78xx_probe(struct usb_interface *intf,
>
> return 0;
>
> -out8:
> - phy_disconnect(netdev->phydev);
> +phy_uninit:
> + lan78xx_phy_uninit(dev);
> free_urbs:
> usb_free_urb(dev->urb_intr);
> out5:
> @@ -5140,6 +5095,10 @@ static int lan78xx_suspend(struct usb_interface *intf, pm_message_t message)
> spin_unlock_irq(&dev->txq.lock);
> }
>
> + rtnl_lock();
> + phylink_suspend(dev->phylink, false);
> + rtnl_unlock();
> +
> /* stop RX */
> ret = lan78xx_stop_rx_path(dev);
> if (ret < 0)
> @@ -5367,11 +5326,15 @@ static int lan78xx_reset_resume(struct usb_interface *intf)
> if (ret < 0)
> return ret;
>
> - phy_start(dev->net->phydev);
> -
> ret = lan78xx_resume(intf);
> + if (ret < 0)
> + return ret;
>
> - return ret;
> + rtnl_lock();
> + phylink_resume(dev->phylink);
> + rtnl_unlock();
> +
> + return 0;
> }
>
> static const struct usb_device_id products[] = {
> --
> 2.39.5
>
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2025-04-11 5:33 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-10 11:52 [PATCH net-next v6 00/12] Convert LAN78xx to PHYLINK Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 01/12] net: usb: lan78xx: Improve error handling in PHY initialization Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 02/12] net: usb: lan78xx: remove explicit check for missing PHY driver Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 03/12] net: usb: lan78xx: refactor PHY init to separate detection and MAC configuration Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 04/12] net: usb: lan78xx: move LED DT configuration to helper Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 05/12] net: usb: lan78xx: Extract PHY interrupt acknowledgment " Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 06/12] net: usb: lan78xx: Refactor USB link power configuration into helper Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 07/12] net: usb: lan78xx: Extract flow control configuration to helper Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 08/12] net: usb: lan78xx: Convert to PHYLINK for improved PHY and MAC management Oleksij Rempel
2025-04-11 5:33 ` Oleksij Rempel
2025-04-10 11:52 ` [PATCH net-next v6 09/12] net: usb: lan78xx: Use ethtool_op_get_link to reflect current link status Oleksij Rempel
2025-04-10 11:53 ` [PATCH net-next v6 10/12] net: usb: lan78xx: port link settings to phylink API Oleksij Rempel
2025-04-10 11:53 ` [PATCH net-next v6 11/12] net: usb: lan78xx: Integrate EEE support with phylink LPI API Oleksij Rempel
2025-04-10 11:53 ` [PATCH net-next v6 12/12] net: usb: lan78xx: remove unused struct members Oleksij Rempel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).