[U-Boot] [PATCH V2 2/4] clk: imx: add pll14xx driver
Ye Li
ye.li at nxp.com
Fri Aug 9 06:52:40 UTC 2019
Hi Peng,
> Add pll14xx driver
>
> Signed-off-by: Peng Fan <peng.fan at nxp.com>
> ---
> drivers/clk/imx/clk-pll14xx.c | 371 ++++++++++++++++++++++++++++++++++++++++++
> drivers/clk/imx/clk.h | 25 +++
> 2 files changed, 396 insertions(+)
> create mode 100644 drivers/clk/imx/clk-pll14xx.c
>
> diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c
> new file mode 100644
> index 0000000000..8b47b0dea9
> --- /dev/null
> +++ b/drivers/clk/imx/clk-pll14xx.c
> @@ -0,0 +1,371 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2017-2019 NXP.
> + *
> + * Peng Fan <peng.fan at nxp.com>
> + */
> +
> +#include <common.h>
> +#include <asm/io.h>
> +#include <malloc.h>
> +#include <clk-uclass.h>
> +#include <dm/device.h>
> +#include <linux/clk-provider.h>
> +#include <linux/iopoll.h>
> +#include <clk.h>
> +#include <div64.h>
> +
> +#include "clk.h"
> +
> +#define UBOOT_DM_CLK_IMX_PLL1443X "imx_clk_pll1443x"
> +#define UBOOT_DM_CLK_IMX_PLL1416X "imx_clk_pll1416x"
> +
> +#define GNRL_CTL 0x0
> +#define DIV_CTL 0x4
> +#define LOCK_STATUS BIT(31)
> +#define LOCK_SEL_MASK BIT(29)
> +#define CLKE_MASK BIT(11)
> +#define RST_MASK BIT(9)
> +#define BYPASS_MASK BIT(4)
> +#define MDIV_SHIFT 12
> +#define MDIV_MASK GENMASK(21, 12)
> +#define PDIV_SHIFT 4
> +#define PDIV_MASK GENMASK(9, 4)
> +#define SDIV_SHIFT 0
> +#define SDIV_MASK GENMASK(2, 0)
> +#define KDIV_SHIFT 0
> +#define KDIV_MASK GENMASK(15, 0)
> +
> +#define LOCK_TIMEOUT_US 10000
> +
> +struct clk_pll14xx {
> + struct clk clk;
> + void __iomem *base;
> + enum imx_pll14xx_type type;
> + const struct imx_pll14xx_rate_table *rate_table;
> + int rate_count;
> +};
> +
> +#define to_clk_pll14xx(_clk) container_of(_clk, struct clk_pll14xx, clk)
> +
> +static const struct imx_pll14xx_rate_table *imx_get_pll_settings(
> + struct clk_pll14xx *pll, unsigned long rate)
> +{
> + const struct imx_pll14xx_rate_table *rate_table = pll->rate_table;
> + int i;
> +
> + for (i = 0; i < pll->rate_count; i++)
> + if (rate == rate_table[i].rate)
> + return &rate_table[i];
> +
> + return NULL;
> +}
> +
> +static unsigned long clk_pll1416x_recalc_rate(struct clk *clk)
> +{
> + struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
> + u64 fvco = clk_get_parent_rate(clk);
> + u32 mdiv, pdiv, sdiv, pll_div;
> +
> + pll_div = readl(pll->base + 4);
> + mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT;
> + pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT;
> + sdiv = (pll_div & SDIV_MASK) >> SDIV_SHIFT;
> +
> + fvco *= mdiv;
> + do_div(fvco, pdiv << sdiv);
> +
> + return fvco;
> +}
> +
> +static unsigned long clk_pll1443x_recalc_rate(struct clk *clk)
> +{
> + struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
> + u64 fvco = clk_get_parent_rate(clk);
> + u32 mdiv, pdiv, sdiv, pll_div_ctl0, pll_div_ctl1;
> + short int kdiv;
> +
> + pll_div_ctl0 = readl(pll->base + 4);
> + pll_div_ctl1 = readl(pll->base + 8);
> + mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
> + pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT;
> + sdiv = (pll_div_ctl0 & SDIV_MASK) >> SDIV_SHIFT;
> + kdiv = pll_div_ctl1 & KDIV_MASK;
> +
> + /* fvco = (m * 65536 + k) * Fin / (p * 65536) */
> + fvco *= (mdiv * 65536 + kdiv);
> + pdiv *= 65536;
> +
> + do_div(fvco, pdiv << sdiv);
> +
> + return fvco;
> +}
> +
> +static inline bool clk_pll1416x_mp_change(const struct imx_pll14xx_rate_table *rate,
> + u32 pll_div)
> +{
> + u32 old_mdiv, old_pdiv;
> +
> + old_mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT;
> + old_pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT;
> +
> + return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv;
> +}
> +
> +static inline bool clk_pll1443x_mpk_change(const struct imx_pll14xx_rate_table *rate,
> + u32 pll_div_ctl0, u32 pll_div_ctl1)
> +{
> + u32 old_mdiv, old_pdiv, old_kdiv;
> +
> + old_mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
> + old_pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT;
> + old_kdiv = (pll_div_ctl1 & KDIV_MASK) >> KDIV_SHIFT;
> +
> + return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv ||
> + rate->kdiv != old_kdiv;
> +}
> +
> +static inline bool clk_pll1443x_mp_change(const struct imx_pll14xx_rate_table *rate,
> + u32 pll_div_ctl0, u32 pll_div_ctl1)
> +{
> + u32 old_mdiv, old_pdiv, old_kdiv;
> +
> + old_mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
> + old_pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT;
> + old_kdiv = (pll_div_ctl1 & KDIV_MASK) >> KDIV_SHIFT;
> +
> + return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv ||
> + rate->kdiv != old_kdiv;
> +}
Not used. And it should not check kdiv.
> +
> +static int clk_pll14xx_wait_lock(struct clk_pll14xx *pll)
> +{
> + u32 val;
> +
> + return readl_poll_timeout(pll->base, val, val & LOCK_TIMEOUT_US,
> + LOCK_TIMEOUT_US);
> +}
> +
> +static ulong clk_pll1416x_set_rate(struct clk *clk, unsigned long drate)
> +{
> + struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
> + const struct imx_pll14xx_rate_table *rate;
> + u32 tmp, div_val;
> + int ret;
> +
> + rate = imx_get_pll_settings(pll, drate);
> + if (!rate) {
> + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
> + drate, "xxxx");
> + return -EINVAL;
> + }
> +
> + tmp = readl(pll->base + 4);
> +
> + if (!clk_pll1416x_mp_change(rate, tmp)) {
> + tmp &= ~(SDIV_MASK) << SDIV_SHIFT;
> + tmp |= rate->sdiv << SDIV_SHIFT;
> + writel(tmp, pll->base + 4);
> +
> + return clk_pll1416x_recalc_rate(clk);
> + }
> +
> + /* Bypass clock and set lock to pll output lock */
> + tmp = readl(pll->base);
> + tmp |= LOCK_SEL_MASK;
> + writel(tmp, pll->base);
> +
> + /* Enable RST */
> + tmp &= ~RST_MASK;
> + writel(tmp, pll->base);
Enable RST won't auto set bypass. It is better set bypass explicitly.
According to manual, there is a lock time after disable RST, and may generate glitch,
so enable bypass before disable RST can avoid such problem.
> +
> + div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) |
> + (rate->sdiv << SDIV_SHIFT);
> + writel(div_val, pll->base + 0x4);
> +
> + /*
> + * According to SPEC, t3 - t2 need to be greater than
> + * 1us and 1/FREF, respectively.
> + * FREF is FIN / Prediv, the prediv is [1, 63], so choose
> + * 3us.
> + */
> + udelay(3);
> +
> + /* Disable RST */
> + tmp |= RST_MASK;
> + writel(tmp, pll->base);
> +
> + /* Wait Lock */
> + ret = clk_pll14xx_wait_lock(pll);
> + if (ret)
> + return ret;
> +
> + /* Bypass */
> + tmp &= ~BYPASS_MASK;
> + writel(tmp, pll->base);
> +
> + return clk_pll1416x_recalc_rate(clk);
> +}
> +
> +static ulong clk_pll1443x_set_rate(struct clk *clk, unsigned long drate)
> +{
> + struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
> + const struct imx_pll14xx_rate_table *rate;
> + u32 tmp, div_val;
> + int ret;
> +
> + rate = imx_get_pll_settings(pll, drate);
> + if (!rate) {
> + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
> + drate, "===");
> + return -EINVAL;
> + }
> +
> + tmp = readl(pll->base + 4);
> + div_val = readl(pll->base + 8);
> +
> + if (!clk_pll1443x_mpk_change(rate, tmp, div_val)) {
> + tmp &= ~(SDIV_MASK) << SDIV_SHIFT;
> + tmp |= rate->sdiv << SDIV_SHIFT;
> + writel(tmp, pll->base + 4);
> +
> + return clk_pll1443x_recalc_rate(clk);
> + }
> +
> + /* Enable RST */
> + tmp = readl(pll->base);
> + tmp &= ~RST_MASK;
> + writel(tmp, pll->base);
Enable bypass.
Best regards,
Ye Li
> +
> + div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) |
> + (rate->sdiv << SDIV_SHIFT);
> + writel(div_val, pll->base + 0x4);
> + writel(rate->kdiv << KDIV_SHIFT, pll->base + 0x8);
> +
> + /*
> + * According to SPEC, t3 - t2 need to be greater than
> + * 1us and 1/FREF, respectively.
> + * FREF is FIN / Prediv, the prediv is [1, 63], so choose
> + * 3us.
> + */
> + udelay(3);
> +
> + /* Disable RST */
> + tmp |= RST_MASK;
> + writel(tmp, pll->base);
> +
> + /* Wait Lock*/
> + ret = clk_pll14xx_wait_lock(pll);
> + if (ret)
> + return ret;
> +
> + /* Bypass */
> + tmp &= ~BYPASS_MASK;
> + writel(tmp, pll->base);
> +
> + return clk_pll1443x_recalc_rate(clk);
> +}
> +
> +static int clk_pll14xx_prepare(struct clk *clk)
> +{
> + struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
> + u32 val;
> +
> + /*
> + * RESETB = 1 from 0, PLL starts its normal
> + * operation after lock time
> + */
> + val = readl(pll->base + GNRL_CTL);
> + val |= RST_MASK;
> + writel(val, pll->base + GNRL_CTL);
> +
> + return clk_pll14xx_wait_lock(pll);
> +}
> +
> +static int clk_pll14xx_unprepare(struct clk *clk)
> +{
> + struct clk_pll14xx *pll = to_clk_pll14xx(dev_get_clk_ptr(clk->dev));
> + u32 val;
> +
> + /*
> + * Set RST to 0, power down mode is enabled and
> + * every digital block is reset
> + */
> + val = readl(pll->base + GNRL_CTL);
> + val &= ~RST_MASK;
> + writel(val, pll->base + GNRL_CTL);
> +
> + return 0;
> +}
> +
> +static const struct clk_ops clk_pll1416x_ops = {
> + .enable = clk_pll14xx_prepare,
> + .disable = clk_pll14xx_unprepare,
> + .set_rate = clk_pll1416x_set_rate,
> + .get_rate = clk_pll1416x_recalc_rate,
> +};
> +
> +static const struct clk_ops clk_pll1443x_ops = {
> + .enable = clk_pll14xx_prepare,
> + .disable = clk_pll14xx_unprepare,
> + .set_rate = clk_pll1443x_set_rate,
> + .get_rate = clk_pll1443x_recalc_rate,
> +};
> +
> +struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
> + void __iomem *base,
> + const struct imx_pll14xx_clk *pll_clk)
> +{
> + struct clk_pll14xx *pll;
> + struct clk *clk;
> + char *type_name;
> + int ret;
> +
> + pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> + if (!pll)
> + return ERR_PTR(-ENOMEM);
> +
> + switch (pll_clk->type) {
> + case PLL_1416X:
> + type_name = UBOOT_DM_CLK_IMX_PLL1416X;
> + break;
> + case PLL_1443X:
> + type_name = UBOOT_DM_CLK_IMX_PLL1443X;
> + break;
> + default:
> + pr_err("%s: Unknown pll type for pll clk %s\n",
> + __func__, name);
> + return ERR_PTR(-EINVAL);
> + };
> +
> + pll->base = base;
> + pll->type = pll_clk->type;
> + pll->rate_table = pll_clk->rate_table;
> + pll->rate_count = pll_clk->rate_count;
> +
> + clk = &pll->clk;
> +
> + ret = clk_register(clk, type_name, name, parent_name);
> + if (ret) {
> + pr_err("%s: failed to register pll %s %d\n",
> + __func__, name, ret);
> + kfree(pll);
> + return ERR_PTR(ret);
> + }
> +
> + return clk;
> +}
> +
> +U_BOOT_DRIVER(clk_pll1443x) = {
> + .name = UBOOT_DM_CLK_IMX_PLL1443X,
> + .id = UCLASS_CLK,
> + .ops = &clk_pll1443x_ops,
> + .flags = DM_FLAG_PRE_RELOC,
> +};
> +
> +U_BOOT_DRIVER(clk_pll1416x) = {
> + .name = UBOOT_DM_CLK_IMX_PLL1416X,
> + .id = UCLASS_CLK,
> + .ops = &clk_pll1416x_ops,
> + .flags = DM_FLAG_PRE_RELOC,
> +};
> diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
> index 1d480d8722..4956e04a92 100644
> --- a/drivers/clk/imx/clk.h
> +++ b/drivers/clk/imx/clk.h
> @@ -20,6 +20,31 @@ enum imx_pllv3_type {
> IMX_PLLV3_DDR_IMX7,
> };
>
> +enum imx_pll14xx_type {
> + PLL_1416X,
> + PLL_1443X,
> +};
> +
> +/* NOTE: Rate table should be kept sorted in descending order. */
> +struct imx_pll14xx_rate_table {
> + unsigned int rate;
> + unsigned int pdiv;
> + unsigned int mdiv;
> + unsigned int sdiv;
> + unsigned int kdiv;
> +};
> +
> +struct imx_pll14xx_clk {
> + enum imx_pll14xx_type type;
> + const struct imx_pll14xx_rate_table *rate_table;
> + int rate_count;
> + int flags;
> +};
> +
> +struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
> + void __iomem *base,
> + const struct imx_pll14xx_clk *pll_clk);
> +
> struct clk *clk_register_gate2(struct device *dev, const char *name,
> const char *parent_name, unsigned long flags,
> void __iomem *reg, u8 bit_idx, u8 cgr_val,
>
More information about the U-Boot
mailing list