mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
CPU clock handling for Rockchip SoCs
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABCAAGBQJUJt+NAAoJEPOmecmc0R2BzmEH/00GYMLZZZrhUUBzF5+O5Wvu 2jjVwZkyDsEL7Dnhen45Z+2umoXat8TbgNoeX+BbLEj55ntfAv1aNV62gO/wLrJy GbUDeAMNLXnJsF3UefBHvDf1F7SOZAm7QPs9oYQMyUDQqzsorfuDjKDPPLAHoiO9 7TfVxLYqkd4lVm0cYo4tMYTgONK2QR6iKEGHHkXIsKZXMdd2gMAxuTIjBcTqbBu1 9Hxt9P2G9InmRaSmW4zXzMlur0it16BnFt2wnJGoawBZkkwnvkze54APQgA4GV61 tqUhh94mQdAzJd1wMtJASK6ZY79pjv9pmGgQ5kNVlDV0SaSafZ1MhQU4k7NdemQ= =myeX -----END PGP SIGNATURE----- Merge tag 'v3.18-rockchip-cpuclk' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip into clk-next CPU clock handling for Rockchip SoCs
This commit is contained in:
commit
5ad67d3e5e
9 changed files with 629 additions and 78 deletions
|
@ -5,6 +5,7 @@
|
||||||
obj-y += clk-rockchip.o
|
obj-y += clk-rockchip.o
|
||||||
obj-y += clk.o
|
obj-y += clk.o
|
||||||
obj-y += clk-pll.o
|
obj-y += clk-pll.o
|
||||||
|
obj-y += clk-cpu.o
|
||||||
obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
|
obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
|
||||||
|
|
||||||
obj-y += clk-rk3188.o
|
obj-y += clk-rk3188.o
|
||||||
|
|
329
drivers/clk/rockchip/clk-cpu.c
Normal file
329
drivers/clk/rockchip/clk-cpu.c
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014 MundoReader S.L.
|
||||||
|
* Author: Heiko Stuebner <heiko@sntech.de>
|
||||||
|
*
|
||||||
|
* based on clk/samsung/clk-cpu.c
|
||||||
|
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||||
|
* Author: Thomas Abraham <thomas.ab@samsung.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* A CPU clock is defined as a clock supplied to a CPU or a group of CPUs.
|
||||||
|
* The CPU clock is typically derived from a hierarchy of clock
|
||||||
|
* blocks which includes mux and divider blocks. There are a number of other
|
||||||
|
* auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
|
||||||
|
* clock for CPU domain. The rates of these auxiliary clocks are related to the
|
||||||
|
* CPU clock rate and this relation is usually specified in the hardware manual
|
||||||
|
* of the SoC or supplied after the SoC characterization.
|
||||||
|
*
|
||||||
|
* The below implementation of the CPU clock allows the rate changes of the CPU
|
||||||
|
* clock and the corresponding rate changes of the auxillary clocks of the CPU
|
||||||
|
* domain. The platform clock driver provides a clock register configuration
|
||||||
|
* for each configurable rate which is then used to program the clock hardware
|
||||||
|
* registers to acheive a fast co-oridinated rate change for all the CPU domain
|
||||||
|
* clocks.
|
||||||
|
*
|
||||||
|
* On a rate change request for the CPU clock, the rate change is propagated
|
||||||
|
* upto the PLL supplying the clock to the CPU domain clock blocks. While the
|
||||||
|
* CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
|
||||||
|
* alternate clock source. If required, the alternate clock source is divided
|
||||||
|
* down in order to keep the output clock rate within the previous OPP limits.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
|
#include "clk.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rockchip_cpuclk: information about clock supplied to a CPU core.
|
||||||
|
* @hw: handle between ccf and cpu clock.
|
||||||
|
* @alt_parent: alternate parent clock to use when switching the speed
|
||||||
|
* of the primary parent clock.
|
||||||
|
* @reg_base: base register for cpu-clock values.
|
||||||
|
* @clk_nb: clock notifier registered for changes in clock speed of the
|
||||||
|
* primary parent clock.
|
||||||
|
* @rate_count: number of rates in the rate_table
|
||||||
|
* @rate_table: pll-rates and their associated dividers
|
||||||
|
* @reg_data: cpu-specific register settings
|
||||||
|
* @lock: clock lock
|
||||||
|
*/
|
||||||
|
struct rockchip_cpuclk {
|
||||||
|
struct clk_hw hw;
|
||||||
|
|
||||||
|
struct clk_mux cpu_mux;
|
||||||
|
const struct clk_ops *cpu_mux_ops;
|
||||||
|
|
||||||
|
struct clk *alt_parent;
|
||||||
|
void __iomem *reg_base;
|
||||||
|
struct notifier_block clk_nb;
|
||||||
|
unsigned int rate_count;
|
||||||
|
struct rockchip_cpuclk_rate_table *rate_table;
|
||||||
|
const struct rockchip_cpuclk_reg_data *reg_data;
|
||||||
|
spinlock_t *lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_rockchip_cpuclk_hw(hw) container_of(hw, struct rockchip_cpuclk, hw)
|
||||||
|
#define to_rockchip_cpuclk_nb(nb) \
|
||||||
|
container_of(nb, struct rockchip_cpuclk, clk_nb)
|
||||||
|
|
||||||
|
static const struct rockchip_cpuclk_rate_table *rockchip_get_cpuclk_settings(
|
||||||
|
struct rockchip_cpuclk *cpuclk, unsigned long rate)
|
||||||
|
{
|
||||||
|
const struct rockchip_cpuclk_rate_table *rate_table =
|
||||||
|
cpuclk->rate_table;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cpuclk->rate_count; i++) {
|
||||||
|
if (rate == rate_table[i].prate)
|
||||||
|
return &rate_table[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long rockchip_cpuclk_recalc_rate(struct clk_hw *hw,
|
||||||
|
unsigned long parent_rate)
|
||||||
|
{
|
||||||
|
struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_hw(hw);
|
||||||
|
const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
|
||||||
|
u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg);
|
||||||
|
|
||||||
|
clksel0 >>= reg_data->div_core_shift;
|
||||||
|
clksel0 &= reg_data->div_core_mask;
|
||||||
|
return parent_rate / (clksel0 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct clk_ops rockchip_cpuclk_ops = {
|
||||||
|
.recalc_rate = rockchip_cpuclk_recalc_rate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void rockchip_cpuclk_set_dividers(struct rockchip_cpuclk *cpuclk,
|
||||||
|
const struct rockchip_cpuclk_rate_table *rate)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* alternate parent is active now. set the dividers */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(rate->divs); i++) {
|
||||||
|
const struct rockchip_cpuclk_clksel *clksel = &rate->divs[i];
|
||||||
|
|
||||||
|
if (!clksel->reg)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pr_debug("%s: setting reg 0x%x to 0x%x\n",
|
||||||
|
__func__, clksel->reg, clksel->val);
|
||||||
|
writel(clksel->val , cpuclk->reg_base + clksel->reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
|
||||||
|
struct clk_notifier_data *ndata)
|
||||||
|
{
|
||||||
|
const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
|
||||||
|
unsigned long alt_prate, alt_div;
|
||||||
|
|
||||||
|
alt_prate = clk_get_rate(cpuclk->alt_parent);
|
||||||
|
|
||||||
|
spin_lock(cpuclk->lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the old parent clock speed is less than the clock speed
|
||||||
|
* of the alternate parent, then it should be ensured that at no point
|
||||||
|
* the armclk speed is more than the old_rate until the dividers are
|
||||||
|
* set.
|
||||||
|
*/
|
||||||
|
if (alt_prate > ndata->old_rate) {
|
||||||
|
/* calculate dividers */
|
||||||
|
alt_div = DIV_ROUND_UP(alt_prate, ndata->old_rate) - 1;
|
||||||
|
if (alt_div > reg_data->div_core_mask) {
|
||||||
|
pr_warn("%s: limiting alt-divider %lu to %d\n",
|
||||||
|
__func__, alt_div, reg_data->div_core_mask);
|
||||||
|
alt_div = reg_data->div_core_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change parents and add dividers in a single transaction.
|
||||||
|
*
|
||||||
|
* NOTE: we do this in a single transaction so we're never
|
||||||
|
* dividing the primary parent by the extra dividers that were
|
||||||
|
* needed for the alt.
|
||||||
|
*/
|
||||||
|
pr_debug("%s: setting div %lu as alt-rate %lu > old-rate %lu\n",
|
||||||
|
__func__, alt_div, alt_prate, ndata->old_rate);
|
||||||
|
|
||||||
|
writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask,
|
||||||
|
reg_data->div_core_shift) |
|
||||||
|
HIWORD_UPDATE(1, 1, reg_data->mux_core_shift),
|
||||||
|
cpuclk->reg_base + reg_data->core_reg);
|
||||||
|
} else {
|
||||||
|
/* select alternate parent */
|
||||||
|
writel(HIWORD_UPDATE(1, 1, reg_data->mux_core_shift),
|
||||||
|
cpuclk->reg_base + reg_data->core_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock(cpuclk->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk,
|
||||||
|
struct clk_notifier_data *ndata)
|
||||||
|
{
|
||||||
|
const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
|
||||||
|
const struct rockchip_cpuclk_rate_table *rate;
|
||||||
|
|
||||||
|
rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate);
|
||||||
|
if (!rate) {
|
||||||
|
pr_err("%s: Invalid rate : %lu for cpuclk\n",
|
||||||
|
__func__, ndata->new_rate);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock(cpuclk->lock);
|
||||||
|
|
||||||
|
if (ndata->old_rate < ndata->new_rate)
|
||||||
|
rockchip_cpuclk_set_dividers(cpuclk, rate);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* post-rate change event, re-mux to primary parent and remove dividers.
|
||||||
|
*
|
||||||
|
* NOTE: we do this in a single transaction so we're never dividing the
|
||||||
|
* primary parent by the extra dividers that were needed for the alt.
|
||||||
|
*/
|
||||||
|
|
||||||
|
writel(HIWORD_UPDATE(0, reg_data->div_core_mask,
|
||||||
|
reg_data->div_core_shift) |
|
||||||
|
HIWORD_UPDATE(0, 1, reg_data->mux_core_shift),
|
||||||
|
cpuclk->reg_base + reg_data->core_reg);
|
||||||
|
|
||||||
|
if (ndata->old_rate > ndata->new_rate)
|
||||||
|
rockchip_cpuclk_set_dividers(cpuclk, rate);
|
||||||
|
|
||||||
|
spin_unlock(cpuclk->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This clock notifier is called when the frequency of the parent clock
|
||||||
|
* of cpuclk is to be changed. This notifier handles the setting up all
|
||||||
|
* the divider clocks, remux to temporary parent and handling the safe
|
||||||
|
* frequency levels when using temporary parent.
|
||||||
|
*/
|
||||||
|
static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb,
|
||||||
|
unsigned long event, void *data)
|
||||||
|
{
|
||||||
|
struct clk_notifier_data *ndata = data;
|
||||||
|
struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
|
||||||
|
__func__, event, ndata->old_rate, ndata->new_rate);
|
||||||
|
if (event == PRE_RATE_CHANGE)
|
||||||
|
ret = rockchip_cpuclk_pre_rate_change(cpuclk, ndata);
|
||||||
|
else if (event == POST_RATE_CHANGE)
|
||||||
|
ret = rockchip_cpuclk_post_rate_change(cpuclk, ndata);
|
||||||
|
|
||||||
|
return notifier_from_errno(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct clk *rockchip_clk_register_cpuclk(const char *name,
|
||||||
|
const char **parent_names, u8 num_parents,
|
||||||
|
const struct rockchip_cpuclk_reg_data *reg_data,
|
||||||
|
const struct rockchip_cpuclk_rate_table *rates,
|
||||||
|
int nrates, void __iomem *reg_base, spinlock_t *lock)
|
||||||
|
{
|
||||||
|
struct rockchip_cpuclk *cpuclk;
|
||||||
|
struct clk_init_data init;
|
||||||
|
struct clk *clk, *cclk;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (num_parents != 2) {
|
||||||
|
pr_err("%s: needs two parent clocks\n", __func__);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
|
||||||
|
if (!cpuclk)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
init.name = name;
|
||||||
|
init.parent_names = &parent_names[0];
|
||||||
|
init.num_parents = 1;
|
||||||
|
init.ops = &rockchip_cpuclk_ops;
|
||||||
|
|
||||||
|
/* only allow rate changes when we have a rate table */
|
||||||
|
init.flags = (nrates > 0) ? CLK_SET_RATE_PARENT : 0;
|
||||||
|
|
||||||
|
/* disallow automatic parent changes by ccf */
|
||||||
|
init.flags |= CLK_SET_RATE_NO_REPARENT;
|
||||||
|
|
||||||
|
init.flags |= CLK_GET_RATE_NOCACHE;
|
||||||
|
|
||||||
|
cpuclk->reg_base = reg_base;
|
||||||
|
cpuclk->lock = lock;
|
||||||
|
cpuclk->reg_data = reg_data;
|
||||||
|
cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb;
|
||||||
|
cpuclk->hw.init = &init;
|
||||||
|
|
||||||
|
cpuclk->alt_parent = __clk_lookup(parent_names[1]);
|
||||||
|
if (!cpuclk->alt_parent) {
|
||||||
|
pr_err("%s: could not lookup alternate parent\n",
|
||||||
|
__func__);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto free_cpuclk;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(cpuclk->alt_parent);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s: could not enable alternate parent\n",
|
||||||
|
__func__);
|
||||||
|
goto free_cpuclk;
|
||||||
|
}
|
||||||
|
|
||||||
|
clk = __clk_lookup(parent_names[0]);
|
||||||
|
if (!clk) {
|
||||||
|
pr_err("%s: could not lookup parent clock %s\n",
|
||||||
|
__func__, parent_names[0]);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto free_cpuclk;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_notifier_register(clk, &cpuclk->clk_nb);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s: failed to register clock notifier for %s\n",
|
||||||
|
__func__, name);
|
||||||
|
goto free_cpuclk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nrates > 0) {
|
||||||
|
cpuclk->rate_count = nrates;
|
||||||
|
cpuclk->rate_table = kmemdup(rates,
|
||||||
|
sizeof(*rates) * nrates,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!cpuclk->rate_table) {
|
||||||
|
pr_err("%s: could not allocate memory for cpuclk rates\n",
|
||||||
|
__func__);
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto unregister_notifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cclk = clk_register(NULL, &cpuclk->hw);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
|
pr_err("%s: could not register cpuclk %s\n", __func__, name);
|
||||||
|
ret = PTR_ERR(clk);
|
||||||
|
goto free_rate_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cclk;
|
||||||
|
|
||||||
|
free_rate_table:
|
||||||
|
kfree(cpuclk->rate_table);
|
||||||
|
unregister_notifier:
|
||||||
|
clk_notifier_unregister(clk, &cpuclk->clk_nb);
|
||||||
|
free_cpuclk:
|
||||||
|
kfree(cpuclk);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
|
@ -34,7 +34,6 @@ struct rockchip_clk_pll {
|
||||||
const struct clk_ops *pll_mux_ops;
|
const struct clk_ops *pll_mux_ops;
|
||||||
|
|
||||||
struct notifier_block clk_nb;
|
struct notifier_block clk_nb;
|
||||||
bool rate_change_remuxed;
|
|
||||||
|
|
||||||
void __iomem *reg_base;
|
void __iomem *reg_base;
|
||||||
int lock_offset;
|
int lock_offset;
|
||||||
|
@ -108,38 +107,6 @@ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set pll mux when changing the pll rate.
|
|
||||||
* This makes sure to move the pll mux away from the actual pll before
|
|
||||||
* changing its rate and back to the original parent after the change.
|
|
||||||
*/
|
|
||||||
static int rockchip_pll_notifier_cb(struct notifier_block *nb,
|
|
||||||
unsigned long event, void *data)
|
|
||||||
{
|
|
||||||
struct rockchip_clk_pll *pll = to_rockchip_clk_pll_nb(nb);
|
|
||||||
struct clk_mux *pll_mux = &pll->pll_mux;
|
|
||||||
const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
|
|
||||||
int cur_parent;
|
|
||||||
|
|
||||||
switch (event) {
|
|
||||||
case PRE_RATE_CHANGE:
|
|
||||||
cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
|
|
||||||
if (cur_parent == PLL_MODE_NORM) {
|
|
||||||
pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
|
|
||||||
pll->rate_change_remuxed = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case POST_RATE_CHANGE:
|
|
||||||
if (pll->rate_change_remuxed) {
|
|
||||||
pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
|
|
||||||
pll->rate_change_remuxed = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NOTIFY_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PLL used in RK3066, RK3188 and RK3288
|
* PLL used in RK3066, RK3188 and RK3288
|
||||||
*/
|
*/
|
||||||
|
@ -194,6 +161,10 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
|
||||||
const struct rockchip_pll_rate_table *rate;
|
const struct rockchip_pll_rate_table *rate;
|
||||||
unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate);
|
unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate);
|
||||||
struct regmap *grf = rockchip_clk_get_grf();
|
struct regmap *grf = rockchip_clk_get_grf();
|
||||||
|
struct clk_mux *pll_mux = &pll->pll_mux;
|
||||||
|
const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
|
||||||
|
int rate_change_remuxed = 0;
|
||||||
|
int cur_parent;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (IS_ERR(grf)) {
|
if (IS_ERR(grf)) {
|
||||||
|
@ -216,6 +187,12 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
|
||||||
pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n",
|
pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n",
|
||||||
__func__, rate->rate, rate->nr, rate->no, rate->nf);
|
__func__, rate->rate, rate->nr, rate->no, rate->nf);
|
||||||
|
|
||||||
|
cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
|
||||||
|
if (cur_parent == PLL_MODE_NORM) {
|
||||||
|
pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
|
||||||
|
rate_change_remuxed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* enter reset mode */
|
/* enter reset mode */
|
||||||
writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0),
|
writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0),
|
||||||
pll->reg_base + RK3066_PLLCON(3));
|
pll->reg_base + RK3066_PLLCON(3));
|
||||||
|
@ -247,6 +224,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
|
||||||
rockchip_rk3066_pll_set_rate(hw, old_rate, prate);
|
rockchip_rk3066_pll_set_rate(hw, old_rate, prate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rate_change_remuxed)
|
||||||
|
pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +290,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
|
||||||
struct clk_mux *pll_mux;
|
struct clk_mux *pll_mux;
|
||||||
struct clk *pll_clk, *mux_clk;
|
struct clk *pll_clk, *mux_clk;
|
||||||
char pll_name[20];
|
char pll_name[20];
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (num_parents != 2) {
|
if (num_parents != 2) {
|
||||||
pr_err("%s: needs two parent clocks\n", __func__);
|
pr_err("%s: needs two parent clocks\n", __func__);
|
||||||
|
@ -367,7 +346,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
|
||||||
pll->lock_offset = grf_lock_offset;
|
pll->lock_offset = grf_lock_offset;
|
||||||
pll->lock_shift = lock_shift;
|
pll->lock_shift = lock_shift;
|
||||||
pll->lock = lock;
|
pll->lock = lock;
|
||||||
pll->clk_nb.notifier_call = rockchip_pll_notifier_cb;
|
|
||||||
|
|
||||||
pll_clk = clk_register(NULL, &pll->hw);
|
pll_clk = clk_register(NULL, &pll->hw);
|
||||||
if (IS_ERR(pll_clk)) {
|
if (IS_ERR(pll_clk)) {
|
||||||
|
@ -377,14 +355,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
|
||||||
goto err_pll;
|
goto err_pll;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_notifier_register(pll_clk, &pll->clk_nb);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("%s: failed to register clock notifier for %s : %d\n",
|
|
||||||
__func__, name, ret);
|
|
||||||
mux_clk = ERR_PTR(ret);
|
|
||||||
goto err_pll_notifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create the mux on top of the real pll */
|
/* create the mux on top of the real pll */
|
||||||
pll->pll_mux_ops = &clk_mux_ops;
|
pll->pll_mux_ops = &clk_mux_ops;
|
||||||
pll_mux = &pll->pll_mux;
|
pll_mux = &pll->pll_mux;
|
||||||
|
@ -417,13 +387,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
|
||||||
return mux_clk;
|
return mux_clk;
|
||||||
|
|
||||||
err_mux:
|
err_mux:
|
||||||
ret = clk_notifier_unregister(pll_clk, &pll->clk_nb);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("%s: could not unregister clock notifier in error path : %d\n",
|
|
||||||
__func__, ret);
|
|
||||||
return mux_clk;
|
|
||||||
}
|
|
||||||
err_pll_notifier:
|
|
||||||
clk_unregister(pll_clk);
|
clk_unregister(pll_clk);
|
||||||
err_pll:
|
err_pll:
|
||||||
kfree(pll);
|
kfree(pll);
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <dt-bindings/clock/rk3188-cru-common.h>
|
#include <dt-bindings/clock/rk3188-cru-common.h>
|
||||||
#include "clk.h"
|
#include "clk.h"
|
||||||
|
|
||||||
|
#define RK3066_GRF_SOC_STATUS 0x15c
|
||||||
#define RK3188_GRF_SOC_STATUS 0xac
|
#define RK3188_GRF_SOC_STATUS 0xac
|
||||||
|
|
||||||
enum rk3188_plls {
|
enum rk3188_plls {
|
||||||
|
@ -100,6 +101,98 @@ struct rockchip_pll_rate_table rk3188_pll_rates[] = {
|
||||||
{ /* sentinel */ },
|
{ /* sentinel */ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define RK3066_DIV_CORE_PERIPH_MASK 0x3
|
||||||
|
#define RK3066_DIV_CORE_PERIPH_SHIFT 6
|
||||||
|
#define RK3066_DIV_ACLK_CORE_MASK 0x7
|
||||||
|
#define RK3066_DIV_ACLK_CORE_SHIFT 0
|
||||||
|
#define RK3066_DIV_ACLK_HCLK_MASK 0x3
|
||||||
|
#define RK3066_DIV_ACLK_HCLK_SHIFT 8
|
||||||
|
#define RK3066_DIV_ACLK_PCLK_MASK 0x3
|
||||||
|
#define RK3066_DIV_ACLK_PCLK_SHIFT 12
|
||||||
|
#define RK3066_DIV_AHB2APB_MASK 0x3
|
||||||
|
#define RK3066_DIV_AHB2APB_SHIFT 14
|
||||||
|
|
||||||
|
#define RK3066_CLKSEL0(_core_peri) \
|
||||||
|
{ \
|
||||||
|
.reg = RK2928_CLKSEL_CON(0), \
|
||||||
|
.val = HIWORD_UPDATE(_core_peri, RK3066_DIV_CORE_PERIPH_MASK, \
|
||||||
|
RK3066_DIV_CORE_PERIPH_SHIFT) \
|
||||||
|
}
|
||||||
|
#define RK3066_CLKSEL1(_aclk_core, _aclk_hclk, _aclk_pclk, _ahb2apb) \
|
||||||
|
{ \
|
||||||
|
.reg = RK2928_CLKSEL_CON(1), \
|
||||||
|
.val = HIWORD_UPDATE(_aclk_core, RK3066_DIV_ACLK_CORE_MASK, \
|
||||||
|
RK3066_DIV_ACLK_CORE_SHIFT) | \
|
||||||
|
HIWORD_UPDATE(_aclk_hclk, RK3066_DIV_ACLK_HCLK_MASK, \
|
||||||
|
RK3066_DIV_ACLK_HCLK_SHIFT) | \
|
||||||
|
HIWORD_UPDATE(_aclk_pclk, RK3066_DIV_ACLK_PCLK_MASK, \
|
||||||
|
RK3066_DIV_ACLK_PCLK_SHIFT) | \
|
||||||
|
HIWORD_UPDATE(_ahb2apb, RK3066_DIV_AHB2APB_MASK, \
|
||||||
|
RK3066_DIV_AHB2APB_SHIFT), \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RK3066_CPUCLK_RATE(_prate, _core_peri, _acore, _ahclk, _apclk, _h2p) \
|
||||||
|
{ \
|
||||||
|
.prate = _prate, \
|
||||||
|
.divs = { \
|
||||||
|
RK3066_CLKSEL0(_core_peri), \
|
||||||
|
RK3066_CLKSEL1(_acore, _ahclk, _apclk, _h2p), \
|
||||||
|
}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rockchip_cpuclk_rate_table rk3066_cpuclk_rates[] __initdata = {
|
||||||
|
RK3066_CPUCLK_RATE(1416000000, 2, 3, 1, 2, 1),
|
||||||
|
RK3066_CPUCLK_RATE(1200000000, 2, 3, 1, 2, 1),
|
||||||
|
RK3066_CPUCLK_RATE(1008000000, 2, 2, 1, 2, 1),
|
||||||
|
RK3066_CPUCLK_RATE( 816000000, 2, 2, 1, 2, 1),
|
||||||
|
RK3066_CPUCLK_RATE( 600000000, 1, 2, 1, 2, 1),
|
||||||
|
RK3066_CPUCLK_RATE( 504000000, 1, 1, 1, 2, 1),
|
||||||
|
RK3066_CPUCLK_RATE( 312000000, 0, 1, 1, 1, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rockchip_cpuclk_reg_data rk3066_cpuclk_data = {
|
||||||
|
.core_reg = RK2928_CLKSEL_CON(0),
|
||||||
|
.div_core_shift = 0,
|
||||||
|
.div_core_mask = 0x1f,
|
||||||
|
.mux_core_shift = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RK3188_DIV_ACLK_CORE_MASK 0x7
|
||||||
|
#define RK3188_DIV_ACLK_CORE_SHIFT 3
|
||||||
|
|
||||||
|
#define RK3188_CLKSEL1(_aclk_core) \
|
||||||
|
{ \
|
||||||
|
.reg = RK2928_CLKSEL_CON(1), \
|
||||||
|
.val = HIWORD_UPDATE(_aclk_core, RK3188_DIV_ACLK_CORE_MASK,\
|
||||||
|
RK3188_DIV_ACLK_CORE_SHIFT) \
|
||||||
|
}
|
||||||
|
#define RK3188_CPUCLK_RATE(_prate, _core_peri, _aclk_core) \
|
||||||
|
{ \
|
||||||
|
.prate = _prate, \
|
||||||
|
.divs = { \
|
||||||
|
RK3066_CLKSEL0(_core_peri), \
|
||||||
|
RK3188_CLKSEL1(_aclk_core), \
|
||||||
|
}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rockchip_cpuclk_rate_table rk3188_cpuclk_rates[] __initdata = {
|
||||||
|
RK3188_CPUCLK_RATE(1608000000, 2, 3),
|
||||||
|
RK3188_CPUCLK_RATE(1416000000, 2, 3),
|
||||||
|
RK3188_CPUCLK_RATE(1200000000, 2, 3),
|
||||||
|
RK3188_CPUCLK_RATE(1008000000, 2, 3),
|
||||||
|
RK3188_CPUCLK_RATE( 816000000, 2, 3),
|
||||||
|
RK3188_CPUCLK_RATE( 600000000, 1, 3),
|
||||||
|
RK3188_CPUCLK_RATE( 504000000, 1, 3),
|
||||||
|
RK3188_CPUCLK_RATE( 312000000, 0, 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rockchip_cpuclk_reg_data rk3188_cpuclk_data = {
|
||||||
|
.core_reg = RK2928_CLKSEL_CON(0),
|
||||||
|
.div_core_shift = 9,
|
||||||
|
.div_core_mask = 0x1f,
|
||||||
|
.mux_core_shift = 8,
|
||||||
|
};
|
||||||
|
|
||||||
PNAME(mux_pll_p) = { "xin24m", "xin32k" };
|
PNAME(mux_pll_p) = { "xin24m", "xin32k" };
|
||||||
PNAME(mux_armclk_p) = { "apll", "gpll_armclk" };
|
PNAME(mux_armclk_p) = { "apll", "gpll_armclk" };
|
||||||
PNAME(mux_ddrphy_p) = { "dpll", "gpll_ddr" };
|
PNAME(mux_ddrphy_p) = { "dpll", "gpll_ddr" };
|
||||||
|
@ -173,17 +266,10 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
|
||||||
GATE(0, "aclk_cpu", "aclk_cpu_pre", 0,
|
GATE(0, "aclk_cpu", "aclk_cpu_pre", 0,
|
||||||
RK2928_CLKGATE_CON(0), 3, GFLAGS),
|
RK2928_CLKGATE_CON(0), 3, GFLAGS),
|
||||||
|
|
||||||
DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0,
|
|
||||||
RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
|
|
||||||
GATE(0, "atclk_cpu", "pclk_cpu_pre", 0,
|
GATE(0, "atclk_cpu", "pclk_cpu_pre", 0,
|
||||||
RK2928_CLKGATE_CON(0), 6, GFLAGS),
|
RK2928_CLKGATE_CON(0), 6, GFLAGS),
|
||||||
GATE(0, "pclk_cpu", "pclk_cpu_pre", 0,
|
GATE(0, "pclk_cpu", "pclk_cpu_pre", 0,
|
||||||
RK2928_CLKGATE_CON(0), 5, GFLAGS),
|
RK2928_CLKGATE_CON(0), 5, GFLAGS),
|
||||||
DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0,
|
|
||||||
RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
|
|
||||||
COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0,
|
|
||||||
RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
|
|
||||||
RK2928_CLKGATE_CON(4), 9, GFLAGS),
|
|
||||||
GATE(0, "hclk_cpu", "hclk_cpu_pre", 0,
|
GATE(0, "hclk_cpu", "hclk_cpu_pre", 0,
|
||||||
RK2928_CLKGATE_CON(0), 4, GFLAGS),
|
RK2928_CLKGATE_CON(0), 4, GFLAGS),
|
||||||
|
|
||||||
|
@ -412,10 +498,18 @@ static struct clk_div_table div_aclk_cpu_t[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = {
|
static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = {
|
||||||
COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0,
|
|
||||||
RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 0, 5, DFLAGS),
|
|
||||||
DIVTBL(0, "aclk_cpu_pre", "armclk", 0,
|
DIVTBL(0, "aclk_cpu_pre", "armclk", 0,
|
||||||
RK2928_CLKSEL_CON(1), 0, 3, DFLAGS, div_aclk_cpu_t),
|
RK2928_CLKSEL_CON(1), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, div_aclk_cpu_t),
|
||||||
|
DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0,
|
||||||
|
RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO
|
||||||
|
| CLK_DIVIDER_READ_ONLY),
|
||||||
|
DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0,
|
||||||
|
RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO
|
||||||
|
| CLK_DIVIDER_READ_ONLY),
|
||||||
|
COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0,
|
||||||
|
RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO
|
||||||
|
| CLK_DIVIDER_READ_ONLY,
|
||||||
|
RK2928_CLKGATE_CON(4), 9, GFLAGS),
|
||||||
|
|
||||||
GATE(CORE_L2C, "core_l2c", "aclk_cpu", 0,
|
GATE(CORE_L2C, "core_l2c", "aclk_cpu", 0,
|
||||||
RK2928_CLKGATE_CON(9), 4, GFLAGS),
|
RK2928_CLKGATE_CON(9), 4, GFLAGS),
|
||||||
|
@ -524,8 +618,6 @@ PNAME(mux_hsicphy_p) = { "sclk_otgphy0", "sclk_otgphy1",
|
||||||
"gpll", "cpll" };
|
"gpll", "cpll" };
|
||||||
|
|
||||||
static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
|
static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
|
||||||
COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0,
|
|
||||||
RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 9, 5, DFLAGS),
|
|
||||||
COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", 0,
|
COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", 0,
|
||||||
RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
||||||
div_rk3188_aclk_core_t, RK2928_CLKGATE_CON(0), 7, GFLAGS),
|
div_rk3188_aclk_core_t, RK2928_CLKGATE_CON(0), 7, GFLAGS),
|
||||||
|
@ -533,6 +625,13 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
|
||||||
/* do not source aclk_cpu_pre from the apll, to keep complexity down */
|
/* do not source aclk_cpu_pre from the apll, to keep complexity down */
|
||||||
COMPOSITE_NOGATE(0, "aclk_cpu_pre", mux_aclk_cpu_p, CLK_SET_RATE_NO_REPARENT,
|
COMPOSITE_NOGATE(0, "aclk_cpu_pre", mux_aclk_cpu_p, CLK_SET_RATE_NO_REPARENT,
|
||||||
RK2928_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS),
|
RK2928_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS),
|
||||||
|
DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0,
|
||||||
|
RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
|
||||||
|
DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0,
|
||||||
|
RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
|
||||||
|
COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0,
|
||||||
|
RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
|
||||||
|
RK2928_CLKGATE_CON(4), 9, GFLAGS),
|
||||||
|
|
||||||
GATE(CORE_L2C, "core_l2c", "armclk", 0,
|
GATE(CORE_L2C, "core_l2c", "armclk", 0,
|
||||||
RK2928_CLKGATE_CON(9), 4, GFLAGS),
|
RK2928_CLKGATE_CON(9), 4, GFLAGS),
|
||||||
|
@ -629,9 +728,6 @@ static void __init rk3188_common_clk_init(struct device_node *np)
|
||||||
pr_warn("%s: could not register clock usb480m: %ld\n",
|
pr_warn("%s: could not register clock usb480m: %ld\n",
|
||||||
__func__, PTR_ERR(clk));
|
__func__, PTR_ERR(clk));
|
||||||
|
|
||||||
rockchip_clk_register_plls(rk3188_pll_clks,
|
|
||||||
ARRAY_SIZE(rk3188_pll_clks),
|
|
||||||
RK3188_GRF_SOC_STATUS);
|
|
||||||
rockchip_clk_register_branches(common_clk_branches,
|
rockchip_clk_register_branches(common_clk_branches,
|
||||||
ARRAY_SIZE(common_clk_branches));
|
ARRAY_SIZE(common_clk_branches));
|
||||||
rockchip_clk_protect_critical(rk3188_critical_clocks,
|
rockchip_clk_protect_critical(rk3188_critical_clocks,
|
||||||
|
@ -644,16 +740,51 @@ static void __init rk3188_common_clk_init(struct device_node *np)
|
||||||
static void __init rk3066a_clk_init(struct device_node *np)
|
static void __init rk3066a_clk_init(struct device_node *np)
|
||||||
{
|
{
|
||||||
rk3188_common_clk_init(np);
|
rk3188_common_clk_init(np);
|
||||||
|
rockchip_clk_register_plls(rk3188_pll_clks,
|
||||||
|
ARRAY_SIZE(rk3188_pll_clks),
|
||||||
|
RK3066_GRF_SOC_STATUS);
|
||||||
rockchip_clk_register_branches(rk3066a_clk_branches,
|
rockchip_clk_register_branches(rk3066a_clk_branches,
|
||||||
ARRAY_SIZE(rk3066a_clk_branches));
|
ARRAY_SIZE(rk3066a_clk_branches));
|
||||||
|
rockchip_clk_register_armclk(ARMCLK, "armclk",
|
||||||
|
mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
|
||||||
|
&rk3066_cpuclk_data, rk3066_cpuclk_rates,
|
||||||
|
ARRAY_SIZE(rk3066_cpuclk_rates));
|
||||||
}
|
}
|
||||||
CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);
|
CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);
|
||||||
|
|
||||||
static void __init rk3188a_clk_init(struct device_node *np)
|
static void __init rk3188a_clk_init(struct device_node *np)
|
||||||
{
|
{
|
||||||
|
struct clk *clk1, *clk2;
|
||||||
|
unsigned long rate;
|
||||||
|
int ret;
|
||||||
|
|
||||||
rk3188_common_clk_init(np);
|
rk3188_common_clk_init(np);
|
||||||
|
rockchip_clk_register_plls(rk3188_pll_clks,
|
||||||
|
ARRAY_SIZE(rk3188_pll_clks),
|
||||||
|
RK3188_GRF_SOC_STATUS);
|
||||||
rockchip_clk_register_branches(rk3188_clk_branches,
|
rockchip_clk_register_branches(rk3188_clk_branches,
|
||||||
ARRAY_SIZE(rk3188_clk_branches));
|
ARRAY_SIZE(rk3188_clk_branches));
|
||||||
|
rockchip_clk_register_armclk(ARMCLK, "armclk",
|
||||||
|
mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
|
||||||
|
&rk3188_cpuclk_data, rk3188_cpuclk_rates,
|
||||||
|
ARRAY_SIZE(rk3188_cpuclk_rates));
|
||||||
|
|
||||||
|
/* reparent aclk_cpu_pre from apll */
|
||||||
|
clk1 = __clk_lookup("aclk_cpu_pre");
|
||||||
|
clk2 = __clk_lookup("gpll");
|
||||||
|
if (clk1 && clk2) {
|
||||||
|
rate = clk_get_rate(clk1);
|
||||||
|
|
||||||
|
ret = clk_set_parent(clk1, clk2);
|
||||||
|
if (ret < 0)
|
||||||
|
pr_warn("%s: could not reparent aclk_cpu_pre to gpll\n",
|
||||||
|
__func__);
|
||||||
|
|
||||||
|
clk_set_rate(clk1, rate);
|
||||||
|
} else {
|
||||||
|
pr_warn("%s: missing clocks to reparent aclk_cpu_pre to gpll\n",
|
||||||
|
__func__);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);
|
CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "clk.h"
|
#include "clk.h"
|
||||||
|
|
||||||
#define RK3288_GRF_SOC_CON(x) (0x244 + x * 4)
|
#define RK3288_GRF_SOC_CON(x) (0x244 + x * 4)
|
||||||
#define RK3288_GRF_SOC_STATUS 0x280
|
#define RK3288_GRF_SOC_STATUS1 0x284
|
||||||
|
|
||||||
enum rk3288_plls {
|
enum rk3288_plls {
|
||||||
apll, dpll, cpll, gpll, npll,
|
apll, dpll, cpll, gpll, npll,
|
||||||
|
@ -101,6 +101,70 @@ struct rockchip_pll_rate_table rk3288_pll_rates[] = {
|
||||||
{ /* sentinel */ },
|
{ /* sentinel */ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define RK3288_DIV_ACLK_CORE_M0_MASK 0xf
|
||||||
|
#define RK3288_DIV_ACLK_CORE_M0_SHIFT 0
|
||||||
|
#define RK3288_DIV_ACLK_CORE_MP_MASK 0xf
|
||||||
|
#define RK3288_DIV_ACLK_CORE_MP_SHIFT 4
|
||||||
|
#define RK3288_DIV_L2RAM_MASK 0x7
|
||||||
|
#define RK3288_DIV_L2RAM_SHIFT 0
|
||||||
|
#define RK3288_DIV_ATCLK_MASK 0x1f
|
||||||
|
#define RK3288_DIV_ATCLK_SHIFT 4
|
||||||
|
#define RK3288_DIV_PCLK_DBGPRE_MASK 0x1f
|
||||||
|
#define RK3288_DIV_PCLK_DBGPRE_SHIFT 9
|
||||||
|
|
||||||
|
#define RK3288_CLKSEL0(_core_m0, _core_mp) \
|
||||||
|
{ \
|
||||||
|
.reg = RK3288_CLKSEL_CON(0), \
|
||||||
|
.val = HIWORD_UPDATE(_core_m0, RK3288_DIV_ACLK_CORE_M0_MASK, \
|
||||||
|
RK3288_DIV_ACLK_CORE_M0_SHIFT) | \
|
||||||
|
HIWORD_UPDATE(_core_mp, RK3288_DIV_ACLK_CORE_MP_MASK, \
|
||||||
|
RK3288_DIV_ACLK_CORE_MP_SHIFT), \
|
||||||
|
}
|
||||||
|
#define RK3288_CLKSEL37(_l2ram, _atclk, _pclk_dbg_pre) \
|
||||||
|
{ \
|
||||||
|
.reg = RK3288_CLKSEL_CON(37), \
|
||||||
|
.val = HIWORD_UPDATE(_l2ram, RK3288_DIV_L2RAM_MASK, \
|
||||||
|
RK3288_DIV_L2RAM_SHIFT) | \
|
||||||
|
HIWORD_UPDATE(_atclk, RK3288_DIV_ATCLK_MASK, \
|
||||||
|
RK3288_DIV_ATCLK_SHIFT) | \
|
||||||
|
HIWORD_UPDATE(_pclk_dbg_pre, \
|
||||||
|
RK3288_DIV_PCLK_DBGPRE_MASK, \
|
||||||
|
RK3288_DIV_PCLK_DBGPRE_SHIFT), \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RK3288_CPUCLK_RATE(_prate, _core_m0, _core_mp, _l2ram, _atclk, _pdbg) \
|
||||||
|
{ \
|
||||||
|
.prate = _prate, \
|
||||||
|
.divs = { \
|
||||||
|
RK3288_CLKSEL0(_core_m0, _core_mp), \
|
||||||
|
RK3288_CLKSEL37(_l2ram, _atclk, _pdbg), \
|
||||||
|
}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rockchip_cpuclk_rate_table rk3288_cpuclk_rates[] __initdata = {
|
||||||
|
RK3288_CPUCLK_RATE(1800000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE(1704000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE(1608000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE(1512000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE(1416000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE(1200000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE(1008000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE( 816000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE( 696000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE( 600000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE( 408000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE( 312000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE( 216000000, 2, 4, 2, 4, 4),
|
||||||
|
RK3288_CPUCLK_RATE( 126000000, 2, 4, 2, 4, 4),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rockchip_cpuclk_reg_data rk3288_cpuclk_data = {
|
||||||
|
.core_reg = RK3288_CLKSEL_CON(0),
|
||||||
|
.div_core_shift = 8,
|
||||||
|
.div_core_mask = 0x1f,
|
||||||
|
.mux_core_shift = 15,
|
||||||
|
};
|
||||||
|
|
||||||
PNAME(mux_pll_p) = { "xin24m", "xin32k" };
|
PNAME(mux_pll_p) = { "xin24m", "xin32k" };
|
||||||
PNAME(mux_armclk_p) = { "apll_core", "gpll_core" };
|
PNAME(mux_armclk_p) = { "apll_core", "gpll_core" };
|
||||||
PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" };
|
PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" };
|
||||||
|
@ -166,35 +230,33 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
|
||||||
RK3288_CLKGATE_CON(0), 1, GFLAGS),
|
RK3288_CLKGATE_CON(0), 1, GFLAGS),
|
||||||
GATE(0, "gpll_core", "gpll", 0,
|
GATE(0, "gpll_core", "gpll", 0,
|
||||||
RK3288_CLKGATE_CON(0), 2, GFLAGS),
|
RK3288_CLKGATE_CON(0), 2, GFLAGS),
|
||||||
COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0,
|
|
||||||
RK3288_CLKSEL_CON(0), 15, 1, MFLAGS, 8, 5, DFLAGS),
|
|
||||||
|
|
||||||
COMPOSITE_NOMUX(0, "armcore0", "armclk", 0,
|
COMPOSITE_NOMUX(0, "armcore0", "armclk", 0,
|
||||||
RK3288_CLKSEL_CON(36), 0, 3, DFLAGS,
|
RK3288_CLKSEL_CON(36), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
||||||
RK3288_CLKGATE_CON(12), 0, GFLAGS),
|
RK3288_CLKGATE_CON(12), 0, GFLAGS),
|
||||||
COMPOSITE_NOMUX(0, "armcore1", "armclk", 0,
|
COMPOSITE_NOMUX(0, "armcore1", "armclk", 0,
|
||||||
RK3288_CLKSEL_CON(36), 4, 3, DFLAGS,
|
RK3288_CLKSEL_CON(36), 4, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
||||||
RK3288_CLKGATE_CON(12), 1, GFLAGS),
|
RK3288_CLKGATE_CON(12), 1, GFLAGS),
|
||||||
COMPOSITE_NOMUX(0, "armcore2", "armclk", 0,
|
COMPOSITE_NOMUX(0, "armcore2", "armclk", 0,
|
||||||
RK3288_CLKSEL_CON(36), 8, 3, DFLAGS,
|
RK3288_CLKSEL_CON(36), 8, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
||||||
RK3288_CLKGATE_CON(12), 2, GFLAGS),
|
RK3288_CLKGATE_CON(12), 2, GFLAGS),
|
||||||
COMPOSITE_NOMUX(0, "armcore3", "armclk", 0,
|
COMPOSITE_NOMUX(0, "armcore3", "armclk", 0,
|
||||||
RK3288_CLKSEL_CON(36), 12, 3, DFLAGS,
|
RK3288_CLKSEL_CON(36), 12, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
||||||
RK3288_CLKGATE_CON(12), 3, GFLAGS),
|
RK3288_CLKGATE_CON(12), 3, GFLAGS),
|
||||||
COMPOSITE_NOMUX(0, "l2ram", "armclk", 0,
|
COMPOSITE_NOMUX(0, "l2ram", "armclk", 0,
|
||||||
RK3288_CLKSEL_CON(37), 0, 3, DFLAGS,
|
RK3288_CLKSEL_CON(37), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
||||||
RK3288_CLKGATE_CON(12), 4, GFLAGS),
|
RK3288_CLKGATE_CON(12), 4, GFLAGS),
|
||||||
COMPOSITE_NOMUX(0, "aclk_core_m0", "armclk", 0,
|
COMPOSITE_NOMUX(0, "aclk_core_m0", "armclk", 0,
|
||||||
RK3288_CLKSEL_CON(0), 0, 4, DFLAGS,
|
RK3288_CLKSEL_CON(0), 0, 4, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
||||||
RK3288_CLKGATE_CON(12), 5, GFLAGS),
|
RK3288_CLKGATE_CON(12), 5, GFLAGS),
|
||||||
COMPOSITE_NOMUX(0, "aclk_core_mp", "armclk", 0,
|
COMPOSITE_NOMUX(0, "aclk_core_mp", "armclk", 0,
|
||||||
RK3288_CLKSEL_CON(0), 4, 4, DFLAGS,
|
RK3288_CLKSEL_CON(0), 4, 4, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
||||||
RK3288_CLKGATE_CON(12), 6, GFLAGS),
|
RK3288_CLKGATE_CON(12), 6, GFLAGS),
|
||||||
COMPOSITE_NOMUX(0, "atclk", "armclk", 0,
|
COMPOSITE_NOMUX(0, "atclk", "armclk", 0,
|
||||||
RK3288_CLKSEL_CON(37), 4, 5, DFLAGS,
|
RK3288_CLKSEL_CON(37), 4, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
||||||
RK3288_CLKGATE_CON(12), 7, GFLAGS),
|
RK3288_CLKGATE_CON(12), 7, GFLAGS),
|
||||||
COMPOSITE_NOMUX(0, "pclk_dbg_pre", "armclk", 0,
|
COMPOSITE_NOMUX(0, "pclk_dbg_pre", "armclk", 0,
|
||||||
RK3288_CLKSEL_CON(37), 9, 5, DFLAGS,
|
RK3288_CLKSEL_CON(37), 9, 5, DFLAGS | CLK_DIVIDER_READ_ONLY,
|
||||||
RK3288_CLKGATE_CON(12), 8, GFLAGS),
|
RK3288_CLKGATE_CON(12), 8, GFLAGS),
|
||||||
GATE(0, "pclk_dbg", "pclk_dbg_pre", 0,
|
GATE(0, "pclk_dbg", "pclk_dbg_pre", 0,
|
||||||
RK3288_CLKGATE_CON(12), 9, GFLAGS),
|
RK3288_CLKGATE_CON(12), 9, GFLAGS),
|
||||||
|
@ -733,12 +795,17 @@ static void __init rk3288_clk_init(struct device_node *np)
|
||||||
|
|
||||||
rockchip_clk_register_plls(rk3288_pll_clks,
|
rockchip_clk_register_plls(rk3288_pll_clks,
|
||||||
ARRAY_SIZE(rk3288_pll_clks),
|
ARRAY_SIZE(rk3288_pll_clks),
|
||||||
RK3288_GRF_SOC_STATUS);
|
RK3288_GRF_SOC_STATUS1);
|
||||||
rockchip_clk_register_branches(rk3288_clk_branches,
|
rockchip_clk_register_branches(rk3288_clk_branches,
|
||||||
ARRAY_SIZE(rk3288_clk_branches));
|
ARRAY_SIZE(rk3288_clk_branches));
|
||||||
rockchip_clk_protect_critical(rk3288_critical_clocks,
|
rockchip_clk_protect_critical(rk3288_critical_clocks,
|
||||||
ARRAY_SIZE(rk3288_critical_clocks));
|
ARRAY_SIZE(rk3288_critical_clocks));
|
||||||
|
|
||||||
|
rockchip_clk_register_armclk(ARMCLK, "armclk",
|
||||||
|
mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
|
||||||
|
&rk3288_cpuclk_data, rk3288_cpuclk_rates,
|
||||||
|
ARRAY_SIZE(rk3288_cpuclk_rates));
|
||||||
|
|
||||||
rockchip_register_softrst(np, 12, reg_base + RK3288_SOFTRST_CON(0),
|
rockchip_register_softrst(np, 12, reg_base + RK3288_SOFTRST_CON(0),
|
||||||
ROCKCHIP_SOFTRST_HIWORD_MASK);
|
ROCKCHIP_SOFTRST_HIWORD_MASK);
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,6 +297,27 @@ void __init rockchip_clk_register_branches(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __init rockchip_clk_register_armclk(unsigned int lookup_id,
|
||||||
|
const char *name, const char **parent_names,
|
||||||
|
u8 num_parents,
|
||||||
|
const struct rockchip_cpuclk_reg_data *reg_data,
|
||||||
|
const struct rockchip_cpuclk_rate_table *rates,
|
||||||
|
int nrates)
|
||||||
|
{
|
||||||
|
struct clk *clk;
|
||||||
|
|
||||||
|
clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
|
||||||
|
reg_data, rates, nrates, reg_base,
|
||||||
|
&clk_lock);
|
||||||
|
if (IS_ERR(clk)) {
|
||||||
|
pr_err("%s: failed to register clock %s: %ld\n",
|
||||||
|
__func__, name, PTR_ERR(clk));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rockchip_clk_add_lookup(clk, lookup_id);
|
||||||
|
}
|
||||||
|
|
||||||
void __init rockchip_clk_protect_critical(const char *clocks[], int nclocks)
|
void __init rockchip_clk_protect_critical(const char *clocks[], int nclocks)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
|
@ -120,6 +120,38 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
|
||||||
struct rockchip_pll_rate_table *rate_table,
|
struct rockchip_pll_rate_table *rate_table,
|
||||||
spinlock_t *lock);
|
spinlock_t *lock);
|
||||||
|
|
||||||
|
struct rockchip_cpuclk_clksel {
|
||||||
|
int reg;
|
||||||
|
u32 val;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ROCKCHIP_CPUCLK_NUM_DIVIDERS 2
|
||||||
|
struct rockchip_cpuclk_rate_table {
|
||||||
|
unsigned long prate;
|
||||||
|
struct rockchip_cpuclk_clksel divs[ROCKCHIP_CPUCLK_NUM_DIVIDERS];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rockchip_cpuclk_reg_data: describes register offsets and masks of the cpuclock
|
||||||
|
* @core_reg: register offset of the core settings register
|
||||||
|
* @div_core_shift: core divider offset used to divide the pll value
|
||||||
|
* @div_core_mask: core divider mask
|
||||||
|
* @mux_core_shift: offset of the core multiplexer
|
||||||
|
*/
|
||||||
|
struct rockchip_cpuclk_reg_data {
|
||||||
|
int core_reg;
|
||||||
|
u8 div_core_shift;
|
||||||
|
u32 div_core_mask;
|
||||||
|
int mux_core_reg;
|
||||||
|
u8 mux_core_shift;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct clk *rockchip_clk_register_cpuclk(const char *name,
|
||||||
|
const char **parent_names, u8 num_parents,
|
||||||
|
const struct rockchip_cpuclk_reg_data *reg_data,
|
||||||
|
const struct rockchip_cpuclk_rate_table *rates,
|
||||||
|
int nrates, void __iomem *reg_base, spinlock_t *lock);
|
||||||
|
|
||||||
#define PNAME(x) static const char *x[] __initconst
|
#define PNAME(x) static const char *x[] __initconst
|
||||||
|
|
||||||
enum rockchip_clk_branch_type {
|
enum rockchip_clk_branch_type {
|
||||||
|
@ -329,6 +361,11 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list,
|
||||||
unsigned int nr_clk);
|
unsigned int nr_clk);
|
||||||
void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list,
|
void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list,
|
||||||
unsigned int nr_pll, int grf_lock_offset);
|
unsigned int nr_pll, int grf_lock_offset);
|
||||||
|
void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name,
|
||||||
|
const char **parent_names, u8 num_parents,
|
||||||
|
const struct rockchip_cpuclk_reg_data *reg_data,
|
||||||
|
const struct rockchip_cpuclk_rate_table *rates,
|
||||||
|
int nrates);
|
||||||
void rockchip_clk_protect_critical(const char *clocks[], int nclocks);
|
void rockchip_clk_protect_critical(const char *clocks[], int nclocks);
|
||||||
|
|
||||||
#define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0)
|
#define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0)
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#define PLL_GPLL 4
|
#define PLL_GPLL 4
|
||||||
#define CORE_PERI 5
|
#define CORE_PERI 5
|
||||||
#define CORE_L2C 6
|
#define CORE_L2C 6
|
||||||
|
#define ARMCLK 7
|
||||||
|
|
||||||
/* sclk gates (special clocks) */
|
/* sclk gates (special clocks) */
|
||||||
#define SCLK_UART0 64
|
#define SCLK_UART0 64
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#define PLL_CPLL 3
|
#define PLL_CPLL 3
|
||||||
#define PLL_GPLL 4
|
#define PLL_GPLL 4
|
||||||
#define PLL_NPLL 5
|
#define PLL_NPLL 5
|
||||||
|
#define ARMCLK 6
|
||||||
|
|
||||||
/* sclk gates (special clocks) */
|
/* sclk gates (special clocks) */
|
||||||
#define SCLK_GPU 64
|
#define SCLK_GPU 64
|
||||||
|
|
Loading…
Reference in a new issue