diff options
Diffstat (limited to 'drivers/phy/qualcomm/phy-qcom-qmp.c')
| -rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp.c | 528 | 
1 files changed, 465 insertions, 63 deletions
| diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 9cdebe7f26cb..7877f70cf86f 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -1840,6 +1840,86 @@ static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_pcs_tbl[] = {  	QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),  }; +static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl[] = { +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_SVS_MODE_CLK_SEL, 0x05), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x3b), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x02), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x0c), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x06), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x30), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x02), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x00), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x00), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0x00), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x0a), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE0, 0x0a), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_CTRL, 0x00), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x17), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORE_CLK_EN, 0x1f), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_rbr[] = { +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x05), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x6f), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x08), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr[] = { +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x03), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x0f), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x0e), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr2[] = { +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x8c), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x00), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x0a), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x1f), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x1c), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_serdes_tbl_hbr3[] = { +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x00), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x69), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0x80), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x07), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x2f), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x2a), +	QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x08), +}; + +static const struct qmp_phy_init_tbl qmp_v4_dp_tx_tbl[] = { +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_VMODE_CTRL1, 0x40), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_PRE_STALL_LDO_BOOST_EN, 0x30), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_INTERFACE_SELECT, 0x3b), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_CLKBUF_ENABLE, 0x0f), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_RESET_TSYNC_EN, 0x03), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0f), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_INTERFACE_MODE, 0x00), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x11), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_BAND, 0x4), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_POL_INV, 0x0a), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_DRV_LVL, 0x2a), +	QMP_PHY_INIT_CFG(QSERDES_V4_TX_TX_EMP_POST1_LVL, 0x20), +}; +  static const struct qmp_phy_init_tbl sm8250_qmp_pcie_serdes_tbl[] = {  	QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x08),  	QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x34), @@ -2268,6 +2348,8 @@ static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_pcs_tbl[] = {  	QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),  }; +struct qmp_phy; +  /* struct qmp_phy_cfg - per-PHY initialization config */  struct qmp_phy_cfg {  	/* phy-type - PCIE/UFS/USB */ @@ -2307,6 +2389,12 @@ struct qmp_phy_cfg {  	const struct qmp_phy_init_tbl *serdes_tbl_hbr3;  	int serdes_tbl_hbr3_num; +	/* DP PHY callbacks */ +	int (*configure_dp_phy)(struct qmp_phy *qphy); +	void (*configure_dp_tx)(struct qmp_phy *qphy); +	int (*calibrate_dp_phy)(struct qmp_phy *qphy); +	void (*dp_aux_init)(struct qmp_phy *qphy); +  	/* clock ids to be requested */  	const char * const *clk_list;  	int num_clks; @@ -2423,6 +2511,16 @@ struct qcom_qmp {  	struct reset_control *ufs_reset;  }; +static void qcom_qmp_v3_phy_dp_aux_init(struct qmp_phy *qphy); +static void qcom_qmp_v3_phy_configure_dp_tx(struct qmp_phy *qphy); +static int qcom_qmp_v3_phy_configure_dp_phy(struct qmp_phy *qphy); +static int qcom_qmp_v3_dp_phy_calibrate(struct qmp_phy *qphy); + +static void qcom_qmp_v4_phy_dp_aux_init(struct qmp_phy *qphy); +static void qcom_qmp_v4_phy_configure_dp_tx(struct qmp_phy *qphy); +static int qcom_qmp_v4_phy_configure_dp_phy(struct qmp_phy *qphy); +static int qcom_qmp_v4_dp_phy_calibrate(struct qmp_phy *qphy); +  static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)  {  	u32 reg; @@ -2871,6 +2969,11 @@ static const struct qmp_phy_cfg sc7180_dpphy_cfg = {  	.has_phy_dp_com_ctrl	= true,  	.is_dual_lane_phy	= true, + +	.dp_aux_init = qcom_qmp_v3_phy_dp_aux_init, +	.configure_dp_tx = qcom_qmp_v3_phy_configure_dp_tx, +	.configure_dp_phy = qcom_qmp_v3_phy_configure_dp_phy, +	.calibrate_dp_phy = qcom_qmp_v3_dp_phy_calibrate,  };  static const struct qmp_phy_combo_cfg sc7180_usb3dpphy_cfg = { @@ -3123,6 +3226,46 @@ static const struct qmp_phy_cfg sm8250_usb3_uniphy_cfg = {  	.pwrdn_delay_max	= POWER_DOWN_DELAY_US_MAX,  }; +static const struct qmp_phy_cfg sm8250_dpphy_cfg = { +	.type			= PHY_TYPE_DP, +	.nlanes			= 1, + +	.serdes_tbl		= qmp_v4_dp_serdes_tbl, +	.serdes_tbl_num		= ARRAY_SIZE(qmp_v4_dp_serdes_tbl), +	.tx_tbl			= qmp_v4_dp_tx_tbl, +	.tx_tbl_num		= ARRAY_SIZE(qmp_v4_dp_tx_tbl), + +	.serdes_tbl_rbr		= qmp_v4_dp_serdes_tbl_rbr, +	.serdes_tbl_rbr_num	= ARRAY_SIZE(qmp_v4_dp_serdes_tbl_rbr), +	.serdes_tbl_hbr		= qmp_v4_dp_serdes_tbl_hbr, +	.serdes_tbl_hbr_num	= ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr), +	.serdes_tbl_hbr2	= qmp_v4_dp_serdes_tbl_hbr2, +	.serdes_tbl_hbr2_num	= ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr2), +	.serdes_tbl_hbr3	= qmp_v4_dp_serdes_tbl_hbr3, +	.serdes_tbl_hbr3_num	= ARRAY_SIZE(qmp_v4_dp_serdes_tbl_hbr3), + +	.clk_list		= qmp_v4_phy_clk_l, +	.num_clks		= ARRAY_SIZE(qmp_v4_phy_clk_l), +	.reset_list		= msm8996_usb3phy_reset_l, +	.num_resets		= ARRAY_SIZE(msm8996_usb3phy_reset_l), +	.vreg_list		= qmp_phy_vreg_l, +	.num_vregs		= ARRAY_SIZE(qmp_phy_vreg_l), +	.regs			= qmp_v4_usb3phy_regs_layout, + +	.has_phy_dp_com_ctrl	= true, +	.is_dual_lane_phy	= true, + +	.dp_aux_init = qcom_qmp_v4_phy_dp_aux_init, +	.configure_dp_tx = qcom_qmp_v4_phy_configure_dp_tx, +	.configure_dp_phy = qcom_qmp_v4_phy_configure_dp_phy, +	.calibrate_dp_phy = qcom_qmp_v4_dp_phy_calibrate, +}; + +static const struct qmp_phy_combo_cfg sm8250_usb3dpphy_cfg = { +	.usb_cfg		= &sm8250_usb3phy_cfg, +	.dp_cfg			= &sm8250_dpphy_cfg, +}; +  static const struct qmp_phy_cfg sdx55_usb3_uniphy_cfg = {  	.type			= PHY_TYPE_USB3,  	.nlanes			= 1, @@ -3332,24 +3475,24 @@ static int qcom_qmp_phy_serdes_init(struct qmp_phy *qphy)  	return 0;  } -static void qcom_qmp_phy_dp_aux_init(struct qmp_phy *qphy) +static void qcom_qmp_v3_phy_dp_aux_init(struct qmp_phy *qphy)  {  	writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |  	       DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, -	       qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL); +	       qphy->pcs + QSERDES_DP_PHY_PD_CTL);  	/* Turn on BIAS current for PHY/PLL */  	writel(QSERDES_V3_COM_BIAS_EN | QSERDES_V3_COM_BIAS_EN_MUX |  	       QSERDES_V3_COM_CLKBUF_L_EN | QSERDES_V3_COM_EN_SYSCLK_TX_SEL,  	       qphy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN); -	writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL); +	writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_DP_PHY_PD_CTL);  	writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |  	       DP_PHY_PD_CTL_LANE_0_1_PWRDN |  	       DP_PHY_PD_CTL_LANE_2_3_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN |  	       DP_PHY_PD_CTL_DP_CLAMP_EN, -	       qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL); +	       qphy->pcs + QSERDES_DP_PHY_PD_CTL);  	writel(QSERDES_V3_COM_BIAS_EN |  	       QSERDES_V3_COM_BIAS_EN_MUX | QSERDES_V3_COM_CLKBUF_R_EN | @@ -3357,16 +3500,16 @@ static void qcom_qmp_phy_dp_aux_init(struct qmp_phy *qphy)  	       QSERDES_V3_COM_CLKBUF_RX_DRIVE_L,  	       qphy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN); -	writel(0x00, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG0); -	writel(0x13, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG1); -	writel(0x24, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG2); -	writel(0x00, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG3); -	writel(0x0a, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG4); -	writel(0x26, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG5); -	writel(0x0a, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG6); -	writel(0x03, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG7); -	writel(0xbb, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG8); -	writel(0x03, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG9); +	writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG0); +	writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1); +	writel(0x24, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2); +	writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG3); +	writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG4); +	writel(0x26, qphy->pcs + QSERDES_DP_PHY_AUX_CFG5); +	writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG6); +	writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG7); +	writel(0xbb, qphy->pcs + QSERDES_DP_PHY_AUX_CFG8); +	writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG9);  	qphy->dp_aux_cfg = 0;  	writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK | @@ -3375,6 +3518,20 @@ static void qcom_qmp_phy_dp_aux_init(struct qmp_phy *qphy)  	       qphy->pcs + QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK);  } +static const u8 qmp_dp_v3_pre_emphasis_hbr3_hbr2[4][4] = { +	{ 0x00, 0x0c, 0x15, 0x1a }, +	{ 0x02, 0x0e, 0x16, 0xff }, +	{ 0x02, 0x11, 0xff, 0xff }, +	{ 0x04, 0xff, 0xff, 0xff } +}; + +static const u8 qmp_dp_v3_voltage_swing_hbr3_hbr2[4][4] = { +	{ 0x02, 0x12, 0x16, 0x1a }, +	{ 0x09, 0x19, 0x1f, 0xff }, +	{ 0x10, 0x1f, 0xff, 0xff }, +	{ 0x1f, 0xff, 0xff, 0xff } +}; +  static const u8 qmp_dp_v3_pre_emphasis_hbr_rbr[4][4] = {  	{ 0x00, 0x0c, 0x14, 0x19 },  	{ 0x00, 0x0b, 0x12, 0xff }, @@ -3389,11 +3546,11 @@ static const u8 qmp_dp_v3_voltage_swing_hbr_rbr[4][4] = {  	{ 0x1f, 0xff, 0xff, 0xff }  }; -static void qcom_qmp_phy_configure_dp_tx(struct qmp_phy *qphy) +static int qcom_qmp_phy_configure_dp_swing(struct qmp_phy *qphy, +		unsigned int drv_lvl_reg, unsigned int emp_post_reg)  {  	const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;  	unsigned int v_level = 0, p_level = 0; -	u32 bias_en, drvr_en;  	u8 voltage_swing_cfg, pre_emphasis_cfg;  	int i; @@ -3402,56 +3559,58 @@ static void qcom_qmp_phy_configure_dp_tx(struct qmp_phy *qphy)  		p_level = max(p_level, dp_opts->pre[i]);  	} -	if (dp_opts->lanes == 1) { -		bias_en = 0x3e; -		drvr_en = 0x13; +	if (dp_opts->link_rate <= 2700) { +		voltage_swing_cfg = qmp_dp_v3_voltage_swing_hbr_rbr[v_level][p_level]; +		pre_emphasis_cfg = qmp_dp_v3_pre_emphasis_hbr_rbr[v_level][p_level];  	} else { -		bias_en = 0x3f; -		drvr_en = 0x10; +		voltage_swing_cfg = qmp_dp_v3_voltage_swing_hbr3_hbr2[v_level][p_level]; +		pre_emphasis_cfg = qmp_dp_v3_pre_emphasis_hbr3_hbr2[v_level][p_level];  	} -	voltage_swing_cfg = qmp_dp_v3_voltage_swing_hbr_rbr[v_level][p_level]; -	pre_emphasis_cfg = qmp_dp_v3_pre_emphasis_hbr_rbr[v_level][p_level]; -  	/* TODO: Move check to config check */  	if (voltage_swing_cfg == 0xFF && pre_emphasis_cfg == 0xFF) -		return; +		return -EINVAL;  	/* Enable MUX to use Cursor values from these registers */  	voltage_swing_cfg |= DP_PHY_TXn_TX_DRV_LVL_MUX_EN;  	pre_emphasis_cfg |= DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN; -	writel(voltage_swing_cfg, qphy->tx + QSERDES_V3_TX_TX_DRV_LVL); -	writel(pre_emphasis_cfg, qphy->tx + QSERDES_V3_TX_TX_EMP_POST1_LVL); -	writel(voltage_swing_cfg, qphy->tx2 + QSERDES_V3_TX_TX_DRV_LVL); -	writel(pre_emphasis_cfg, qphy->tx2 + QSERDES_V3_TX_TX_EMP_POST1_LVL); +	writel(voltage_swing_cfg, qphy->tx + drv_lvl_reg); +	writel(pre_emphasis_cfg, qphy->tx + emp_post_reg); +	writel(voltage_swing_cfg, qphy->tx2 + drv_lvl_reg); +	writel(pre_emphasis_cfg, qphy->tx2 + emp_post_reg); -	writel(drvr_en, qphy->tx + QSERDES_V3_TX_HIGHZ_DRVR_EN); -	writel(bias_en, qphy->tx + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); -	writel(drvr_en, qphy->tx2 + QSERDES_V3_TX_HIGHZ_DRVR_EN); -	writel(bias_en, qphy->tx2 + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); +	return 0;  } -static int qcom_qmp_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts) +static void qcom_qmp_v3_phy_configure_dp_tx(struct qmp_phy *qphy)  { -	const struct phy_configure_opts_dp *dp_opts = &opts->dp; -	struct qmp_phy *qphy = phy_get_drvdata(phy); +	const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; +	u32 bias_en, drvr_en; -	memcpy(&qphy->dp_opts, dp_opts, sizeof(*dp_opts)); -	if (qphy->dp_opts.set_voltages) { -		qcom_qmp_phy_configure_dp_tx(qphy); -		qphy->dp_opts.set_voltages = 0; +	if (qcom_qmp_phy_configure_dp_swing(qphy, +				QSERDES_V3_TX_TX_DRV_LVL, +				QSERDES_V3_TX_TX_EMP_POST1_LVL) < 0) +		return; + +	if (dp_opts->lanes == 1) { +		bias_en = 0x3e; +		drvr_en = 0x13; +	} else { +		bias_en = 0x3f; +		drvr_en = 0x10;  	} -	return 0; +	writel(drvr_en, qphy->tx + QSERDES_V3_TX_HIGHZ_DRVR_EN); +	writel(bias_en, qphy->tx + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); +	writel(drvr_en, qphy->tx2 + QSERDES_V3_TX_HIGHZ_DRVR_EN); +	writel(bias_en, qphy->tx2 + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN);  } -static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy) +static bool qcom_qmp_phy_configure_dp_mode(struct qmp_phy *qphy)  { -	const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks; -	const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; -	u32 val, phy_vco_div, status; -	unsigned long pixel_freq; +	u32 val; +	bool reverse = false;  	val = DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |  	      DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN; @@ -3471,9 +3630,22 @@ static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)  	 *	writel(0x4c, qphy->pcs + QSERDES_V3_DP_PHY_MODE);  	 */  	val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN; -	writel(val, qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL); +	writel(val, qphy->pcs + QSERDES_DP_PHY_PD_CTL); + +	writel(0x5c, qphy->pcs + QSERDES_DP_PHY_MODE); + +	return reverse; +} + +static int qcom_qmp_v3_phy_configure_dp_phy(struct qmp_phy *qphy) +{ +	const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks; +	const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; +	u32 phy_vco_div, status; +	unsigned long pixel_freq; + +	qcom_qmp_phy_configure_dp_mode(qphy); -	writel(0x5c, qphy->pcs + QSERDES_V3_DP_PHY_MODE);  	writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL);  	writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL); @@ -3503,11 +3675,11 @@ static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)  	clk_set_rate(dp_clks->dp_link_hw.clk, dp_opts->link_rate * 100000);  	clk_set_rate(dp_clks->dp_pixel_hw.clk, pixel_freq); -	writel(0x04, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG2); -	writel(0x01, qphy->pcs + QSERDES_V3_DP_PHY_CFG); -	writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_CFG); -	writel(0x01, qphy->pcs + QSERDES_V3_DP_PHY_CFG); -	writel(0x09, qphy->pcs + QSERDES_V3_DP_PHY_CFG); +	writel(0x04, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2); +	writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG); +	writel(0x05, qphy->pcs + QSERDES_DP_PHY_CFG); +	writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG); +	writel(0x09, qphy->pcs + QSERDES_DP_PHY_CFG);  	writel(0x20, qphy->serdes + QSERDES_V3_COM_RESETSM_CNTRL); @@ -3518,7 +3690,7 @@ static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)  			10000))  		return -ETIMEDOUT; -	writel(0x19, qphy->pcs + QSERDES_V3_DP_PHY_CFG); +	writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);  	if (readl_poll_timeout(qphy->pcs + QSERDES_V3_DP_PHY_STATUS,  			status, @@ -3527,9 +3699,9 @@ static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)  			10000))  		return -ETIMEDOUT; -	writel(0x18, qphy->pcs + QSERDES_V3_DP_PHY_CFG); +	writel(0x18, qphy->pcs + QSERDES_DP_PHY_CFG);  	udelay(2000); -	writel(0x19, qphy->pcs + QSERDES_V3_DP_PHY_CFG); +	writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG);  	return readl_poll_timeout(qphy->pcs + QSERDES_V3_DP_PHY_STATUS,  			status, @@ -3542,9 +3714,8 @@ static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)   * We need to calibrate the aux setting here as many times   * as the caller tries   */ -static int qcom_qmp_dp_phy_calibrate(struct phy *phy) +static int qcom_qmp_v3_dp_phy_calibrate(struct qmp_phy *qphy)  { -	struct qmp_phy *qphy = phy_get_drvdata(phy);  	static const u8 cfg1_settings[] = { 0x13, 0x23, 0x1d };  	u8 val; @@ -3552,7 +3723,231 @@ static int qcom_qmp_dp_phy_calibrate(struct phy *phy)  	qphy->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings);  	val = cfg1_settings[qphy->dp_aux_cfg]; -	writel(val, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG1); +	writel(val, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1); + +	return 0; +} + +static void qcom_qmp_v4_phy_dp_aux_init(struct qmp_phy *qphy) +{ +	writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_PSR_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | +	       DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, +	       qphy->pcs + QSERDES_DP_PHY_PD_CTL); + +	/* Turn on BIAS current for PHY/PLL */ +	writel(0x17, qphy->serdes + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN); + +	writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG0); +	writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1); +	writel(0xa4, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2); +	writel(0x00, qphy->pcs + QSERDES_DP_PHY_AUX_CFG3); +	writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG4); +	writel(0x26, qphy->pcs + QSERDES_DP_PHY_AUX_CFG5); +	writel(0x0a, qphy->pcs + QSERDES_DP_PHY_AUX_CFG6); +	writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG7); +	writel(0xb7, qphy->pcs + QSERDES_DP_PHY_AUX_CFG8); +	writel(0x03, qphy->pcs + QSERDES_DP_PHY_AUX_CFG9); +	qphy->dp_aux_cfg = 0; + +	writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK | +	       PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK | +	       PHY_AUX_REQ_ERR_MASK, +	       qphy->pcs + QSERDES_V4_DP_PHY_AUX_INTERRUPT_MASK); +} + +static void qcom_qmp_v4_phy_configure_dp_tx(struct qmp_phy *qphy) +{ +	/* Program default values before writing proper values */ +	writel(0x27, qphy->tx + QSERDES_V4_TX_TX_DRV_LVL); +	writel(0x27, qphy->tx2 + QSERDES_V4_TX_TX_DRV_LVL); + +	writel(0x20, qphy->tx + QSERDES_V4_TX_TX_EMP_POST1_LVL); +	writel(0x20, qphy->tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL); + +	qcom_qmp_phy_configure_dp_swing(qphy, +			QSERDES_V4_TX_TX_DRV_LVL, +			QSERDES_V4_TX_TX_EMP_POST1_LVL); +} + +static int qcom_qmp_v4_phy_configure_dp_phy(struct qmp_phy *qphy) +{ +	const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks; +	const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts; +	u32 phy_vco_div, status; +	unsigned long pixel_freq; +	u32 bias0_en, drvr0_en, bias1_en, drvr1_en; +	bool reverse; + +	writel(0x0f, qphy->pcs + QSERDES_V4_DP_PHY_CFG_1); + +	reverse = qcom_qmp_phy_configure_dp_mode(qphy); + +	writel(0x13, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1); +	writel(0xa4, qphy->pcs + QSERDES_DP_PHY_AUX_CFG2); + +	writel(0x05, qphy->pcs + QSERDES_V4_DP_PHY_TX0_TX1_LANE_CTL); +	writel(0x05, qphy->pcs + QSERDES_V4_DP_PHY_TX2_TX3_LANE_CTL); + +	switch (dp_opts->link_rate) { +	case 1620: +		phy_vco_div = 0x1; +		pixel_freq = 1620000000UL / 2; +		break; +	case 2700: +		phy_vco_div = 0x1; +		pixel_freq = 2700000000UL / 2; +		break; +	case 5400: +		phy_vco_div = 0x2; +		pixel_freq = 5400000000UL / 4; +		break; +	case 8100: +		phy_vco_div = 0x0; +		pixel_freq = 8100000000UL / 6; +		break; +	default: +		/* Other link rates aren't supported */ +		return -EINVAL; +	} +	writel(phy_vco_div, qphy->pcs + QSERDES_V4_DP_PHY_VCO_DIV); + +	clk_set_rate(dp_clks->dp_link_hw.clk, dp_opts->link_rate * 100000); +	clk_set_rate(dp_clks->dp_pixel_hw.clk, pixel_freq); + +	writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG); +	writel(0x05, qphy->pcs + QSERDES_DP_PHY_CFG); +	writel(0x01, qphy->pcs + QSERDES_DP_PHY_CFG); +	writel(0x09, qphy->pcs + QSERDES_DP_PHY_CFG); + +	writel(0x20, qphy->serdes + QSERDES_V4_COM_RESETSM_CNTRL); + +	if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_C_READY_STATUS, +			status, +			((status & BIT(0)) > 0), +			500, +			10000)) +		return -ETIMEDOUT; + +	if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_CMN_STATUS, +			status, +			((status & BIT(0)) > 0), +			500, +			10000)) +		return -ETIMEDOUT; + +	if (readl_poll_timeout(qphy->serdes + QSERDES_V4_COM_CMN_STATUS, +			status, +			((status & BIT(1)) > 0), +			500, +			10000)) +		return -ETIMEDOUT; + +	writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG); + +	if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS, +			status, +			((status & BIT(0)) > 0), +			500, +			10000)) +		return -ETIMEDOUT; + +	if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS, +			status, +			((status & BIT(1)) > 0), +			500, +			10000)) +		return -ETIMEDOUT; + +	/* +	 * At least for 7nm DP PHY this has to be done after enabling link +	 * clock. +	 */ + +	if (dp_opts->lanes == 1) { +		bias0_en = reverse ? 0x3e : 0x15; +		bias1_en = reverse ? 0x15 : 0x3e; +		drvr0_en = reverse ? 0x13 : 0x10; +		drvr1_en = reverse ? 0x10 : 0x13; +	} else if (dp_opts->lanes == 2) { +		bias0_en = reverse ? 0x3f : 0x15; +		bias1_en = reverse ? 0x15 : 0x3f; +		drvr0_en = 0x10; +		drvr1_en = 0x10; +	} else { +		bias0_en = 0x3f; +		bias1_en = 0x3f; +		drvr0_en = 0x10; +		drvr1_en = 0x10; +	} + +	writel(drvr0_en, qphy->tx + QSERDES_V4_TX_HIGHZ_DRVR_EN); +	writel(bias0_en, qphy->tx + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN); +	writel(drvr1_en, qphy->tx2 + QSERDES_V4_TX_HIGHZ_DRVR_EN); +	writel(bias1_en, qphy->tx2 + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN); + +	writel(0x18, qphy->pcs + QSERDES_DP_PHY_CFG); +	udelay(2000); +	writel(0x19, qphy->pcs + QSERDES_DP_PHY_CFG); + +	if (readl_poll_timeout(qphy->pcs + QSERDES_V4_DP_PHY_STATUS, +			status, +			((status & BIT(1)) > 0), +			500, +			10000)) +		return -ETIMEDOUT; + +	writel(0x0a, qphy->tx + QSERDES_V4_TX_TX_POL_INV); +	writel(0x0a, qphy->tx2 + QSERDES_V4_TX_TX_POL_INV); + +	writel(0x27, qphy->tx + QSERDES_V4_TX_TX_DRV_LVL); +	writel(0x27, qphy->tx2 + QSERDES_V4_TX_TX_DRV_LVL); + +	writel(0x20, qphy->tx + QSERDES_V4_TX_TX_EMP_POST1_LVL); +	writel(0x20, qphy->tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL); + +	return 0; +} + +/* + * We need to calibrate the aux setting here as many times + * as the caller tries + */ +static int qcom_qmp_v4_dp_phy_calibrate(struct qmp_phy *qphy) +{ +	static const u8 cfg1_settings[] = { 0x20, 0x13, 0x23, 0x1d }; +	u8 val; + +	qphy->dp_aux_cfg++; +	qphy->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings); +	val = cfg1_settings[qphy->dp_aux_cfg]; + +	writel(val, qphy->pcs + QSERDES_DP_PHY_AUX_CFG1); + +	return 0; +} + +static int qcom_qmp_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ +	const struct phy_configure_opts_dp *dp_opts = &opts->dp; +	struct qmp_phy *qphy = phy_get_drvdata(phy); +	const struct qmp_phy_cfg *cfg = qphy->cfg; + +	memcpy(&qphy->dp_opts, dp_opts, sizeof(*dp_opts)); +	if (qphy->dp_opts.set_voltages) { +		cfg->configure_dp_tx(qphy); +		qphy->dp_opts.set_voltages = 0; +	} + +	return 0; +} + +static int qcom_qmp_dp_phy_calibrate(struct phy *phy) +{ +	struct qmp_phy *qphy = phy_get_drvdata(phy); +	const struct qmp_phy_cfg *cfg = qphy->cfg; + +	if (cfg->calibrate_dp_phy) +		return cfg->calibrate_dp_phy(qphy);  	return 0;  } @@ -3729,7 +4124,7 @@ static int qcom_qmp_phy_init(struct phy *phy)  		return ret;  	if (cfg->type == PHY_TYPE_DP) -		qcom_qmp_phy_dp_aux_init(qphy); +		cfg->dp_aux_init(qphy);  	return 0;  } @@ -3783,7 +4178,7 @@ static int qcom_qmp_phy_power_on(struct phy *phy)  	/* Configure special DP tx tunings */  	if (cfg->type == PHY_TYPE_DP) -		qcom_qmp_phy_configure_dp_tx(qphy); +		cfg->configure_dp_tx(qphy);  	qcom_qmp_phy_configure_lane(rx, cfg->regs,  				    cfg->rx_tbl, cfg->rx_tbl_num, 1); @@ -3802,7 +4197,7 @@ static int qcom_qmp_phy_power_on(struct phy *phy)  	/* Configure link rate, swing, etc. */  	if (cfg->type == PHY_TYPE_DP) { -		qcom_qmp_phy_configure_dp_phy(qphy); +		cfg->configure_dp_phy(qphy);  	} else {  		qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);  		if (cfg->pcs_tbl_sec) @@ -3874,7 +4269,7 @@ static int qcom_qmp_phy_power_off(struct phy *phy)  	if (cfg->type == PHY_TYPE_DP) {  		/* Assert DP PHY power down */ -		writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL); +		writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_DP_PHY_PD_CTL);  	} else {  		/* PHY reset */  		if (!cfg->no_pcs_sw_reset) @@ -4578,6 +4973,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {  		.compatible = "qcom,sm8250-qmp-usb3-phy",  		.data = &sm8250_usb3phy_cfg,  	}, { +		.compatible = "qcom,sm8250-qmp-usb3-dp-phy", +		/* It's a combo phy */ +	}, {  		.compatible = "qcom,sm8250-qmp-usb3-uni-phy",  		.data = &sm8250_usb3_uniphy_cfg,  	}, { @@ -4611,6 +5009,10 @@ static const struct of_device_id qcom_qmp_combo_phy_of_match_table[] = {  		.compatible = "qcom,sc7180-qmp-usb3-dp-phy",  		.data = &sc7180_usb3dpphy_cfg,  	}, +	{ +		.compatible = "qcom,sm8250-qmp-usb3-dp-phy", +		.data = &sm8250_usb3dpphy_cfg, +	},  	{ }  }; | 
