PCIe PHY can use it when there is no external refclock provided.

Signed-off-by: Sumit Garg <sumit.g...@linaro.org>
---
 drivers/power/domain/imx8mp-hsiomix.c | 79 +++++++++++++++++++++++++--
 1 file changed, 73 insertions(+), 6 deletions(-)

diff --git a/drivers/power/domain/imx8mp-hsiomix.c 
b/drivers/power/domain/imx8mp-hsiomix.c
index 62145e0261b..4cefe642724 100644
--- a/drivers/power/domain/imx8mp-hsiomix.c
+++ b/drivers/power/domain/imx8mp-hsiomix.c
@@ -9,6 +9,8 @@
 #include <dm.h>
 #include <dm/device.h>
 #include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
 #include <power-domain-uclass.h>
 
 #include <dt-bindings/power/imx8mp-power.h>
@@ -18,6 +20,15 @@
 #define  USB_CLOCK_MODULE_EN   BIT(1)
 #define  PCIE_PHY_APB_RST      BIT(4)
 #define  PCIE_PHY_INIT_RST     BIT(5)
+#define GPR_REG1               0x4
+#define  PLL_LOCK              BIT(13)
+#define GPR_REG2               0x8
+#define  P_PLL_MASK            GENMASK(5, 0)
+#define  M_PLL_MASK            GENMASK(15, 6)
+#define  S_PLL_MASK            GENMASK(18, 16)
+#define GPR_REG3               0xc
+#define  PLL_CKE               BIT(17)
+#define  PLL_RST               BIT(31)
 
 struct imx8mp_hsiomix_priv {
        void __iomem *base;
@@ -31,6 +42,53 @@ struct imx8mp_hsiomix_priv {
        struct power_domain pd_pcie_phy;
 };
 
+static int hsio_pll_enable(struct udevice *dev)
+{
+       struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev);
+       unsigned long start;
+       u32 val;
+
+       /* Setup HSIO PLL */
+       val = readl(priv->base + GPR_REG2);
+       val &= ~(P_PLL_MASK | M_PLL_MASK | S_PLL_MASK);
+       val |= (FIELD_PREP(P_PLL_MASK, 12) | FIELD_PREP(M_PLL_MASK, 800) |
+               FIELD_PREP(S_PLL_MASK, 4));
+       writel(val, priv->base + GPR_REG2);
+
+       /* de-assert PLL reset */
+       setbits_le32(priv->base + GPR_REG3, PLL_RST);
+
+       /* enable PLL */
+       setbits_le32(priv->base + GPR_REG3, PLL_CKE);
+
+       /* Check if PLL is locked */
+       start = get_timer(0);
+       for (;;) {
+               if (readl(priv->base + GPR_REG1) & PLL_LOCK)
+                       break;
+
+               if (get_timer(start) > 100) {
+                       dev_err(dev, "failed to lock HSIO PLL\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(10);
+       }
+
+       return 0;
+}
+
+static void hsio_pll_disable(struct udevice *dev)
+{
+       struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev);
+
+       /* de-assert PLL reset */
+       clrbits_le32(priv->base + GPR_REG3, PLL_RST);
+
+       /* enable PLL */
+       clrbits_le32(priv->base + GPR_REG3, PLL_CKE);
+}
+
 static int imx8mp_hsiomix_on(struct power_domain *power_domain)
 {
        struct udevice *dev = power_domain->dev;
@@ -69,16 +127,23 @@ static int imx8mp_hsiomix_on(struct power_domain 
*power_domain)
        if (ret)
                goto err_clk_pcie;
 
-       if (power_domain->id == IMX8MP_HSIOBLK_PD_USB)
+       if (power_domain->id == IMX8MP_HSIOBLK_PD_USB) {
                setbits_le32(priv->base + GPR_REG0, USB_CLOCK_MODULE_EN);
-       else if (power_domain->id == IMX8MP_HSIOBLK_PD_PCIE)
+       } else if (power_domain->id == IMX8MP_HSIOBLK_PD_PCIE) {
                setbits_le32(priv->base + GPR_REG0, PCIE_CLOCK_MODULE_EN);
-       else if (power_domain->id == IMX8MP_HSIOBLK_PD_PCIE_PHY)
+       } else if (power_domain->id == IMX8MP_HSIOBLK_PD_PCIE_PHY) {
                setbits_le32(priv->base + GPR_REG0, PCIE_PHY_APB_RST |
                                                    PCIE_PHY_INIT_RST);
 
+               ret = hsio_pll_enable(dev);
+               if (ret)
+                       goto err_hsio_pll;
+       }
+
        return 0;
 
+err_hsio_pll:
+       clk_disable(&priv->clk_pcie);
 err_clk_pcie:
        clk_disable(&priv->clk_usb);
 err_clk_usb:
@@ -93,13 +158,15 @@ static int imx8mp_hsiomix_off(struct power_domain 
*power_domain)
        struct udevice *dev = power_domain->dev;
        struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev);
 
-       if (power_domain->id == IMX8MP_HSIOBLK_PD_USB)
+       if (power_domain->id == IMX8MP_HSIOBLK_PD_USB) {
                clrbits_le32(priv->base + GPR_REG0, USB_CLOCK_MODULE_EN);
-       else if (power_domain->id == IMX8MP_HSIOBLK_PD_PCIE)
+       } else if (power_domain->id == IMX8MP_HSIOBLK_PD_PCIE) {
                clrbits_le32(priv->base + GPR_REG0, PCIE_CLOCK_MODULE_EN);
-       else if (power_domain->id == IMX8MP_HSIOBLK_PD_PCIE_PHY)
+       } else if (power_domain->id == IMX8MP_HSIOBLK_PD_PCIE_PHY) {
                clrbits_le32(priv->base + GPR_REG0, PCIE_PHY_APB_RST |
                                                    PCIE_PHY_INIT_RST);
+               hsio_pll_disable(dev);
+       }
 
        clk_disable(&priv->clk_usb);
        clk_disable(&priv->clk_pcie);
-- 
2.34.1

Reply via email to