clk: rockchip: Implement rockchip_clk_register_armclk_v2()

The clock path of CPU may be simplified as follows:

  --gpll--|--\
          |   \
          |    \
          |     \
 --v0pll--| mux |--[gate]--[div]--clk_core--
          |     /
          |    /
 --v1pll--|   /
          |--/

Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
Change-Id: Ia8b56830a77130e12454dcff9a5de83a5c868fd9
This commit is contained in:
Finley Xiao
2023-05-26 14:40:00 +08:00
committed by Tao Huang
parent 3868949da6
commit 643ea50b93
3 changed files with 204 additions and 0 deletions
+165
View File
@@ -430,3 +430,168 @@ free_cpuclk:
kfree(cpuclk);
return ERR_PTR(ret);
}
static int rockchip_cpuclk_v2_pre_rate_change(struct rockchip_cpuclk *cpuclk,
struct clk_notifier_data *ndata)
{
unsigned long new_rate = roundup(ndata->new_rate, 1000);
const struct rockchip_cpuclk_rate_table *rate;
unsigned long flags;
rate = rockchip_get_cpuclk_settings(cpuclk, new_rate);
if (!rate) {
pr_err("%s: Invalid rate : %lu for cpuclk\n",
__func__, new_rate);
return -EINVAL;
}
if (new_rate > ndata->old_rate) {
spin_lock_irqsave(cpuclk->lock, flags);
rockchip_cpuclk_set_dividers(cpuclk, rate);
spin_unlock_irqrestore(cpuclk->lock, flags);
}
return 0;
}
static int rockchip_cpuclk_v2_post_rate_change(struct rockchip_cpuclk *cpuclk,
struct clk_notifier_data *ndata)
{
unsigned long new_rate = roundup(ndata->new_rate, 1000);
const struct rockchip_cpuclk_rate_table *rate;
unsigned long flags;
rate = rockchip_get_cpuclk_settings(cpuclk, new_rate);
if (!rate) {
pr_err("%s: Invalid rate : %lu for cpuclk\n",
__func__, new_rate);
return -EINVAL;
}
if (new_rate < ndata->old_rate) {
spin_lock_irqsave(cpuclk->lock, flags);
rockchip_cpuclk_set_dividers(cpuclk, rate);
spin_unlock_irqrestore(cpuclk->lock, flags);
}
return 0;
}
static int rockchip_cpuclk_v2_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_v2_pre_rate_change(cpuclk, ndata);
else if (event == POST_RATE_CHANGE)
ret = rockchip_cpuclk_v2_post_rate_change(cpuclk, ndata);
return notifier_from_errno(ret);
}
struct clk *rockchip_clk_register_cpuclk_v2(const char *name,
const char *const *parent_names,
u8 num_parents, void __iomem *base,
int muxdiv_offset, u8 mux_shift,
u8 mux_width, u8 mux_flags,
int div_offset, u8 div_shift,
u8 div_width, u8 div_flags,
unsigned long flags, spinlock_t *lock,
const struct rockchip_cpuclk_rate_table *rates,
int nrates)
{
struct rockchip_cpuclk *cpuclk;
struct clk_hw *hw;
struct clk_mux *mux = NULL;
struct clk_divider *div = NULL;
const struct clk_ops *mux_ops = NULL, *div_ops = NULL;
int ret;
if (num_parents > 1) {
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);
mux->reg = base + muxdiv_offset;
mux->shift = mux_shift;
mux->mask = BIT(mux_width) - 1;
mux->flags = mux_flags;
mux->lock = lock;
mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
: &clk_mux_ops;
}
if (div_width > 0) {
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div) {
ret = -ENOMEM;
goto free_mux;
}
div->flags = div_flags;
if (div_offset)
div->reg = base + div_offset;
else
div->reg = base + muxdiv_offset;
div->shift = div_shift;
div->width = div_width;
div->lock = lock;
div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
? &clk_divider_ro_ops
: &clk_divider_ops;
}
hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
mux ? &mux->hw : NULL, mux_ops,
div ? &div->hw : NULL, div_ops,
NULL, NULL, flags);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto free_div;
}
cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
if (!cpuclk) {
ret = -ENOMEM;
goto unregister_clk;
}
cpuclk->reg_base = base;
cpuclk->lock = lock;
cpuclk->clk_nb.notifier_call = rockchip_cpuclk_v2_notifier_cb;
ret = clk_notifier_register(hw->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) {
ret = -ENOMEM;
goto free_cpuclk;
}
}
return hw->clk;
free_cpuclk:
kfree(cpuclk);
unregister_clk:
clk_hw_unregister_composite(hw);
free_div:
kfree(div);
free_mux:
kfree(mux);
return ERR_PTR(ret);
}
+24
View File
@@ -656,6 +656,30 @@ void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk);
void rockchip_clk_register_armclk_v2(struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *list,
const struct rockchip_cpuclk_rate_table *rates,
int nrates)
{
struct clk *clk;
clk = rockchip_clk_register_cpuclk_v2(list->name, list->parent_names,
list->num_parents, ctx->reg_base,
list->muxdiv_offset, list->mux_shift,
list->mux_width, list->mux_flags,
list->div_offset, list->div_shift,
list->div_width, list->div_flags,
list->flags, &ctx->lock, rates, nrates);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s: %ld\n",
__func__, list->name, PTR_ERR(clk));
return;
}
rockchip_clk_add_lookup(ctx, clk, list->id);
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk_v2);
void (*rk_dump_cru)(void);
EXPORT_SYMBOL(rk_dump_cru);
+15
View File
@@ -659,6 +659,17 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
const struct rockchip_cpuclk_rate_table *rates,
int nrates, void __iomem *reg_base, spinlock_t *lock);
struct clk *rockchip_clk_register_cpuclk_v2(const char *name,
const char *const *parent_names,
u8 num_parents, void __iomem *base,
int muxdiv_offset, u8 mux_shift,
u8 mux_width, u8 mux_flags,
int div_offset, u8 div_shift,
u8 div_width, u8 div_flags,
unsigned long flags, spinlock_t *lock,
const struct rockchip_cpuclk_rate_table *rates,
int nrates);
struct clk *rockchip_clk_register_mmc(const char *name,
const char *const *parent_names, u8 num_parents,
void __iomem *reg, int shift);
@@ -1262,6 +1273,10 @@ void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
const struct rockchip_cpuclk_reg_data *reg_data,
const struct rockchip_cpuclk_rate_table *rates,
int nrates);
void rockchip_clk_register_armclk_v2(struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *list,
const struct rockchip_cpuclk_rate_table *rates,
int nrates);
int rockchip_pll_clk_rate_to_scale(struct clk *clk, unsigned long rate);
int rockchip_pll_clk_scale_to_rate(struct clk *clk, unsigned int scale);
int rockchip_pll_clk_adaptive_scaling(struct clk *clk, int sel);