From: Caleb James DeLisle <cjd@cjdns.fr>
To: Christian Marangi <ansuelsmth@gmail.com>,
Andrew Lunn <andrew@lunn.ch>,
Heiner Kallweit <hkallweit1@gmail.com>,
Russell King <linux@armlinux.org.uk>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Daniel Golle <daniel@makrotopia.org>,
Qingfang Deng <dqfext@gmail.com>,
SkyLake Huang <SkyLake.Huang@mediatek.com>,
Matthias Brugger <matthias.bgg@gmail.com>,
AngeloGioacchino Del Regno
<angelogioacchino.delregno@collabora.com>,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org
Subject: Re: [PATCH net-next v2 5/6] net: phy: mediatek: add calibration logic for AN7581
Date: Tue, 25 Aug 2026 20:15:50 +0200 [thread overview]
Message-ID: <d18d84e4-af86-405e-aadd-bd9a8dfe8742@cjdns.fr> (raw)
In-Reply-To: <20260724190811.198339-6-ansuelsmth@gmail.com>
On 24/07/2026 21:08, Christian Marangi wrote:
> Airoha AN7581 require dedicated calibration and specific tuneup to make
> the Ethernet port work on specific HW PCB layout.
>
> The calibration process is similar to Mediatek PHY but Airoha PHY use
> Software calibration process instead of making use of EFUSE.
>
> It does also require some complex table for TX Amp compensation and
> multiple register settings for EEE and 10/100M to correctly work.
> Also the PHY apply a different TX Amp compensation table based on the
> SoC PDIDR version. Such version info is stored in the SCU SoC registers.
>
> Add module version as suggested by Airoha to identify the calibration logic
> with a specific version.
>
> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> ---
> drivers/net/phy/mediatek/airoha-ge-soc.c | 1489 ++++++++++++++++++++++
> drivers/net/phy/mediatek/mtk-ge-soc.h | 13 +
> 2 files changed, 1502 insertions(+)
>
> diff --git a/drivers/net/phy/mediatek/airoha-ge-soc.c b/drivers/net/phy/mediatek/airoha-ge-soc.c
> index 429594f27792..bc487b093cfa 100644
> --- a/drivers/net/phy/mediatek/airoha-ge-soc.c
> +++ b/drivers/net/phy/mediatek/airoha-ge-soc.c
> @@ -1,16 +1,1493 @@
> // SPDX-License-Identifier: GPL-2.0+
> +#include <linux/bitfield.h>
> #include <linux/pinctrl/consumer.h>
> #include <linux/phy.h>
>
> +#include "../phylib.h"
> #include "mtk.h"
> #include "mtk-ge-soc.h"
>
> +#define AIROHA_DEFAULT_PORT0_ADDR 0x9
> #define AIROHA_PHY_MAX_LEDS 2
>
> +enum airoha_mdi_resister_type {
> + MDI_0R,
> + MDI_5R,
> +
> + MDI_TYPE_MAX,
> +};
> +
> +enum airoha_transformer_type {
> + TXMR,
> + DISCRETE,
> +
> + TRANSFORMER_TYPE_MAX,
> +};
> +
> +enum airoha_calib_const_type {
> + TX_AMP_TEST_A,
> + TX_AMP_TEST_B,
> + TX_AMP_TEST_C,
> + TX_AMP_TEST_D,
> + TX_AMP_1G_A,
> + TX_AMP_1G_B,
> + TX_AMP_1G_C,
> + TX_AMP_1G_D,
> + TX_AMP_100M_A,
> + TX_AMP_100M_B,
> + TX_AMP_10M_A,
> + TX_AMP_10M_B,
> + R50_A,
> + R50_B,
> + R50_C,
> + R50_D,
> +
> + CALIB_CONST_TYPE_MAX,
> +};
> +
> +struct airoha_socphy_shared {
> + struct phy_device *phydev_p0;
> + enum airoha_transformer_type transformer_type[4];
> + enum airoha_mdi_resister_type mdi_resister_type;
> + bool rext_sw_calib_done;
> + int (*tx_amp_compensation_tbl)[TRANSFORMER_TYPE_MAX][MDI_TYPE_MAX][CALIB_CONST_TYPE_MAX][4];
> + u8 *r50_cal_tbl;
> +};
> +
> +/*
> + * 2 chip revision
> + * TXMR or discrete
> + * 2 MDI type
> + * TX AMP test/TX AMP 1G/TX AMP 100M/TX AMP 10M/R50
> + * 4 PHY
> + */
> +static int an7581_tx_amp_compensation_tbl[2][TRANSFORMER_TYPE_MAX][MDI_TYPE_MAX][CALIB_CONST_TYPE_MAX][4] = {
> + { /* IC version 1 */
> + [TXMR] = {
> + { },
> + {
> + [TX_AMP_TEST_A] = { -12, -11, -9, -6 },
> + [TX_AMP_TEST_B] = { -9, -13, -9, -5 },
> + [TX_AMP_TEST_C] = { -5, -4, -3, 1 },
> + [TX_AMP_TEST_D] = { -9, -8, -7, -5 },
> + [TX_AMP_1G_A] = { -12, -11, -9, -6 },
> + [TX_AMP_1G_B] = { -9, -13, -9, -5 },
> + [TX_AMP_1G_C] = { -5, -4, -3, 1 },
> + [TX_AMP_1G_D] = { -9, -8, -7, -5 },
> + [TX_AMP_100M_A] = { -2, -2, 0, 2 },
> + [TX_AMP_100M_B] = { -2, -2, 1, 1 },
> + [TX_AMP_10M_A] = { 0, 0, 0, 0 },
> + [TX_AMP_10M_B] = { 0, 0, 0, 0 },
> + [R50_A] = { 15, 15, 15, 15 },
> + [R50_B] = { 15, 15, 18, 15 },
> + [R50_C] = { -8, -8, -8, -8 },
> + [R50_D] = { -8, -8, -8, -8 },
> + },
> + },
> + [DISCRETE] = {
> + { },
> + {
> + [TX_AMP_TEST_A] = { -9, -7, -7, -5 },
> + [TX_AMP_TEST_B] = { -9, -9, -7, -4 },
> + [TX_AMP_TEST_C] = { -9, -7, -9, -6 },
> + [TX_AMP_TEST_D] = { -11, -9, -10, -8 },
> + [TX_AMP_1G_A] = { -9, -7, -7, -5 },
> + [TX_AMP_1G_B] = { -9, -8, -7, -4 },
> + [TX_AMP_1G_C] = { -9, -7, -9, -6 },
> + [TX_AMP_1G_D] = { -11, -8, -10, -8 },
> + [TX_AMP_100M_A] = { 4, 3, 4, 4 },
> + [TX_AMP_100M_B] = { 4, 4, 5, 4 },
> + [TX_AMP_10M_A] = { 0, 0, 0, 0 },
> + [TX_AMP_10M_B] = { 0, 0, 0, 0 },
> + [R50_A] = { 0, 0, 0, 0 },
> + [R50_B] = { 0, 0, 0, 0 },
> + [R50_C] = { 0, 0, 0, 0 },
> + [R50_D] = { 0, 0, 0, 0 },
> + },
> + },
> + },
> + { /* IC version 2 */
> + [TXMR] = {
> + { },
> + {
> + [TX_AMP_TEST_A] = { -10, -12, -12, -11 },
> + [TX_AMP_TEST_B] = { -10, -11, -11, -10 },
> + [TX_AMP_TEST_C] = { -5, -7, -9, -3 },
> + [TX_AMP_TEST_D] = { -8, -10, -7, -8 },
> + [TX_AMP_1G_A] = { -12, -6, -6, 4 },
> + [TX_AMP_1G_B] = { -10, -5, -5, 1 },
> + [TX_AMP_1G_C] = { -5, -3, -4, 11 },
> + [TX_AMP_1G_D] = { -8, -3, -3, 4 },
> + [TX_AMP_100M_A] = { 2, 1, 0, 3 },
> + [TX_AMP_100M_B] = { 0, 0, 0, 3 },
> + [TX_AMP_10M_A] = { 0, 0, 0, 0 },
> + [TX_AMP_10M_B] = { 0, 0, 0, 0 },
> + [R50_A] = { 15, 15, 15, 15 },
> + [R50_B] = { 15, 15, 18, 15 },
> + [R50_C] = { 4, 4, 4, 4 },
> + [R50_D] = { 4, 4, 4, 4 },
> + },
> + },
> + [DISCRETE] = {
> + { },
> + {
> + [TX_AMP_TEST_A] = { -11, 11, -12, -7 },
> + [TX_AMP_TEST_B] = { -9, -10, -9, -8 },
> + [TX_AMP_TEST_C] = { -6, -3, -7, -4 },
> + [TX_AMP_TEST_D] = { -7, -8, -10, -7 },
> + [TX_AMP_1G_A] = { -11, -11, -12, -7 },
> + [TX_AMP_1G_B] = { -9, -10, -9, -8 },
> + [TX_AMP_1G_C] = { -6, -3, -7, -4 },
> + [TX_AMP_1G_D] = { -7, -8, -10, -7 },
> + [TX_AMP_100M_A] = { 2, 2, 1, 2 },
> + [TX_AMP_100M_B] = { 3, 3, 3, 3 },
> + [TX_AMP_10M_A] = { 0, 0, 0, 0 },
> + [TX_AMP_10M_B] = { 0, 0, 0, 0 },
> + [R50_A] = { 12, 12, 12, 12 },
> + [R50_B] = { 7, 7, 7, 7 },
> + [R50_C] = { 2, 2, 0, 0 },
> + [R50_D] = { 0, 0, 2, 2 },
> + },
> + },
> + },
> +};
> +
> +static u8 an7581_zcal_to_r45ohm[64] = {
> + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
> + 127, 127, 127, 127, 127, 127, 127, 127, 123, 119, 115, 112, 108, 104, 100, 96,
> + 94, 92, 88, 85, 82, 80, 76, 74, 72, 68, 66, 64, 62, 60, 56, 55,
> + 52, 50, 48, 46, 44, 42, 40, 39, 36, 35, 32, 32, 30, 28, 27, 25
> +};
> +
> +static int airoha_cal_cycle(struct phy_device *phydev, int devad,
> + u32 regnum, u16 mask, u16 cal_val)
> +{
> + struct airoha_socphy_shared *shared = phy_package_get_priv(phydev);
> + struct phy_device *phydev_p0;
> + int ret;
> +
> + phydev_p0 = shared->phydev_p0;
> +
> + phy_modify_mmd(phydev, devad, regnum, mask, cal_val);
> +
> + ret = mtk_cal_cycle_wait(phydev_p0);
> + phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret);
> +
> + return ret;
> +}
> +
> +static int airoha_rext_cal_sw(struct phy_device *phydev)
> +{
> + int calibration_polarity;
> + u8 zcal_ctrl = 32;
> + int first_calib;
> + int ret;
> +
> + /* BG voltage output */
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x100, 0xc000);
> + /* tst_mode2 */
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xff, 0x2);
> +
> + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, 0xff,
> + GENMASK(15, 4) | GENMASK(1, 0));
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0,
> + MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN |
> + MTK_PHY_RG_REXT_CALEN);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG6, 0);
> +
> + phydev_dbg(phydev, "Start REXT SW cal.\n");
> + first_calib = airoha_cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5,
> + MTK_PHY_RG_REXT_ZCAL_CTRL_MASK, zcal_ctrl);
> +
> + if (first_calib < 0) {
> + phydev_err(phydev, "REXT SW calibration failed.\n");
> + return -EINVAL;
> + }
> +
> + /* If REXT calibration failed:
> + * - increase dB until calibration succeed.
> + * If REXT calibration succeeded:
> + * - decrease dB until calibration fail to fine tune it.
> + */
> + if (first_calib == 1)
> + calibration_polarity = -1;
> + else
> + calibration_polarity = 1;
> +
> + while (zcal_ctrl > 0 &&
> + zcal_ctrl < FIELD_MAX(MTK_PHY_RG_REXT_ZCAL_CTRL_MASK)) {
> + zcal_ctrl += calibration_polarity;
> +
> + ret = airoha_cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5,
> + MTK_PHY_RG_REXT_ZCAL_CTRL_MASK, zcal_ctrl);
> + /* Exit if we either failed or succeeded compared to the
> + * first calibration result. (aka we finished fine tuning or
> + * we succeeded with calibration)
> + */
> + if (ret != first_calib)
> + break;
> + }
> +
> + if (ret < 0) {
> + phydev_err(phydev, "REXT SW calibration failed.\n");
> + return -EINVAL;
> + }
> +
> + if (zcal_ctrl == 0 ||
> + zcal_ctrl == FIELD_MAX(MTK_PHY_RG_REXT_ZCAL_CTRL_MASK)) {
> + zcal_ctrl = 32;
> + phydev_err(phydev, "REXT SW calibration saturation. Defaulting to %x.\n",
> + zcal_ctrl);
> + }
> +
> + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5,
> + MTK_PHY_RG_REXT_TRIM_MASK,
> + FIELD_PREP(MTK_PHY_RG_REXT_TRIM_MASK, zcal_ctrl));
> + phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_BG_RASEL,
> + MTK_PHY_RG_BG_RASEL_MASK,
> + FIELD_PREP(MTK_PHY_RG_BG_RASEL_MASK, zcal_ctrl >> 3));
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, 0);
> +
> + return 0;
> +}
> +
> +static int airoha_tx_offset_cal_sw(struct phy_device *phydev, u8 txg_calen_x)
> +{
> + struct airoha_socphy_shared *shared = phy_package_get_priv(phydev);
> + struct phy_device *phydev_p0;
> + u16 dev1e_145_tmp, bmcr_tmp;
> + int calibration_polarity;
> + u16 reg_dac1, reg_dac2;
> + int zcal_ctrl = 0;
> + int first_calib;
> + u16 reg, mask;
> + int ret;
> +
> + phydev_p0 = shared->phydev_p0;
> +
> + /* BG voltage output */
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND2, 0x100, 0xc000);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0,
> + MTK_PHY_RG_ANA_CALEN);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1,
> + MTK_PHY_RG_TXVOS_CALEN);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0,
> + MTK_PHY_RG_ANA_CALEN);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1,
> + MTK_PHY_RG_TXVOS_CALEN);
> +
> + /* Force 1G full duplex for calibration */
> + bmcr_tmp = phy_read(phydev, MII_BMCR);
> + phy_write(phydev, MII_BMCR, BMCR_FULLDPLX | BMCR_SPEED1000);
> +
> + /* Force MDI */
> + dev1e_145_tmp = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x0145);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0145, 0x1010);
> +
> + /* 1e_96[15]:bypass_tx_offset_cal, Hw bypass, Fw cal */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x96,
> + 0x8000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x3e,
> + 0xf808);
> +
> + switch (txg_calen_x) {
> + case PAIR_A:
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, BIT(12));
> + reg_dac1 = MTK_PHY_RG_DASN_DAC_IN0_A;
> + reg_dac2 = MTK_PHY_RG_DASN_DAC_IN1_A;
> + reg = MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B;
> + mask = MTK_PHY_CR_TX_AMP_OFFSET_A_MASK;
> + break;
> + case PAIR_B:
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, BIT(8));
> + reg_dac1 = MTK_PHY_RG_DASN_DAC_IN0_B;
> + reg_dac2 = MTK_PHY_RG_DASN_DAC_IN1_B;
> + reg = MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B;
> + mask = MTK_PHY_CR_TX_AMP_OFFSET_B_MASK;
> + break;
> + case PAIR_C:
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, BIT(4));
> + reg_dac1 = MTK_PHY_RG_DASN_DAC_IN0_C;
> + reg_dac2 = MTK_PHY_RG_DASN_DAC_IN1_C;
> + reg = MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D;
> + mask = MTK_PHY_CR_TX_AMP_OFFSET_C_MASK;
> + break;
> + case PAIR_D:
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, BIT(0));
> + reg_dac1 = MTK_PHY_RG_DASN_DAC_IN0_D;
> + reg_dac2 = MTK_PHY_RG_DASN_DAC_IN1_D;
> + reg = MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D;
> + mask = MTK_PHY_CR_TX_AMP_OFFSET_D_MASK;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, reg_dac1,
> + 0x8000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, reg_dac2,
> + 0x8000);
> +
> + phydev_dbg(phydev, "Start TX Offset SW cal.\n");
> +
> + first_calib = airoha_cal_cycle(phydev, MDIO_MMD_VEND1, reg, mask,
> + zcal_ctrl << __ffs(mask));
> +
> + /* If TX Offset calibration failed:
> + * - increase dB until calibration succeed.
> + * If TX Offset calibration succeeded:
> + * - decrease dB until calibration fail to fine tune it.
> + */
> + if (first_calib == 1)
> + calibration_polarity = -1;
> + else
> + calibration_polarity = 1;
> +
> + while (zcal_ctrl > -32 && zcal_ctrl < 32) {
> + u32 val;
> +
> + zcal_ctrl += calibration_polarity;
> + if (zcal_ctrl >= 0)
> + val = zcal_ctrl;
> + else
> + /* BIT(5) signal negative number for TX Offset */
> + val = BIT(5) | abs(zcal_ctrl);
> +
> + ret = airoha_cal_cycle(phydev, MDIO_MMD_VEND1, reg, mask,
> + val << __ffs(mask));
> + /* Exit if we either failed or succeeded compared to the
> + * first calibration result. (aka we finished fine tuning or
> + * we succeeded with calibration)
> + */
> + if (ret != first_calib)
> + break;
> + }
> +
> + if (ret < 0) {
> + phydev_err(phydev, "TX Offset calibration failed.\n");
> + return -EINVAL;
> + }
> +
> + if (zcal_ctrl == -32 ||
> + zcal_ctrl == 32) {
> + zcal_ctrl = 0;
> + phydev_err(phydev, "TX Offset SW calibration saturation. Defaulting to %x.\n",
> + zcal_ctrl);
> +
> + phy_modify_mmd(phydev, MDIO_MMD_VEND1, reg, mask,
> + zcal_ctrl << __ffs(mask));
> + }
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x100, 0x0);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_A, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_B, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_C, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_D, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_A, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_B, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_C, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_D, 0);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, 0);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, 0);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x96, 0x0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x3e, 0xc000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, 0);
> +
> + /* Restore BMCR */
> + phy_write(phydev, MII_BMCR, bmcr_tmp);
> +
> + /* Restore MDI */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0145, dev1e_145_tmp);
> +
> + return 0;
> +}
> +
> +static int airoha_tx_amp_cal_sw(struct phy_device *phydev, u8 txg_calen_x)
> +{
> + struct airoha_socphy_shared *shared = phy_package_get_priv(phydev);
> + u16 mask_gbe, mask_tbt, mask_tst, mask_hbt;
> + u16 reg, reg_100, reg_dac1, reg_dac2;
> + struct phy_device *phydev_p0;
> + int calibration_polarity;
> + u8 zcal_ctrl = 32;
> + int first_calib;
> + int ret;
> +
> + phydev_p0 = shared->phydev_p0;
> +
> + /* BG voltage output */
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND2, 0x100, 0xc000);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x145, 0x1010);
> +
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0,
> + MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1,
> + MTK_PHY_RG_TXVOS_CALEN);
> +
> + /* select 1V */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG6, 0x10);
> +
> + /* enable Tx VLD*/
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x3e, 0xf808);
> +
> + /* Force 1G full duplex for calibration */
> + phy_write(phydev, MII_BMCR, BMCR_FULLDPLX | BMCR_SPEED1000);
> +
> + switch (txg_calen_x) {
> + case PAIR_A:
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, BIT(12));
> + reg_dac1 = MTK_PHY_RG_DASN_DAC_IN0_A;
> + reg_dac2 = MTK_PHY_RG_DASN_DAC_IN1_A;
> + reg = MTK_PHY_TXVLD_DA_RG;
> + mask_gbe = MTK_PHY_DA_TX_I2MPB_A_GBE_MASK;
> + mask_tbt = MTK_PHY_DA_TX_I2MPB_A_TBT_MASK;
> + reg_100 = MTK_PHY_TX_I2MPB_TEST_MODE_A2;
> + mask_hbt = MTK_PHY_DA_TX_I2MPB_A_HBT_MASK;
> + mask_tst = MTK_PHY_DA_TX_I2MPB_A_TST_MASK;
> + break;
> + case PAIR_B:
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, BIT(8));
> + reg_dac1 = MTK_PHY_RG_DASN_DAC_IN0_B;
> + reg_dac2 = MTK_PHY_RG_DASN_DAC_IN1_B;
> + reg = MTK_PHY_TX_I2MPB_TEST_MODE_B1;
> + mask_gbe = MTK_PHY_DA_TX_I2MPB_B_GBE_MASK;
> + mask_tbt = MTK_PHY_DA_TX_I2MPB_B_TBT_MASK;
> + reg_100 = MTK_PHY_TX_I2MPB_TEST_MODE_B2;
> + mask_hbt = MTK_PHY_DA_TX_I2MPB_B_HBT_MASK;
> + mask_tst = MTK_PHY_DA_TX_I2MPB_B_TST_MASK;
> + break;
> + case PAIR_C:
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, BIT(4));
> + reg_dac1 = MTK_PHY_RG_DASN_DAC_IN0_C;
> + reg_dac2 = MTK_PHY_RG_DASN_DAC_IN1_C;
> + reg = MTK_PHY_TX_I2MPB_TEST_MODE_C1;
> + mask_gbe = MTK_PHY_DA_TX_I2MPB_C_GBE_MASK;
> + mask_tbt = MTK_PHY_DA_TX_I2MPB_C_TBT_MASK;
> + reg_100 = MTK_PHY_TX_I2MPB_TEST_MODE_C2;
> + mask_hbt = MTK_PHY_DA_TX_I2MPB_C_HBT_MASK;
> + mask_tst = MTK_PHY_DA_TX_I2MPB_C_TST_MASK;
> + break;
> + case PAIR_D:
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, BIT(0));
> + reg_dac1 = MTK_PHY_RG_DASN_DAC_IN0_D;
> + reg_dac2 = MTK_PHY_RG_DASN_DAC_IN1_D;
> + reg = MTK_PHY_TX_I2MPB_TEST_MODE_D1;
> + mask_gbe = MTK_PHY_DA_TX_I2MPB_D_GBE_MASK;
> + mask_tbt = MTK_PHY_DA_TX_I2MPB_D_TBT_MASK;
> + reg_100 = MTK_PHY_TX_I2MPB_TEST_MODE_D2;
> + mask_hbt = MTK_PHY_DA_TX_I2MPB_D_HBT_MASK;
> + mask_tst = MTK_PHY_DA_TX_I2MPB_D_TST_MASK;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, reg_dac1,
> + 0x8000 | 0xf0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, reg_dac2,
> + 0x8000 | 0xf0);
> +
> + phydev_dbg(phydev, "Start TX Amp SW cal.\n");
> + first_calib = airoha_cal_cycle(phydev, MDIO_MMD_VEND1, reg,
> + mask_gbe, zcal_ctrl << __ffs(mask_gbe));
> +
> + /* If TX Amp calibration failed:
> + * - increase dB until calibration succeed.
> + * If TX Amp calibration succeeded:
> + * - decrease dB until calibration fail to fine tune it.
> + */
> + if (first_calib == 1)
> + calibration_polarity = -1;
> + else
> + calibration_polarity = 1;
> +
> + while (zcal_ctrl > 0 &&
> + zcal_ctrl < FIELD_MAX(MTK_PHY_RG_REXT_ZCAL_CTRL_MASK)) {
> + zcal_ctrl += calibration_polarity;
> +
> + ret = airoha_cal_cycle(phydev, MDIO_MMD_VEND1, reg,
> + mask_gbe, zcal_ctrl << __ffs(mask_gbe));
> + /* Exit if we either failed or succeeded compared to the
> + * first calibration result. (aka we finished fine tuning or
> + * we succeeded with calibration)
> + */
> + if (ret != first_calib)
> + break;
> + }
> +
> + if (ret < 0) {
> + phydev_err(phydev, "TX AMP calibration failed.\n");
> + phy_modify_mmd(phydev, MDIO_MMD_VEND1, reg,
> + mask_gbe | mask_tbt,
> + (zcal_ctrl << __ffs(mask_gbe)) |
> + (zcal_ctrl << __ffs(mask_tbt)));
> +
> + return -EINVAL;
> + }
> +
> + if (zcal_ctrl == 0 ||
> + zcal_ctrl == FIELD_MAX(MTK_PHY_RG_REXT_ZCAL_CTRL_MASK))
> + phydev_warn(phydev, "TX AMP SW calibration saturation.\n");
> +
> + phy_modify_mmd(phydev, MDIO_MMD_VEND1, reg,
> + mask_gbe | mask_tbt,
> + (zcal_ctrl << __ffs(mask_gbe)) |
> + (zcal_ctrl << __ffs(mask_tbt)));
> +
> + phy_modify_mmd(phydev, MDIO_MMD_VEND1, reg_100,
> + mask_hbt | mask_tst,
> + (zcal_ctrl << __ffs(mask_hbt)) |
> + (zcal_ctrl << __ffs(mask_tst)));
> +
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND2, 0x100, 0x0);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_A, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_B, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_C, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_D, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_A, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_B, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_C, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_D, 0);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, 0x145, 0x1000);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, 0);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, 0);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x3e, 0xc000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, 0);
> +
> + /* Restore AN enable */
> + phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
> +
> + return 0;
> +}
> +
> +static int airoha_tx_r50_cal_sw(struct phy_device *phydev, u8 txg_calen_x)
> +{
> + struct airoha_socphy_shared *shared = phy_package_get_priv(phydev);
> + struct phy_device *phydev_p0;
> + u16 dev1e_145_tmp, bmcr_tmp;
> + int calibration_polarity;
> + u8 zcal_ctrl = 32;
> + int first_calib;
> + u16 reg;
> + int ret;
> +
> + phydev_p0 = shared->phydev_p0;
> +
> + /* BG voltage output */
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND2, 0x100, 0xc000);
> +
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0,
> + MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, 0);
> + /* select 1V */
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG6, 0x10);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0,
> + MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, 0);
> + /* select 1V */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG6, 0x10);
> +
> + /* Force 1G full duplex for calibration */
> + bmcr_tmp = phy_read(phydev, MII_BMCR);
> + phy_write(phydev, MII_BMCR, BMCR_FULLDPLX | BMCR_SPEED1000);
> +
> + /* Force MDI */
> + dev1e_145_tmp = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x0145);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0145, 0x1010);
> +
> + /* disable tx slew control */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0185, 0x0000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x00fb, 0x0100);
> +
> + switch (txg_calen_x) {
> + case PAIR_A:
> + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
> + MTK_PHY_RG_ANA_CAL_RG0,
> + MTK_PHY_RG_ZCALEN_A);
> + break;
> + case PAIR_B:
> + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
> + MTK_PHY_RG_ANA_CAL_RG1,
> + MTK_PHY_RG_ZCALEN_B);
> + break;
> + case PAIR_C:
> + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
> + MTK_PHY_RG_ANA_CAL_RG1,
> + MTK_PHY_RG_ZCALEN_C);
> + break;
> + case PAIR_D:
> + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
> + MTK_PHY_RG_ANA_CAL_RG1,
> + MTK_PHY_RG_ZCALEN_D);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + phydev_dbg(phydev, "Start TX r50 SW cal.\n");
> + first_calib = airoha_cal_cycle(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5,
> + MTK_PHY_RG_REXT_ZCAL_CTRL_MASK, zcal_ctrl);
> +
> + if (first_calib < 0) {
> + phydev_err(phydev, "TX r50 SW calibration failed.\n");
> + return -EINVAL;
> + }
> +
> + /* If TX r50 calibration failed:
> + * - increase dB until calibration succeed.
> + * If TX r50 calibration succeeded:
> + * - decrease dB until calibration fail to fine tune it.
> + */
> + if (first_calib == 1)
> + calibration_polarity = -1;
> + else
> + calibration_polarity = 1;
> +
> + while (zcal_ctrl > 0 &&
> + zcal_ctrl < FIELD_MAX(MTK_PHY_RG_REXT_ZCAL_CTRL_MASK)) {
> + zcal_ctrl += calibration_polarity;
> +
> + ret = airoha_cal_cycle(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5,
> + MTK_PHY_RG_REXT_ZCAL_CTRL_MASK, zcal_ctrl);
> + /* Exit if we either failed or succeeded compared to the
> + * first calibration result. (aka we finished fine tuning or
> + * we succeeded with calibration)
> + */
> + if (ret != first_calib)
> + break;
> + }
> +
> + if (ret < 0) {
> + phydev_err(phydev, "TX r50 SW calibration failed.\n");
> + return -EINVAL;
> + }
> +
> + if (zcal_ctrl == 0 ||
> + zcal_ctrl == FIELD_MAX(MTK_PHY_RG_REXT_ZCAL_CTRL_MASK))
> + phydev_warn(phydev, "TX r50 SW calibration saturation.\n");
> +
> + zcal_ctrl = shared->r50_cal_tbl[zcal_ctrl];
> +
> + switch (txg_calen_x) {
> + case PAIR_A:
> + reg = BIT(7) | FIELD_PREP(MTK_PHY_CR_TX_R50_OFFSET_A_MASK, zcal_ctrl);
> + phy_modify_mmd(phydev, MDIO_MMD_VEND1,
> + MTK_PHY_RG_CR_TX_R50_OFFSET_A_B,
> + BIT(7) | MTK_PHY_CR_TX_R50_OFFSET_A_MASK, reg);
> + break;
> + case PAIR_B:
> + reg = BIT(7) | FIELD_PREP(MTK_PHY_CR_TX_R50_OFFSET_B_MASK, zcal_ctrl);
> + phy_modify_mmd(phydev, MDIO_MMD_VEND1,
> + MTK_PHY_RG_CR_TX_R50_OFFSET_A_B,
> + BIT(7) | MTK_PHY_CR_TX_R50_OFFSET_B_MASK, reg);
> + break;
> + case PAIR_C:
> + reg = BIT(7) | FIELD_PREP(MTK_PHY_CR_TX_R50_OFFSET_C_MASK, zcal_ctrl);
> + phy_modify_mmd(phydev, MDIO_MMD_VEND1,
> + MTK_PHY_RG_CR_TX_R50_OFFSET_C_D,
> + BIT(7) | MTK_PHY_CR_TX_R50_OFFSET_C_MASK, reg);
> + break;
> + case PAIR_D:
> + reg = BIT(7) | FIELD_PREP(MTK_PHY_CR_TX_R50_OFFSET_D_MASK, zcal_ctrl);
> + phy_modify_mmd(phydev, MDIO_MMD_VEND1,
> + MTK_PHY_RG_CR_TX_R50_OFFSET_C_D,
> + BIT(7) | MTK_PHY_CR_TX_R50_OFFSET_D_MASK, reg);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, 0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, 0);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, 0);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, 0);
> +
> + /* Enable tx slew control */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0185, 0x0001);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x00fb, 0x0000);
> +
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND2, 0x100, 0x0);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG6, 0x0);
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG6, 0x0);
> +
> + /* Restore BMCR */
> + phy_write(phydev, MII_BMCR, bmcr_tmp);
> +
> + /* Restore MDI */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0145, dev1e_145_tmp);
> +
> + return 0;
> +}
> +
> +static int airoha_rx_offset_cal_sw(struct phy_device *phydev)
> +{
> + /* Hw bypass, Fw cal */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x96, 0x8000);
> + /* tx/rx_cal_criteria_value */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x37, 0x0033);
> +
> + /* rx offset cal by Hw setup */
> + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0x39,
> + BIT(14) | BIT(11));
> +
> + /* disable rtune calibration */
> + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, 0x107,
> + BIT(12));
> +
> + /* bypass tx/rx dc offset cancellation process */
> + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, 0x171,
> + BIT(8) | BIT(7));
> +
> + /* rx offset calibration start */
> + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, 0x39,
> + BIT(13));
> +
> + /* rx offset calibration stop */
> + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0x39,
> + BIT(13));
> +
> + mdelay(10);
> +
> + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0x171,
> + BIT(8) | BIT(7));
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG6,
> + 0x1);
> +
> + return 0;
> +}
> +
> +static int airoha_cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item,
> + u8 start_pair, u8 end_pair)
> +{
> + u8 pair_n;
> + int ret;
> +
> + for (pair_n = start_pair; pair_n <= end_pair; pair_n++) {
> + switch (cal_item) {
> + case REXT:
> + ret = airoha_rext_cal_sw(phydev);
> + break;
> + case TX_OFFSET:
> + ret = airoha_tx_offset_cal_sw(phydev, pair_n);
> + break;
> + case TX_AMP:
> + ret = airoha_tx_amp_cal_sw(phydev, pair_n);
> + break;
> + case TX_R50:
> + ret = airoha_tx_r50_cal_sw(phydev, pair_n);
> + break;
> + case RX_OFFSET:
> + ret = airoha_rx_offset_cal_sw(phydev);
> + break;
> + default:
> + return -EINVAL;
> + }
> + if (ret)
> + return ret;
> + }
> + return 0;
> +}
> +
> +static int airoha_start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item,
> + enum CAL_MODE cal_mode, u8 start_pair,
> + u8 end_pair, u32 *buf)
> +{
> + int ret;
> +
> + switch (cal_mode) {
> + case SW_M:
> + ret = airoha_cal_sw(phydev, cal_item, start_pair, end_pair);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (ret) {
> + phydev_err(phydev, "cal %d failed\n", cal_item);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int airoha_phy_calib(struct phy_device *phydev)
> +{
> + struct airoha_socphy_shared *shared = phy_package_get_priv(phydev);
> + struct phy_device *phydev_p0;
> + int ret;
> +
> + phydev_p0 = shared->phydev_p0;
> +
> + /* PreCalibrate Set */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x5c, 0x6666);
> +
> + if (!shared->rext_sw_calib_done) {
> + ret = airoha_start_cal(phydev, REXT, SW_M, NO_PAIR, NO_PAIR, NULL);
> + if (ret)
> + return ret;
> +
> + shared->rext_sw_calib_done = true;
> + }
> +
> + ret = airoha_start_cal(phydev, TX_R50, SW_M, PAIR_A, PAIR_D, NULL);
> + if (ret)
> + return ret;
> +
> + ret = airoha_start_cal(phydev, TX_OFFSET, SW_M, PAIR_A, PAIR_D, NULL);
> + if (ret)
> + return ret;
> +
> + ret = airoha_start_cal(phydev, TX_AMP, SW_M, PAIR_A, PAIR_D, NULL);
> + if (ret)
> + return ret;
> +
> + ret = airoha_start_cal(phydev, RX_OFFSET, SW_M, NO_PAIR, NO_PAIR, NULL);
> + if (ret)
> + return ret;
> +
> + /* Gating, short with other pair */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x15, 0x0);
> +
> + phy_write_mmd(phydev_p0, MDIO_MMD_VEND2, 0x100, 0x0);
> +
> + return 0;
> +}
> +
> +static int airoha_phy_auto_select_transformer(struct phy_device *phydev)
> +{
> + struct airoha_socphy_shared *shared = phy_package_get_priv(phydev);
> + struct phy_device *phydev_p0;
> + u8 phy_offset;
> + u16 bmcr_tmp;
> + u16 val;
> + int i;
> +
> + phydev_p0 = shared->phydev_p0;
> + phy_offset = phydev->mdio.addr - phydev_p0->mdio.addr;
> +
> + /* Force 1G full duplex for calibration */
> + bmcr_tmp = phy_read(phydev, MII_BMCR);
> + phy_write(phydev, MII_BMCR, BMCR_FULLDPLX | BMCR_SPEED1000);
> +
> + phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0x271, GENMASK(4, 0),
> + FIELD_PREP(GENMASK(4, 0), 0x3));
> +
> + phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0x269, GENMASK(15, 12),
> + FIELD_PREP(GENMASK(15, 12), 0x2));
> +
> + phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0x26f, GENMASK(14, 12),
> + FIELD_PREP(GENMASK(14, 12), 0x0));
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, BIT(12));
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_A,
> + 0x8000 | 0xf0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_A,
> + 0x8000 | 0xf0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_B,
> + 0x8000 | 0xf0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_B,
> + 0x8000 | 0xf0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_C,
> + 0x8000 | 0xf0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_C,
> + 0x8000 | 0xf0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_D,
> + 0x8000 | 0xf0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_D,
> + 0x8000 | 0xf0);
> +
> + /* Force pGA gain */
> + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
> + __mtk_tr_modify(phydev, 0x1, 0xf, 0x10,
> + GENMASK(20, 15),
> + FIELD_PREP(GENMASK(20, 15), 0x1f));
> +
> + __mtk_tr_modify(phydev, 0x1, 0xf, 0x11,
> + GENMASK(23, 18) | GENMASK(15, 10) | GENMASK(7, 2),
> + FIELD_PREP(GENMASK(23, 18), 0x1f) |
> + FIELD_PREP(GENMASK(15, 10), 0x1f) |
> + FIELD_PREP(GENMASK(7, 2), 0x1f));
> + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xc9, 0xffff);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x151, 0x12);
> +
> + switch (phy_offset) {
> + case 0:
> + val = 0x1;
> + break;
> + case 1:
> + val = 0x3;
> + break;
> + case 2:
> + val = 0x5;
> + break;
> + case 3:
> + val = 0x7;
> + break;
> + default:
> + return -EINVAL;
> + }
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x15,
> + FIELD_PREP(GENMASK(14, 12), val) | 0x161);
> + mdelay(500);
> +
> + for (i = 0; i < 3; i++)
> + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0x1a);
> +
> + /* Check the value and report transformer type for later */
> + shared->transformer_type[phy_offset] = val < 0xe0;
> +
> + /* Switch to Default */
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x15, 0x1000);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xdd, 0x0);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_A,
> + 0x0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_A,
> + 0x0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_B,
> + 0x0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_B,
> + 0x0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_C,
> + 0x0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_C,
> + 0x0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN0_D,
> + 0x0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DASN_DAC_IN1_D,
> + 0x0);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xc9, 0xfff);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x151, 0x0);
> +
> + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
> + __mtk_tr_modify(phydev, 0x1, 0xf, 0x10,
> + GENMASK(20, 15),
> + FIELD_PREP(GENMASK(20, 15), 0x4));
> +
> + __mtk_tr_modify(phydev, 0x1, 0xf, 0x11,
> + GENMASK(23, 18) | GENMASK(15, 10) | GENMASK(7, 2),
> + FIELD_PREP(GENMASK(23, 18), 0x0) |
> + FIELD_PREP(GENMASK(15, 10), 0x0) |
> + FIELD_PREP(GENMASK(7, 2), 0x0));
> + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
> +
> + /* Restore BMCR */
> + phy_write(phydev, MII_BMCR, bmcr_tmp);
> +
> + return 0;
> +}
> +
> +static int airoha_phy_tx_amp_compensation(struct phy_device *phydev)
> +{
> + struct airoha_socphy_shared *shared = phy_package_get_priv(phydev);
> +
> + u16 reg_1e_12_read;
> + u16 reg_1e_16_read;
> + u16 reg_1e_17_read;
> + u16 reg_1e_18_read;
> + u16 reg_1e_19_read;
> + u16 reg_1e_20_read;
> + u16 reg_1e_21_read;
> + u16 reg_1e_22_read;
> + u16 reg_1e_174_read;
> + u16 reg_1e_175_read;
> +
> + int reg_1e_12_header;
> + int reg_1e_16_header;
> + int reg_1e_17_header;
> + int reg_1e_18_header;
> + int reg_1e_19_header;
> + int reg_1e_21_header;
> + int reg_1e_174_header;
> + int reg_1e_175_header;
> +
> + int reg_1e_12_end;
> + int reg_1e_16_end;
> + int reg_1e_17_end;
> + int reg_1e_18_end;
> + int reg_1e_20_end;
> + int reg_1e_22_end;
> + int reg_1e_174_end;
> + int reg_1e_175_end;
> +
> + struct phy_device *phydev_p0;
> + u8 phy_offset;
> +
> + int txamp_low_limit = -3;
> + int txamp_high_limit = 0x42;
> +
> + int r50_low_limit = -8;
> + int r50_high_limit = 0x88;
> +
> + bool overflow;
> + int (*tx_amp_table)[CALIB_CONST_TYPE_MAX][4];
> + int transformer_type, mdi_resister_type;
> +
> + phydev_p0 = shared->phydev_p0;
> + phy_offset = phydev->mdio.addr - phydev_p0->mdio.addr;
> +
> + transformer_type = shared->transformer_type[phy_offset];
> + mdi_resister_type = shared->mdi_resister_type;
> + tx_amp_table = shared->tx_amp_compensation_tbl[transformer_type][mdi_resister_type];
> +
> + reg_1e_12_read = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x12);
> + reg_1e_16_read = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x16);
> + reg_1e_17_read = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x17);
> + reg_1e_18_read = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x18);
> + reg_1e_19_read = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x19);
> + reg_1e_20_read = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x20);
> + reg_1e_21_read = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x21);
> + reg_1e_22_read = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x22);
> + reg_1e_174_read = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x174);
> + reg_1e_175_read = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x175);
> +
> + reg_1e_12_header = ((reg_1e_12_read & 0xfc00) / 1024) + *tx_amp_table[TX_AMP_1G_A][phy_offset];
> + reg_1e_17_header = ((reg_1e_17_read & 0x3f00) / 256) + *tx_amp_table[TX_AMP_1G_B][phy_offset];
> + reg_1e_19_header = ((reg_1e_19_read & 0x3f00) / 256) + *tx_amp_table[TX_AMP_1G_C][phy_offset];
> + reg_1e_21_header = ((reg_1e_21_read & 0x3f00) / 256) + *tx_amp_table[TX_AMP_1G_D][phy_offset];
> +
> + overflow = false;
> + if (reg_1e_12_header < txamp_low_limit ||
> + reg_1e_12_header > txamp_high_limit)
> + overflow = true;
> + if (reg_1e_17_header < txamp_low_limit ||
> + reg_1e_17_header > txamp_high_limit)
> + overflow = true;
> + if (reg_1e_19_header < txamp_low_limit ||
> + reg_1e_19_header > txamp_high_limit)
> + overflow = true;
> + if (reg_1e_21_header < txamp_low_limit ||
> + reg_1e_21_header > txamp_high_limit)
> + overflow = true;
> +
> + if (overflow) {
> + reg_1e_12_header = 0x20;
> + reg_1e_17_header = 0x20;
> + reg_1e_19_header = 0x20;
> + reg_1e_21_header = 0x20;
> + } else {
> + if (reg_1e_12_header < 0)
> + reg_1e_12_header = 0;
> + else if (reg_1e_12_header > 0x3f)
> + reg_1e_12_header = 0x3f;
> + if (reg_1e_17_header < 0)
> + reg_1e_17_header = 0;
> + else if (reg_1e_17_header > 0x3f)
> + reg_1e_17_header = 0x3f;
> + if (reg_1e_19_header < 0)
> + reg_1e_19_header = 0;
> + else if (reg_1e_19_header > 0x3f)
> + reg_1e_19_header = 0x3f;
> + if (reg_1e_21_header < 0)
> + reg_1e_21_header = 0;
> + else if (reg_1e_21_header > 0x3f)
> + reg_1e_21_header = 0x3f;
> + }
> +
> + reg_1e_16_end = (reg_1e_16_read & 0x003f) + *tx_amp_table[TX_AMP_TEST_A][phy_offset];
> + reg_1e_18_end = (reg_1e_18_read & 0x003f) + *tx_amp_table[TX_AMP_TEST_B][phy_offset];
> + reg_1e_20_end = (reg_1e_20_read & 0x003f) + *tx_amp_table[TX_AMP_TEST_C][phy_offset];
> + reg_1e_22_end = (reg_1e_22_read & 0x003f) + *tx_amp_table[TX_AMP_TEST_D][phy_offset];
> +
> + overflow = false;
> + if (reg_1e_16_end < txamp_low_limit ||
> + reg_1e_16_end > txamp_high_limit)
> + overflow = true;
> + if (reg_1e_18_end < txamp_low_limit ||
> + reg_1e_18_end > txamp_high_limit)
> + overflow = true;
> + if (reg_1e_20_end < txamp_low_limit ||
> + reg_1e_20_end > txamp_high_limit)
> + overflow = true;
> + if (reg_1e_22_end < txamp_low_limit ||
> + reg_1e_22_end > txamp_high_limit)
> + overflow = true;
> +
> + if (overflow) {
> + reg_1e_16_end = 0x20;
> + reg_1e_18_end = 0x20;
> + reg_1e_20_end = 0x20;
> + reg_1e_22_end = 0x20;
> + } else {
> + if (reg_1e_16_end < 0)
> + reg_1e_16_end = 0;
> + else if (reg_1e_16_end > 0x3f)
> + reg_1e_16_end = 0x3f;
> + if (reg_1e_18_end < 0)
> + reg_1e_18_end = 0;
> + else if (reg_1e_18_end > 0x3f)
> + reg_1e_18_end = 0x3f;
> + if (reg_1e_20_end < 0)
> + reg_1e_20_end = 0;
> + else if (reg_1e_20_end > 0x3f)
> + reg_1e_20_end = 0x3f;
> + if (reg_1e_22_end < 0)
> + reg_1e_22_end = 0;
> + else if (reg_1e_22_end > 0x3f)
> + reg_1e_22_end = 0x3f;
> + }
> +
> + reg_1e_16_header = ((reg_1e_16_read & 0xfc00) / 1024) + *tx_amp_table[TX_AMP_100M_A][phy_offset];
> + reg_1e_18_header = ((reg_1e_18_read & 0x3f00) / 256) + *tx_amp_table[TX_AMP_100M_B][phy_offset];
> +
> + overflow = false;
> + if (reg_1e_16_header < txamp_low_limit ||
> + reg_1e_16_header > txamp_high_limit)
> + overflow = true;
> + if (reg_1e_18_header < txamp_low_limit ||
> + reg_1e_18_header > txamp_high_limit)
> + overflow = true;
> +
> + if (overflow) {
> + reg_1e_16_header = 0x20;
> + reg_1e_18_header = 0x20;
> + } else {
> + if (reg_1e_16_header < 0)
> + reg_1e_16_header = 0;
> + else if (reg_1e_16_header > 0x3f)
> + reg_1e_16_header = 0x3f;
> + if (reg_1e_18_header < 0)
> + reg_1e_18_header = 0;
> + else if (reg_1e_18_header > 0x3f)
> + reg_1e_18_header = 0x3f;
> + }
> +
> + reg_1e_12_end = (reg_1e_12_read & 0x003f) + *tx_amp_table[TX_AMP_10M_A][phy_offset];
> + reg_1e_17_end = (reg_1e_17_read & 0x003f) + *tx_amp_table[TX_AMP_10M_B][phy_offset];
> +
> + overflow = false;
> + if (reg_1e_12_end < txamp_low_limit ||
> + reg_1e_12_end > txamp_high_limit)
> + overflow = true;
> + if (reg_1e_17_end < txamp_low_limit ||
> + reg_1e_17_end > txamp_high_limit)
> + overflow = true;
> +
> + if (overflow) {
> + reg_1e_12_end = 0x20;
> + reg_1e_17_end = 0x20;
> + } else {
> + if (reg_1e_12_end < 0)
> + reg_1e_12_end = 0;
> + else if (reg_1e_12_end > 0x3f)
> + reg_1e_12_end = 0x3f;
> + if (reg_1e_17_end < 0)
> + reg_1e_17_end = 0;
> + else if (reg_1e_17_end > 0x3f)
> + reg_1e_17_end = 0x3f;
> + }
> +
> + reg_1e_174_header = ((reg_1e_174_read & 0x7f00) / 256) + *tx_amp_table[R50_A][phy_offset];
> + reg_1e_174_end = (reg_1e_174_read & 0x007f) + *tx_amp_table[R50_B][phy_offset];
> + reg_1e_175_header = ((reg_1e_175_read & 0x7f00) / 256) + *tx_amp_table[R50_C][phy_offset];
> + reg_1e_175_end = (reg_1e_175_read & 0x007f) + *tx_amp_table[R50_D][phy_offset];
> +
> + overflow = false;
> + if (reg_1e_174_header < r50_low_limit ||
> + reg_1e_174_header > r50_high_limit)
> + overflow = true;
> + if (reg_1e_174_end < r50_low_limit ||
> + reg_1e_174_end > r50_high_limit)
> + overflow = true;
> + if (reg_1e_175_header < r50_low_limit ||
> + reg_1e_175_header > r50_high_limit)
> + overflow = true;
> + if (reg_1e_175_end < r50_low_limit ||
> + reg_1e_175_end > r50_high_limit)
> + overflow = true;
> +
> + if (overflow) {
> + reg_1e_174_header = 0x34;
> + reg_1e_174_end = 0x34;
> + reg_1e_175_header = 0x34;
> + reg_1e_175_end = 0x34;
> + } else {
> + if (reg_1e_174_header < 0)
> + reg_1e_174_header = 0;
> + else if (reg_1e_174_header > 0x7f)
> + reg_1e_174_header = 0x7f;
> + if (reg_1e_174_end < 0)
> + reg_1e_174_end = 0;
> + else if (reg_1e_174_end > 0x7f)
> + reg_1e_174_end = 0x7f;
> + if (reg_1e_175_header < 0)
> + reg_1e_175_header = 0;
> + else if (reg_1e_175_header > 0x7f)
> + reg_1e_175_header = 0x7f;
> + if (reg_1e_175_end < 0)
> + reg_1e_175_end = 0;
> + else if (reg_1e_175_end > 0x7f)
> + reg_1e_175_end = 0x7f;
> + }
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x12,
> + (reg_1e_12_header * 1024) + reg_1e_12_end);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x16,
> + (reg_1e_16_header * 1024) + reg_1e_16_end);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x17,
> + (reg_1e_17_header * 256) + reg_1e_17_end);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x18,
> + (reg_1e_18_header * 256) + reg_1e_18_end);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x19,
> + (reg_1e_19_read & 0x00ff) + (reg_1e_19_header * 256));
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x20,
> + (reg_1e_20_read & 0xff00) + reg_1e_20_end);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x21,
> + (reg_1e_21_read & 0x00ff) + (reg_1e_21_header * 256));
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x22,
> + (reg_1e_22_read & 0xff00) + reg_1e_22_end);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x174,
> + 0x8080 + (reg_1e_174_header * 256) + reg_1e_174_end);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x175,
> + 0x8080 + (reg_1e_175_header * 256) + reg_1e_175_end);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x176, 0x4400);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x177, 0x0044);
> +
> + return 0;
> +}
> +
> +static int an7581_phy_config_init(struct phy_device *phydev)
> +{
> + struct airoha_socphy_shared *shared = phy_package_get_priv(phydev);
> + struct phy_device *phydev_p0;
> + u32 soc_pdidr;
> + u8 phy_offset;
> + int ret;
> +
> + phydev_p0 = shared->phydev_p0;
> + phy_offset = phydev->mdio.addr - phydev_p0->mdio.addr;
> +
> + /* FIXME: Read SoC PDIDR if available or default to 1 */
> + soc_pdidr = 1;
> +
> + shared->mdi_resister_type = MDI_5R;
> + if (soc_pdidr == 1)
> + shared->tx_amp_compensation_tbl = &an7581_tx_amp_compensation_tbl[0];
> + else
> + shared->tx_amp_compensation_tbl = &an7581_tx_amp_compensation_tbl[1];
> + shared->r50_cal_tbl = an7581_zcal_to_r45ohm;
> +
> + ret = airoha_phy_calib(phydev);
> + if (ret)
> + return ret;
> +
> + ret = airoha_phy_auto_select_transformer(phydev);
> + if (ret)
> + return ret;
> +
> + ret = airoha_phy_tx_amp_compensation(phydev);
> + if (ret)
> + return ret;
> +
> + /* RX setting for 5R_TXMR before AN setting */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xe6, 0x1111);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xe7, 0x5555);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xe9, 0x0001);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xfe, 0x0000);
> +
> + /* Long Loop Reach setting */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x11, 0xff00);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x202, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x203, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x204, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x205, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x206, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x207, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x208, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x209, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x20a, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x20b, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x20e, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x20f, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x210, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x211, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x212, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x213, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x214, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x215, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x216, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x217, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x21a, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x21b, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x21c, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x21d, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x21e, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x21f, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x220, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x221, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x222, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x223, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x226, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x227, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x228, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x229, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x22a, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x22b, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x22c, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x22d, 0x0025);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x22e, 0x2310);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x22f, 0x0025);
> +
> + /* RX Setting */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x3c, 0xc000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x3d, 0x0000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x3e, 0xc000);
> +
> + /* EEE setting */
> + phy_write_mmd(phydev, MDIO_MMD_AN, 0x3c, 0x0000);
> +
> + /* RX */
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x41, 0x3333);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x44, 0x00c0);
> +
> + /* 10M settings */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x1a3, 0x00d2);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x1a4, 0x010e);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x27b, 0x1177);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x27c, 0x0808);
> +
> + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_1);
> + __phy_write(phydev, 0x14, 0x3a18);
> + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
> +
> + phy_write(phydev, 0x9, 0x1e00);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x269, 0x2114);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x26a, 0x1113);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x26f, 0x0000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x271, 0x2c63);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x272, 0x0c2b);
> +
> + /* EEE set because power down can't set tokenring */
> + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
> + __mtk_tr_write(phydev, 0x0, 0x7, 0x15, 0x0055a0);
> + __mtk_tr_write(phydev, 0x0, 0x7, 0x17, 0x07ff3f);
> + __mtk_tr_write(phydev, 0x1, 0xf, 0x0, 0x00001e);
> + __mtk_tr_write(phydev, 0x1, 0xf, 0x1, 0x6fb90a);
> + __mtk_tr_write(phydev, 0x1, 0xf, 0x18, 0x0e2f00);
> + __mtk_tr_write(phydev, 0x1, 0xd, 0x26, 0x444444);
> + __mtk_tr_write(phydev, 0x2, 0xd, 0x3, 0x000004);
> + __mtk_tr_write(phydev, 0x2, 0xd, 0x6, 0x2ebaef);
> + __mtk_tr_write(phydev, 0x2, 0xd, 0x8, 0x00000b);
> + __mtk_tr_write(phydev, 0x2, 0xd, 0xc, 0x00504d);
> + __mtk_tr_write(phydev, 0x2, 0xd, 0xd, 0x02314f);
> + __mtk_tr_write(phydev, 0x2, 0xd, 0xf, 0x003028);
> + __mtk_tr_write(phydev, 0x2, 0xd, 0x10, 0x00000a);
> + __mtk_tr_write(phydev, 0x2, 0xd, 0x11, 0x040001);
> + __mtk_tr_write(phydev, 0x2, 0xd, 0x14, 0x00024a);
> + __mtk_tr_write(phydev, 0x2, 0xd, 0x1c, 0x003210);
> + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x120, 0x8014);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x122, 0xffff);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x144, 0x0200);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14a, 0xee20);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x19b, 0x0111);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x234, 0x1181);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x238, 0x0120);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x239, 0x0117);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x2d1, 0x0733);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x323, 0x0011);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x324, 0x013f);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x326, 0x0037);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x268, 0x07f4);
> +
> + if (shared->transformer_type[phy_offset] == TXMR) {
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x00, 0x0187);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01, 0x01cc);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x02, 0x01c2);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x03, 0x0109);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x04, 0x020b);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x05, 0x0202);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x06, 0x0387);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x07, 0x03c5);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x08, 0x03c2);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x09, 0x0309);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0a, 0x000e);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0b, 0x0002);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x23, 0x0880);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x24, 0x0880);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x25, 0x0880);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x26, 0x0880);
> + } else {
> + if (soc_pdidr == 2) {
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x269, 0x1114);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x26a, 0x1113);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x26f, 0x2000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x271, 0x4c88);
> + phy_write_mmd(phydev, MDIO_MMD_VEND2, 0x272, 0x0c2b);
> +
> + /* 100M sharp */
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x00, 0x0190);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01, 0x01c8);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x02, 0x01c0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x03, 0x0030);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x04, 0x0208);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x05, 0x0000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x06, 0x0318);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x07, 0x03c8);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x08, 0x03c0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x09, 0x0230);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0a, 0x0008);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0b, 0x0200);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x23, 0x0885);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x24, 0x0885);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x25, 0x0885);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x26, 0x0885);
> + } else {
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x00, 0x0190);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01, 0x01cf);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x02, 0x01c0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x03, 0x0030);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x04, 0x020f);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x05, 0x0000);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x06, 0x0318);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x07, 0x03cf);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x08, 0x03c0);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x09, 0x0230);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0a, 0x000f);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0b, 0x0200);
> +
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x23, 0x0003);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x24, 0x0003);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x25, 0x0003);
> + phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x26, 0x0003);
> + }
> + }
> +
> + return 0;
> +}
> +
> static int an7581_phy_probe(struct phy_device *phydev)
> {
> + struct airoha_socphy_shared *shared;
> struct mtk_socphy_priv *priv;
> struct pinctrl *pinctrl;
> + int ret;
>
> /* Toggle pinctrl to enable PHY LED */
> pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led");
> @@ -18,10 +1495,18 @@ static int an7581_phy_probe(struct phy_device *phydev)
> dev_err(&phydev->mdio.bus->dev,
> "Failed to setup PHY LED pinctrl\n");
>
> + ret = devm_phy_package_join(&phydev->mdio.dev, phydev, 0,
This should use AIROHA_DEFAULT_PORT0_ADDR as the base_addr to avoid a
collision if another PHY driver also uses devm_phy_package_join().
Thanks,
Caleb
> + sizeof(struct airoha_socphy_shared));
> + if (ret)
> + return ret;
> +
> priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
> if (!priv)
> return -ENOMEM;
>
> + if (phydev->mdio.addr == AIROHA_DEFAULT_PORT0_ADDR)
> + shared->phydev_p0 = phydev;
> +
> phydev->priv = priv;
>
> return 0;
> @@ -63,6 +1548,7 @@ static struct phy_driver mtk_socphy_driver[] = {
> {
> PHY_ID_MATCH_EXACT(AIROHA_GPHY_ID_AN7581),
> .name = "Airoha AN7581 PHY",
> + .config_init = an7581_phy_config_init,
> .config_intr = genphy_no_config_intr,
> .handle_interrupt = genphy_handle_interrupt_no_ack,
> .probe = an7581_phy_probe,
> @@ -72,6 +1558,8 @@ static struct phy_driver mtk_socphy_driver[] = {
> .led_hw_control_set = mt798x_phy_led_hw_control_set,
> .led_hw_control_get = mt798x_phy_led_hw_control_get,
> .led_polarity_set = an7581_phy_led_polarity_set,
> + .read_page = mtk_phy_read_page,
> + .write_page = mtk_phy_write_page,
> },
> {
> PHY_ID_MATCH_EXACT(AIROHA_GPHY_ID_AN7583),
> @@ -98,5 +1586,6 @@ static const struct mdio_device_id __maybe_unused airoha_socphy_tbl[] = {
> MODULE_DESCRIPTION("Airoha SoC Gigabit Ethernet PHY driver");
> MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
> MODULE_LICENSE("GPL");
> +MODULE_VERSION("01.01.20260306");
>
> MODULE_DEVICE_TABLE(mdio, airoha_socphy_tbl);
> diff --git a/drivers/net/phy/mediatek/mtk-ge-soc.h b/drivers/net/phy/mediatek/mtk-ge-soc.h
> index 507e68343576..5e9d24a9833c 100644
> --- a/drivers/net/phy/mediatek/mtk-ge-soc.h
> +++ b/drivers/net/phy/mediatek/mtk-ge-soc.h
> @@ -12,6 +12,7 @@
>
> #define MTK_EXT_PAGE_ACCESS 0x1f
> #define MTK_PHY_PAGE_STANDARD 0x0000
> +#define MTK_PHY_PAGE_EXTENDED_1 0x0001
> #define MTK_PHY_PAGE_EXTENDED_3 0x0003
>
> #define MTK_PHY_LPI_REG_14 0x14
> @@ -177,6 +178,7 @@
> #define MTK_PHY_RG_ANA_CAL_RG0 0xdb
> #define MTK_PHY_RG_CAL_CKINV BIT(12)
> #define MTK_PHY_RG_ANA_CALEN BIT(8)
> +#define MTK_PHY_RG_REXT_CALEN BIT(4)
> #define MTK_PHY_RG_ZCALEN_A BIT(0)
>
> #define MTK_PHY_RG_ANA_CAL_RG1 0xdc
> @@ -187,6 +189,9 @@
>
> #define MTK_PHY_RG_ANA_CAL_RG5 0xe0
> #define MTK_PHY_RG_REXT_TRIM_MASK GENMASK(13, 8)
> +#define MTK_PHY_RG_REXT_ZCAL_CTRL_MASK GENMASK(5, 0)
> +
> +#define MTK_PHY_RG_ANA_CAL_RG6 0xe1
>
> #define MTK_PHY_RG_TX_FILTER 0xfe
>
> @@ -208,6 +213,14 @@
> #define MTK_PHY_CR_TX_AMP_OFFSET_C_MASK GENMASK(13, 8)
> #define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0)
>
> +#define MTK_PHY_RG_CR_TX_R50_OFFSET_A_B 0x174
> +#define MTK_PHY_CR_TX_R50_OFFSET_A_MASK GENMASK(13, 8)
> +#define MTK_PHY_CR_TX_R50_OFFSET_B_MASK GENMASK(6, 0)
> +
> +#define MTK_PHY_RG_CR_TX_R50_OFFSET_C_D 0x175
> +#define MTK_PHY_CR_TX_R50_OFFSET_C_MASK GENMASK(13, 8)
> +#define MTK_PHY_CR_TX_R50_OFFSET_D_MASK GENMASK(6, 0)
> +
> #define MTK_PHY_RG_AD_CAL_COMP 0x17a
> #define MTK_PHY_AD_CAL_COMP_OUT_MASK GENMASK(8, 8)
>
next prev parent reply other threads:[~2026-08-25 18:15 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-24 19:08 [PATCH net-next v2 0/6] net: phy: mediatek: calibration for AN7581/3 Christian Marangi
2026-07-24 19:08 ` [PATCH net-next v2 1/6] net: phy: mediatek: export __mtk_tr_write Christian Marangi
2026-07-24 19:08 ` [PATCH net-next v2 2/6] net: phy: mediatek: move MTK GE SoC registers define to dedicated header Christian Marangi
2026-07-24 19:08 ` [PATCH net-next v2 3/6] net: phy: mediatek: generalize cal_cycle function for GE SoC Christian Marangi
2026-07-24 19:08 ` [PATCH net-next v2 4/6] net: phy: mediatek: split Airoha code to dedicated source Christian Marangi
2026-07-24 19:08 ` [PATCH net-next v2 5/6] net: phy: mediatek: add calibration logic for AN7581 Christian Marangi
2026-07-30 20:10 ` Jakub Kicinski
2026-08-22 16:24 ` Caleb James DeLisle
2026-08-25 18:15 ` Caleb James DeLisle [this message]
2026-08-25 19:41 ` Andrew Lunn
2026-07-24 19:08 ` [PATCH net-next v2 6/6] net: phy: mediatek: add calibration logic for AN7583 Christian Marangi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d18d84e4-af86-405e-aadd-bd9a8dfe8742@cjdns.fr \
--to=cjd@cjdns.fr \
--cc=SkyLake.Huang@mediatek.com \
--cc=andrew@lunn.ch \
--cc=angelogioacchino.delregno@collabora.com \
--cc=ansuelsmth@gmail.com \
--cc=daniel@makrotopia.org \
--cc=davem@davemloft.net \
--cc=dqfext@gmail.com \
--cc=edumazet@google.com \
--cc=hkallweit1@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux@armlinux.org.uk \
--cc=matthias.bgg@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox