mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
parent
c5e6a55284
commit
e9434ed4ef
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
561
drivers/phy/mediatek/phy-mtk-pcie-sphy.c
Normal file
561
drivers/phy/mediatek/phy-mtk-pcie-sphy.c
Normal 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");
|
||||
Loading…
Reference in New Issue
Block a user