mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 18:43:33 +02:00
Merge branch 'net-phy-microchip-add-downshift-support-for-lan88xx'
Nicolai Buchwitz says: ==================== net: phy: microchip: add downshift support for LAN88xx Add standard ETHTOOL_PHY_DOWNSHIFT tunable support for the Microchip LAN88xx PHY, following the same pattern used by Marvell and other PHY drivers. Ethernet cables with faulty or missing pairs (specifically C and D) can successfully auto-negotiate 1000BASE-T but fail to establish a stable link. The LAN88xx PHY supports automatic downshift to 100BASE-TX after a configurable number of failed attempts (2-5). Patch 1 adds the get/set tunable implementation. Patch 2 enables downshift by default with a count of 2. The setting is stored in the driver's private data so that user changes via ethtool are preserved across suspend/resume cycles. Based on an earlier downstream implementation by Phil Elwell. Tested on Raspberry Pi 3B+ (LAN7515/LAN88xx). ==================== Link: https://patch.msgid.link/20260401123848.696766-1-nb@tipi-net.de Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
0ea7e61f65
|
|
@ -2,6 +2,7 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Microchip Technology
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mii.h>
|
||||
|
|
@ -25,6 +26,7 @@ struct lan88xx_priv {
|
|||
int chip_id;
|
||||
int chip_rev;
|
||||
__u32 wolopts;
|
||||
u8 downshift_cnt;
|
||||
};
|
||||
|
||||
static int lan88xx_read_page(struct phy_device *phydev)
|
||||
|
|
@ -193,6 +195,73 @@ static void lan88xx_config_TR_regs(struct phy_device *phydev)
|
|||
phydev_warn(phydev, "Failed to Set Register[0x1686]\n");
|
||||
}
|
||||
|
||||
static int lan88xx_get_downshift(struct phy_device *phydev, u8 *data)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = phy_read_paged(phydev, 1, LAN78XX_PHY_CTRL3);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (!(val & LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT)) {
|
||||
*data = DOWNSHIFT_DEV_DISABLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*data = FIELD_GET(LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK, val) + 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lan88xx_set_downshift(struct phy_device *phydev, u8 cnt)
|
||||
{
|
||||
u32 mask = LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK |
|
||||
LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT;
|
||||
|
||||
if (cnt == DOWNSHIFT_DEV_DISABLE)
|
||||
return phy_modify_paged(phydev, 1, LAN78XX_PHY_CTRL3,
|
||||
LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT, 0);
|
||||
|
||||
if (cnt == DOWNSHIFT_DEV_DEFAULT_COUNT)
|
||||
cnt = 2;
|
||||
|
||||
if (cnt < 2 || cnt > 5)
|
||||
return -EINVAL;
|
||||
|
||||
return phy_modify_paged(phydev, 1, LAN78XX_PHY_CTRL3, mask,
|
||||
FIELD_PREP(LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK,
|
||||
cnt - 2) |
|
||||
LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT);
|
||||
}
|
||||
|
||||
static int lan88xx_get_tunable(struct phy_device *phydev,
|
||||
struct ethtool_tunable *tuna, void *data)
|
||||
{
|
||||
switch (tuna->id) {
|
||||
case ETHTOOL_PHY_DOWNSHIFT:
|
||||
return lan88xx_get_downshift(phydev, data);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int lan88xx_set_tunable(struct phy_device *phydev,
|
||||
struct ethtool_tunable *tuna, const void *data)
|
||||
{
|
||||
struct lan88xx_priv *priv = phydev->priv;
|
||||
int ret;
|
||||
|
||||
switch (tuna->id) {
|
||||
case ETHTOOL_PHY_DOWNSHIFT:
|
||||
ret = lan88xx_set_downshift(phydev, *(const u8 *)data);
|
||||
if (!ret)
|
||||
priv->downshift_cnt = *(const u8 *)data;
|
||||
return ret;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int lan88xx_probe(struct phy_device *phydev)
|
||||
{
|
||||
struct device *dev = &phydev->mdio.dev;
|
||||
|
|
@ -205,6 +274,7 @@ static int lan88xx_probe(struct phy_device *phydev)
|
|||
return -ENOMEM;
|
||||
|
||||
priv->wolopts = 0;
|
||||
priv->downshift_cnt = 2;
|
||||
|
||||
len = of_property_read_variable_u32_array(dev->of_node,
|
||||
"microchip,led-modes",
|
||||
|
|
@ -284,7 +354,8 @@ static void lan88xx_set_mdix(struct phy_device *phydev)
|
|||
|
||||
static int lan88xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
int val;
|
||||
struct lan88xx_priv *priv = phydev->priv;
|
||||
int val, err;
|
||||
|
||||
/*Zerodetect delay enable */
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_PCS,
|
||||
|
|
@ -297,6 +368,10 @@ static int lan88xx_config_init(struct phy_device *phydev)
|
|||
/* Config DSP registers */
|
||||
lan88xx_config_TR_regs(phydev);
|
||||
|
||||
err = lan88xx_set_downshift(phydev, priv->downshift_cnt);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -499,6 +574,8 @@ static struct phy_driver microchip_phy_driver[] = {
|
|||
.set_wol = lan88xx_set_wol,
|
||||
.read_page = lan88xx_read_page,
|
||||
.write_page = lan88xx_write_page,
|
||||
.get_tunable = lan88xx_get_tunable,
|
||||
.set_tunable = lan88xx_set_tunable,
|
||||
},
|
||||
{
|
||||
PHY_ID_MATCH_MODEL(PHY_ID_LAN937X_TX),
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@
|
|||
/* Registers specific to the LAN7800/LAN7850 embedded phy */
|
||||
#define LAN78XX_PHY_LED_MODE_SELECT (0x1D)
|
||||
|
||||
/* PHY Control 3 register (page 1) */
|
||||
#define LAN78XX_PHY_CTRL3 (0x14)
|
||||
#define LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT BIT(4)
|
||||
#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK GENMASK(3, 2)
|
||||
|
||||
/* DSP registers */
|
||||
#define PHY_ARDENNES_MMD_DEV_3_PHY_CFG (0x806A)
|
||||
#define PHY_ARDENNES_MMD_DEV_3_PHY_CFG_ZD_DLY_EN_ (0x2000)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user