Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a429818 authored by Kevin Hilman's avatar Kevin Hilman
Browse files

Merge branch 'v4.12/clk-drivers' into v4.12/clk

* v4.12/clk-drivers:
  clk: meson-gxbb: Add GXL/GXM GP0 Variant
  clk: meson-gxbb: Add GP0 PLL init parameters
  clk: meson: Add support for parameters for specific PLLs
  clk: meson-gxbb: Add MALI clocks
  clk: meson: mpll: correct N2 maximum value
  clk: meson8b: add the mplls clocks 0, 1 and 2
  clk: meson: gxbb: mpll: use rw operation
  clk: meson: mpll: add rw operation
  clk: gxbb: put dividers and muxes in tables
  clk: meson8b: put dividers and muxes in tables
  clk: meson: add missing const qualifiers on gate arrays
  clk: meson: fix SET_PARM macro
parents 92c2cc5d 0d48fc55
Loading
Loading
Loading
Loading
+147 −5
Original line number Diff line number Diff line
@@ -64,16 +64,50 @@
#include <linux/clk-provider.h>
#include "clkc.h"

#define SDM_MAX 16384
#define SDM_DEN 16384
#define SDM_MIN 1
#define SDM_MAX 16383
#define N2_MIN	4
#define N2_MAX	511

#define to_meson_clk_mpll(_hw) container_of(_hw, struct meson_clk_mpll, hw)

static unsigned long rate_from_params(unsigned long parent_rate,
				      unsigned long sdm,
				      unsigned long n2)
{
	return (parent_rate * SDM_DEN) / ((SDM_DEN * n2) + sdm);
}

static void params_from_rate(unsigned long requested_rate,
			     unsigned long parent_rate,
			     unsigned long *sdm,
			     unsigned long *n2)
{
	uint64_t div = parent_rate;
	unsigned long rem = do_div(div, requested_rate);

	if (div < N2_MIN) {
		*n2 = N2_MIN;
		*sdm = SDM_MIN;
	} else if (div > N2_MAX) {
		*n2 = N2_MAX;
		*sdm = SDM_MAX;
	} else {
		*n2 = div;
		*sdm = DIV_ROUND_UP(rem * SDM_DEN, requested_rate);
		if (*sdm < SDM_MIN)
			*sdm = SDM_MIN;
		else if (*sdm > SDM_MAX)
			*sdm = SDM_MAX;
	}
}

static unsigned long mpll_recalc_rate(struct clk_hw *hw,
		unsigned long parent_rate)
{
	struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
	struct parm *p;
	unsigned long rate = 0;
	unsigned long reg, sdm, n2;

	p = &mpll->sdm;
@@ -84,11 +118,119 @@ static unsigned long mpll_recalc_rate(struct clk_hw *hw,
	reg = readl(mpll->base + p->reg_off);
	n2 = PARM_GET(p->width, p->shift, reg);

	rate = (parent_rate * SDM_MAX) / ((SDM_MAX * n2) + sdm);
	return rate_from_params(parent_rate, sdm, n2);
}

static long mpll_round_rate(struct clk_hw *hw,
			    unsigned long rate,
			    unsigned long *parent_rate)
{
	unsigned long sdm, n2;

	params_from_rate(rate, *parent_rate, &sdm, &n2);
	return rate_from_params(*parent_rate, sdm, n2);
}

static int mpll_set_rate(struct clk_hw *hw,
			 unsigned long rate,
			 unsigned long parent_rate)
{
	struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
	struct parm *p;
	unsigned long reg, sdm, n2;
	unsigned long flags = 0;

	params_from_rate(rate, parent_rate, &sdm, &n2);

	if (mpll->lock)
		spin_lock_irqsave(mpll->lock, flags);
	else
		__acquire(mpll->lock);

	p = &mpll->sdm;
	reg = readl(mpll->base + p->reg_off);
	reg = PARM_SET(p->width, p->shift, reg, sdm);
	writel(reg, mpll->base + p->reg_off);

	p = &mpll->sdm_en;
	reg = readl(mpll->base + p->reg_off);
	reg = PARM_SET(p->width, p->shift, reg, 1);
	writel(reg, mpll->base + p->reg_off);

	p = &mpll->n2;
	reg = readl(mpll->base + p->reg_off);
	reg = PARM_SET(p->width, p->shift, reg, n2);
	writel(reg, mpll->base + p->reg_off);

	if (mpll->lock)
		spin_unlock_irqrestore(mpll->lock, flags);
	else
		__release(mpll->lock);

	return 0;
}

static void mpll_enable_core(struct clk_hw *hw, int enable)
{
	struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
	struct parm *p;
	unsigned long reg;
	unsigned long flags = 0;

	if (mpll->lock)
		spin_lock_irqsave(mpll->lock, flags);
	else
		__acquire(mpll->lock);

	p = &mpll->en;
	reg = readl(mpll->base + p->reg_off);
	reg = PARM_SET(p->width, p->shift, reg, enable ? 1 : 0);
	writel(reg, mpll->base + p->reg_off);

	if (mpll->lock)
		spin_unlock_irqrestore(mpll->lock, flags);
	else
		__release(mpll->lock);
}


static int mpll_enable(struct clk_hw *hw)
{
	mpll_enable_core(hw, 1);

	return rate;
	return 0;
}

static void mpll_disable(struct clk_hw *hw)
{
	mpll_enable_core(hw, 0);
}

static int mpll_is_enabled(struct clk_hw *hw)
{
	struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
	struct parm *p;
	unsigned long reg;
	int en;

	p = &mpll->en;
	reg = readl(mpll->base + p->reg_off);
	en = PARM_GET(p->width, p->shift, reg);

	return en;
}

const struct clk_ops meson_clk_mpll_ro_ops = {
	.recalc_rate	= mpll_recalc_rate,
	.round_rate	= mpll_round_rate,
	.is_enabled	= mpll_is_enabled,
};

const struct clk_ops meson_clk_mpll_ops = {
	.recalc_rate	= mpll_recalc_rate,
	.round_rate	= mpll_round_rate,
	.set_rate	= mpll_set_rate,
	.enable		= mpll_enable,
	.disable	= mpll_disable,
	.is_enabled	= mpll_is_enabled,
};
+51 −2
Original line number Diff line number Diff line
@@ -116,6 +116,30 @@ static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_
	return NULL;
}

/* Specific wait loop for GXL/GXM GP0 PLL */
static int meson_clk_pll_wait_lock_reset(struct meson_clk_pll *pll,
					 struct parm *p_n)
{
	int delay = 100;
	u32 reg;

	while (delay > 0) {
		reg = readl(pll->base + p_n->reg_off);
		writel(reg | MESON_PLL_RESET, pll->base + p_n->reg_off);
		udelay(10);
		writel(reg & ~MESON_PLL_RESET, pll->base + p_n->reg_off);

		/* This delay comes from AMLogic tree clk-gp0-gxl driver */
		mdelay(1);

		reg = readl(pll->base + p_n->reg_off);
		if (reg & MESON_PLL_LOCK)
			return 0;
		delay--;
	}
	return -ETIMEDOUT;
}

static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
				   struct parm *p_n)
{
@@ -132,6 +156,15 @@ static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
	return -ETIMEDOUT;
}

static void meson_clk_pll_init_params(struct meson_clk_pll *pll)
{
	int i;

	for (i = 0 ; i < pll->params.params_count ; ++i)
		writel(pll->params.params_table[i].value,
		       pll->base + pll->params.params_table[i].reg_off);
}

static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
				  unsigned long parent_rate)
{
@@ -151,9 +184,15 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
	if (!rate_set)
		return -EINVAL;

	/* Initialize the PLL in a clean state if specified */
	if (pll->params.params_count)
		meson_clk_pll_init_params(pll);

	/* PLL reset */
	p = &pll->n;
	reg = readl(pll->base + p->reg_off);
	/* If no_init_reset is provided, avoid resetting at this point */
	if (!pll->params.no_init_reset)
		writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);

	reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
@@ -184,6 +223,16 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
	}

	p = &pll->n;
	/* If clear_reset_for_lock is provided, remove the reset bit here */
	if (pll->params.clear_reset_for_lock) {
		reg = readl(pll->base + p->reg_off);
		writel(reg & ~MESON_PLL_RESET, pll->base + p->reg_off);
	}

	/* If reset_lock_loop, use a special loop including resetting */
	if (pll->params.reset_lock_loop)
		ret = meson_clk_pll_wait_lock_reset(pll, p);
	else
		ret = meson_clk_pll_wait_lock(pll, p);
	if (ret) {
		pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
+27 −2
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@
#define PARM_GET(width, shift, reg)					\
	(((reg) & SETPMASK(width, shift)) >> (shift))
#define PARM_SET(width, shift, reg, val)				\
	(((reg) & CLRPMASK(width, shift)) | (val << (shift)))
	(((reg) & CLRPMASK(width, shift)) | ((val) << (shift)))

#define MESON_PARM_APPLICABLE(p)		(!!((p)->width))

@@ -62,6 +62,28 @@ struct pll_rate_table {
		.frac		= (_frac),				\
	}								\

struct pll_params_table {
	unsigned int reg_off;
	unsigned int value;
};

#define PLL_PARAM(_reg, _val)						\
	{								\
		.reg_off	= (_reg),				\
		.value		= (_val),				\
	}

struct pll_setup_params {
	struct pll_params_table *params_table;
	unsigned int params_count;
	/* Workaround for GP0, do not reset before configuring */
	bool no_init_reset;
	/* Workaround for GP0, unreset right before checking for lock */
	bool clear_reset_for_lock;
	/* Workaround for GXL GP0, reset in the lock checking loop */
	bool reset_lock_loop;
};

struct meson_clk_pll {
	struct clk_hw hw;
	void __iomem *base;
@@ -70,6 +92,7 @@ struct meson_clk_pll {
	struct parm frac;
	struct parm od;
	struct parm od2;
	const struct pll_setup_params params;
	const struct pll_rate_table *rate_table;
	unsigned int rate_count;
	spinlock_t *lock;
@@ -92,8 +115,9 @@ struct meson_clk_mpll {
	struct clk_hw hw;
	void __iomem *base;
	struct parm sdm;
	struct parm sdm_en;
	struct parm n2;
	/* FIXME ssen gate control? */
	struct parm en;
	spinlock_t *lock;
};

@@ -116,5 +140,6 @@ extern const struct clk_ops meson_clk_pll_ro_ops;
extern const struct clk_ops meson_clk_pll_ops;
extern const struct clk_ops meson_clk_cpu_ops;
extern const struct clk_ops meson_clk_mpll_ro_ops;
extern const struct clk_ops meson_clk_mpll_ops;

#endif /* __CLKC_H */
+472 −33

File changed.

Preview size limit exceeded, changes collapsed.

+2 −0
Original line number Diff line number Diff line
@@ -71,6 +71,8 @@
#define HHI_GP0_PLL_CNTL2		0x44 /* 0x11 offset in data sheet */
#define HHI_GP0_PLL_CNTL3		0x48 /* 0x12 offset in data sheet */
#define HHI_GP0_PLL_CNTL4		0x4c /* 0x13 offset in data sheet */
#define	HHI_GP0_PLL_CNTL5		0x50 /* 0x14 offset in data sheet */
#define	HHI_GP0_PLL_CNTL1		0x58 /* 0x16 offset in data sheet */

#define HHI_XTAL_DIVN_CNTL		0xbc /* 0x2f offset in data sheet */
#define HHI_TIMER90K			0xec /* 0x3b offset in data sheet */
Loading