// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2022 MediaTek Inc. * Copyright (c) 2026 Collabora Ltd. * Author: AngeloGioacchino Del Regno */ #include #include #include #include #include #include #include #include #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 "); MODULE_LICENSE("GPL");