On Fri, 29 Nov 2024 at 09:59, Xiangxu Yin <quic_xiangxuy@xxxxxxxxxxx> wrote: > > Extended DP support for QCS615 USB or DP phy. Differentiated between > USBC and DP PHY using the match table’s type, dynamically generating > different types of cfg and layout attributes during initialization based > on this type. Static variables are stored in cfg, while parsed values > are organized into the layout structure. We didn't have an understanding / conclusion whether qcom,usb-ssphy-qmp-usb3-or-dp PHYs are actually a single device / PHY or two PHYs being placed next to each other. Could you please start your commit message by explaining it? Or even better, make that a part of the cover letter for a new series touching just the USBC PHY driver. DP changes don't have anything in common with the PHY changes, so you can split the series into two. > > Signed-off-by: Xiangxu Yin <quic_xiangxuy@xxxxxxxxxxx> > --- > drivers/phy/qualcomm/phy-qcom-qmp-dp-phy.h | 1 + > drivers/phy/qualcomm/phy-qcom-qmp-usbc.c | 1453 ++++++++++++++++++++++++---- Too many changes for a single patch. Please split into logical chunks. > 2 files changed, 1254 insertions(+), 200 deletions(-) > > diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-dp-phy.h b/drivers/phy/qualcomm/phy-qcom-qmp-dp-phy.h > index 0ebd405bcaf0cac8215550bfc9b226f30cc43a59..59885616405f878885d0837838a0bac1899fb69f 100644 > --- a/drivers/phy/qualcomm/phy-qcom-qmp-dp-phy.h > +++ b/drivers/phy/qualcomm/phy-qcom-qmp-dp-phy.h > @@ -25,6 +25,7 @@ > #define QSERDES_DP_PHY_AUX_CFG7 0x03c > #define QSERDES_DP_PHY_AUX_CFG8 0x040 > #define QSERDES_DP_PHY_AUX_CFG9 0x044 > +#define QSERDES_DP_PHY_VCO_DIV 0x068 > > /* QSERDES COM_BIAS_EN_CLKBUFLR_EN bits */ > # define QSERDES_V3_COM_BIAS_EN 0x0001 > diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c > index cf12a6f12134dc77ff032f967b2efa43e3de4b21..7fece9d7dc959ed5a7c62077d8552324c3734859 100644 > --- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c > +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c > @@ -22,13 +22,20 @@ > #include <linux/slab.h> > #include <linux/usb/typec.h> > #include <linux/usb/typec_mux.h> > +#include <dt-bindings/phy/phy-qcom-qmp.h> > +#include <drm/bridge/aux-bridge.h> > > #include "phy-qcom-qmp-common.h" > > #include "phy-qcom-qmp.h" > #include "phy-qcom-qmp-pcs-misc-v3.h" > > +#include "phy-qcom-qmp-dp-phy.h" > +#include "phy-qcom-qmp-dp-phy-v3.h" > + > #define PHY_INIT_COMPLETE_TIMEOUT 10000 > +#define SW_PORTSELECT_VAL BIT(0) > +#define SW_PORTSELECT_MUX BIT(1) > > /* set of registers with offsets different per-PHY */ > enum qphy_reg_layout { > @@ -284,7 +291,26 @@ static const struct qmp_phy_init_tbl qcm2290_usb3_pcs_tbl[] = { > QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88), > }; > > -struct qmp_usbc_offsets { > +enum qmp_phy_usbc_type { > + QMP_PHY_USBC_INVALID, How can a type be invalid? > + QMP_PHY_USBC_USB, > + QMP_PHY_USBC_DP, > +}; > + > +/* list of regulators */ > +struct qmp_regulator_data { > + const char *name; > + unsigned int enable_load; > +}; > + > +struct dev_cfg { > + int type; > + const void *cfg; > +}; > + > +struct qmp_usbc; > + > +struct qmp_usbc_usb_offsets { > u16 serdes; > u16 pcs; > u16 pcs_misc; > @@ -295,9 +321,9 @@ struct qmp_usbc_offsets { > u16 rx2; > }; > > -/* struct qmp_phy_cfg - per-PHY initialization config */ > -struct qmp_phy_cfg { > - const struct qmp_usbc_offsets *offsets; > +/* struct qmp_phy_usb_cfg - per-usb PHY initialization config */ what is "per-usb PHY"? > +struct qmp_phy_usb_cfg { > + const struct qmp_usbc_usb_offsets *offsets; > > /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ > const struct qmp_phy_init_tbl *serdes_tbl; > @@ -317,11 +343,7 @@ struct qmp_phy_cfg { > const unsigned int *regs; > }; > > -struct qmp_usbc { > - struct device *dev; > - > - const struct qmp_phy_cfg *cfg; > - > +struct qmp_phy_usb_layout { > void __iomem *serdes; > void __iomem *pcs; > void __iomem *pcs_misc; > @@ -329,28 +351,67 @@ struct qmp_usbc { > void __iomem *rx; > void __iomem *tx2; > void __iomem *rx2; > - > struct regmap *tcsr_map; > u32 vls_clamp_reg; > - > + enum phy_mode mode; > + struct typec_switch_dev *sw; > struct clk *pipe_clk; > + struct clk_fixed_rate pipe_clk_fixed; > +}; > + > +struct qmp_usbc_dp_offsets { > + u16 dp_serdes; > + u16 dp_txa; > + u16 dp_txb; > + u16 dp_phy; > +}; > + > +/* struct qmp_phy_dp_cfg - per-dp PHY initialization config */ > +struct qmp_phy_dp_cfg { > + const struct qmp_usbc_dp_offsets *offsets; > + > + /* DP PHY swing and pre_emphasis tables */ > + const u8 (*swing_tbl)[4][4]; > + const u8 (*pre_emphasis_tbl)[4][4]; > + > + // /* DP PHY callbacks */ > + int (*dp_aux_init)(struct qmp_usbc *qmp); > + int (*configure_dp_serdes)(struct qmp_usbc *qmp); > + int (*configure_dp_voltages)(struct qmp_usbc *qmp); > + int (*configure_dp_phy)(struct qmp_usbc *qmp); > + int (*calibrate_dp_phy)(struct qmp_usbc *qmp); > + > + const struct qmp_regulator_data *vreg_list; > + int num_vregs; > +}; > + > +struct qmp_phy_dp_layout { > + void __iomem *dp_phy; > + void __iomem *dp_tx; > + void __iomem *dp_tx2; > + void __iomem *dp_serdes; > + struct regmap *tcsr_map; > + u32 dp_phy_mode; > + unsigned int dp_aux_cfg; > + struct phy_configure_opts_dp dp_opts; > + struct clk_hw dp_link_hw; > + struct clk_hw dp_pixel_hw; > +}; > + > +struct qmp_usbc { > + struct device *dev; > + int type; > struct clk_bulk_data *clks; > int num_clks; > int num_resets; > struct reset_control_bulk_data *resets; > struct regulator_bulk_data *vregs; > - > struct mutex phy_mutex; > - > - enum phy_mode mode; > - unsigned int usb_init_count; > - > struct phy *phy; > - > - struct clk_fixed_rate pipe_clk_fixed; > - > - struct typec_switch_dev *sw; > enum typec_orientation orientation; > + unsigned int init_count; > + const void *cfg; > + void *layout; The patch contains a mixture of renames bundled with actual changes. Please explain why old names are bad in a separate patch. > }; > > static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) > @@ -391,12 +452,21 @@ static const char * const usb3phy_reset_l[] = { > "phy_phy", "phy", > }; > > +static const char * const dp_usb3phy_reset_l[] = { > + "phy", > +}; > + > /* list of regulators */ > -static const char * const qmp_phy_vreg_l[] = { > +static const char * const qmp_phy_usb_vreg_l[] = { > "vdda-phy", "vdda-pll", > }; > > -static const struct qmp_usbc_offsets qmp_usbc_offsets_v3_qcm2290 = { > +static struct qmp_regulator_data qmp_phy_dp_vreg_l[] = { > + { .name = "vdda-phy", .enable_load = 21800 }, > + { .name = "vdda-pll", .enable_load = 36000 }, > +}; > + > +static const struct qmp_usbc_usb_offsets qmp_usbc_usb_offsets_v3_qcm2290 = { > .serdes = 0x0, > .pcs = 0xc00, > .pcs_misc = 0xa00, > @@ -406,8 +476,15 @@ static const struct qmp_usbc_offsets qmp_usbc_offsets_v3_qcm2290 = { > .rx2 = 0x800, > }; > > -static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { > - .offsets = &qmp_usbc_offsets_v3_qcm2290, > +static const struct qmp_usbc_dp_offsets qmp_usbc_dp_offsets_qcs615 = { > + .dp_serdes = 0x0C00, > + .dp_txa = 0x0400, > + .dp_txb = 0x0800, > + .dp_phy = 0x0000, > +}; > + > +static const struct qmp_phy_usb_cfg msm8998_usb3phy_cfg = { > + .offsets = &qmp_usbc_usb_offsets_v3_qcm2290, > > .serdes_tbl = msm8998_usb3_serdes_tbl, > .serdes_tbl_num = ARRAY_SIZE(msm8998_usb3_serdes_tbl), > @@ -417,13 +494,13 @@ static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { > .rx_tbl_num = ARRAY_SIZE(msm8998_usb3_rx_tbl), > .pcs_tbl = msm8998_usb3_pcs_tbl, > .pcs_tbl_num = ARRAY_SIZE(msm8998_usb3_pcs_tbl), > - .vreg_list = qmp_phy_vreg_l, > - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), > + .vreg_list = qmp_phy_usb_vreg_l, > + .num_vregs = ARRAY_SIZE(qmp_phy_usb_vreg_l), > .regs = qmp_v3_usb3phy_regs_layout, > }; > > -static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { > - .offsets = &qmp_usbc_offsets_v3_qcm2290, > +static const struct qmp_phy_usb_cfg qcm2290_usb3phy_cfg = { > + .offsets = &qmp_usbc_usb_offsets_v3_qcm2290, > > .serdes_tbl = qcm2290_usb3_serdes_tbl, > .serdes_tbl_num = ARRAY_SIZE(qcm2290_usb3_serdes_tbl), > @@ -433,13 +510,13 @@ static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { > .rx_tbl_num = ARRAY_SIZE(qcm2290_usb3_rx_tbl), > .pcs_tbl = qcm2290_usb3_pcs_tbl, > .pcs_tbl_num = ARRAY_SIZE(qcm2290_usb3_pcs_tbl), > - .vreg_list = qmp_phy_vreg_l, > - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), > + .vreg_list = qmp_phy_usb_vreg_l, > + .num_vregs = ARRAY_SIZE(qmp_phy_usb_vreg_l), > .regs = qmp_v3_usb3phy_regs_layout_qcm2290, > }; > > -static const struct qmp_phy_cfg sdm660_usb3phy_cfg = { > - .offsets = &qmp_usbc_offsets_v3_qcm2290, > +static const struct qmp_phy_usb_cfg sdm660_usb3phy_cfg = { > + .offsets = &qmp_usbc_usb_offsets_v3_qcm2290, > > .serdes_tbl = qcm2290_usb3_serdes_tbl, > .serdes_tbl_num = ARRAY_SIZE(qcm2290_usb3_serdes_tbl), > @@ -449,20 +526,352 @@ static const struct qmp_phy_cfg sdm660_usb3phy_cfg = { > .rx_tbl_num = ARRAY_SIZE(sdm660_usb3_rx_tbl), > .pcs_tbl = qcm2290_usb3_pcs_tbl, > .pcs_tbl_num = ARRAY_SIZE(qcm2290_usb3_pcs_tbl), > - .vreg_list = qmp_phy_vreg_l, > - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), > + .vreg_list = qmp_phy_usb_vreg_l, > + .num_vregs = ARRAY_SIZE(qmp_phy_usb_vreg_l), > .regs = qmp_v3_usb3phy_regs_layout_qcm2290, > }; > > -static int qmp_usbc_init(struct phy *phy) > +static const u8 qmp_dp_pre_emphasis_hbr2_rbr[4][4] = { > + {0x00, 0x0B, 0x12, 0xFF}, /* pe0, 0 db */ > + {0x00, 0x0A, 0x12, 0xFF}, /* pe1, 3.5 db */ > + {0x00, 0x0C, 0xFF, 0xFF}, /* pe2, 6.0 db */ > + {0xFF, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */ > +}; > + > +static const u8 qmp_dp_voltage_swing_hbr2_rbr[4][4] = { > + {0x07, 0x0F, 0x14, 0xFF}, /* sw0, 0.4v */ > + {0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6 v */ > + {0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */ > + {0xFF, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ > +}; > + > +static int qcs615_qmp_dp_aux_init(struct qmp_usbc *qmp); > +static int qcs615_qmp_configure_dp_serdes(struct qmp_usbc *qmp); > +static int qcs615_qmp_configure_dp_voltages(struct qmp_usbc *qmp); > +static int qcs615_qmp_configure_dp_phy(struct qmp_usbc *qmp); > +static int qcs615_qmp_calibrate_dp_phy(struct qmp_usbc *qmp); Are those functions really platform-specific? > + > +static void qmp_usbc_check_dp_phy(struct qmp_usbc *qmp, const char *pos); > + > +static const struct qmp_phy_dp_cfg qcs615_dpphy_cfg = { > + .offsets = &qmp_usbc_dp_offsets_qcs615, > + > + .swing_tbl = &qmp_dp_voltage_swing_hbr2_rbr, > + .pre_emphasis_tbl = &qmp_dp_pre_emphasis_hbr2_rbr, > + > + .dp_aux_init = qcs615_qmp_dp_aux_init, > + .configure_dp_serdes = qcs615_qmp_configure_dp_serdes, > + .configure_dp_voltages = qcs615_qmp_configure_dp_voltages, > + .configure_dp_phy = qcs615_qmp_configure_dp_phy, > + .calibrate_dp_phy = qcs615_qmp_calibrate_dp_phy, > + > + .vreg_list = qmp_phy_dp_vreg_l, > + .num_vregs = ARRAY_SIZE(qmp_phy_dp_vreg_l), > +}; > + > +#define to_usb_cfg(x) ((struct qmp_phy_usb_cfg *)(x->cfg)) > +#define to_dp_cfg(x) ((struct qmp_phy_dp_cfg *)(x->cfg)) > +#define to_usb_layout(x) ((struct qmp_phy_usb_layout *)(x->layout)) > +#define to_dp_layout(x) ((struct qmp_phy_dp_layout *)(x->layout)) > + > +static int qcs615_qmp_dp_aux_init(struct qmp_usbc *qmp) > +{ > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + > + regmap_write(layout->tcsr_map, layout->dp_phy_mode, 0x1); > + > + writel(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, > + layout->dp_phy + 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, > + layout->dp_phy + QSERDES_DP_PHY_PD_CTL); > + > + writel(0x00, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG0); > + writel(0x13, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG1); > + writel(0x00, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG2); > + writel(0x00, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG3); > + writel(0x0a, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG4); > + writel(0x26, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG5); > + writel(0x0a, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG6); > + writel(0x03, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG7); > + writel(0xbb, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG8); > + writel(0x03, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG9); > + layout->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, > + layout->dp_phy + QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK); > + return 0; > +} We've had DP PHY implementation in QMP Combo PHY and in eDP PHY. Please review them and work on extracting common bits into some kind of a library. At least -combo and your -usbc implementation seem close enough to warrant common library code. > + > +static int qcs615_qmp_configure_dp_serdes(struct qmp_usbc *qmp) > +{ > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + void __iomem *serdes = layout->dp_serdes; > + const struct phy_configure_opts_dp *dp_opts = &layout->dp_opts; > + u8 hsclk_sel; > + u8 dec_start_mode0; > + u8 div_frac_start1_mode0; > + u8 div_frac_start2_mode0; > + u8 div_frac_start3_mode0; > + u8 lock_cmp1_mode0; > + u8 lock_cmp2_mode0; > + u8 lock_cmp3_mode0; > + > + switch (dp_opts->link_rate) { > + case 1620: > + hsclk_sel = 0x2c; > + dec_start_mode0 = 0x69; > + div_frac_start1_mode0 = 0x00; > + div_frac_start2_mode0 = 0x80; > + div_frac_start3_mode0 = 0x07; > + lock_cmp1_mode0 = 0xbf; > + lock_cmp2_mode0 = 0x21; > + lock_cmp3_mode0 = 0x00; > + break; > + case 2700: > + hsclk_sel = 0x24; > + dec_start_mode0 = 0x69; > + div_frac_start1_mode0 = 0x00; > + div_frac_start2_mode0 = 0x80; > + div_frac_start3_mode0 = 0x07; > + lock_cmp1_mode0 = 0x3f; > + lock_cmp2_mode0 = 0x38; > + lock_cmp3_mode0 = 0x00; > + break; > + case 5400: > + hsclk_sel = 0x20; > + dec_start_mode0 = 0x8c; > + div_frac_start1_mode0 = 0x00; > + div_frac_start2_mode0 = 0x00; > + div_frac_start3_mode0 = 0x0a; > + lock_cmp1_mode0 = 0x7f; > + lock_cmp2_mode0 = 0x70; > + lock_cmp3_mode0 = 0x00; > + break; > + default: > + /* Other link rates aren't supported */ > + return -EINVAL; > + } > + > + writel(0x01, serdes + QSERDES_COM_SVS_MODE_CLK_SEL); > + writel(0x37, serdes + QSERDES_COM_SYSCLK_EN_SEL); > + writel(0x00, serdes + QSERDES_COM_CLK_SELECT); > + writel(0x06, serdes + QSERDES_COM_SYS_CLK_CTRL); > + writel(0x3f, serdes + QSERDES_COM_BIAS_EN_CLKBUFLR_EN); > + writel(0x0e, serdes + QSERDES_COM_CLK_ENABLE1); > + writel(0x0f, serdes + QSERDES_COM_BG_CTRL); > + writel(0x06, serdes + QSERDES_COM_SYSCLK_BUF_ENABLE); > + writel(0x30, serdes + QSERDES_COM_CLK_SELECT); > + writel(0x0f, serdes + QSERDES_COM_PLL_IVCO); > + writel(0x28, serdes + QSERDES_COM_PLL_CCTRL_MODE0); > + writel(0x16, serdes + QSERDES_COM_PLL_RCTRL_MODE0); > + writel(0x0b, serdes + QSERDES_COM_CP_CTRL_MODE0); > + > + writel(hsclk_sel, serdes + QSERDES_COM_HSCLK_SEL); > + writel(dec_start_mode0, serdes + QSERDES_COM_DEC_START_MODE0); > + writel(div_frac_start1_mode0, serdes + QSERDES_COM_DIV_FRAC_START1_MODE0); > + writel(div_frac_start2_mode0, serdes + QSERDES_COM_DIV_FRAC_START2_MODE0); > + writel(div_frac_start3_mode0, serdes + QSERDES_COM_DIV_FRAC_START3_MODE0); > + writel(lock_cmp1_mode0, serdes + QSERDES_COM_LOCK_CMP1_MODE0); > + writel(lock_cmp2_mode0, serdes + QSERDES_COM_LOCK_CMP2_MODE0); > + writel(lock_cmp3_mode0, serdes + QSERDES_COM_LOCK_CMP3_MODE0); > + > + writel(0x40, serdes + QSERDES_COM_INTEGLOOP_GAIN0_MODE0); > + writel(0x00, serdes + QSERDES_COM_INTEGLOOP_GAIN1_MODE0); > + writel(0x00, serdes + QSERDES_COM_VCO_TUNE_MAP); > + writel(0x08, serdes + QSERDES_COM_BG_TIMER); > + writel(0x05, serdes + QSERDES_COM_CORECLK_DIV); > + writel(0x00, serdes + QSERDES_COM_VCO_TUNE_CTRL); > + writel(0x00, serdes + QSERDES_COM_VCO_TUNE1_MODE0); > + writel(0x00, serdes + QSERDES_COM_VCO_TUNE2_MODE0); > + writel(0x00, serdes + QSERDES_COM_VCO_TUNE_CTRL); > + > + writel(0x0f, serdes + QSERDES_COM_CORE_CLK_EN); > + > + return 0; > +} > + > +static int qcs615_qmp_configure_dp_voltages(struct qmp_usbc *qmp) > +{ > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + struct qmp_phy_dp_cfg *cfg = to_dp_cfg(qmp); > + const struct phy_configure_opts_dp *dp_opts = &layout->dp_opts; > + void __iomem *tx = layout->dp_tx; > + void __iomem *tx2 = layout->dp_tx2; > + unsigned int v_level = 0, p_level = 0; > + u8 voltage_swing_cfg, pre_emphasis_cfg; > + int i; > + > + if (dp_opts->lanes > 4) { > + dev_err(qmp->dev, "Invalid lane_num(%d)\n", dp_opts->lanes); > + return -EINVAL; > + } > + > + for (i = 0; i < dp_opts->lanes; i++) { > + v_level = max(v_level, dp_opts->voltage[i]); > + p_level = max(p_level, dp_opts->pre[i]); > + } > + > + if ((v_level > 4) || (pre_emphasis_cfg > 4)) { > + dev_err(qmp->dev, "Invalid v(%d) | p(%d) level)\n", > + v_level, pre_emphasis_cfg); > + return -EINVAL; > + } > + > + voltage_swing_cfg = (*cfg->swing_tbl)[v_level][p_level]; > + pre_emphasis_cfg = (*cfg->pre_emphasis_tbl)[v_level][p_level]; > + > + /* 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; > + > + if (voltage_swing_cfg == 0xFF && pre_emphasis_cfg == 0xFF) > + return -EINVAL; > + > + /* program default setting first */ > + writel(0x2A, tx + QSERDES_V3_TX_TX_DRV_LVL); > + writel(0x20, tx + QSERDES_V3_TX_TX_EMP_POST1_LVL); > + writel(0x2A, tx2 + QSERDES_V3_TX_TX_DRV_LVL); > + writel(0x20, tx2 + QSERDES_V3_TX_TX_EMP_POST1_LVL); Lowercase all hex numbers. > + > + writel(voltage_swing_cfg, tx + QSERDES_V3_TX_TX_DRV_LVL); > + writel(pre_emphasis_cfg, tx + QSERDES_V3_TX_TX_EMP_POST1_LVL); > + writel(voltage_swing_cfg, tx2 + QSERDES_V3_TX_TX_DRV_LVL); > + writel(pre_emphasis_cfg, tx2 + QSERDES_V3_TX_TX_EMP_POST1_LVL); > + > + return 0; > +} > + > +static int qcs615_qmp_configure_dp_phy(struct qmp_usbc *qmp) > +{ > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + u32 status; > + > + writel(0x01, layout->dp_phy + QSERDES_DP_PHY_CFG); > + writel(0x05, layout->dp_phy + QSERDES_DP_PHY_CFG); > + writel(0x01, layout->dp_phy + QSERDES_DP_PHY_CFG); > + writel(0x09, layout->dp_phy + QSERDES_DP_PHY_CFG); > + > + writel(0x20, layout->dp_serdes + QSERDES_COM_RESETSM_CNTRL); > + > + // C_READY > + if (readl_poll_timeout(layout->dp_serdes + QSERDES_COM_C_READY_STATUS, > + status, > + ((status & BIT(0)) > 0), > + 500, > + 10000)) { > + dev_err(qmp->dev, "C_READY not ready\n"); > + return -ETIMEDOUT; > + } > + > + // FREQ_DONE > + if (readl_poll_timeout(layout->dp_serdes + QSERDES_COM_CMN_STATUS, > + status, > + ((status & BIT(0)) > 0), > + 500, > + 10000)){ > + dev_err(qmp->dev, "FREQ_DONE not ready\n"); > + return -ETIMEDOUT; > + } > + > + // PLL_LOCKED > + if (readl_poll_timeout(layout->dp_serdes + QSERDES_COM_CMN_STATUS, > + status, > + ((status & BIT(1)) > 0), > + 500, > + 10000)){ > + dev_err(qmp->dev, "PLL_LOCKED not ready\n"); > + return -ETIMEDOUT; > + } > + > + writel(0x19, layout->dp_phy + QSERDES_DP_PHY_CFG); > + udelay(10); > + > + // TSYNC_DONE > + if (readl_poll_timeout(layout->dp_phy + QSERDES_V3_DP_PHY_STATUS, > + status, > + ((status & BIT(0)) > 0), > + 500, > + 10000)){ > + dev_err(qmp->dev, "TSYNC_DONE not ready\n"); > + return -ETIMEDOUT; > + } > + > + // PHY_READY > + if (readl_poll_timeout(layout->dp_phy + QSERDES_V3_DP_PHY_STATUS, > + status, > + ((status & BIT(1)) > 0), > + 500, > + 10000)){ > + dev_err(qmp->dev, "PHY_READY not ready\n"); > + return -ETIMEDOUT; > + } > + > + writel(0x3f, layout->dp_tx + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); > + writel(0x10, layout->dp_tx + QSERDES_V3_TX_HIGHZ_DRVR_EN); > + writel(0x0a, layout->dp_tx + QSERDES_V3_TX_TX_POL_INV); > + writel(0x3f, layout->dp_tx2 + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); > + writel(0x10, layout->dp_tx2 + QSERDES_V3_TX_HIGHZ_DRVR_EN); > + writel(0x0a, layout->dp_tx2 + QSERDES_V3_TX_TX_POL_INV); > + > + writel(0x18, layout->dp_phy + QSERDES_DP_PHY_CFG); > + writel(0x19, layout->dp_phy + QSERDES_DP_PHY_CFG); > + > + if (readl_poll_timeout(layout->dp_phy + QSERDES_V3_DP_PHY_STATUS, > + status, > + ((status & BIT(1)) > 0), > + 500, > + 10000)){ > + dev_err(qmp->dev, "PHY_READY not ready\n"); > + return -ETIMEDOUT; > + } > + > + return 0; > +} > + > +static int qcs615_qmp_calibrate_dp_phy(struct qmp_usbc *qmp) > +{ > + static const u8 cfg1_settings[] = {0x13, 0x23, 0x1d}; > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + u8 val; > + > + layout->dp_aux_cfg++; > + layout->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings); > + val = cfg1_settings[layout->dp_aux_cfg]; > + > + writel(val, layout->dp_phy + QSERDES_DP_PHY_AUX_CFG1); > + > + qmp_usbc_check_dp_phy(qmp, "pos_calibrate"); > + > + return 0; > +} > + > +static int qmp_usbc_com_init(struct phy *phy) > { > struct qmp_usbc *qmp = phy_get_drvdata(phy); > - const struct qmp_phy_cfg *cfg = qmp->cfg; > - void __iomem *pcs = qmp->pcs; > + int num_vregs; > u32 val = 0; > int ret; > + unsigned int reg_pwr_dn; > > - ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); > + if (qmp->type == QMP_PHY_USBC_USB) { > + struct qmp_phy_usb_cfg *cfg = to_usb_cfg(qmp); > + > + num_vregs = cfg->num_vregs; > + reg_pwr_dn = cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]; > + } else { > + struct qmp_phy_dp_cfg *cfg = to_dp_cfg(qmp); > + > + num_vregs = cfg->num_vregs; > + } > + > + ret = regulator_bulk_enable(num_vregs, qmp->vregs); > if (ret) { > dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); > return ret; > @@ -484,73 +893,85 @@ static int qmp_usbc_init(struct phy *phy) > if (ret) > goto err_assert_reset; > > - qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], SW_PWRDN); > - > -#define SW_PORTSELECT_VAL BIT(0) > -#define SW_PORTSELECT_MUX BIT(1) > /* Use software based port select and switch on typec orientation */ > val = SW_PORTSELECT_MUX; > if (qmp->orientation == TYPEC_ORIENTATION_REVERSE) > val |= SW_PORTSELECT_VAL; > - writel(val, qmp->pcs_misc); > + > + if (qmp->type == QMP_PHY_USBC_USB) { Why? > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > + > + qphy_setbits(layout->pcs, reg_pwr_dn, SW_PWRDN); > + writel(val, layout->pcs_misc); > + } > > return 0; > > err_assert_reset: > reset_control_bulk_assert(qmp->num_resets, qmp->resets); > err_disable_regulators: > - regulator_bulk_disable(cfg->num_vregs, qmp->vregs); > + regulator_bulk_disable(num_vregs, qmp->vregs); > > return ret; > } > > -static int qmp_usbc_exit(struct phy *phy) > +static int qmp_usbc_com_exit(struct phy *phy) > { > struct qmp_usbc *qmp = phy_get_drvdata(phy); > - const struct qmp_phy_cfg *cfg = qmp->cfg; > + int num_vregs; > > reset_control_bulk_assert(qmp->num_resets, qmp->resets); > > clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks); > > - regulator_bulk_disable(cfg->num_vregs, qmp->vregs); > + if (qmp->type == QMP_PHY_USBC_USB) { > + struct qmp_phy_usb_cfg *cfg = to_usb_cfg(qmp); > + > + num_vregs = cfg->num_vregs; > + } else { > + struct qmp_phy_dp_cfg *cfg = to_dp_cfg(qmp); > + > + num_vregs = cfg->num_vregs; > + } > + regulator_bulk_disable(num_vregs, qmp->vregs); > > return 0; > } > > -static int qmp_usbc_power_on(struct phy *phy) > +static int qmp_usbc_usb_power_on(struct phy *phy) > { > struct qmp_usbc *qmp = phy_get_drvdata(phy); > - const struct qmp_phy_cfg *cfg = qmp->cfg; > + const struct qmp_phy_usb_cfg *cfg = to_usb_cfg(qmp); > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > void __iomem *status; > unsigned int val; > int ret; > > - qmp_configure(qmp->dev, qmp->serdes, cfg->serdes_tbl, > + qmp_configure(qmp->dev, layout->serdes, cfg->serdes_tbl, > cfg->serdes_tbl_num); > > - ret = clk_prepare_enable(qmp->pipe_clk); > + ret = clk_prepare_enable(layout->pipe_clk); > if (ret) { > dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); > return ret; > } > > /* Tx, Rx, and PCS configurations */ > - qmp_configure_lane(qmp->dev, qmp->tx, cfg->tx_tbl, cfg->tx_tbl_num, 1); > - qmp_configure_lane(qmp->dev, qmp->rx, cfg->rx_tbl, cfg->rx_tbl_num, 1); > + qmp_configure_lane(qmp->dev, layout->tx, cfg->tx_tbl, cfg->tx_tbl_num, 1); > + qmp_configure_lane(qmp->dev, layout->rx, cfg->rx_tbl, cfg->rx_tbl_num, 1); > > - qmp_configure_lane(qmp->dev, qmp->tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2); > - qmp_configure_lane(qmp->dev, qmp->rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2); > + qmp_configure_lane(qmp->dev, layout->tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2); > + qmp_configure_lane(qmp->dev, layout->rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2); > > - qmp_configure(qmp->dev, qmp->pcs, cfg->pcs_tbl, cfg->pcs_tbl_num); > + qmp_configure(qmp->dev, layout->pcs, cfg->pcs_tbl, cfg->pcs_tbl_num); > > /* Pull PHY out of reset state */ > - qphy_clrbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); > + qphy_clrbits(layout->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); > > /* start SerDes and Phy-Coding-Sublayer */ > - qphy_setbits(qmp->pcs, cfg->regs[QPHY_START_CTRL], SERDES_START | PCS_START); > + qphy_setbits(layout->pcs, cfg->regs[QPHY_START_CTRL], SERDES_START | PCS_START); > > - status = qmp->pcs + cfg->regs[QPHY_PCS_STATUS]; > + status = layout->pcs + cfg->regs[QPHY_PCS_STATUS]; > ret = readl_poll_timeout(status, val, !(val & PHYSTATUS), 200, > PHY_INIT_COMPLETE_TIMEOUT); > if (ret) { > @@ -561,92 +982,348 @@ static int qmp_usbc_power_on(struct phy *phy) > return 0; > > err_disable_pipe_clk: > - clk_disable_unprepare(qmp->pipe_clk); > + clk_disable_unprepare(layout->pipe_clk); > > return ret; > } > > -static int qmp_usbc_power_off(struct phy *phy) > +static int qmp_usbc_usb_power_off(struct phy *phy) > { > struct qmp_usbc *qmp = phy_get_drvdata(phy); > - const struct qmp_phy_cfg *cfg = qmp->cfg; > + const struct qmp_phy_usb_cfg *cfg = to_usb_cfg(qmp); > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > > - clk_disable_unprepare(qmp->pipe_clk); > + clk_disable_unprepare(layout->pipe_clk); > > /* PHY reset */ > - qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); > + qphy_setbits(layout->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); > > /* stop SerDes and Phy-Coding-Sublayer */ > - qphy_clrbits(qmp->pcs, cfg->regs[QPHY_START_CTRL], > + qphy_clrbits(layout->pcs, cfg->regs[QPHY_START_CTRL], > SERDES_START | PCS_START); > > /* Put PHY into POWER DOWN state: active low */ > - qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], > + qphy_clrbits(layout->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], > SW_PWRDN); > > return 0; > } > > -static int qmp_usbc_enable(struct phy *phy) > +static int qmp_usbc_usb_enable(struct phy *phy) > { > struct qmp_usbc *qmp = phy_get_drvdata(phy); > int ret; > > mutex_lock(&qmp->phy_mutex); > > - ret = qmp_usbc_init(phy); > + ret = qmp_usbc_com_init(phy); > if (ret) > goto out_unlock; > > - ret = qmp_usbc_power_on(phy); > + ret = qmp_usbc_usb_power_on(phy); > if (ret) { > - qmp_usbc_exit(phy); > + qmp_usbc_com_exit(phy); > goto out_unlock; > } > > - qmp->usb_init_count++; > + qmp->init_count++; > out_unlock: > mutex_unlock(&qmp->phy_mutex); > > return ret; > } > > -static int qmp_usbc_disable(struct phy *phy) > +static int qmp_usbc_usb_disable(struct phy *phy) > { > struct qmp_usbc *qmp = phy_get_drvdata(phy); > int ret; > > - qmp->usb_init_count--; > - ret = qmp_usbc_power_off(phy); > + qmp->init_count--; > + ret = qmp_usbc_usb_power_off(phy); > if (ret) > return ret; > - return qmp_usbc_exit(phy); > + return qmp_usbc_com_exit(phy); > +} > + > +static int qmp_usbc_usb_set_mode(struct phy *phy, enum phy_mode mode, int submode) > +{ > + struct qmp_usbc *qmp = phy_get_drvdata(phy); > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > + > + layout->mode = mode; > + > + return 0; > +} > + > +static int qmp_usbc_dp_init(struct phy *phy) > +{ > + struct qmp_usbc *qmp = phy_get_drvdata(phy); > + const struct qmp_phy_dp_cfg *cfg = to_dp_cfg(qmp); > + int ret; > + > + if (qmp->init_count) { > + dev_err(qmp->dev, "type(%d) inited(%d)\n", qmp->type, qmp->init_count); > + return 0; > + } > + > + mutex_lock(&qmp->phy_mutex); > + > + ret = qmp_usbc_com_init(phy); > + if (ret) { > + dev_err(qmp->dev, "type(%d) com_init fail\n", qmp->type); > + goto dp_init_unlock; > + } > + > + cfg->dp_aux_init(qmp); > + > + qmp->init_count++; > + > +dp_init_unlock: > + mutex_unlock(&qmp->phy_mutex); > + return ret; > +} > + > +static int qmp_usbc_dp_exit(struct phy *phy) > +{ > + struct qmp_usbc *qmp = phy_get_drvdata(phy); > + > + mutex_lock(&qmp->phy_mutex); > + > + qmp_usbc_com_exit(phy); > + > + qmp->init_count--; > + > + mutex_unlock(&qmp->phy_mutex); > + > + return 0; > +} > + > +static int qmp_usbc_dp_configure(struct phy *phy, union phy_configure_opts *opts) > +{ > + const struct phy_configure_opts_dp *dp_opts = &opts->dp; > + struct qmp_usbc *qmp = phy_get_drvdata(phy); > + struct qmp_phy_dp_cfg *cfg = to_dp_cfg(qmp); > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + int ret; > + > + mutex_lock(&qmp->phy_mutex); > + > + memcpy(&layout->dp_opts, dp_opts, sizeof(*dp_opts)); > + if (layout->dp_opts.set_voltages) { > + ret = cfg->configure_dp_voltages(qmp); > + if (ret) { > + dev_err(qmp->dev, "type(%d) err(%d)\n", qmp->type, ret); > + mutex_unlock(&qmp->phy_mutex); > + return ret; > + } > + > + layout->dp_opts.set_voltages = 0; > + } > + > + mutex_unlock(&qmp->phy_mutex); > + > + return 0; > +} > + > +static int qmp_usbc_dp_calibrate(struct phy *phy) > +{ > + struct qmp_usbc *qmp = phy_get_drvdata(phy); > + struct qmp_phy_dp_cfg *cfg = to_dp_cfg(qmp); > + int ret = 0; > + > + mutex_lock(&qmp->phy_mutex); > + > + if (cfg->calibrate_dp_phy) { > + ret = cfg->calibrate_dp_phy(qmp); > + if (ret) { > + dev_err(qmp->dev, "type(%d) err(%d)\n", qmp->type, ret); > + mutex_unlock(&qmp->phy_mutex); > + return ret; > + } > + } > + > + mutex_unlock(&qmp->phy_mutex); > + return 0; > } > > -static int qmp_usbc_set_mode(struct phy *phy, enum phy_mode mode, int submode) > +static int qmp_usbc_configure_dp_clocks(struct qmp_usbc *qmp) > +{ > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + const struct phy_configure_opts_dp *dp_opts = &layout->dp_opts; > + u32 phy_vco_div; > + unsigned long pixel_freq; > + > + 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, layout->dp_phy + QSERDES_DP_PHY_VCO_DIV); > + > + clk_set_rate(layout->dp_link_hw.clk, dp_opts->link_rate * 100000); > + clk_set_rate(layout->dp_pixel_hw.clk, pixel_freq); > + > + return 0; > +} > + > +static void qmp_usbc_check_dp_phy(struct qmp_usbc *qmp, const char *pos) > +{ > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + u8 c_ready, cmn_status, phy_status; > + > + c_ready = readl(layout->dp_serdes + QSERDES_COM_C_READY_STATUS); > + cmn_status = readl(layout->dp_serdes + QSERDES_COM_CMN_STATUS); > + phy_status = readl(layout->dp_phy + QSERDES_V3_DP_PHY_STATUS); > + > + dev_dbg(qmp->dev, "pos(%s) c_ready(0x%x) cmn_status(0x%x) phy_status(0x%x)\n", > + pos, c_ready, cmn_status, phy_status); > +} > + > +static int qmp_usbc_dp_power_on(struct phy *phy) > +{ > + struct qmp_usbc *qmp = phy_get_drvdata(phy); > + const struct qmp_phy_dp_cfg *cfg = to_dp_cfg(qmp); > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + const struct phy_configure_opts_dp *dp_opts = &layout->dp_opts; > + bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); > + void __iomem *tx = layout->dp_tx; > + void __iomem *tx2 = layout->dp_tx2; > + u8 lane_mode_1; > + int ret = 0; > + > + mutex_lock(&qmp->phy_mutex); > + > + 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, > + layout->dp_phy + QSERDES_DP_PHY_PD_CTL); > + > + ret = cfg->configure_dp_serdes(qmp); > + if (ret) { > + dev_err(qmp->dev, "failed to config pll\n"); > + goto power_on_unlock; > + } > + > + if (dp_opts->link_rate >= 2700) > + lane_mode_1 = 0xc4; > + else > + lane_mode_1 = 0xc6; > + > + writel(lane_mode_1, tx + QSERDES_V3_TX_LANE_MODE_1); > + writel(lane_mode_1, tx2 + QSERDES_V3_TX_LANE_MODE_1); > + > + if (reverse) > + writel(0xc9, layout->dp_phy + QSERDES_DP_PHY_MODE); > + else > + writel(0xd9, layout->dp_phy + QSERDES_DP_PHY_MODE); > + > + writel(0x05, layout->dp_phy + QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL); > + writel(0x05, layout->dp_phy + QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL); > + > + writel(0x1a, tx + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); > + writel(0x40, tx + QSERDES_V3_TX_VMODE_CTRL1); > + writel(0x30, tx + QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN); > + writel(0x3d, tx + QSERDES_V3_TX_INTERFACE_SELECT); > + writel(0x0f, tx + QSERDES_V3_TX_CLKBUF_ENABLE); > + writel(0x03, tx + QSERDES_V3_TX_RESET_TSYNC_EN); > + writel(0x03, tx + QSERDES_V3_TX_TRAN_DRVR_EMP_EN); > + writel(0x00, tx + QSERDES_V3_TX_PARRATE_REC_DETECT_IDLE_EN); > + writel(0x00, tx + QSERDES_V3_TX_TX_INTERFACE_MODE); > + writel(0x2b, tx + QSERDES_V3_TX_TX_EMP_POST1_LVL); > + writel(0x2f, tx + QSERDES_V3_TX_TX_DRV_LVL); > + writel(0x04, tx + QSERDES_V3_TX_TX_BAND); > + writel(0x12, tx + QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX); > + writel(0x12, tx + QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX); > + > + writel(0x1a, tx2 + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN); > + writel(0x40, tx2 + QSERDES_V3_TX_VMODE_CTRL1); > + writel(0x30, tx2 + QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN); > + writel(0x3d, tx2 + QSERDES_V3_TX_INTERFACE_SELECT); > + writel(0x0f, tx2 + QSERDES_V3_TX_CLKBUF_ENABLE); > + writel(0x03, tx2 + QSERDES_V3_TX_RESET_TSYNC_EN); > + writel(0x03, tx2 + QSERDES_V3_TX_TRAN_DRVR_EMP_EN); > + writel(0x00, tx2 + QSERDES_V3_TX_PARRATE_REC_DETECT_IDLE_EN); > + writel(0x00, tx2 + QSERDES_V3_TX_TX_INTERFACE_MODE); > + writel(0x2b, tx2 + QSERDES_V3_TX_TX_EMP_POST1_LVL); > + writel(0x2f, tx2 + QSERDES_V3_TX_TX_DRV_LVL); > + writel(0x04, tx2 + QSERDES_V3_TX_TX_BAND); > + writel(0x12, tx2 + QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX); > + writel(0x12, tx2 + QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX); > + > + writel(0x02, layout->dp_serdes + QSERDES_COM_CMN_CONFIG); > + qmp_usbc_configure_dp_clocks(qmp); > + > + ret = cfg->configure_dp_phy(qmp); > + if (ret) { > + dev_err(qmp->dev, "failed to config dp phy\n"); > + goto power_on_unlock; > + } > + > + qmp_usbc_check_dp_phy(qmp, "usbc_dp_power_on_finish"); > + > +power_on_unlock: > + mutex_unlock(&qmp->phy_mutex); > + > + return ret; > +} > + > +static int qmp_usbc_dp_power_off(struct phy *phy) > { > struct qmp_usbc *qmp = phy_get_drvdata(phy); > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + > + mutex_lock(&qmp->phy_mutex); > > - qmp->mode = mode; > + /* Assert DP PHY power down */ > + writel(DP_PHY_PD_CTL_PSR_PWRDN, layout->dp_phy + QSERDES_DP_PHY_PD_CTL); > + > + mutex_unlock(&qmp->phy_mutex); > > return 0; > } > > -static const struct phy_ops qmp_usbc_phy_ops = { > - .init = qmp_usbc_enable, > - .exit = qmp_usbc_disable, > - .set_mode = qmp_usbc_set_mode, > +static const struct phy_ops qmp_usbc_usb_phy_ops = { > + .init = qmp_usbc_usb_enable, > + .exit = qmp_usbc_usb_disable, > + .set_mode = qmp_usbc_usb_set_mode, > + .owner = THIS_MODULE, > +}; > + > +static const struct phy_ops qmp_usbc_dp_phy_ops = { > + .init = qmp_usbc_dp_init, > + .exit = qmp_usbc_dp_exit, > + .configure = qmp_usbc_dp_configure, > + .calibrate = qmp_usbc_dp_calibrate, > + .power_on = qmp_usbc_dp_power_on, > + .power_off = qmp_usbc_dp_power_off, > .owner = THIS_MODULE, > }; > > static void qmp_usbc_enable_autonomous_mode(struct qmp_usbc *qmp) > { > - const struct qmp_phy_cfg *cfg = qmp->cfg; > - void __iomem *pcs = qmp->pcs; > + const struct qmp_phy_usb_cfg *cfg = to_usb_cfg(qmp); > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > + void __iomem *pcs = layout->pcs; > u32 intr_mask; > > - if (qmp->mode == PHY_MODE_USB_HOST_SS || > - qmp->mode == PHY_MODE_USB_DEVICE_SS) > + if (layout->mode == PHY_MODE_USB_HOST_SS || > + layout->mode == PHY_MODE_USB_DEVICE_SS) > intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN; > else > intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL; > @@ -663,18 +1340,19 @@ static void qmp_usbc_enable_autonomous_mode(struct qmp_usbc *qmp) > qphy_setbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask); > > /* Enable i/o clamp_n for autonomous mode */ > - if (qmp->tcsr_map && qmp->vls_clamp_reg) > - regmap_write(qmp->tcsr_map, qmp->vls_clamp_reg, 1); > + if (layout->tcsr_map && layout->vls_clamp_reg) > + regmap_write(layout->tcsr_map, layout->vls_clamp_reg, 1); > } > > static void qmp_usbc_disable_autonomous_mode(struct qmp_usbc *qmp) > { > - const struct qmp_phy_cfg *cfg = qmp->cfg; > - void __iomem *pcs = qmp->pcs; > + const struct qmp_phy_usb_cfg *cfg = to_usb_cfg(qmp); > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > + void __iomem *pcs = layout->pcs; > > /* Disable i/o clamp_n on resume for normal mode */ > - if (qmp->tcsr_map && qmp->vls_clamp_reg) > - regmap_write(qmp->tcsr_map, qmp->vls_clamp_reg, 0); > + if (layout->tcsr_map && layout->vls_clamp_reg) > + regmap_write(layout->tcsr_map, layout->vls_clamp_reg, 0); > > qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], > ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN); > @@ -688,16 +1366,19 @@ static int __maybe_unused qmp_usbc_runtime_suspend(struct device *dev) > { > struct qmp_usbc *qmp = dev_get_drvdata(dev); > > - dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode); > - > if (!qmp->phy->init_count) { > dev_vdbg(dev, "PHY not initialized, bailing out\n"); > return 0; > } > > - qmp_usbc_enable_autonomous_mode(qmp); > + if (qmp->type == QMP_PHY_USBC_USB) { > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > + > + dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", layout->mode); > + qmp_usbc_enable_autonomous_mode(qmp); > + clk_disable_unprepare(layout->pipe_clk); > + } > > - clk_disable_unprepare(qmp->pipe_clk); > clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks); > > return 0; > @@ -708,8 +1389,6 @@ static int __maybe_unused qmp_usbc_runtime_resume(struct device *dev) > struct qmp_usbc *qmp = dev_get_drvdata(dev); > int ret = 0; > > - dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode); > - > if (!qmp->phy->init_count) { > dev_vdbg(dev, "PHY not initialized, bailing out\n"); > return 0; > @@ -719,14 +1398,19 @@ static int __maybe_unused qmp_usbc_runtime_resume(struct device *dev) > if (ret) > return ret; > > - ret = clk_prepare_enable(qmp->pipe_clk); > - if (ret) { > - dev_err(dev, "pipe_clk enable failed, err=%d\n", ret); > - clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks); > - return ret; > - } > + if (qmp->type == QMP_PHY_USBC_USB) { > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > > - qmp_usbc_disable_autonomous_mode(qmp); > + dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", layout->mode); > + ret = clk_prepare_enable(layout->pipe_clk); > + if (ret) { > + dev_err(dev, "pipe_clk enable failed, err=%d\n", ret); > + clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks); > + return ret; > + } > + > + qmp_usbc_disable_autonomous_mode(qmp); > + } > > return 0; > } > @@ -738,19 +1422,54 @@ static const struct dev_pm_ops qmp_usbc_pm_ops = { > > static int qmp_usbc_vreg_init(struct qmp_usbc *qmp) > { > - const struct qmp_phy_cfg *cfg = qmp->cfg; > struct device *dev = qmp->dev; > - int num = cfg->num_vregs; > - int i; > + int ret, i; > > - qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); > - if (!qmp->vregs) > - return -ENOMEM; > + if (qmp->type == QMP_PHY_USBC_USB) { > + struct qmp_phy_usb_cfg *cfg = to_usb_cfg(qmp); > + int num = cfg->num_vregs; > > - for (i = 0; i < num; i++) > - qmp->vregs[i].supply = cfg->vreg_list[i]; > + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); > + if (!qmp->vregs) > + return -ENOMEM; > + > + for (i = 0; i < num; i++) > + qmp->vregs[i].supply = cfg->vreg_list[i]; > > - return devm_regulator_bulk_get(dev, num, qmp->vregs); > + ret = devm_regulator_bulk_get(dev, num, qmp->vregs); > + if (ret) { > + dev_err(dev, "failed at devm_regulator_bulk_get\n"); > + return ret; > + } > + } else { > + struct qmp_phy_dp_cfg *cfg = to_dp_cfg(qmp); > + int num = cfg->num_vregs; > + > + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); > + if (!qmp->vregs) > + return -ENOMEM; > + > + for (i = 0; i < num; i++) > + qmp->vregs[i].supply = cfg->vreg_list[i].name; > + > + ret = devm_regulator_bulk_get(dev, num, qmp->vregs); > + if (ret) { > + dev_err(dev, "failed at devm_regulator_bulk_get\n"); > + return ret; > + } > + > + for (i = 0; i < num; i++) { > + ret = regulator_set_load(qmp->vregs[i].consumer, > + cfg->vreg_list[i].enable_load); > + if (ret) { > + dev_err(dev, "failed to set load at %s\n", > + qmp->vregs[i].supply); > + return ret; > + } > + } > + } > + > + return 0; > } > > static int qmp_usbc_reset_init(struct qmp_usbc *qmp, > @@ -821,7 +1540,9 @@ static void phy_clk_release_provider(void *res) > */ > static int phy_pipe_clk_register(struct qmp_usbc *qmp, struct device_node *np) > { > - struct clk_fixed_rate *fixed = &qmp->pipe_clk_fixed; > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > + > + struct clk_fixed_rate *fixed = &layout->pipe_clk_fixed; > struct clk_init_data init = { }; > int ret; > > @@ -864,12 +1585,12 @@ static int qmp_usbc_typec_switch_set(struct typec_switch_dev *sw, > mutex_lock(&qmp->phy_mutex); > qmp->orientation = orientation; > > - if (qmp->usb_init_count) { > - qmp_usbc_power_off(qmp->phy); > - qmp_usbc_exit(qmp->phy); > + if (qmp->init_count) { > + qmp_usbc_usb_power_off(qmp->phy); > + qmp_usbc_com_exit(qmp->phy); > > - qmp_usbc_init(qmp->phy); > - qmp_usbc_power_on(qmp->phy); > + qmp_usbc_com_init(qmp->phy); > + qmp_usbc_usb_power_on(qmp->phy); > } > > mutex_unlock(&qmp->phy_mutex); > @@ -880,22 +1601,24 @@ static int qmp_usbc_typec_switch_set(struct typec_switch_dev *sw, > static void qmp_usbc_typec_unregister(void *data) > { > struct qmp_usbc *qmp = data; > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > > - typec_switch_unregister(qmp->sw); > + typec_switch_unregister(layout->sw); > } > > static int qmp_usbc_typec_switch_register(struct qmp_usbc *qmp) > { > struct typec_switch_desc sw_desc = {}; > struct device *dev = qmp->dev; > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > > sw_desc.drvdata = qmp; > sw_desc.fwnode = dev->fwnode; > sw_desc.set = qmp_usbc_typec_switch_set; > - qmp->sw = typec_switch_register(dev, &sw_desc); > - if (IS_ERR(qmp->sw)) { > - dev_err(dev, "Unable to register typec switch: %pe\n", qmp->sw); > - return PTR_ERR(qmp->sw); > + layout->sw = typec_switch_register(dev, &sw_desc); > + if (IS_ERR(layout->sw)) { > + dev_err(dev, "Unable to register typec switch: %pe\n", layout->sw); > + return PTR_ERR(layout->sw); > } > > return devm_add_action_or_reset(dev, qmp_usbc_typec_unregister, qmp); > @@ -907,15 +1630,16 @@ static int qmp_usbc_typec_switch_register(struct qmp_usbc *qmp) > } > #endif > > -static int qmp_usbc_parse_dt_legacy(struct qmp_usbc *qmp, struct device_node *np) > +static int qmp_usbc_parse_usb_dt_legacy(struct qmp_usbc *qmp, struct device_node *np) > { > struct platform_device *pdev = to_platform_device(qmp->dev); > struct device *dev = qmp->dev; > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > int ret; > > - qmp->serdes = devm_platform_ioremap_resource(pdev, 0); > - if (IS_ERR(qmp->serdes)) > - return PTR_ERR(qmp->serdes); > + layout->serdes = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(layout->serdes)) > + return PTR_ERR(layout->serdes); > > /* > * Get memory resources for the PHY: > @@ -923,35 +1647,35 @@ static int qmp_usbc_parse_dt_legacy(struct qmp_usbc *qmp, struct device_node *np > * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5 > * For single lane PHYs: pcs_misc (optional) -> 3. > */ > - qmp->tx = devm_of_iomap(dev, np, 0, NULL); > - if (IS_ERR(qmp->tx)) > - return PTR_ERR(qmp->tx); > + layout->tx = devm_of_iomap(dev, np, 0, NULL); > + if (IS_ERR(layout->tx)) > + return PTR_ERR(layout->tx); > > - qmp->rx = devm_of_iomap(dev, np, 1, NULL); > - if (IS_ERR(qmp->rx)) > - return PTR_ERR(qmp->rx); > + layout->rx = devm_of_iomap(dev, np, 1, NULL); > + if (IS_ERR(layout->rx)) > + return PTR_ERR(layout->rx); > > - qmp->pcs = devm_of_iomap(dev, np, 2, NULL); > - if (IS_ERR(qmp->pcs)) > - return PTR_ERR(qmp->pcs); > + layout->pcs = devm_of_iomap(dev, np, 2, NULL); > + if (IS_ERR(layout->pcs)) > + return PTR_ERR(layout->pcs); > > - qmp->tx2 = devm_of_iomap(dev, np, 3, NULL); > - if (IS_ERR(qmp->tx2)) > - return PTR_ERR(qmp->tx2); > + layout->tx2 = devm_of_iomap(dev, np, 3, NULL); > + if (IS_ERR(layout->tx2)) > + return PTR_ERR(layout->tx2); > > - qmp->rx2 = devm_of_iomap(dev, np, 4, NULL); > - if (IS_ERR(qmp->rx2)) > - return PTR_ERR(qmp->rx2); > + layout->rx2 = devm_of_iomap(dev, np, 4, NULL); > + if (IS_ERR(layout->rx2)) > + return PTR_ERR(layout->rx2); > > - qmp->pcs_misc = devm_of_iomap(dev, np, 5, NULL); > - if (IS_ERR(qmp->pcs_misc)) { > + layout->pcs_misc = devm_of_iomap(dev, np, 5, NULL); > + if (IS_ERR(layout->pcs_misc)) { > dev_vdbg(dev, "PHY pcs_misc-reg not used\n"); > - qmp->pcs_misc = NULL; > + layout->pcs_misc = NULL; > } > > - qmp->pipe_clk = devm_get_clk_from_child(dev, np, NULL); > - if (IS_ERR(qmp->pipe_clk)) { > - return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk), > + layout->pipe_clk = devm_get_clk_from_child(dev, np, NULL); > + if (IS_ERR(layout->pipe_clk)) { > + return dev_err_probe(dev, PTR_ERR(layout->pipe_clk), > "failed to get pipe clock\n"); > } > > @@ -969,11 +1693,12 @@ static int qmp_usbc_parse_dt_legacy(struct qmp_usbc *qmp, struct device_node *np > return 0; > } > > -static int qmp_usbc_parse_dt(struct qmp_usbc *qmp) > +static int qmp_usbc_parse_usb_dt(struct qmp_usbc *qmp) > { > struct platform_device *pdev = to_platform_device(qmp->dev); > - const struct qmp_phy_cfg *cfg = qmp->cfg; > - const struct qmp_usbc_offsets *offs = cfg->offsets; > + const struct qmp_phy_usb_cfg *cfg = to_usb_cfg(qmp); > + const struct qmp_usbc_usb_offsets *offs = cfg->offsets; > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > struct device *dev = qmp->dev; > void __iomem *base; > int ret; > @@ -985,23 +1710,23 @@ static int qmp_usbc_parse_dt(struct qmp_usbc *qmp) > if (IS_ERR(base)) > return PTR_ERR(base); > > - qmp->serdes = base + offs->serdes; > - qmp->pcs = base + offs->pcs; > + layout->serdes = base + offs->serdes; > + layout->pcs = base + offs->pcs; > if (offs->pcs_misc) > - qmp->pcs_misc = base + offs->pcs_misc; > - qmp->tx = base + offs->tx; > - qmp->rx = base + offs->rx; > + layout->pcs_misc = base + offs->pcs_misc; > + layout->tx = base + offs->tx; > + layout->rx = base + offs->rx; > > - qmp->tx2 = base + offs->tx2; > - qmp->rx2 = base + offs->rx2; > + layout->tx2 = base + offs->tx2; > + layout->rx2 = base + offs->rx2; > > ret = qmp_usbc_clk_init(qmp); > if (ret) > return ret; > > - qmp->pipe_clk = devm_clk_get(dev, "pipe"); > - if (IS_ERR(qmp->pipe_clk)) { > - return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk), > + layout->pipe_clk = devm_clk_get(dev, "pipe"); > + if (IS_ERR(layout->pipe_clk)) { > + return dev_err_probe(dev, PTR_ERR(layout->pipe_clk), > "failed to get pipe clock\n"); > } > > @@ -1013,10 +1738,11 @@ static int qmp_usbc_parse_dt(struct qmp_usbc *qmp) > return 0; > } > > -static int qmp_usbc_parse_vls_clamp(struct qmp_usbc *qmp) > +static int qmp_usbc_parse_usb_vls_clamp(struct qmp_usbc *qmp) > { > struct of_phandle_args tcsr_args; > struct device *dev = qmp->dev; > + struct qmp_phy_usb_layout *layout = to_usb_layout(qmp); > int ret; > > /* for backwards compatibility ignore if there is no property */ > @@ -1027,22 +1753,280 @@ static int qmp_usbc_parse_vls_clamp(struct qmp_usbc *qmp) > else if (ret < 0) > return dev_err_probe(dev, ret, "Failed to parse qcom,tcsr-reg\n"); > > - qmp->tcsr_map = syscon_node_to_regmap(tcsr_args.np); > + layout->tcsr_map = syscon_node_to_regmap(tcsr_args.np); > of_node_put(tcsr_args.np); > - if (IS_ERR(qmp->tcsr_map)) > - return PTR_ERR(qmp->tcsr_map); > + if (IS_ERR(layout->tcsr_map)) > + return PTR_ERR(layout->tcsr_map); > > - qmp->vls_clamp_reg = tcsr_args.args[0]; > + layout->vls_clamp_reg = tcsr_args.args[0]; > > return 0; > } > > +static int qmp_usbc_parse_dp_phy_mode(struct qmp_usbc *qmp) > +{ > + struct of_phandle_args tcsr_args; > + struct device *dev = qmp->dev; > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + int ret; > + > + /* for backwards compatibility ignore if there is no property */ > + ret = of_parse_phandle_with_fixed_args(dev->of_node, "qcom,tcsr-reg", 1, 0, > + &tcsr_args); > + if (ret < 0) > + return dev_err_probe(dev, ret, "Failed to parse qcom,tcsr-reg\n"); > + > + layout->tcsr_map = syscon_node_to_regmap(tcsr_args.np); > + of_node_put(tcsr_args.np); > + if (IS_ERR(layout->tcsr_map)) > + return PTR_ERR(layout->tcsr_map); > + > + layout->dp_phy_mode = tcsr_args.args[0]; > + > + return 0; > +} > + > +static int qmp_usbc_parse_dp_dt(struct qmp_usbc *qmp) > +{ > + struct platform_device *pdev = to_platform_device(qmp->dev); > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + struct qmp_phy_dp_cfg *cfg = to_dp_cfg(qmp); > + const struct qmp_usbc_dp_offsets *offs = cfg->offsets; > + struct device *dev = qmp->dev; > + void __iomem *base; > + int ret; > + > + base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(base)) { > + dev_err(dev, "get resource fail, ret:%d\n", ret); > + return PTR_ERR(base); > + } > + > + layout->dp_serdes = base + offs->dp_serdes; > + layout->dp_tx = base + offs->dp_txa; > + layout->dp_tx2 = base + offs->dp_txb; > + layout->dp_phy = base + offs->dp_phy; > + > + ret = qmp_usbc_clk_init(qmp); > + if (ret) { > + dev_err(dev, "clk init fail, ret:%d\n", ret); > + return ret; > + } > + > + ret = qmp_usbc_reset_init(qmp, dp_usb3phy_reset_l, > + ARRAY_SIZE(dp_usb3phy_reset_l)); > + if (ret) > + return ret; > + > + return 0; > +} > + > +/* > + * Display Port PLL driver block diagram for branch clocks > + * > + * +------------------------------+ > + * | DP_VCO_CLK | > + * | | > + * | +-------------------+ | > + * | | (DP PLL/VCO) | | > + * | +---------+---------+ | > + * | v | > + * | +----------+-----------+ | > + * | | hsclk_divsel_clk_src | | > + * | +----------+-----------+ | > + * +------------------------------+ > + * | > + * +---------<---------v------------>----------+ > + * | | > + * +--------v----------------+ | > + * | dp_phy_pll_link_clk | | > + * | link_clk | | > + * +--------+----------------+ | > + * | | > + * | | > + * v v > + * Input to DISPCC block | > + * for link clk, crypto clk | > + * and interface clock | > + * | > + * | > + * +--------<------------+-----------------+---<---+ > + * | | | > + * +----v---------+ +--------v-----+ +--------v------+ > + * | vco_divided | | vco_divided | | vco_divided | > + * | _clk_src | | _clk_src | | _clk_src | > + * | | | | | | > + * |divsel_six | | divsel_two | | divsel_four | > + * +-------+------+ +-----+--------+ +--------+------+ > + * | | | > + * v---->----------v-------------<------v > + * | > + * +----------+-----------------+ > + * | dp_phy_pll_vco_div_clk | > + * +---------+------------------+ > + * | > + * v > + * Input to DISPCC block > + * for DP pixel clock > + * > + */ > +static int qmp_dp_pixel_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) > +{ > + switch (req->rate) { > + case 1620000000UL / 2: > + case 2700000000UL / 2: > + /* 5.4 and 8.1 GHz are same link rate as 2.7GHz, i.e. div 4 and div 6 */ > + return 0; > + default: > + return -EINVAL; > + } > +} > + > +static unsigned long qmp_dp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) > +{ > + // const struct qmp_usbc *qmp; > + struct qmp_phy_dp_layout *layout; > + const struct phy_configure_opts_dp *dp_opts; > + > + layout = container_of(hw, struct qmp_phy_dp_layout, dp_pixel_hw); > + > + dp_opts = &layout->dp_opts; > + > + switch (dp_opts->link_rate) { > + case 1620: > + return 1620000000UL / 2; > + case 2700: > + return 2700000000UL / 2; > + case 5400: > + return 5400000000UL / 4; > + case 8100: > + return 8100000000UL / 6; > + default: > + return 0; > + } > +} > + > +static const struct clk_ops qmp_dp_pixel_clk_ops = { > + .determine_rate = qmp_dp_pixel_clk_determine_rate, > + .recalc_rate = qmp_dp_pixel_clk_recalc_rate, > +}; > + > +static int qmp_dp_link_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) > +{ > + switch (req->rate) { > + case 162000000: > + case 270000000: > + case 540000000: > + case 810000000: > + return 0; > + default: > + return -EINVAL; > + } > +} > + > +static unsigned long qmp_dp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) > +{ > + // const struct qmp_combo *qmp; > + struct qmp_phy_dp_layout *layout; > + const struct phy_configure_opts_dp *dp_opts; > + > + layout = container_of(hw, struct qmp_phy_dp_layout, dp_link_hw); > + dp_opts = &layout->dp_opts; > + > + switch (dp_opts->link_rate) { > + case 1620: > + case 2700: > + case 5400: > + case 8100: > + return dp_opts->link_rate * 100000; > + default: > + return 0; > + } > +} > + > +static const struct clk_ops qmp_dp_link_clk_ops = { > + .determine_rate = qmp_dp_link_clk_determine_rate, > + .recalc_rate = qmp_dp_link_clk_recalc_rate, > +}; > + > +static int phy_dp_clks_register(struct qmp_usbc *qmp, struct device_node *np) > +{ > + struct clk_init_data init = { }; > + int ret = 0; > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + > + ret = of_property_read_string_index(np, "clock-output-names", 0, &init.name); > + if (ret < 0) { > + dev_err(qmp->dev, "%pOFn: No link clock-output-names\n", np); > + return ret; > + } > + > + init.ops = &qmp_dp_link_clk_ops; > + layout->dp_link_hw.init = &init; > + ret = devm_clk_hw_register(qmp->dev, &layout->dp_link_hw); > + if (ret < 0) { > + dev_err(qmp->dev, "link clk reg fail ret=%d\n", ret); > + return ret; > + } > + > + ret = of_property_read_string_index(np, "clock-output-names", 1, &init.name); > + if (ret) { > + dev_err(qmp->dev, "%pOFn: No div clock-output-names\n", np); > + return ret; > + } > + > + init.ops = &qmp_dp_pixel_clk_ops; > + layout->dp_pixel_hw.init = &init; > + ret = devm_clk_hw_register(qmp->dev, &layout->dp_pixel_hw); > + if (ret) { > + dev_err(qmp->dev, "pxl clk reg fail ret=%d\n", ret); > + return ret; > + } > + > + return 0; > +} > + > +static struct clk_hw *qmp_dp_clks_hw_get(struct of_phandle_args *clkspec, void *data) > +{ > + struct qmp_usbc *qmp = data; > + struct qmp_phy_dp_layout *layout = to_dp_layout(qmp); > + > + switch (clkspec->args[0]) { > + case QMP_USB43DP_DP_LINK_CLK: > + return &layout->dp_link_hw; > + case QMP_USB43DP_DP_VCO_DIV_CLK: > + return &layout->dp_pixel_hw; > + } > + > + return ERR_PTR(-EINVAL); > +} > + > +static int qmp_dp_register_clocks(struct qmp_usbc *qmp, struct device_node *dp_np) > +{ > + int ret; > + > + ret = phy_dp_clks_register(qmp, dp_np); > + if (ret) { > + dev_err(qmp->dev, "dp clk reg fail ret:%d\n", ret); > + return ret; > + } > + > + ret = of_clk_add_hw_provider(dp_np, qmp_dp_clks_hw_get, qmp); > + if (ret) { > + dev_err(qmp->dev, "add provider fail ret:%d\n", ret); > + return ret; > + } > + > + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, dp_np); > +} > + > static int qmp_usbc_probe(struct platform_device *pdev) > { > struct device *dev = &pdev->dev; > struct phy_provider *phy_provider; > struct device_node *np; > struct qmp_usbc *qmp; > + const struct dev_cfg *data_cfg; > int ret; > > qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); > @@ -1050,38 +2034,74 @@ static int qmp_usbc_probe(struct platform_device *pdev) > return -ENOMEM; > > qmp->dev = dev; > - dev_set_drvdata(dev, qmp); > > qmp->orientation = TYPEC_ORIENTATION_NORMAL; > > - qmp->cfg = of_device_get_match_data(dev); > - if (!qmp->cfg) > + qmp->init_count = 0; > + > + data_cfg = of_device_get_match_data(dev); > + if (!data_cfg) { > + dev_err(qmp->dev, "get data fail\n"); > return -EINVAL; > + } > > mutex_init(&qmp->phy_mutex); > > - ret = qmp_usbc_vreg_init(qmp); > - if (ret) > - return ret; > + qmp->type = data_cfg->type; > + qmp->cfg = data_cfg->cfg; > > - ret = qmp_usbc_typec_switch_register(qmp); > - if (ret) > + ret = qmp_usbc_vreg_init(qmp); > + if (ret) { > + dev_err(qmp->dev, "qmp_type(%d) vreg init fail\n", qmp->type); > return ret; > + } > > - ret = qmp_usbc_parse_vls_clamp(qmp); > - if (ret) > - return ret; > + if (qmp->type == QMP_PHY_USBC_USB) { > + qmp->layout = devm_kzalloc(dev, sizeof(struct qmp_phy_usb_layout), GFP_KERNEL); > + if (!qmp->layout) > + return -ENOMEM; > + > + ret = qmp_usbc_typec_switch_register(qmp); > + if (ret) > + return ret; > + > + ret = qmp_usbc_parse_usb_vls_clamp(qmp); > + if (ret) > + return ret; > + > + /* Check for legacy binding with child node. */ > + np = of_get_child_by_name(dev->of_node, "phy"); > + if (np) { > + ret = qmp_usbc_parse_usb_dt_legacy(qmp, np); > + } else { > + np = of_node_get(dev->of_node); > + ret = qmp_usbc_parse_usb_dt(qmp); > + } > + if (ret) > + goto err_node_put; > + } else if (qmp->type == QMP_PHY_USBC_DP) { > + qmp->layout = devm_kzalloc(dev, sizeof(struct qmp_phy_dp_layout), GFP_KERNEL); > + if (!qmp->layout) > + return -ENOMEM; > > - /* Check for legacy binding with child node. */ > - np = of_get_child_by_name(dev->of_node, "phy"); > - if (np) { > - ret = qmp_usbc_parse_dt_legacy(qmp, np); > - } else { > np = of_node_get(dev->of_node); > - ret = qmp_usbc_parse_dt(qmp); > - } > - if (ret) > + ret = qmp_usbc_parse_dp_phy_mode(qmp); > + if (ret) > + goto err_node_put; > + > + ret = qmp_usbc_parse_dp_dt(qmp); > + if (ret) > + goto err_node_put; > + > + ret = drm_aux_bridge_register(dev); > + if (ret) { > + dev_err(qmp->dev, "aux bridge reg fail ret=%d\n", ret); > + goto err_node_put; > + } > + } else { > + dev_err(dev, "invalid phy type: %d\n", qmp->type); > goto err_node_put; > + } > > pm_runtime_set_active(dev); > ret = devm_pm_runtime_enable(dev); > @@ -1093,19 +2113,33 @@ static int qmp_usbc_probe(struct platform_device *pdev) > */ > pm_runtime_forbid(dev); > > - ret = phy_pipe_clk_register(qmp, np); > - if (ret) > - goto err_node_put; > - > - qmp->phy = devm_phy_create(dev, np, &qmp_usbc_phy_ops); > - if (IS_ERR(qmp->phy)) { > - ret = PTR_ERR(qmp->phy); > - dev_err(dev, "failed to create PHY: %d\n", ret); > - goto err_node_put; > + if (qmp->type == QMP_PHY_USBC_USB) { > + // pipe clk process > + ret = phy_pipe_clk_register(qmp, np); > + if (ret) > + goto err_node_put; > + > + qmp->phy = devm_phy_create(dev, np, &qmp_usbc_usb_phy_ops); > + if (IS_ERR(qmp->phy)) { > + ret = PTR_ERR(qmp->phy); > + dev_err(dev, "failed to create PHY: %d\n", ret); > + goto err_node_put; > + } > + } else { > + ret = qmp_dp_register_clocks(qmp, np); > + if (ret) > + goto err_node_put; > + > + qmp->phy = devm_phy_create(dev, np, &qmp_usbc_dp_phy_ops); > + if (IS_ERR(qmp->phy)) { > + ret = PTR_ERR(qmp->phy); > + dev_err(dev, "failed to create PHY: %d\n", ret); > + goto err_node_put; > + } > } > > phy_set_drvdata(qmp->phy, qmp); > - > + dev_set_drvdata(dev, qmp); > of_node_put(np); > > phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > @@ -1120,19 +2154,38 @@ static int qmp_usbc_probe(struct platform_device *pdev) > static const struct of_device_id qmp_usbc_of_match_table[] = { > { > .compatible = "qcom,msm8998-qmp-usb3-phy", > - .data = &msm8998_usb3phy_cfg, > + .data = &(struct dev_cfg) { > + .type = QMP_PHY_USBC_USB, > + .cfg = &msm8998_usb3phy_cfg, > + }, > }, { > .compatible = "qcom,qcm2290-qmp-usb3-phy", > - .data = &qcm2290_usb3phy_cfg, > + .data = &(struct dev_cfg) { > + .type = QMP_PHY_USBC_USB, > + .cfg = &qcm2290_usb3phy_cfg, > + }, > + }, { > + .compatible = "qcom,qcs615-qmp-dp-phy", > + .data = &(struct dev_cfg) { > + .type = QMP_PHY_USBC_DP, > + .cfg = &qcs615_dpphy_cfg, > + }, > }, { > .compatible = "qcom,sdm660-qmp-usb3-phy", > - .data = &sdm660_usb3phy_cfg, > + .data = &(struct dev_cfg) { > + .type = QMP_PHY_USBC_USB, > + .cfg = &sdm660_usb3phy_cfg, > + }, > }, { > .compatible = "qcom,sm6115-qmp-usb3-phy", > - .data = &qcm2290_usb3phy_cfg, > + .data = &(struct dev_cfg) { > + .type = QMP_PHY_USBC_USB, > + .cfg = &qcm2290_usb3phy_cfg, > + }, > }, > { }, > }; > + > MODULE_DEVICE_TABLE(of, qmp_usbc_of_match_table); > > static struct platform_driver qmp_usbc_driver = { > > -- > 2.25.1 > -- With best wishes Dmitry