> Add pll14xx driver
> 
> Signed-off-by: Peng Fan <peng....@nxp.com>
> ---
>  drivers/clk/imx/clk-pll14xx.c | 381 
> ++++++++++++++++++++++++++++++++++++++++++
>  drivers/clk/imx/clk.h         |  25 +++
>  2 files changed, 406 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..2246beb21b
> --- /dev/null
> +++ b/drivers/clk/imx/clk-pll14xx.c
> @@ -0,0 +1,381 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2017-2019 NXP.
> + *
> + * Peng Fan <peng....@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;
> +}
> +
> +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 BYPASS */
> +     tmp |= BYPASS_MASK;
> +     writel(tmp, pll->base);
> +
> +
> +     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);
> +     }
> +
> +     tmp = readl(pll->base);
> +
> +     /* Enable RST */
> +     tmp &= ~RST_MASK;
> +     writel(tmp, pll->base);
> +
> +     /* Enable BYPASS */
> +     tmp |= BYPASS_MASK;
> +     writel(tmp, pll->base);
> +
> +     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,
> 
Reviewed-by: Ye Li <ye...@nxp.com>

Best regards,
Ye Li
_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to