On SoCs like MSM8953 the fixed "se_clk_scheme_default" variable does not
reflect the correct value for that SoC, so support reading the correct
value from TCSR.

The code was adapted from the Linux driver.

Signed-off-by: Luca Weiss <[email protected]>
---
 drivers/phy/qcom/Kconfig          |  1 +
 drivers/phy/qcom/phy-qcom-qusb2.c | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/drivers/phy/qcom/Kconfig b/drivers/phy/qcom/Kconfig
index 1fdadaccb12..ff85efeec32 100644
--- a/drivers/phy/qcom/Kconfig
+++ b/drivers/phy/qcom/Kconfig
@@ -35,6 +35,7 @@ config PHY_QCOM_QMP_UFS
 config PHY_QCOM_QUSB2
        bool "Qualcomm USB QUSB2 PHY driver"
        depends on PHY && ARCH_SNAPDRAGON
+       depends on SYSCON
        help
          Enable this to support the Super-Speed USB transceiver on various
          Qualcomm chipsets.
diff --git a/drivers/phy/qcom/phy-qcom-qusb2.c 
b/drivers/phy/qcom/phy-qcom-qusb2.c
index a6c1095f412..01e64542c89 100644
--- a/drivers/phy/qcom/phy-qcom-qusb2.c
+++ b/drivers/phy/qcom/phy-qcom-qusb2.c
@@ -11,7 +11,9 @@
 #include <generic-phy.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
+#include <regmap.h>
 #include <reset.h>
+#include <syscon.h>
 
 #include <dt-bindings/phy/phy-qcom-qusb2.h>
 
@@ -318,6 +320,7 @@ static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = {
  *
  * @cfg_ahb_clk: AHB2PHY interface clock
  * @phy_rst: phy reset control
+ * @tcsr: TCSR syscon register map
  *
  * @cfg: phy config data
  * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme
@@ -328,6 +331,7 @@ struct qusb2_phy {
 
        struct clk cfg_ahb_clk;
        struct reset_ctl phy_rst;
+       struct regmap *tcsr;
 
        const struct qusb2_phy_cfg *cfg;
        bool has_se_clk_scheme;
@@ -369,6 +373,7 @@ static int qusb2phy_power_on(struct phy *phy)
 {
        struct qusb2_phy *qphy = dev_get_priv(phy->dev);
        const struct qusb2_phy_cfg *cfg = qphy->cfg;
+       unsigned int clk_scheme;
        int ret;
        u32 val;
 
@@ -401,6 +406,31 @@ static int qusb2phy_power_on(struct phy *phy)
         */
        qphy->has_se_clk_scheme = cfg->se_clk_scheme_default;
 
+       /*
+        * read TCSR_PHY_CLK_SCHEME register to check if single-ended
+        * clock scheme is selected. If yes, then disable differential
+        * ref_clk and use single-ended clock, otherwise use differential
+        * ref_clk only.
+        */
+       if (qphy->tcsr) {
+               ret = regmap_read(qphy->tcsr, qphy->cfg->clk_scheme_offset,
+                                 &clk_scheme);
+               if (ret) {
+                       printf("%s: failed to read clk scheme reg\n", __func__);
+                       return ret;
+               }
+
+               /* is it a differential clock scheme ? */
+               if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) {
+                       debug("%s(): select differential clk\n",
+                             __func__);
+                       qphy->has_se_clk_scheme = false;
+               } else {
+                       debug("%s(): select single-ended clk\n",
+                             __func__);
+               }
+       }
+
        if (cfg->has_pll_test) {
                if (!qphy->has_se_clk_scheme)
                        val &= ~CLK_REF_SEL;
@@ -488,6 +518,13 @@ static int qusb2phy_probe(struct udevice *dev)
                return -EINVAL;
        }
 
+       qphy->tcsr = syscon_regmap_lookup_by_phandle(dev,
+                                                    "qcom,tcsr-syscon");
+       if (IS_ERR(qphy->tcsr)) {
+               debug("%s: failed to lookup TCSR regmap\n", __func__);
+               qphy->tcsr = NULL;
+       }
+
        debug("%s success qusb phy cfg %p\n", __func__, qphy->cfg);
        return 0;
 }

-- 
2.55.0

Reply via email to