Skip to content

Commit

Permalink
Merge tag 'amlogic-clk' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/khilman/linux-amlogic into clk-next

Same great taste as the previous pull request, but now with 50% less DT
bikeshedding!

Amlogic clock driver updates for v4.12
- meson8: add some new PLLs
- new clocks for Mali
- misc fixes.
  • Loading branch information
mturquette committed Apr 12, 2017
2 parents 17c34c5 + 3a42981 commit 0d7a532
Show file tree
Hide file tree
Showing 9 changed files with 867 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ controllers within the SoC.

Required Properties:

- compatible: should be "amlogic,gxbb-clkc"
- compatible: should be "amlogic,gxbb-clkc" for GXBB SoC,
or "amlogic,gxl-clkc" for GXL and GXM SoC.
- reg: physical base address of the clock controller and length of memory
mapped region.

Expand Down
152 changes: 147 additions & 5 deletions drivers/clk/meson/clk-mpll.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 rate;
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 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,
.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,
};
53 changes: 51 additions & 2 deletions drivers/clk/meson/clk-pll.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
{
Expand All @@ -151,10 +184,16 @@ 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);
writel(reg | MESON_PLL_RESET, 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);
writel(reg, pll->base + p->reg_off);
Expand Down Expand Up @@ -184,7 +223,17 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
}

p = &pll->n;
ret = meson_clk_pll_wait_lock(pll, p);
/* 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",
__func__, old_rate);
Expand Down
29 changes: 27 additions & 2 deletions drivers/clk/meson/clkc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
};

Expand All @@ -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 */
Loading

0 comments on commit 0d7a532

Please sign in to comment.