root/drivers/net/pcs/pcs-xpcs-nxp.c
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2021 NXP
 */
#include <linux/pcs/pcs-xpcs.h>
#include "pcs-xpcs.h"

/* LANE_DRIVER1_0 register */
#define SJA1110_LANE_DRIVER1_0          0x8038
#define SJA1110_TXDRV(x)                (((x) << 12) & GENMASK(14, 12))

/* LANE_DRIVER2_0 register */
#define SJA1110_LANE_DRIVER2_0          0x803a
#define SJA1110_TXDRVTRIM_LSB(x)        ((x) & GENMASK_ULL(15, 0))

/* LANE_DRIVER2_1 register */
#define SJA1110_LANE_DRIVER2_1          0x803b
#define SJA1110_LANE_DRIVER2_1_RSV      BIT(9)
#define SJA1110_TXDRVTRIM_MSB(x)        (((x) & GENMASK_ULL(23, 16)) >> 16)

/* LANE_TRIM register */
#define SJA1110_LANE_TRIM               0x8040
#define SJA1110_TXTEN                   BIT(11)
#define SJA1110_TXRTRIM(x)              (((x) << 8) & GENMASK(10, 8))
#define SJA1110_TXPLL_BWSEL             BIT(7)
#define SJA1110_RXTEN                   BIT(6)
#define SJA1110_RXRTRIM(x)              (((x) << 3) & GENMASK(5, 3))
#define SJA1110_CDR_GAIN                BIT(2)
#define SJA1110_ACCOUPLE_RXVCM_EN       BIT(0)

/* LANE_DATAPATH_1 register */
#define SJA1110_LANE_DATAPATH_1         0x8037

/* POWERDOWN_ENABLE register */
#define SJA1110_POWERDOWN_ENABLE        0x8041
#define SJA1110_TXPLL_PD                BIT(12)
#define SJA1110_TXPD                    BIT(11)
#define SJA1110_RXPKDETEN               BIT(10)
#define SJA1110_RXCH_PD                 BIT(9)
#define SJA1110_RXBIAS_PD               BIT(8)
#define SJA1110_RESET_SER_EN            BIT(7)
#define SJA1110_RESET_SER               BIT(6)
#define SJA1110_RESET_DES               BIT(5)
#define SJA1110_RCVEN                   BIT(4)

/* RXPLL_CTRL0 register */
#define SJA1110_RXPLL_CTRL0             0x8065
#define SJA1110_RXPLL_FBDIV(x)          (((x) << 2) & GENMASK(9, 2))

/* RXPLL_CTRL1 register */
#define SJA1110_RXPLL_CTRL1             0x8066
#define SJA1110_RXPLL_REFDIV(x)         ((x) & GENMASK(4, 0))

/* TXPLL_CTRL0 register */
#define SJA1110_TXPLL_CTRL0             0x806d
#define SJA1110_TXPLL_FBDIV(x)          ((x) & GENMASK(11, 0))

/* TXPLL_CTRL1 register */
#define SJA1110_TXPLL_CTRL1             0x806e
#define SJA1110_TXPLL_REFDIV(x)         ((x) & GENMASK(5, 0))

/* RX_DATA_DETECT register */
#define SJA1110_RX_DATA_DETECT          0x8045

/* RX_CDR_CTLE register */
#define SJA1110_RX_CDR_CTLE             0x8042

/* In NXP SJA1105, the PCS is integrated with a PMA that has the TX lane
 * polarity inverted by default (PLUS is MINUS, MINUS is PLUS). To obtain
 * normal non-inverted behavior, the TX lane polarity must be inverted in the
 * PCS, via the DIGITAL_CONTROL_2 register.
 */
int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs)
{
        return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL2,
                          DW_VR_MII_DIG_CTRL2_TX_POL_INV);
}

static int nxp_sja1110_pma_config(struct dw_xpcs *xpcs,
                                  u16 txpll_fbdiv, u16 txpll_refdiv,
                                  u16 rxpll_fbdiv, u16 rxpll_refdiv,
                                  u16 rx_cdr_ctle)
{
        u16 val;
        int ret;

        /* Program TX PLL feedback divider and reference divider settings for
         * correct oscillation frequency.
         */
        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_TXPLL_CTRL0,
                         SJA1110_TXPLL_FBDIV(txpll_fbdiv));
        if (ret < 0)
                return ret;

        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_TXPLL_CTRL1,
                         SJA1110_TXPLL_REFDIV(txpll_refdiv));
        if (ret < 0)
                return ret;

        /* Program transmitter amplitude and disable amplitude trimming */
        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_LANE_DRIVER1_0,
                         SJA1110_TXDRV(0x5));
        if (ret < 0)
                return ret;

        val = SJA1110_TXDRVTRIM_LSB(0xffffffull);

        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_LANE_DRIVER2_0, val);
        if (ret < 0)
                return ret;

        val = SJA1110_TXDRVTRIM_MSB(0xffffffull) | SJA1110_LANE_DRIVER2_1_RSV;

        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_LANE_DRIVER2_1, val);
        if (ret < 0)
                return ret;

        /* Enable input and output resistor terminations for low BER. */
        val = SJA1110_ACCOUPLE_RXVCM_EN | SJA1110_CDR_GAIN |
              SJA1110_RXRTRIM(4) | SJA1110_RXTEN | SJA1110_TXPLL_BWSEL |
              SJA1110_TXRTRIM(3) | SJA1110_TXTEN;

        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_LANE_TRIM, val);
        if (ret < 0)
                return ret;

        /* Select PCS as transmitter data source. */
        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_LANE_DATAPATH_1, 0);
        if (ret < 0)
                return ret;

        /* Program RX PLL feedback divider and reference divider for correct
         * oscillation frequency.
         */
        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RXPLL_CTRL0,
                         SJA1110_RXPLL_FBDIV(rxpll_fbdiv));
        if (ret < 0)
                return ret;

        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RXPLL_CTRL1,
                         SJA1110_RXPLL_REFDIV(rxpll_refdiv));
        if (ret < 0)
                return ret;

        /* Program threshold for receiver signal detector.
         * Enable control of RXPLL by receiver signal detector to disable RXPLL
         * when an input signal is not present.
         */
        ret = xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RX_DATA_DETECT, 0x0005);
        if (ret < 0)
                return ret;

        /* Enable TX and RX PLLs and circuits.
         * Release reset of PMA to enable data flow to/from PCS.
         */
        ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, SJA1110_POWERDOWN_ENABLE,
                          SJA1110_TXPLL_PD | SJA1110_TXPD | SJA1110_RXCH_PD |
                          SJA1110_RXBIAS_PD | SJA1110_RESET_SER_EN |
                          SJA1110_RESET_SER | SJA1110_RESET_DES |
                          SJA1110_RXPKDETEN | SJA1110_RCVEN,
                          SJA1110_RXPKDETEN | SJA1110_RCVEN);
        if (ret < 0)
                return ret;

        /* Program continuous-time linear equalizer (CTLE) settings. */
        return xpcs_write(xpcs, MDIO_MMD_VEND2, SJA1110_RX_CDR_CTLE,
                          rx_cdr_ctle);
}

int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs)
{
        return nxp_sja1110_pma_config(xpcs, 0x19, 0x1, 0x19, 0x1, 0x212a);
}

int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs)
{
        return nxp_sja1110_pma_config(xpcs, 0x7d, 0x2, 0x7d, 0x2, 0x732a);
}