clk: meson: mpll: add rw operation

This patch adds new callbacks to the meson-mpll driver to control
and set the pll rate. For this, we also need to add the enable bit and
sdm enable bit. The corresponding parameters are added to mpll data
structure.

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Michael Turquette <mturquette@baylibre.com>
Link: lkml.kernel.org/r/20170309104154.28295-6-jbrunet@baylibre.com
This commit is contained in:
Jerome Brunet 2017-03-09 11:41:50 +01:00 коммит произвёл Michael Turquette
Родитель b92332eea8
Коммит 007e6e5c5f
3 изменённых файлов: 180 добавлений и 6 удалений

Просмотреть файл

@ -64,16 +64,50 @@
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include "clkc.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 127
#define to_meson_clk_mpll(_hw) container_of(_hw, struct meson_clk_mpll, hw) #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, static unsigned long mpll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate) unsigned long parent_rate)
{ {
struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw); struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
struct parm *p; struct parm *p;
unsigned long rate = 0;
unsigned long reg, sdm, n2; unsigned long reg, sdm, n2;
p = &mpll->sdm; p = &mpll->sdm;
@ -84,11 +118,119 @@ static unsigned long mpll_recalc_rate(struct clk_hw *hw,
reg = readl(mpll->base + p->reg_off); reg = readl(mpll->base + p->reg_off);
n2 = PARM_GET(p->width, p->shift, reg); 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);
}
return rate; 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 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 = { 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,
}; };

Просмотреть файл

@ -92,8 +92,9 @@ struct meson_clk_mpll {
struct clk_hw hw; struct clk_hw hw;
void __iomem *base; void __iomem *base;
struct parm sdm; struct parm sdm;
struct parm sdm_en;
struct parm n2; struct parm n2;
/* FIXME ssen gate control? */ struct parm en;
spinlock_t *lock; spinlock_t *lock;
}; };
@ -116,5 +117,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_pll_ops;
extern const struct clk_ops meson_clk_cpu_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_ro_ops;
extern const struct clk_ops meson_clk_mpll_ops;
#endif /* __CLKC_H */ #endif /* __CLKC_H */

Просмотреть файл

@ -441,11 +441,21 @@ static struct meson_clk_mpll gxbb_mpll0 = {
.shift = 0, .shift = 0,
.width = 14, .width = 14,
}, },
.sdm_en = {
.reg_off = HHI_MPLL_CNTL7,
.shift = 15,
.width = 1,
},
.n2 = { .n2 = {
.reg_off = HHI_MPLL_CNTL7, .reg_off = HHI_MPLL_CNTL7,
.shift = 16, .shift = 16,
.width = 9, .width = 9,
}, },
.en = {
.reg_off = HHI_MPLL_CNTL7,
.shift = 14,
.width = 1,
},
.lock = &clk_lock, .lock = &clk_lock,
.hw.init = &(struct clk_init_data){ .hw.init = &(struct clk_init_data){
.name = "mpll0", .name = "mpll0",
@ -461,11 +471,21 @@ static struct meson_clk_mpll gxbb_mpll1 = {
.shift = 0, .shift = 0,
.width = 14, .width = 14,
}, },
.sdm_en = {
.reg_off = HHI_MPLL_CNTL8,
.shift = 15,
.width = 1,
},
.n2 = { .n2 = {
.reg_off = HHI_MPLL_CNTL8, .reg_off = HHI_MPLL_CNTL8,
.shift = 16, .shift = 16,
.width = 9, .width = 9,
}, },
.en = {
.reg_off = HHI_MPLL_CNTL8,
.shift = 14,
.width = 1,
},
.lock = &clk_lock, .lock = &clk_lock,
.hw.init = &(struct clk_init_data){ .hw.init = &(struct clk_init_data){
.name = "mpll1", .name = "mpll1",
@ -481,11 +501,21 @@ static struct meson_clk_mpll gxbb_mpll2 = {
.shift = 0, .shift = 0,
.width = 14, .width = 14,
}, },
.sdm_en = {
.reg_off = HHI_MPLL_CNTL9,
.shift = 15,
.width = 1,
},
.n2 = { .n2 = {
.reg_off = HHI_MPLL_CNTL9, .reg_off = HHI_MPLL_CNTL9,
.shift = 16, .shift = 16,
.width = 9, .width = 9,
}, },
.en = {
.reg_off = HHI_MPLL_CNTL9,
.shift = 14,
.width = 1,
},
.lock = &clk_lock, .lock = &clk_lock,
.hw.init = &(struct clk_init_data){ .hw.init = &(struct clk_init_data){
.name = "mpll2", .name = "mpll2",