[PATCH v2 2/2] phy: mediatek: Add support for PCI-Express Gen4 S-PHY
AngeloGioacchino Del Regno <[email protected]>
| Newsgroups | org.infradead.lists.linux-mediatek,org.infradead.lists.linux-arm-kernel,org.infradead.lists.linux-phy,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Add support for the PCI-Express Gen4 S-PHY found in the latest MediaTek SoCs, including MT8196, MT8894 and similar. Signed-off-by: AngeloGioacchino Del Regno <[email protected]> --- drivers/phy/mediatek/Kconfig | 9 + drivers/phy/mediatek/Makefile | 1 + drivers/phy/mediatek/phy-mtk-pcie-sphy.c | 561 +++++++++++++++++++++++ 3 files changed, 571 insertions(+) create mode 100644 drivers/phy/mediatek/phy-mtk-pcie-sphy.c diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index ba6461350951..77236f3084c8 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -13,6 +13,15 @@ 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 + Say 'Y' here to add support for MediaTek PCIe S-PHY driver for + PCI-Express Gen4 controllers as found in MT6991, MT8196 and others. + config PHY_MTK_XFI_TPHY tristate "MediaTek 10GE SerDes XFI T-PHY driver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index ed0da708759b..7e984c7cfea3 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -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 diff --git a/drivers/phy/mediatek/phy-mtk-pcie-sphy.c b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c new file mode 100644 index 000000000000..6c1362b70fab --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-pcie-sphy.c @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 MediaTek Inc. + * Copyright (c) 2026 Collabora Ltd. + * AngeloGioacchino Del Regno <[email protected]> + */ + +#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) +# define AEQ_EGEQ_RATIO_GEN3_TO_22 0x16 +# define AEQ_EGEQ_RATIO_GEN4_TO_22 0x16 + +/* 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 + * @int_r_ctrl: Internal resistor selection of TX Bias Current + * @xtp_vtrim: XTP Bias V2V voltage calibration + * @cktx_impsel: SPLL CKTX Impedance Selection (P and N MOSFET) + * @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_impsel: TX Impedance Selection (P and N MOSFET) + * @tx_rswn_impsel: TX RSWn (Switch Resistance) impedance selection + * @supported: eFuse calibration data is supported + */ +struct mtk_pcie_sphy_efuse { + u8 int_r_ctrl; + u8 xtp_vtrim; + struct mtk_pcie_sphy_imp_sel cktx_impsel; + u8 cktx_r_mid; + u8 rx_leq_rl_ctle; + u8 rx_leq_rl_vga; + u8 rx_leq_rl_dfe; + u8 rx_impsel; + struct mtk_pcie_sphy_imp_sel tx_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_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_TX_IMPSEL_PMOS, cal->tx_impsel.pmos); + + mtk_phy_update_field(sif_lane_base + PEXTP_ANA_LN_TRX_A0, + RG_XTP_LN_TX_IMPSEL_NMOS, cal->tx_impsel.nmos); + } +} + +/** + * 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) + return dev_err_probe(dev, ret, + "Cannot get calibration data for %s\n", name); + + if (buf == 0) { + dev_info(dev, "No calibration for %s. Using defaults\n", name); + return -ENOENT; + } + + 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; + + 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. + */ + if (ret == -ENOENT) { + 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; + + if (pcie_sphy->num_lanes <= MTK_PCIE_SPHY_CALIBRATION_MAX_DATA_LANES) { + ret = mtk_pcie_sphy_get_calibration_data(pcie_sphy); + if (ret) + return ret; + } + + phy_set_drvdata(pcie_sphy->phy, pcie_sphy); + + 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 <[email protected]>"); +MODULE_LICENSE("GPL"); -- 2.55.0