diff options
Diffstat (limited to 'drivers/phy/qualcomm')
-rw-r--r-- | drivers/phy/qualcomm/Kconfig | 12 | ||||
-rw-r--r-- | drivers/phy/qualcomm/Makefile | 3 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 263 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp-usb.c | 182 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qusb2.c | 3 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-sgmii-eth.c | 451 |
6 files changed, 860 insertions, 54 deletions
diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index 4850d48f31fa..97ca5952e34e 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -59,8 +59,11 @@ if PHY_QCOM_QMP config PHY_QCOM_QMP_COMBO tristate "Qualcomm QMP Combo PHY Driver" default PHY_QCOM_QMP + depends on TYPEC || TYPEC=n + depends on DRM || DRM=n select GENERIC_PHY select MFD_SYSCON + select DRM_PANEL_BRIDGE if DRM help Enable this to support the QMP Combo PHY transceiver that is used with USB3 and DisplayPort controllers on Qualcomm chips. @@ -185,3 +188,12 @@ config PHY_QCOM_IPQ806X_USB This option enables support for the Synopsis PHYs present inside the Qualcomm USB3.0 DWC3 controller on ipq806x SoC. This driver supports both HS and SS PHY controllers. + +config PHY_QCOM_SGMII_ETH + tristate "Qualcomm DWMAC SGMII SerDes/PHY driver" + depends on OF && (ARCH_QCOM || COMPILE_TEST) + depends on HAS_IOMEM + select GENERIC_PHY + help + Enable this to support the internal SerDes/SGMII PHY on various + Qualcomm chipsets. diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index de3dc9ccf067..b030858e0f8d 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -20,4 +20,5 @@ obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o obj-$(CONFIG_PHY_QCOM_USB_SS) += phy-qcom-usb-ss.o obj-$(CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2)+= phy-qcom-snps-femto-v2.o -obj-$(CONFIG_PHY_QCOM_IPQ806X_USB) += phy-qcom-ipq806x-usb.o +obj-$(CONFIG_PHY_QCOM_IPQ806X_USB) += phy-qcom-ipq806x-usb.o +obj-$(CONFIG_PHY_QCOM_SGMII_ETH) += phy-qcom-sgmii-eth.o diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index 87b17e5877ab..bebce8c591a3 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -19,6 +19,10 @@ #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/slab.h> +#include <linux/usb/typec.h> +#include <linux/usb/typec_mux.h> + +#include <drm/drm_bridge.h> #include <dt-bindings/phy/phy-qcom-qmp.h> @@ -63,6 +67,10 @@ /* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */ #define CLAMP_EN BIT(0) /* enables i/o clamp_n */ +/* QPHY_V3_DP_COM_TYPEC_CTRL register bits */ +#define SW_PORTSELECT_VAL BIT(0) +#define SW_PORTSELECT_MUX BIT(1) + #define PHY_INIT_COMPLETE_TIMEOUT 10000 struct qmp_phy_init_tbl { @@ -1315,14 +1323,21 @@ struct qmp_combo { struct phy *usb_phy; enum phy_mode mode; + unsigned int usb_init_count; struct phy *dp_phy; unsigned int dp_aux_cfg; struct phy_configure_opts_dp dp_opts; + unsigned int dp_init_count; struct clk_fixed_rate pipe_clk_fixed; struct clk_hw dp_link_hw; struct clk_hw dp_pixel_hw; + + struct drm_bridge bridge; + + struct typec_switch_dev *sw; + enum typec_orientation orientation; }; static void qmp_v3_dp_aux_init(struct qmp_combo *qmp); @@ -1954,30 +1969,24 @@ static void qmp_v3_configure_dp_tx(struct qmp_combo *qmp) static bool qmp_combo_configure_dp_mode(struct qmp_combo *qmp) { + bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); + const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; 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; - /* - * TODO: Assume orientation is CC1 for now and two lanes, need to - * use type-c connector to understand orientation and lanes. - * - * Otherwise val changes to be like below if this code understood - * the orientation of the type-c cable. - * - * if (lane_cnt == 4 || orientation == ORIENTATION_CC2) - * val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN; - * if (lane_cnt == 4 || orientation == ORIENTATION_CC1) - * val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN; - * if (orientation == ORIENTATION_CC2) - * writel(0x4c, qmp->dp_dp_phy + QSERDES_V3_DP_PHY_MODE); - */ - val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN; + if (dp_opts->lanes == 4 || reverse) + val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN; + if (dp_opts->lanes == 4 || !reverse) + val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN; + writel(val, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL); - writel(0x5c, qmp->dp_dp_phy + QSERDES_DP_PHY_MODE); + if (reverse) + writel(0x4c, qmp->pcs + QSERDES_DP_PHY_MODE); + else + writel(0x5c, qmp->pcs + QSERDES_DP_PHY_MODE); return reverse; } @@ -2142,6 +2151,7 @@ static void qmp_v4_configure_dp_tx(struct qmp_combo *qmp) static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp, unsigned int com_resetm_ctrl_reg, unsigned int com_c_ready_status_reg, + unsigned int com_cmn_status_reg, unsigned int dp_phy_status_reg) { const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; @@ -2198,14 +2208,14 @@ static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp, 10000)) return -ETIMEDOUT; - if (readl_poll_timeout(qmp->dp_serdes + QSERDES_V4_COM_CMN_STATUS, + if (readl_poll_timeout(qmp->dp_serdes + com_cmn_status_reg, status, ((status & BIT(0)) > 0), 500, 10000)) return -ETIMEDOUT; - if (readl_poll_timeout(qmp->dp_serdes + QSERDES_V4_COM_CMN_STATUS, + if (readl_poll_timeout(qmp->dp_serdes + com_cmn_status_reg, status, ((status & BIT(1)) > 0), 500, @@ -2233,14 +2243,15 @@ static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp, static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp) { + bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; u32 bias0_en, drvr0_en, bias1_en, drvr1_en; - bool reverse = false; u32 status; int ret; ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V4_COM_RESETSM_CNTRL, QSERDES_V4_COM_C_READY_STATUS, + QSERDES_V4_COM_CMN_STATUS, QSERDES_V4_DP_PHY_STATUS); if (ret < 0) return ret; @@ -2297,14 +2308,15 @@ static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp) static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp) { + bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; u32 bias0_en, drvr0_en, bias1_en, drvr1_en; - bool reverse = false; u32 status; int ret; ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V4_COM_RESETSM_CNTRL, QSERDES_V4_COM_C_READY_STATUS, + QSERDES_V4_COM_CMN_STATUS, QSERDES_V4_DP_PHY_STATUS); if (ret < 0) return ret; @@ -2356,14 +2368,15 @@ static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp) static int qmp_v6_configure_dp_phy(struct qmp_combo *qmp) { + bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; u32 bias0_en, drvr0_en, bias1_en, drvr1_en; - bool reverse = false; u32 status; int ret; ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V6_COM_RESETSM_CNTRL, QSERDES_V6_COM_C_READY_STATUS, + QSERDES_V6_COM_CMN_STATUS, QSERDES_V6_DP_PHY_STATUS); if (ret < 0) return ret; @@ -2437,12 +2450,16 @@ static int qmp_combo_dp_configure(struct phy *phy, union phy_configure_opts *opt struct qmp_combo *qmp = phy_get_drvdata(phy); const struct qmp_phy_cfg *cfg = qmp->cfg; + mutex_lock(&qmp->phy_mutex); + memcpy(&qmp->dp_opts, dp_opts, sizeof(*dp_opts)); if (qmp->dp_opts.set_voltages) { cfg->configure_dp_tx(qmp); qmp->dp_opts.set_voltages = 0; } + mutex_unlock(&qmp->phy_mutex); + return 0; } @@ -2450,24 +2467,27 @@ static int qmp_combo_dp_calibrate(struct phy *phy) { struct qmp_combo *qmp = phy_get_drvdata(phy); const struct qmp_phy_cfg *cfg = qmp->cfg; + int ret = 0; + + mutex_lock(&qmp->phy_mutex); if (cfg->calibrate_dp_phy) - return cfg->calibrate_dp_phy(qmp); + ret = cfg->calibrate_dp_phy(qmp); - return 0; + mutex_unlock(&qmp->phy_mutex); + + return ret; } -static int qmp_combo_com_init(struct qmp_combo *qmp) +static int qmp_combo_com_init(struct qmp_combo *qmp, bool force) { const struct qmp_phy_cfg *cfg = qmp->cfg; void __iomem *com = qmp->com; int ret; + u32 val; - mutex_lock(&qmp->phy_mutex); - if (qmp->init_count++) { - mutex_unlock(&qmp->phy_mutex); + if (!force && qmp->init_count++) return 0; - } ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); if (ret) { @@ -2498,10 +2518,12 @@ static int qmp_combo_com_init(struct qmp_combo *qmp) SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); - /* Default type-c orientation, i.e CC1 */ - qphy_setbits(com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02); - - qphy_setbits(com, QPHY_V3_DP_COM_PHY_MODE_CTRL, USB3_MODE | DP_MODE); + /* 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, com + QPHY_V3_DP_COM_TYPEC_CTRL); + writel(USB3_MODE | DP_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL); /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, @@ -2514,8 +2536,6 @@ static int qmp_combo_com_init(struct qmp_combo *qmp) qphy_setbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], SW_PWRDN); - mutex_unlock(&qmp->phy_mutex); - return 0; err_assert_reset: @@ -2524,20 +2544,16 @@ err_disable_regulators: regulator_bulk_disable(cfg->num_vregs, qmp->vregs); err_decrement_count: qmp->init_count--; - mutex_unlock(&qmp->phy_mutex); return ret; } -static int qmp_combo_com_exit(struct qmp_combo *qmp) +static int qmp_combo_com_exit(struct qmp_combo *qmp, bool force) { const struct qmp_phy_cfg *cfg = qmp->cfg; - mutex_lock(&qmp->phy_mutex); - if (--qmp->init_count) { - mutex_unlock(&qmp->phy_mutex); + if (!force && --qmp->init_count) return 0; - } reset_control_bulk_assert(cfg->num_resets, qmp->resets); @@ -2545,8 +2561,6 @@ static int qmp_combo_com_exit(struct qmp_combo *qmp) regulator_bulk_disable(cfg->num_vregs, qmp->vregs); - mutex_unlock(&qmp->phy_mutex); - return 0; } @@ -2556,20 +2570,32 @@ static int qmp_combo_dp_init(struct phy *phy) const struct qmp_phy_cfg *cfg = qmp->cfg; int ret; - ret = qmp_combo_com_init(qmp); + mutex_lock(&qmp->phy_mutex); + + ret = qmp_combo_com_init(qmp, false); if (ret) - return ret; + goto out_unlock; cfg->dp_aux_init(qmp); - return 0; + qmp->dp_init_count++; + +out_unlock: + mutex_unlock(&qmp->phy_mutex); + return ret; } static int qmp_combo_dp_exit(struct phy *phy) { struct qmp_combo *qmp = phy_get_drvdata(phy); - qmp_combo_com_exit(qmp); + mutex_lock(&qmp->phy_mutex); + + qmp_combo_com_exit(qmp, false); + + qmp->dp_init_count--; + + mutex_unlock(&qmp->phy_mutex); return 0; } @@ -2581,6 +2607,8 @@ static int qmp_combo_dp_power_on(struct phy *phy) void __iomem *tx = qmp->dp_tx; void __iomem *tx2 = qmp->dp_tx2; + mutex_lock(&qmp->phy_mutex); + qmp_combo_dp_serdes_init(qmp); qmp_combo_configure_lane(tx, cfg->dp_tx_tbl, cfg->dp_tx_tbl_num, 1); @@ -2592,6 +2620,8 @@ static int qmp_combo_dp_power_on(struct phy *phy) /* Configure link rate, swing, etc. */ cfg->configure_dp_phy(qmp); + mutex_unlock(&qmp->phy_mutex); + return 0; } @@ -2599,9 +2629,13 @@ static int qmp_combo_dp_power_off(struct phy *phy) { struct qmp_combo *qmp = phy_get_drvdata(phy); + mutex_lock(&qmp->phy_mutex); + /* Assert DP PHY power down */ writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL); + mutex_unlock(&qmp->phy_mutex); + return 0; } @@ -2687,14 +2721,21 @@ static int qmp_combo_usb_init(struct phy *phy) struct qmp_combo *qmp = phy_get_drvdata(phy); int ret; - ret = qmp_combo_com_init(qmp); + mutex_lock(&qmp->phy_mutex); + ret = qmp_combo_com_init(qmp, false); if (ret) - return ret; + goto out_unlock; ret = qmp_combo_usb_power_on(phy); - if (ret) - qmp_combo_com_exit(qmp); + if (ret) { + qmp_combo_com_exit(qmp, false); + goto out_unlock; + } + + qmp->usb_init_count++; +out_unlock: + mutex_unlock(&qmp->phy_mutex); return ret; } @@ -2703,11 +2744,20 @@ static int qmp_combo_usb_exit(struct phy *phy) struct qmp_combo *qmp = phy_get_drvdata(phy); int ret; + mutex_lock(&qmp->phy_mutex); ret = qmp_combo_usb_power_off(phy); if (ret) - return ret; + goto out_unlock; + + ret = qmp_combo_com_exit(qmp, false); + if (ret) + goto out_unlock; + + qmp->usb_init_count--; - return qmp_combo_com_exit(qmp); +out_unlock: + mutex_unlock(&qmp->phy_mutex); + return ret; } static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submode) @@ -3173,6 +3223,103 @@ static int qmp_combo_register_clocks(struct qmp_combo *qmp, struct device_node * return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, dp_np); } +#if IS_ENABLED(CONFIG_TYPEC) +static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct qmp_combo *qmp = typec_switch_get_drvdata(sw); + const struct qmp_phy_cfg *cfg = qmp->cfg; + + if (orientation == qmp->orientation || orientation == TYPEC_ORIENTATION_NONE) + return 0; + + mutex_lock(&qmp->phy_mutex); + qmp->orientation = orientation; + + if (qmp->init_count) { + if (qmp->usb_init_count) + qmp_combo_usb_power_off(qmp->usb_phy); + qmp_combo_com_exit(qmp, true); + + qmp_combo_com_init(qmp, true); + if (qmp->usb_init_count) + qmp_combo_usb_power_on(qmp->usb_phy); + if (qmp->dp_init_count) + cfg->dp_aux_init(qmp); + } + mutex_unlock(&qmp->phy_mutex); + + return 0; +} + +static void qmp_combo_typec_unregister(void *data) +{ + struct qmp_combo *qmp = data; + + typec_switch_unregister(qmp->sw); +} + +static int qmp_combo_typec_switch_register(struct qmp_combo *qmp) +{ + struct typec_switch_desc sw_desc = {}; + struct device *dev = qmp->dev; + + sw_desc.drvdata = qmp; + sw_desc.fwnode = dev->fwnode; + sw_desc.set = qmp_combo_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); + } + + return devm_add_action_or_reset(dev, qmp_combo_typec_unregister, qmp); +} +#else +static int qmp_combo_typec_switch_register(struct qmp_combo *qmp) +{ + return 0; +} +#endif + +#if IS_ENABLED(CONFIG_DRM) +static int qmp_combo_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct qmp_combo *qmp = container_of(bridge, struct qmp_combo, bridge); + struct drm_bridge *next_bridge; + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; + + next_bridge = devm_drm_of_get_bridge(qmp->dev, qmp->dev->of_node, 0, 0); + if (IS_ERR(next_bridge)) { + dev_err(qmp->dev, "failed to acquire drm_bridge: %pe\n", next_bridge); + return PTR_ERR(next_bridge); + } + + return drm_bridge_attach(bridge->encoder, next_bridge, bridge, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); +} + +static const struct drm_bridge_funcs qmp_combo_bridge_funcs = { + .attach = qmp_combo_bridge_attach, +}; + +static int qmp_combo_dp_register_bridge(struct qmp_combo *qmp) +{ + qmp->bridge.funcs = &qmp_combo_bridge_funcs; + qmp->bridge.of_node = qmp->dev->of_node; + + return devm_drm_bridge_add(qmp->dev, &qmp->bridge); +} +#else +static int qmp_combo_dp_register_bridge(struct qmp_combo *qmp) +{ + return 0; +} +#endif + static int qmp_combo_parse_dt_lecacy_dp(struct qmp_combo *qmp, struct device_node *np) { struct device *dev = qmp->dev; @@ -3353,6 +3500,8 @@ static int qmp_combo_probe(struct platform_device *pdev) qmp->dev = dev; + qmp->orientation = TYPEC_ORIENTATION_NORMAL; + qmp->cfg = of_device_get_match_data(dev); if (!qmp->cfg) return -EINVAL; @@ -3371,6 +3520,14 @@ static int qmp_combo_probe(struct platform_device *pdev) if (ret) return ret; + ret = qmp_combo_typec_switch_register(qmp); + if (ret) + return ret; + + ret = qmp_combo_dp_register_bridge(qmp); + if (ret) + return ret; + /* Check for legacy binding with child nodes. */ usb_np = of_get_child_by_name(dev->of_node, "usb3-phy"); if (usb_np) { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index a49711c5a63d..466f0a56c82e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -139,6 +139,88 @@ static const unsigned int qmp_v5_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, }; +static const struct qmp_phy_init_tbl ipq9574_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), + /* PLL and Loop filter settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x68), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x09), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x29), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + /* SSC settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x7d), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x05), +}; + +static const struct qmp_phy_init_tbl ipq9574_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), +}; + +static const struct qmp_phy_init_tbl ipq9574_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x6c), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x0c), +}; + +static const struct qmp_phy_init_tbl ipq9574_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0e), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x85), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f), +}; + static const struct qmp_phy_init_tbl ipq8074_usb3_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a), QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), @@ -1408,12 +1490,36 @@ static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x21), }; +static const struct qmp_phy_init_tbl sa8775p_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG1, 0xc4), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG2, 0x89), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_POWER_STATE_CONFIG1, 0x6f), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x21), +}; + struct qmp_usb_offsets { u16 serdes; u16 pcs; + u16 pcs_misc; u16 pcs_usb; u16 tx; u16 rx; + /* for PHYs with >= 2 lanes */ + u16 tx2; + u16 rx2; }; /* struct qmp_phy_cfg - per-PHY initialization config */ @@ -1558,6 +1664,24 @@ static const char * const qmp_phy_vreg_l[] = { "vdda-phy", "vdda-pll", }; +static const struct qmp_usb_offsets qmp_usb_offsets_ipq9574 = { + .serdes = 0, + .pcs = 0x800, + .pcs_usb = 0x800, + .tx = 0x200, + .rx = 0x400, +}; + +static const struct qmp_usb_offsets qmp_usb_offsets_v3 = { + .serdes = 0, + .pcs = 0xc00, + .pcs_misc = 0xa00, + .tx = 0x200, + .rx = 0x400, + .tx2 = 0x600, + .rx2 = 0x800, +}; + static const struct qmp_usb_offsets qmp_usb_offsets_v5 = { .serdes = 0, .pcs = 0x0200, @@ -1586,6 +1710,28 @@ static const struct qmp_phy_cfg ipq8074_usb3phy_cfg = { .regs = qmp_v3_usb3phy_regs_layout, }; +static const struct qmp_phy_cfg ipq9574_usb3phy_cfg = { + .lanes = 1, + + .offsets = &qmp_usb_offsets_ipq9574, + + .serdes_tbl = ipq9574_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ipq9574_usb3_serdes_tbl), + .tx_tbl = ipq9574_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(ipq9574_usb3_tx_tbl), + .rx_tbl = ipq9574_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ipq9574_usb3_rx_tbl), + .pcs_tbl = ipq9574_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ipq9574_usb3_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = qcm2290_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(qcm2290_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, +}; + static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { .lanes = 1, @@ -1629,6 +1775,28 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .has_phy_dp_com_ctrl = true, }; +static const struct qmp_phy_cfg sa8775p_usb3_uniphy_cfg = { + .lanes = 1, + + .offsets = &qmp_usb_offsets_v5, + + .serdes_tbl = sc8280xp_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_serdes_tbl), + .tx_tbl = sc8280xp_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_tx_tbl), + .rx_tbl = sc8280xp_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_rx_tbl), + .pcs_tbl = sa8775p_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sa8775p_usb3_uniphy_pcs_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = qcm2290_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(qcm2290_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v5_usb3phy_regs_layout, +}; + static const struct qmp_phy_cfg sc7180_usb3phy_cfg = { .lanes = 2, @@ -1922,6 +2090,8 @@ static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = { static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { .lanes = 2, + .offsets = &qmp_usb_offsets_v3, + .serdes_tbl = qcm2290_usb3_serdes_tbl, .serdes_tbl_num = ARRAY_SIZE(qcm2290_usb3_serdes_tbl), .tx_tbl = qcm2290_usb3_tx_tbl, @@ -2493,10 +2663,16 @@ static int qmp_usb_parse_dt(struct qmp_usb *qmp) qmp->serdes = base + offs->serdes; qmp->pcs = base + offs->pcs; + qmp->pcs_misc = base + offs->pcs_misc; qmp->pcs_usb = base + offs->pcs_usb; qmp->tx = base + offs->tx; qmp->rx = base + offs->rx; + if (cfg->lanes >= 2) { + qmp->tx2 = base + offs->tx2; + qmp->rx2 = base + offs->rx2; + } + qmp->pipe_clk = devm_clk_get(dev, "pipe"); if (IS_ERR(qmp->pipe_clk)) { return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk), @@ -2589,6 +2765,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = { .compatible = "qcom,ipq8074-qmp-usb3-phy", .data = &ipq8074_usb3phy_cfg, }, { + .compatible = "qcom,ipq9574-qmp-usb3-phy", + .data = &ipq9574_usb3phy_cfg, + }, { .compatible = "qcom,msm8996-qmp-usb3-phy", .data = &msm8996_usb3phy_cfg, }, { @@ -2598,6 +2777,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = { .compatible = "qcom,qcm2290-qmp-usb3-phy", .data = &qcm2290_usb3phy_cfg, }, { + .compatible = "qcom,sa8775p-qmp-usb3-uni-phy", + .data = &sa8775p_usb3_uniphy_cfg, + }, { .compatible = "qcom,sc7180-qmp-usb3-phy", .data = &sc7180_usb3phy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 2ef638b32e8f..bec6e40d5280 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -912,6 +912,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = { .compatible = "qcom,ipq8074-qusb2-phy", .data = &msm8996_phy_cfg, }, { + .compatible = "qcom,ipq9574-qusb2-phy", + .data = &ipq6018_phy_cfg, + }, { .compatible = "qcom,msm8953-qusb2-phy", .data = &msm8996_phy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c new file mode 100644 index 000000000000..03dc753f0de1 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Linaro Limited + */ + +#include <linux/clk.h> +#include <linux/ethtool.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define QSERDES_QMP_PLL 0x0 +#define QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0 (QSERDES_QMP_PLL + 0x1ac) +#define QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0 (QSERDES_QMP_PLL + 0x1b0) +#define QSERDES_COM_BIN_VCOCAL_HSCLK_SEL (QSERDES_QMP_PLL + 0x1bc) +#define QSERDES_COM_CORE_CLK_EN (QSERDES_QMP_PLL + 0x174) +#define QSERDES_COM_CORECLK_DIV_MODE0 (QSERDES_QMP_PLL + 0x168) +#define QSERDES_COM_CP_CTRL_MODE0 (QSERDES_QMP_PLL + 0x74) +#define QSERDES_COM_DEC_START_MODE0 (QSERDES_QMP_PLL + 0xbc) +#define QSERDES_COM_DIV_FRAC_START1_MODE0 (QSERDES_QMP_PLL + 0xcc) +#define QSERDES_COM_DIV_FRAC_START2_MODE0 (QSERDES_QMP_PLL + 0xd0) +#define QSERDES_COM_DIV_FRAC_START3_MODE0 (QSERDES_QMP_PLL + 0xd4) +#define QSERDES_COM_HSCLK_HS_SWITCH_SEL (QSERDES_QMP_PLL + 0x15c) +#define QSERDES_COM_HSCLK_SEL (QSERDES_QMP_PLL + 0x158) +#define QSERDES_COM_LOCK_CMP1_MODE0 (QSERDES_QMP_PLL + 0xac) +#define QSERDES_COM_LOCK_CMP2_MODE0 (QSERDES_QMP_PLL + 0xb0) +#define QSERDES_COM_PLL_CCTRL_MODE0 (QSERDES_QMP_PLL + 0x84) +#define QSERDES_COM_PLL_IVCO (QSERDES_QMP_PLL + 0x58) +#define QSERDES_COM_PLL_RCTRL_MODE0 (QSERDES_QMP_PLL + 0x7c) +#define QSERDES_COM_SYSCLK_EN_SEL (QSERDES_QMP_PLL + 0x94) +#define QSERDES_COM_VCO_TUNE1_MODE0 (QSERDES_QMP_PLL + 0x110) +#define QSERDES_COM_VCO_TUNE2_MODE0 (QSERDES_QMP_PLL + 0x114) +#define QSERDES_COM_VCO_TUNE_INITVAL2 (QSERDES_QMP_PLL + 0x124) +#define QSERDES_COM_C_READY_STATUS (QSERDES_QMP_PLL + 0x178) +#define QSERDES_COM_CMN_STATUS (QSERDES_QMP_PLL + 0x140) + +#define QSERDES_RX 0x600 +#define QSERDES_RX_UCDR_FO_GAIN (QSERDES_RX + 0x8) +#define QSERDES_RX_UCDR_SO_GAIN (QSERDES_RX + 0x14) +#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN (QSERDES_RX + 0x30) +#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE (QSERDES_RX + 0x34) +#define QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW (QSERDES_RX + 0x3c) +#define QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH (QSERDES_RX + 0x40) +#define QSERDES_RX_UCDR_PI_CONTROLS (QSERDES_RX + 0x44) +#define QSERDES_RX_UCDR_PI_CTRL2 (QSERDES_RX + 0x48) +#define QSERDES_RX_RX_TERM_BW (QSERDES_RX + 0x80) +#define QSERDES_RX_VGA_CAL_CNTRL2 (QSERDES_RX + 0xd8) +#define QSERDES_RX_GM_CAL (QSERDES_RX + 0xdc) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1 (QSERDES_RX + 0xe8) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 (QSERDES_RX + 0xec) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 (QSERDES_RX + 0xf0) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 (QSERDES_RX + 0xf4) +#define QSERDES_RX_RX_IDAC_TSETTLE_LOW (QSERDES_RX + 0xf8) +#define QSERDES_RX_RX_IDAC_TSETTLE_HIGH (QSERDES_RX + 0xfc) +#define QSERDES_RX_RX_IDAC_MEASURE_TIME (QSERDES_RX + 0x100) +#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 (QSERDES_RX + 0x110) +#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 (QSERDES_RX + 0x114) +#define QSERDES_RX_SIGDET_CNTRL (QSERDES_RX + 0x11c) +#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL (QSERDES_RX + 0x124) +#define QSERDES_RX_RX_BAND (QSERDES_RX + 0x128) +#define QSERDES_RX_RX_MODE_00_LOW (QSERDES_RX + 0x15c) +#define QSERDES_RX_RX_MODE_00_HIGH (QSERDES_RX + 0x160) +#define QSERDES_RX_RX_MODE_00_HIGH2 (QSERDES_RX + 0x164) +#define QSERDES_RX_RX_MODE_00_HIGH3 (QSERDES_RX + 0x168) +#define QSERDES_RX_RX_MODE_00_HIGH4 (QSERDES_RX + 0x16c) +#define QSERDES_RX_RX_MODE_01_LOW (QSERDES_RX + 0x170) +#define QSERDES_RX_RX_MODE_01_HIGH (QSERDES_RX + 0x174) +#define QSERDES_RX_RX_MODE_01_HIGH2 (QSERDES_RX + 0x178) +#define QSERDES_RX_RX_MODE_01_HIGH3 (QSERDES_RX + 0x17c) +#define QSERDES_RX_RX_MODE_01_HIGH4 (QSERDES_RX + 0x180) +#define QSERDES_RX_RX_MODE_10_LOW (QSERDES_RX + 0x184) +#define QSERDES_RX_RX_MODE_10_HIGH (QSERDES_RX + 0x188) +#define QSERDES_RX_RX_MODE_10_HIGH2 (QSERDES_RX + 0x18c) +#define QSERDES_RX_RX_MODE_10_HIGH3 (QSERDES_RX + 0x190) +#define QSERDES_RX_RX_MODE_10_HIGH4 (QSERDES_RX + 0x194) +#define QSERDES_RX_DCC_CTRL1 (QSERDES_RX + 0x1a8) + +#define QSERDES_TX 0x400 +#define QSERDES_TX_TX_BAND (QSERDES_TX + 0x24) +#define QSERDES_TX_SLEW_CNTL (QSERDES_TX + 0x28) +#define QSERDES_TX_RES_CODE_LANE_OFFSET_TX (QSERDES_TX + 0x3c) +#define QSERDES_TX_RES_CODE_LANE_OFFSET_RX (QSERDES_TX + 0x40) +#define QSERDES_TX_LANE_MODE_1 (QSERDES_TX + 0x84) +#define QSERDES_TX_LANE_MODE_3 (QSERDES_TX + 0x8c) +#define QSERDES_TX_RCV_DETECT_LVL_2 (QSERDES_TX + 0xa4) +#define QSERDES_TX_TRAN_DRVR_EMP_EN (QSERDES_TX + 0xc0) + +#define QSERDES_PCS 0xC00 +#define QSERDES_PCS_PHY_START (QSERDES_PCS + 0x0) +#define QSERDES_PCS_POWER_DOWN_CONTROL (QSERDES_PCS + 0x4) +#define QSERDES_PCS_SW_RESET (QSERDES_PCS + 0x8) +#define QSERDES_PCS_LINE_RESET_TIME (QSERDES_PCS + 0xc) +#define QSERDES_PCS_TX_LARGE_AMP_DRV_LVL (QSERDES_PCS + 0x20) +#define QSERDES_PCS_TX_SMALL_AMP_DRV_LVL (QSERDES_PCS + 0x28) +#define QSERDES_PCS_TX_MID_TERM_CTRL1 (QSERDES_PCS + 0xd8) +#define QSERDES_PCS_TX_MID_TERM_CTRL2 (QSERDES_PCS + 0xdc) +#define QSERDES_PCS_SGMII_MISC_CTRL8 (QSERDES_PCS + 0x118) +#define QSERDES_PCS_PCS_READY_STATUS (QSERDES_PCS + 0x94) + +#define QSERDES_COM_C_READY BIT(0) +#define QSERDES_PCS_READY BIT(0) +#define QSERDES_PCS_SGMIIPHY_READY BIT(7) +#define QSERDES_COM_C_PLL_LOCKED BIT(1) + +struct qcom_dwmac_sgmii_phy_data { + struct regmap *regmap; + struct clk *refclk; + int speed; +}; + +static void qcom_dwmac_sgmii_phy_init_1g(struct regmap *regmap) +{ + regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x01); + regmap_write(regmap, QSERDES_PCS_POWER_DOWN_CONTROL, 0x01); + + regmap_write(regmap, QSERDES_COM_PLL_IVCO, 0x0F); + regmap_write(regmap, QSERDES_COM_CP_CTRL_MODE0, 0x06); + regmap_write(regmap, QSERDES_COM_PLL_RCTRL_MODE0, 0x16); + regmap_write(regmap, QSERDES_COM_PLL_CCTRL_MODE0, 0x36); + regmap_write(regmap, QSERDES_COM_SYSCLK_EN_SEL, 0x1A); + regmap_write(regmap, QSERDES_COM_LOCK_CMP1_MODE0, 0x0A); + regmap_write(regmap, QSERDES_COM_LOCK_CMP2_MODE0, 0x1A); + regmap_write(regmap, QSERDES_COM_DEC_START_MODE0, 0x82); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03); + regmap_write(regmap, QSERDES_COM_VCO_TUNE1_MODE0, 0x24); + + regmap_write(regmap, QSERDES_COM_VCO_TUNE2_MODE0, 0x02); + regmap_write(regmap, QSERDES_COM_VCO_TUNE_INITVAL2, 0x00); + regmap_write(regmap, QSERDES_COM_HSCLK_SEL, 0x04); + regmap_write(regmap, QSERDES_COM_HSCLK_HS_SWITCH_SEL, 0x00); + regmap_write(regmap, QSERDES_COM_CORECLK_DIV_MODE0, 0x0A); + regmap_write(regmap, QSERDES_COM_CORE_CLK_EN, 0x00); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xB9); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1E); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_HSCLK_SEL, 0x11); + + regmap_write(regmap, QSERDES_TX_TX_BAND, 0x05); + regmap_write(regmap, QSERDES_TX_SLEW_CNTL, 0x0A); + regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_TX, 0x09); + regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_RX, 0x09); + regmap_write(regmap, QSERDES_TX_LANE_MODE_1, 0x05); + regmap_write(regmap, QSERDES_TX_LANE_MODE_3, 0x00); + regmap_write(regmap, QSERDES_TX_RCV_DETECT_LVL_2, 0x12); + regmap_write(regmap, QSERDES_TX_TRAN_DRVR_EMP_EN, 0x0C); + + regmap_write(regmap, QSERDES_RX_UCDR_FO_GAIN, 0x0A); + regmap_write(regmap, QSERDES_RX_UCDR_SO_GAIN, 0x06); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0A); + regmap_write(regmap, QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7F); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x01); + regmap_write(regmap, QSERDES_RX_UCDR_PI_CONTROLS, 0x81); + regmap_write(regmap, QSERDES_RX_UCDR_PI_CTRL2, 0x80); + regmap_write(regmap, QSERDES_RX_RX_TERM_BW, 0x04); + regmap_write(regmap, QSERDES_RX_VGA_CAL_CNTRL2, 0x08); + regmap_write(regmap, QSERDES_RX_GM_CAL, 0x0F); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x00); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4A); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0A); + regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_LOW, 0x80); + regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_HIGH, 0x01); + regmap_write(regmap, QSERDES_RX_RX_IDAC_MEASURE_TIME, 0x20); + regmap_write(regmap, QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17); + regmap_write(regmap, QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00); + regmap_write(regmap, QSERDES_RX_SIGDET_CNTRL, 0x0F); + regmap_write(regmap, QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E); + regmap_write(regmap, QSERDES_RX_RX_BAND, 0x05); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_LOW, 0xE0); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH3, 0x09); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH4, 0xB1); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_LOW, 0xE0); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH3, 0x09); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH4, 0xB1); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_LOW, 0xE0); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH3, 0x3B); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH4, 0xB7); + regmap_write(regmap, QSERDES_RX_DCC_CTRL1, 0x0C); + + regmap_write(regmap, QSERDES_PCS_LINE_RESET_TIME, 0x0C); + regmap_write(regmap, QSERDES_PCS_TX_LARGE_AMP_DRV_LVL, 0x1F); + regmap_write(regmap, QSERDES_PCS_TX_SMALL_AMP_DRV_LVL, 0x03); + regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL1, 0x83); + regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL2, 0x08); + regmap_write(regmap, QSERDES_PCS_SGMII_MISC_CTRL8, 0x0C); + regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x00); + + regmap_write(regmap, QSERDES_PCS_PHY_START, 0x01); +} + +static void qcom_dwmac_sgmii_phy_init_2p5g(struct regmap *regmap) +{ + regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x01); + regmap_write(regmap, QSERDES_PCS_POWER_DOWN_CONTROL, 0x01); + + regmap_write(regmap, QSERDES_COM_PLL_IVCO, 0x0F); + regmap_write(regmap, QSERDES_COM_CP_CTRL_MODE0, 0x06); + regmap_write(regmap, QSERDES_COM_PLL_RCTRL_MODE0, 0x16); + regmap_write(regmap, QSERDES_COM_PLL_CCTRL_MODE0, 0x36); + regmap_write(regmap, QSERDES_COM_SYSCLK_EN_SEL, 0x1A); + regmap_write(regmap, QSERDES_COM_LOCK_CMP1_MODE0, 0x1A); + regmap_write(regmap, QSERDES_COM_LOCK_CMP2_MODE0, 0x41); + regmap_write(regmap, QSERDES_COM_DEC_START_MODE0, 0x7A); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x20); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x01); + regmap_write(regmap, QSERDES_COM_VCO_TUNE1_MODE0, 0xA1); + + regmap_write(regmap, QSERDES_COM_VCO_TUNE2_MODE0, 0x02); + regmap_write(regmap, QSERDES_COM_VCO_TUNE_INITVAL2, 0x00); + regmap_write(regmap, QSERDES_COM_HSCLK_SEL, 0x03); + regmap_write(regmap, QSERDES_COM_HSCLK_HS_SWITCH_SEL, 0x00); + regmap_write(regmap, QSERDES_COM_CORECLK_DIV_MODE0, 0x05); + regmap_write(regmap, QSERDES_COM_CORE_CLK_EN, 0x00); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xCD); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1C); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_HSCLK_SEL, 0x11); + + regmap_write(regmap, QSERDES_TX_TX_BAND, 0x04); + regmap_write(regmap, QSERDES_TX_SLEW_CNTL, 0x0A); + regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_TX, 0x09); + regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_RX, 0x02); + regmap_write(regmap, QSERDES_TX_LANE_MODE_1, 0x05); + regmap_write(regmap, QSERDES_TX_LANE_MODE_3, 0x00); + regmap_write(regmap, QSERDES_TX_RCV_DETECT_LVL_2, 0x12); + regmap_write(regmap, QSERDES_TX_TRAN_DRVR_EMP_EN, 0x0C); + + regmap_write(regmap, QSERDES_RX_UCDR_FO_GAIN, 0x0A); + regmap_write(regmap, QSERDES_RX_UCDR_SO_GAIN, 0x06); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0A); + regmap_write(regmap, QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7F); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x01); + regmap_write(regmap, QSERDES_RX_UCDR_PI_CONTROLS, 0x81); + regmap_write(regmap, QSERDES_RX_UCDR_PI_CTRL2, 0x80); + regmap_write(regmap, QSERDES_RX_RX_TERM_BW, 0x00); + regmap_write(regmap, QSERDES_RX_VGA_CAL_CNTRL2, 0x08); + regmap_write(regmap, QSERDES_RX_GM_CAL, 0x0F); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x00); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4A); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0A); + regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_LOW, 0x80); + regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_HIGH, 0x01); + regmap_write(regmap, QSERDES_RX_RX_IDAC_MEASURE_TIME, 0x20); + regmap_write(regmap, QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17); + regmap_write(regmap, QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00); + regmap_write(regmap, QSERDES_RX_SIGDET_CNTRL, 0x0F); + regmap_write(regmap, QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E); + regmap_write(regmap, QSERDES_RX_RX_BAND, 0x18); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_LOW, 0x18); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH3, 0x0C); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH4, 0xB8); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_LOW, 0xE0); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH3, 0x09); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH4, 0xB1); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_LOW, 0xE0); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH3, 0x3B); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH4, 0xB7); + regmap_write(regmap, QSERDES_RX_DCC_CTRL1, 0x0C); + + regmap_write(regmap, QSERDES_PCS_LINE_RESET_TIME, 0x0C); + regmap_write(regmap, QSERDES_PCS_TX_LARGE_AMP_DRV_LVL, 0x1F); + regmap_write(regmap, QSERDES_PCS_TX_SMALL_AMP_DRV_LVL, 0x03); + regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL1, 0x83); + regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL2, 0x08); + regmap_write(regmap, QSERDES_PCS_SGMII_MISC_CTRL8, 0x8C); + regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x00); + + regmap_write(regmap, QSERDES_PCS_PHY_START, 0x01); +} + +static inline int +qcom_dwmac_sgmii_phy_poll_status(struct regmap *regmap, unsigned int reg, + unsigned int bit) +{ + unsigned int val; + + return regmap_read_poll_timeout(regmap, reg, val, + val & bit, 1500, 750000); +} + +static int qcom_dwmac_sgmii_phy_calibrate(struct phy *phy) +{ + struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy); + struct device *dev = phy->dev.parent; + + switch (data->speed) { + case SPEED_10: + case SPEED_100: + case SPEED_1000: + qcom_dwmac_sgmii_phy_init_1g(data->regmap); + break; + case SPEED_2500: + qcom_dwmac_sgmii_phy_init_2p5g(data->regmap); + break; + } + + if (qcom_dwmac_sgmii_phy_poll_status(data->regmap, + QSERDES_COM_C_READY_STATUS, + QSERDES_COM_C_READY)) { + dev_err(dev, "QSERDES_COM_C_READY_STATUS timed-out"); + return -ETIMEDOUT; + } + + if (qcom_dwmac_sgmii_phy_poll_status(data->regmap, + QSERDES_PCS_PCS_READY_STATUS, + QSERDES_PCS_READY)) { + dev_err(dev, "PCS_READY timed-out"); + return -ETIMEDOUT; + } + + if (qcom_dwmac_sgmii_phy_poll_status(data->regmap, + QSERDES_PCS_PCS_READY_STATUS, + QSERDES_PCS_SGMIIPHY_READY)) { + dev_err(dev, "SGMIIPHY_READY timed-out"); + return -ETIMEDOUT; + } + + if (qcom_dwmac_sgmii_phy_poll_status(data->regmap, + QSERDES_COM_CMN_STATUS, + QSERDES_COM_C_PLL_LOCKED)) { + dev_err(dev, "PLL Lock Status timed-out"); + return -ETIMEDOUT; + } + + return 0; +} + +static int qcom_dwmac_sgmii_phy_power_on(struct phy *phy) +{ + struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy); + + return clk_prepare_enable(data->refclk); +} + +static int qcom_dwmac_sgmii_phy_power_off(struct phy *phy) +{ + struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy); + + regmap_write(data->regmap, QSERDES_PCS_TX_MID_TERM_CTRL2, 0x08); + regmap_write(data->regmap, QSERDES_PCS_SW_RESET, 0x01); + udelay(100); + regmap_write(data->regmap, QSERDES_PCS_SW_RESET, 0x00); + regmap_write(data->regmap, QSERDES_PCS_PHY_START, 0x01); + + clk_disable_unprepare(data->refclk); + + return 0; +} + +static int qcom_dwmac_sgmii_phy_set_speed(struct phy *phy, int speed) +{ + struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy); + + if (speed != data->speed) + data->speed = speed; + + return qcom_dwmac_sgmii_phy_calibrate(phy); +} + +static const struct phy_ops qcom_dwmac_sgmii_phy_ops = { + .power_on = qcom_dwmac_sgmii_phy_power_on, + .power_off = qcom_dwmac_sgmii_phy_power_off, + .set_speed = qcom_dwmac_sgmii_phy_set_speed, + .calibrate = qcom_dwmac_sgmii_phy_calibrate, + .owner = THIS_MODULE, +}; + +static const struct regmap_config qcom_dwmac_sgmii_phy_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .use_relaxed_mmio = true, + .disable_locking = true, +}; + +static int qcom_dwmac_sgmii_phy_probe(struct platform_device *pdev) +{ + struct qcom_dwmac_sgmii_phy_data *data; + struct device *dev = &pdev->dev; + struct phy_provider *provider; + void __iomem *base; + struct phy *phy; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->speed = SPEED_10; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + data->regmap = devm_regmap_init_mmio(dev, base, + &qcom_dwmac_sgmii_phy_regmap_cfg); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + phy = devm_phy_create(dev, NULL, &qcom_dwmac_sgmii_phy_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + data->refclk = devm_clk_get(dev, "sgmi_ref"); + if (IS_ERR(data->refclk)) + return PTR_ERR(data->refclk); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) + return PTR_ERR(provider); + + phy_set_drvdata(phy, data); + + return 0; +} + +static const struct of_device_id qcom_dwmac_sgmii_phy_of_match[] = { + { .compatible = "qcom,sa8775p-dwmac-sgmii-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_dwmac_sgmii_phy_of_match); + +static struct platform_driver qcom_dwmac_sgmii_phy_driver = { + .probe = qcom_dwmac_sgmii_phy_probe, + .driver = { + .name = "qcom-dwmac-sgmii-phy", + .of_match_table = qcom_dwmac_sgmii_phy_of_match, + } +}; + +module_platform_driver(qcom_dwmac_sgmii_phy_driver); + +MODULE_DESCRIPTION("Qualcomm DWMAC SGMII PHY driver"); +MODULE_LICENSE("GPL"); |