Merge branch 'net-phy-marvell-88q2xxx-enable-auto-negotiation-for-mv88q2110'

Niklas Söderlund says:

====================
net: phy: marvell-88q2xxx: Enable auto negotiation for mv88q2110

This series enables auto negotiation for the mv88q2110 device.
Previously this feature have been disabled for mv88q2110, while enabled
for other devices supported by this driver.

The initial driver implementation states this is due to the
configuration sequence provided by the vendor did not work. By comparing
the initialization sequence of other devices this driver supports and
the out-of-tree PHY driver for mv88q2110 found in the Renesas BSP [1]
I was able to figure out a working configuration.

As I have no access to the datasheets of either of these devices it
would be super if someone who has could sanity check the initialization
sequence.

With this series I'm able to auto negotiate both 1000Mbps and 100Mbps
links without issue.

    # ethtool eth0
    Settings for eth0:
            Supported ports: [  ]
            Supported link modes:   100baseT1/Full
                                    1000baseT1/Full
            Supported pause frame use: Symmetric Receive-only
            Supports auto-negotiation: Yes
            Supported FEC modes: Not reported
            Advertised link modes:  100baseT1/Full
                                    1000baseT1/Full
            Advertised pause frame use: No
            Advertised auto-negotiation: Yes
            Advertised FEC modes: Not reported
            Link partner advertised link modes:  100baseT1/Full
                                                 1000baseT1/Full
            Link partner advertised pause frame use: No
            Link partner advertised auto-negotiation: Yes
            Link partner advertised FEC modes: Not reported
            Speed: 1000Mb/s
            Duplex: Full
            Auto-negotiation: on
            master-slave cfg: preferred master
            master-slave status: slave
            Port: Twisted Pair
            PHYAD: 0
            Transceiver: external
            MDI-X: Unknown
            Link detected: yes
            SQI: 15/15

And the performance is good too. Without this change I was not able to
manually configure a 1000Mbps link, only 100Mbps ones. So this gives a
huge performance boost for my use-case.

    [  5] local 10.1.0.2 port 5201 connected to 10.1.0.1 port 38346
    [ ID] Interval           Transfer     Bitrate         Retr  Cwnd
    [  5]   0.00-1.00   sec  96.8 MBytes   812 Mbits/sec    0    469 KBytes
    [  5]   1.00-2.00   sec  94.3 MBytes   791 Mbits/sec    0    469 KBytes
    [  5]   2.00-3.00   sec  96.1 MBytes   806 Mbits/sec    0    469 KBytes
    [  5]   3.00-4.00   sec  98.3 MBytes   825 Mbits/sec    0    469 KBytes
    [  5]   4.00-5.00   sec  98.4 MBytes   825 Mbits/sec    0    469 KBytes
    [  5]   5.00-6.00   sec  98.4 MBytes   826 Mbits/sec    0    469 KBytes
    [  5]   6.00-7.00   sec  98.9 MBytes   830 Mbits/sec    0    469 KBytes
    [  5]   7.00-8.00   sec  91.7 MBytes   769 Mbits/sec    0    469 KBytes
    [  5]   8.00-9.00   sec  99.4 MBytes   834 Mbits/sec    0    747 KBytes
    [  5]   9.00-10.00  sec   101 MBytes   851 Mbits/sec    0    747 KBytes

Patch 1/3 and 2/3 are preparation patches that align and move functions
around as the mv88q2110 code paths can now reuses much of what is done
for mv88q2220. While patch 3/3 adds the new initialization sequence and
removes the auto negotiation limit for mv88q2110.

1.  2a1f07d0e7
====================

Link: https://patch.msgid.link/20241005112412.544360-1-niklas.soderlund+renesas@ragnatech.se
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2024-10-08 18:18:21 -07:00

View File

@@ -101,6 +101,22 @@ struct mmd_val {
u16 val;
};
static const struct mmd_val mv88q2110_init_seq0[] = {
{ MDIO_MMD_PCS, 0xffe4, 0x07b5 },
{ MDIO_MMD_PCS, 0xffe4, 0x06b6 },
};
static const struct mmd_val mv88q2110_init_seq1[] = {
{ MDIO_MMD_PCS, 0xffde, 0x402f },
{ MDIO_MMD_PCS, 0xfe34, 0x4040 },
{ MDIO_MMD_PCS, 0xfe2a, 0x3c1d },
{ MDIO_MMD_PCS, 0xfe34, 0x0040 },
{ MDIO_MMD_AN, 0x8032, 0x0064 },
{ MDIO_MMD_AN, 0x8031, 0x0a01 },
{ MDIO_MMD_AN, 0x8031, 0x0c01 },
{ MDIO_MMD_PCS, 0xffdb, 0x0010 },
};
static const struct mmd_val mv88q222x_revb0_init_seq0[] = {
{ MDIO_MMD_PCS, 0x8033, 0x6801 },
{ MDIO_MMD_AN, MDIO_AN_T1_CTRL, 0x0 },
@@ -174,20 +190,54 @@ static const struct mmd_val mv88q222x_revb1_revb2_init_seq1[] = {
{ MDIO_MMD_PCS, 0xfe11, 0x1105 },
};
static int mv88q2xxx_write_mmd_vals(struct phy_device *phydev,
const struct mmd_val *vals, size_t len)
{
int ret;
for (; len; vals++, len--) {
ret = phy_write_mmd(phydev, vals->devad, vals->regnum,
vals->val);
if (ret < 0)
return ret;
}
return 0;
}
static int mv88q2xxx_soft_reset(struct phy_device *phydev)
{
int ret;
int val;
ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
MDIO_PCS_1000BT1_CTRL, MDIO_PCS_1000BT1_CTRL_RESET);
/* Enable RESET of DCL */
if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000) {
ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x48);
if (ret < 0)
return ret;
}
ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_CTRL,
MDIO_PCS_1000BT1_CTRL_RESET);
if (ret < 0)
return ret;
return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS,
MDIO_PCS_1000BT1_CTRL, val,
!(val & MDIO_PCS_1000BT1_CTRL_RESET),
50000, 600000, true);
ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS,
MDIO_PCS_1000BT1_CTRL, val,
!(val & MDIO_PCS_1000BT1_CTRL_RESET),
50000, 600000, true);
if (ret < 0)
return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xffe4, 0xc);
if (ret < 0)
return ret;
/* Disable RESET of DCL */
if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000)
return phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x58);
return 0;
}
static int mv88q2xxx_read_link_gbit(struct phy_device *phydev)
@@ -390,15 +440,6 @@ static int mv88q2xxx_get_features(struct phy_device *phydev)
if (ret)
return ret;
/* The PHY signalizes it supports autonegotiation. Unfortunately, so
* far it was not possible to get a link even when following the init
* sequence provided by Marvell. Disable it for now until a proper
* workaround is found or a new PHY revision is released.
*/
if (phydev->drv->phy_id == MARVELL_PHY_ID_88Q2110)
linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
phydev->supported);
return 0;
}
@@ -705,60 +746,37 @@ static int mv88q2xxx_probe(struct phy_device *phydev)
return mv88q2xxx_hwmon_probe(phydev);
}
static int mv88q222x_soft_reset(struct phy_device *phydev)
static int mv88q2110_config_init(struct phy_device *phydev)
{
int ret;
/* Enable RESET of DCL */
if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000) {
ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x48);
if (ret < 0)
return ret;
}
ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_CTRL,
MDIO_PCS_1000BT1_CTRL_RESET);
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q2110_init_seq0,
ARRAY_SIZE(mv88q2110_init_seq0));
if (ret < 0)
return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_PCS, 0xffe4, 0xc);
usleep_range(5000, 10000);
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q2110_init_seq1,
ARRAY_SIZE(mv88q2110_init_seq1));
if (ret < 0)
return ret;
/* Disable RESET of DCL */
if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000)
return phy_write_mmd(phydev, MDIO_MMD_PCS, 0xfe1b, 0x58);
return 0;
}
static int mv88q222x_write_mmd_vals(struct phy_device *phydev,
const struct mmd_val *vals, size_t len)
{
int ret;
for (; len; vals++, len--) {
ret = phy_write_mmd(phydev, vals->devad, vals->regnum,
vals->val);
if (ret < 0)
return ret;
}
return 0;
return mv88q2xxx_config_init(phydev);
}
static int mv88q222x_revb0_config_init(struct phy_device *phydev)
{
int ret;
ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb0_init_seq0,
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb0_init_seq0,
ARRAY_SIZE(mv88q222x_revb0_init_seq0));
if (ret < 0)
return ret;
usleep_range(5000, 10000);
ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb0_init_seq1,
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb0_init_seq1,
ARRAY_SIZE(mv88q222x_revb0_init_seq1));
if (ret < 0)
return ret;
@@ -772,17 +790,17 @@ static int mv88q222x_revb1_revb2_config_init(struct phy_device *phydev)
int ret;
if (is_rev_b1)
ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb1_init_seq0,
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb1_init_seq0,
ARRAY_SIZE(mv88q222x_revb1_init_seq0));
else
ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb2_init_seq0,
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb2_init_seq0,
ARRAY_SIZE(mv88q222x_revb2_init_seq0));
if (ret < 0)
return ret;
usleep_range(3000, 5000);
ret = mv88q222x_write_mmd_vals(phydev, mv88q222x_revb1_revb2_init_seq1,
ret = mv88q2xxx_write_mmd_vals(phydev, mv88q222x_revb1_revb2_init_seq1,
ARRAY_SIZE(mv88q222x_revb1_revb2_init_seq1));
if (ret < 0)
return ret;
@@ -888,7 +906,7 @@ static struct phy_driver mv88q2xxx_driver[] = {
.name = "mv88q2110",
.get_features = mv88q2xxx_get_features,
.config_aneg = mv88q2xxx_config_aneg,
.config_init = mv88q2xxx_config_init,
.config_init = mv88q2110_config_init,
.read_status = mv88q2xxx_read_status,
.soft_reset = mv88q2xxx_soft_reset,
.set_loopback = genphy_c45_loopback,
@@ -906,7 +924,7 @@ static struct phy_driver mv88q2xxx_driver[] = {
.aneg_done = genphy_c45_aneg_done,
.config_init = mv88q222x_config_init,
.read_status = mv88q2xxx_read_status,
.soft_reset = mv88q222x_soft_reset,
.soft_reset = mv88q2xxx_soft_reset,
.config_intr = mv88q2xxx_config_intr,
.handle_interrupt = mv88q2xxx_handle_interrupt,
.set_loopback = genphy_c45_loopback,