Message ID | 20240220131056.2962331-5-sumit.garg@linaro.org |
---|---|
State | New |
Headers | show |
Series | imx8mp: Enable PCIe/NVMe support | expand |
On 2/20/24 14:10, Sumit Garg wrote: > PCIe PHY can use it when there is no external refclock provided. Commit message needs to be fixed. > +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); clrsetbits_le32() > + /* 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); wait_for_bit() or readl_poll_timeout() > + 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); Is this how Linux handles this PLL ? Seems like this should be either syscon or clock driver . [...]
On Tue, 20 Feb 2024 at 21:02, Marek Vasut <marex@denx.de> wrote: > > On 2/20/24 14:10, Sumit Garg wrote: > > PCIe PHY can use it when there is no external refclock provided. > > Commit message needs to be fixed. How about the following? Expose high performance PLL clock, so the PCIe PHY can use it when there is no external refclock provided. > > > +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); > > clrsetbits_le32() Ack > > > + /* 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); > > wait_for_bit() or readl_poll_timeout() Let me use readl_poll_timeout() instead. > > > + 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); > > Is this how Linux handles this PLL ? > > Seems like this should be either syscon or clock driver . It isn't similar to what Linux does but I can't find suitable infrastructure in U-Boot to expose it as a regular clock. Are there any APIs available similar to devm_of_clk_add_hw_provider() in Linux? -Sumit > > [...]
On 2/21/24 07:14, Sumit Garg wrote: > On Tue, 20 Feb 2024 at 21:02, Marek Vasut <marex@denx.de> wrote: >> >> On 2/20/24 14:10, Sumit Garg wrote: >>> PCIe PHY can use it when there is no external refclock provided. >> >> Commit message needs to be fixed. > > How about the following? > > Expose high performance PLL clock, so the PCIe PHY can > use it when there is no external refclock provided. If this code is imported from Linux 2cbee26e5d59 ("soc: imx: imx8mp-blk-ctrl: expose high performance PLL clock") then just include that reference too . [...] >>> @@ -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); >> >> Is this how Linux handles this PLL ? >> >> Seems like this should be either syscon or clock driver . > > It isn't similar to what Linux does but I can't find suitable > infrastructure in U-Boot to expose it as a regular clock. Are there > any APIs available similar to devm_of_clk_add_hw_provider() in Linux? Have a look at the very end of: drivers/clk/renesas/clk-rcar-gen3.c that registers clock and reset drivers for the same IP at the same address. In this case, you would register power domain and clock drivers instead .
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);
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(-)