phy: mediatek: Add support for PCI-Express Gen4 S-PHY

Add support for the PCI-Express Gen4 S-PHY found in the latest
MediaTek SoCs, including MT6991, MT8196, MT8894 and variants.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Link: https://patch.msgid.link/20260915101022.23852-3-angelogioacchino.delregno@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
AngeloGioacchino Del Regno 2026-09-15 12:10:22 +02:00 committed by Vinod Koul
parent c5e6a55284
commit e9434ed4ef
3 changed files with 574 additions and 0 deletions

View File

@ -13,6 +13,18 @@ config PHY_MTK_PCIE
callback for PCIe GEN3 port, it supports software efuse
initialization.
config PHY_MTK_PCIE_SPHY
tristate "MediaTek PCIe S-PHY Driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on OF
select GENERIC_PHY
help
Enable this to add support for MediaTek PCIe S-PHYs used with the
PCI-Express Gen4 controllers as found in MT6991, MT8196 and others.
To compile this driver as module, say 'M' here: the module will be
called phy-mtk-pcie-sphy.
config PHY_MTK_XFI_TPHY
tristate "MediaTek 10GE SerDes XFI T-PHY driver"
depends on ARCH_MEDIATEK || COMPILE_TEST

View File

@ -5,6 +5,7 @@
obj-$(CONFIG_PHY_MTK_DP) += phy-mtk-dp.o
obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-pcie.o
obj-$(CONFIG_PHY_MTK_PCIE_SPHY) += phy-mtk-pcie-sphy.o
obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o
obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o

View File

@ -0,0 +1,561 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 MediaTek Inc.
* Copyright (c) 2026 Collabora Ltd.
* Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "phy-mtk-io.h"
/* PHY System Interface (SIF) registers */
#define PEXTP_DIG_GLB_TOP 0x20
# define RG_XTP_BYPASS_PIPE_RST_RC BIT(17)
#define PEXTP_DIG_GLB_CKBG0 0x30
# define RG_XTP_CKBG_XTAL_STABLE_TIME_SEL GENMASK(25, 16)
#define PEXTP_DIG_GLB_TPLL_CTL0 0x38
# define RG_XTP_TPLL_SET_STABLE_TIME_SEL GENMASK(7, 2)
# define RG_XTP_TPLL_PWE_ON_STABLE_TIME_SEL GENMASK(9, 8)
#define PEXTP_DIG_GLB_CLKREQ_CTL 0x50
# define RG_XTP_CKM_EN_L1S0 BIT(13)
# define RG_XTP_CKM_EN_L1S1 BIT(14)
#define PEXTP_DIG_GLB_TPLL_CTL2 0xf4
# define RG_XTP_TPLL_ISO_EN_STABLE_TIME_SEL GENMASK(13, 12)
/* PHY System Interface Digital registers */
#define PEXTP_DIG_LN_TRX_PIPE_IF_17 0x30e8
# define RG_XTP_LN_RX_LF_CTLE_CSEL_GEN4 GENMASK(14, 12)
#define PEXTP_DIG_LN_RX_F0 0x50f0
# define RG_XTP_LN_RX_GEN1_CTLE1_CSEL GENMASK(3, 0)
# define RG_XTP_LN_RX_GEN2_CTLE1_CSEL GENMASK(7, 4)
# define RG_XTP_LN_RX_GEN3_CTLE1_CSEL GENMASK(11, 8)
# define RG_XTP_LN_RX_GEN4_CTLE1_CSEL GENMASK(15, 12)
#define PEXTP_DIG_LN_RX2_AEQ_EDGE_0 0x6004
# define RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN3 GENMASK(21, 16)
# define RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN4 GENMASK(29, 24)
/* PHY System Interface Analog registers */
#define PEXTP_ANA_GLB_TPLL1_RSVD 0x902c
# define RG_XTP_GLB_TPLL1_P_PATH_GAIN GENMASK(2, 0)
#define PEXTP_ANA_GLB_BIAS_0 0x9060
# define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(5, 0)
#define PEXTP_ANA_GLB_BIAS_1 0x90c0
# define RG_XTP_GLB_BIAS_V2V_VTRIM GENMASK(9, 6)
#define PEXTP_ANA_LN_TRX_0C 0xa00c
# define RG_XTP_LN_TX_RSWN_IMPSEL GENMASK(20, 16)
#define PEXTP_ANA_LN_TRX_34 0xa034
# define RG_XTP_LN_RX_FE BIT(15)
#define PEXTP_ANA_LN_TRX_6C 0xa06c
# define RG_XTP_LN_RX_AEQ_CTLE_ERR_TYPE GENMASK(14, 13)
# define AEQ_CTLE_SEARCH_ERR_TYPE_H1P5 0
# define AEQ_CTLE_SEARCH_ERR_TYPE_H1P5_H2P5 1
# define AEQ_CTLE_SEARCH_ERR_TYPE_P1P5_H2P5_H3P5 2
#define PEXTP_ANA_LN_TRX_A0 0xa0a0
# define RG_XTP_LN_TX_IMPSEL_PMOS GENMASK(4, 0)
# define RG_XTP_LN_TX_IMPSEL_NMOS GENMASK(11, 7)
# define RG_XTP_LN_RX_IMPSEL GENMASK(15, 12)
#define PEXTP_ANA_LN_TRX_A8 0xa0a8
# define RG_XTP_LN_RX_LEQ_RL_CTLE_CAL GENMASK(6, 2)
# define RG_XTP_LN_RX_LEQ_RL_VGA_CAL GENMASK(11, 7)
# define RG_XTP_LN_RX_LEQ_RL_DFE_CAL GENMASK(23, 19)
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4 0xb004
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8 0xb008
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_C 0xb00c
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10 0xb010
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14 0xb014
#define PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18 0xb018
# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1 GENMASK(5, 0)
# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0 GENMASK(13, 8)
# define RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1 GENMASK(21, 16)
#define PEXTP_REG_LANE(x) ((x) * 0x100)
/* PHY Clock Management (CKM) registers */
#define XTP_CKM_FORCE_6 0x38
# define RG_CKM_BIAS_WAIT_PRD_US GENMASK(21, 16)
#define XTP_CKM_REG_SPLL_FBKDIV_5 0xd4
# define RG_CKM_CKTX_IMPSEL_PMOS GENMASK(19, 16)
# define RG_CKM_CKTX_IMPSEL_NMOS GENMASK(23, 20)
# define RG_CKM_CKTX_IMPSEL_SW GENMASK(27, 24)
/* Calibration data from eFuses */
#define MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES 2
#define MTK_PCIE_SPHY_CALIBRATION_LAST_QUIRK_VER 4
/**
* struct mtk_pcie_sphy_imp_sel - Impedance Selection parameters
* @pmos: Impedance selection for P-Channel MOSFET
* @nmos: Impedance selection for N-Channel MOSFET
*/
struct mtk_pcie_sphy_imp_sel {
u8 pmos;
u8 nmos;
};
/**
* struct mtk_pcie_sphy_efuse - eFuse calibration data for S-PHY
* @cktx_impsel: SPLL CKTX Impedance Selection (P and N MOSFET)
* @tx_impsel: TX Impedance Selection (P and N MOSFET)
* @int_r_ctrl: Internal resistor selection of TX Bias Current
* @xtp_vtrim: XTP Bias V2V voltage calibration
* @cktx_r_mid: SPLL CKTX Intermediate Transition Impedance (Rmid)
* @rx_leq_rl_ctle: RX Front-End Return Loss Continuous Time Linear Equalization value
* @rx_leq_rl_vga: RX Front-End Return Loss Variable Gain Amplifier value
* @rx_leq_rl_dfe: RX Front-End Return Loss Decision Feedback Equalization value
* @rx_impsel: RX Impedance Selection
* @tx_rswn_impsel: TX RSWn (Switch Resistance) impedance selection
* @supported: eFuse calibration data is supported
*/
struct mtk_pcie_sphy_efuse {
struct mtk_pcie_sphy_imp_sel cktx_impsel;
struct mtk_pcie_sphy_imp_sel tx_impsel;
u8 int_r_ctrl;
u8 xtp_vtrim;
u8 cktx_r_mid;
u8 rx_leq_rl_ctle;
u8 rx_leq_rl_vga;
u8 rx_leq_rl_dfe;
u8 rx_impsel;
u8 tx_rswn_impsel[MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES];
bool supported;
};
/**
* struct mtk_pcie_sphy - PCI-Express S-PHY driver main structure
* @dev: Pointer to device structure
* @phy: Pointer to generic phy structure
* @sif_base: IO mapped register base address of system interface
* @ckm_base: IO mapped register base address of clock management interface
* @num_lanes: Number of lanes
* @calibration: eFuse calibration data for S-PHY
*/
struct mtk_pcie_sphy {
struct device *dev;
struct phy *phy;
void __iomem *sif_base;
void __iomem *ckm_base;
u8 num_lanes;
struct mtk_pcie_sphy_efuse calibration;
};
static void mtk_pcie_sphy_apply_calibration(struct mtk_pcie_sphy *pcie_sphy)
{
struct mtk_pcie_sphy_efuse *cal = &pcie_sphy->calibration;
int i;
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_BIAS_0,
RG_XTP_GLB_BIAS_INTR_CTRL, cal->int_r_ctrl);
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_BIAS_1,
RG_XTP_GLB_BIAS_V2V_VTRIM, cal->xtp_vtrim);
mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5,
RG_CKM_CKTX_IMPSEL_PMOS, cal->cktx_impsel.pmos);
mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5,
RG_CKM_CKTX_IMPSEL_NMOS, cal->cktx_impsel.nmos);
mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_REG_SPLL_FBKDIV_5,
RG_CKM_CKTX_IMPSEL_SW, cal->cktx_r_mid);
for (i = 0; i < pcie_sphy->num_lanes; i++) {
void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_0C,
RG_XTP_LN_TX_RSWN_IMPSEL, cal->tx_rswn_impsel[i]);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8,
RG_XTP_LN_RX_LEQ_RL_CTLE_CAL, cal->rx_leq_rl_ctle);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8,
RG_XTP_LN_RX_LEQ_RL_VGA_CAL, cal->rx_leq_rl_vga);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A8,
RG_XTP_LN_RX_LEQ_RL_DFE_CAL, cal->rx_leq_rl_dfe);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0,
RG_XTP_LN_RX_IMPSEL, cal->rx_impsel);
}
}
/**
* mtk_pcie_sphy_init() - Initialize the PCI-Express S-PHY
* @phy: the phy to be initialized
*
* The hardware settings will be reset during suspend, it should be
* reinitialized when the consumer calls phy_init() again on resume.
*/
static int mtk_pcie_sphy_init(struct phy *phy)
{
struct mtk_pcie_sphy *pcie_sphy = phy_get_drvdata(phy);
struct mtk_pcie_sphy_imp_sel tx_impsel;
int i;
/* Set CKM Bias wait time to 4 microseconds */
mtk_phy_update_field(pcie_sphy->ckm_base + XTP_CKM_FORCE_6,
RG_CKM_BIAS_WAIT_PRD_US, 4);
/* TPLL needs 63 ref_ck ticks to stabilize when setting frequency */
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL0,
RG_XTP_TPLL_SET_STABLE_TIME_SEL, 63);
/* TPLL needs 3 ref_ck ticks to stabilize when powering on... */
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL0,
RG_XTP_TPLL_PWE_ON_STABLE_TIME_SEL, 3);
/* ...and the same goes for setting isolation */
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_TPLL_CTL2,
RG_XTP_TPLL_ISO_EN_STABLE_TIME_SEL, 3);
/* XTAL doesn't need any stabilization time */
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_DIG_GLB_CKBG0,
RG_XTP_CKBG_XTAL_STABLE_TIME_SEL, 0);
/* Keep pextp_ckm enabled when in L1SS_L1S1 state */
mtk_phy_clear_bits(pcie_sphy->sif_base + PEXTP_DIG_GLB_CLKREQ_CTL, RG_XTP_CKM_EN_L1S1);
/* Set PIPE to reset TPLL */
mtk_phy_clear_bits(pcie_sphy->sif_base + PEXTP_DIG_GLB_TOP, RG_XTP_BYPASS_PIPE_RST_RC);
/* Set TPLL P-Path gain compensation to 1 */
mtk_phy_update_field(pcie_sphy->sif_base + PEXTP_ANA_GLB_TPLL1_RSVD,
RG_XTP_GLB_TPLL1_P_PATH_GAIN, 1);
for (i = 0; i < pcie_sphy->num_lanes; i++) {
void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i);
/* Set RX Lane AEQ CTRL-E Search Error type to h1.5 + h2.5 */
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_6C,
RG_XTP_LN_RX_AEQ_CTLE_ERR_TYPE,
AEQ_CTLE_SEARCH_ERR_TYPE_H1P5_H2P5);
mtk_phy_set_bits(sif_lane_base + PEXTP_ANA_LN_TRX_34, RG_XTP_LN_RX_FE);
/* TRX: Select CTLE1 for RX Lane AutoEQ CTRL-E Setting on Gen4 */
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TRX_PIPE_IF_17,
RG_XTP_LN_RX_LF_CTLE_CSEL_GEN4, 1);
/* Set RX Lane AutoEQ CTRL-E for PCI-Express Gen1 to Gen 4 */
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
RG_XTP_LN_RX_GEN1_CTLE1_CSEL, 13);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
RG_XTP_LN_RX_GEN2_CTLE1_CSEL, 13);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
RG_XTP_LN_RX_GEN3_CTLE1_CSEL, 13);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX_F0,
RG_XTP_LN_RX_GEN4_CTLE1_CSEL, 0);
/* Set RX Lane AutoEQ's Edge EQ Ratio to 22 * 0.0625 = 1.375 */
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX2_AEQ_EDGE_0,
RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN3, 22);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_RX2_AEQ_EDGE_0,
RG_XTP_LN_RX_AEQ_EGEQ_RATIO_GEN4, 22);
/* Setup Digital lane TX Link Characteristics Table */
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 10);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_4,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1, 2);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 11);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_8,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_CP1, 1);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_C,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 12);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 13);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_10,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 1);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 11);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_14,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 1);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_C0, 10);
mtk_phy_update_field(sif_lane_base + PEXTP_DIG_LN_TX_LC_TABLE_RSWN_18,
RG_XTP_LN_TX_LC_PRESET_MGx_Px_CM1, 2);
}
if (pcie_sphy->calibration.supported) {
mtk_pcie_sphy_apply_calibration(pcie_sphy);
tx_impsel.pmos = pcie_sphy->calibration.tx_impsel.pmos;
tx_impsel.nmos = pcie_sphy->calibration.tx_impsel.nmos;
} else {
/* Set P=10, N=9 to prevent EMI if no calibration present */
tx_impsel.pmos = 10;
tx_impsel.nmos = 9;
}
/* Select TX Impedance on N and P MOSFETs */
for (i = 0; i < pcie_sphy->num_lanes; i++) {
void __iomem *sif_lane_base = pcie_sphy->sif_base + PEXTP_REG_LANE(i);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0,
RG_XTP_LN_TX_IMPSEL_PMOS, tx_impsel.pmos);
mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0,
RG_XTP_LN_TX_IMPSEL_NMOS, tx_impsel.nmos);
}
return 0;
}
static const struct phy_ops mtk_pcie_sphy_ops = {
.init = mtk_pcie_sphy_init,
.owner = THIS_MODULE,
};
static int mtk_pcie_sphy_get_one_cal_para(struct device *dev, const char *name, u8 max_val)
{
u16 buf;
u8 tmp;
int ret;
/*
* All of the calibrations are always max 8 bits long, but some may
* be split between two different 8-bits cells: handle this corner
* case by retrying reading as u16.
*/
ret = nvmem_cell_read_u8(dev, name, &tmp);
if (ret == 0)
buf = tmp;
else
ret = nvmem_cell_read_u16(dev, name, &buf);
if (ret == -ENOENT) {
dev_info(dev, "No calibration for %s. Using defaults\n", name);
return -ENOENT;
} else if (ret)
return dev_err_probe(dev, ret,
"Cannot get calibration data for %s\n", name);
if (buf > max_val)
return dev_err_probe(dev, -ERANGE,
"Bad value %u retrieved for %s.\n", buf, name);
return buf;
}
static int mtk_pcie_sphy_get_calibration_data(struct mtk_pcie_sphy *pcie_sphy)
{
struct mtk_pcie_sphy_efuse *cal = &pcie_sphy->calibration;
struct device *dev = pcie_sphy->dev;
u8 version;
int ret;
if (pcie_sphy->num_lanes > MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES) {
dev_info(dev, "Skipping PHY calibration for more than %u lanes.\n",
MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES);
ret = -EOPNOTSUPP;
goto end;
}
ret = mtk_pcie_sphy_get_one_cal_para(dev, "int-r",
FIELD_MAX(RG_XTP_GLB_BIAS_INTR_CTRL));
if (ret < 0)
goto end;
cal->int_r_ctrl = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "xtp-vtrim",
FIELD_MAX(RG_XTP_GLB_BIAS_V2V_VTRIM));
if (ret < 0)
goto end;
cal->xtp_vtrim = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-pmos",
FIELD_MAX(RG_CKM_CKTX_IMPSEL_PMOS));
if (ret < 0)
goto end;
cal->cktx_impsel.pmos = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-nmos",
FIELD_MAX(RG_CKM_CKTX_IMPSEL_NMOS));
if (ret < 0)
goto end;
cal->cktx_impsel.nmos = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "cktx-r-mid",
FIELD_MAX(RG_CKM_CKTX_IMPSEL_SW));
if (ret < 0)
goto end;
cal->cktx_r_mid = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-ctle",
FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_CTLE_CAL));
if (ret < 0)
goto end;
cal->rx_leq_rl_ctle = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-vga",
FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_VGA_CAL));
if (ret < 0)
goto end;
cal->rx_leq_rl_vga = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "rxfe-lanes-rl-dfe",
FIELD_MAX(RG_XTP_LN_RX_LEQ_RL_DFE_CAL));
if (ret < 0)
goto end;
cal->rx_leq_rl_dfe = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "rx-lanes-imp",
FIELD_MAX(RG_XTP_LN_RX_IMPSEL));
if (ret < 0)
goto end;
cal->rx_impsel = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-lanes-pmos",
FIELD_MAX(RG_XTP_LN_TX_IMPSEL_PMOS));
if (ret < 0)
goto end;
cal->tx_impsel.pmos = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-lanes-nmos",
FIELD_MAX(RG_XTP_LN_TX_IMPSEL_NMOS));
if (ret < 0)
goto end;
cal->tx_impsel.nmos = ret;
ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln0-rswn",
FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL));
if (ret < 0)
goto end;
cal->tx_rswn_impsel[0] = ret;
if (pcie_sphy->num_lanes == 2) {
ret = mtk_pcie_sphy_get_one_cal_para(dev, "tx-ln1-rswn",
FIELD_MAX(RG_XTP_LN_TX_RSWN_IMPSEL));
if (ret < 0)
goto end;
cal->tx_rswn_impsel[1] = ret;
}
ret = mtk_pcie_sphy_get_one_cal_para(dev, "cal-version", 15);
if (ret < 0)
goto end;
version = ret;
/* Quirk for eFuse calibration table versions 0 to 4 */
if ((version <= MTK_PCIE_SPHY_CALIBRATION_LAST_QUIRK_VER) &&
cal->rx_leq_rl_ctle == 10) {
cal->rx_leq_rl_vga = cal->rx_leq_rl_ctle;
cal->rx_leq_rl_dfe = cal->rx_leq_rl_ctle;
}
end:
if (ret < 0) {
/*
* If any of the calibration values is missing, or if there is
* no calibration at all in the eFuses, this is not a problem,
* as the PHY doesn't require one to actually work.
*
* Note that if even just one of the calibration values is
* missing it would not be safe to trust the correctness of
* the others as this may be a different eFuse layout.
*/
if (ret == -ENOENT || ret == -EOPNOTSUPP) {
cal->supported = false;
return 0;
}
return ret;
};
cal->supported = true;
return 0;
}
static int mtk_pcie_sphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *provider;
struct mtk_pcie_sphy *pcie_sphy;
u32 num_lanes;
int ret;
pcie_sphy = devm_kzalloc(dev, sizeof(*pcie_sphy), GFP_KERNEL);
if (!pcie_sphy)
return -ENOMEM;
pcie_sphy->sif_base = devm_platform_ioremap_resource_byname(pdev, "sif");
if (IS_ERR(pcie_sphy->sif_base))
return dev_err_probe(dev, PTR_ERR(pcie_sphy->sif_base),
"Failed to map phy-sif base\n");
pcie_sphy->ckm_base = devm_platform_ioremap_resource_byname(pdev, "ckm");
if (IS_ERR(pcie_sphy->ckm_base))
return dev_err_probe(dev, PTR_ERR(pcie_sphy->ckm_base),
"Failed to map phy-ckm base\n");
pcie_sphy->phy = devm_phy_create(dev, dev->of_node, &mtk_pcie_sphy_ops);
if (IS_ERR(pcie_sphy->phy))
return dev_err_probe(dev, PTR_ERR(pcie_sphy->phy),
"Failed to create PCIe phy\n");
ret = of_property_read_u32(dev->of_node, "num-lanes", &num_lanes);
if (ret)
num_lanes = 1;
else if (num_lanes > 4)
return dev_err_probe(dev, -EINVAL, "Invalid number of lanes.\n");
pcie_sphy->num_lanes = num_lanes;
pcie_sphy->dev = dev;
ret = mtk_pcie_sphy_get_calibration_data(pcie_sphy);
if (ret)
return ret;
phy_set_drvdata(pcie_sphy->phy, pcie_sphy);
pm_runtime_no_callbacks(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider))
return dev_err_probe(dev, PTR_ERR(provider),
"Could not register PCI-Express S-PHY\n");
return 0;
}
static const struct of_device_id mtk_pcie_sphy_of_match[] = {
{ .compatible = "mediatek,mt8196-pcie-sphy" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_pcie_sphy_of_match);
static struct platform_driver mtk_pcie_sphy_driver = {
.probe = mtk_pcie_sphy_probe,
.driver = {
.name = "mtk-pcie-sphy",
.of_match_table = mtk_pcie_sphy_of_match,
},
};
module_platform_driver(mtk_pcie_sphy_driver);
MODULE_DESCRIPTION("MediaTek PCIe SPHY driver");
MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_LICENSE("GPL");