[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