
PCIe PHY can use it when there is no external refclock provided.
Signed-off-by: Sumit Garg sumit.garg@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);