diff options
Diffstat (limited to 'drivers/clk/rockchip/clk-cpu.c')
-rw-r--r-- | drivers/clk/rockchip/clk-cpu.c | 87 |
1 files changed, 50 insertions, 37 deletions
diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 226b819242..8b5d4a0330 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 MundoReader S.L. * Author: Heiko Stuebner <heiko@sntech.de> @@ -6,10 +7,6 @@ * 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 @@ -37,8 +34,10 @@ #include <malloc.h> #include <io.h> #include <xfuncs.h> -#include "clk.h" #include <linux/barebox-wrapper.h> +#include <linux/clk.h> +#include <linux/spinlock.h> +#include "clk.h" /** * struct rockchip_cpuclk: information about clock supplied to a CPU core. @@ -46,31 +45,34 @@ * @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; - + struct clk_hw hw; struct clk *alt_parent; void __iomem *reg_base; 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) -static unsigned long rockchip_cpuclk_recalc_rate(struct clk *hw, +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(cpuclk->reg_base + reg_data->core_reg); + u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg[0]); - clksel0 >>= reg_data->div_core_shift; - clksel0 &= reg_data->div_core_mask; + clksel0 >>= reg_data->div_core_shift[0]; + clksel0 &= reg_data->div_core_mask[0]; return parent_rate / (clksel0 + 1); } @@ -79,17 +81,18 @@ static const struct clk_ops rockchip_cpuclk_ops = { }; struct clk *rockchip_clk_register_cpuclk(const char *name, - const char **parent_names, u8 num_parents, + const char *const *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) + int nrates, void __iomem *reg_base, spinlock_t *lock) { struct rockchip_cpuclk *cpuclk; - struct clk *clk; + struct clk_init_data init; + struct clk *clk, *cclk; int ret; - if (num_parents != 2) { - pr_err("%s: needs two parent clocks\n", __func__); + if (num_parents < 2) { + pr_err("%s: needs at least two parent clocks\n", __func__); return ERR_PTR(-EINVAL); } @@ -97,21 +100,28 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, if (!cpuclk) return ERR_PTR(-ENOMEM); - cpuclk->hw.name = name; - cpuclk->hw.parent_names = &parent_names[0]; - cpuclk->hw.num_parents = 1; - cpuclk->hw.ops = &rockchip_cpuclk_ops; + init.name = name; + init.parent_names = &parent_names[reg_data->mux_core_main]; + init.num_parents = 1; + init.ops = &rockchip_cpuclk_ops; /* only allow rate changes when we have a rate table */ - cpuclk->hw.flags = (nrates > 0) ? CLK_SET_RATE_PARENT : 0; + 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->hw.init = &init; - cpuclk->alt_parent = __clk_lookup(parent_names[1]); + cpuclk->alt_parent = __clk_lookup(parent_names[reg_data->mux_core_alt]); if (!cpuclk->alt_parent) { - pr_err("%s: could not lookup alternate parent\n", - __func__); + pr_err("%s: could not lookup alternate parent: (%d)\n", + __func__, reg_data->mux_core_alt); ret = -EINVAL; goto free_cpuclk; } @@ -123,37 +133,40 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, goto free_cpuclk; } - clk = __clk_lookup(parent_names[0]); + clk = __clk_lookup(parent_names[reg_data->mux_core_main]); if (!clk) { - pr_err("%s: could not lookup parent clock %s\n", - __func__, parent_names[0]); + pr_err("%s: could not lookup parent clock: (%d) %s\n", + __func__, reg_data->mux_core_main, + parent_names[reg_data->mux_core_main]); ret = -EINVAL; - goto free_cpuclk; + goto free_alt_parent; } if (nrates > 0) { cpuclk->rate_count = nrates; - cpuclk->rate_table = xmemdup(rates, - sizeof(*rates) * 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 free_cpuclk; + goto unregister_notifier; } } - ret = clk_register(&cpuclk->hw); - if (ret) { + cclk = clk_register(NULL, &cpuclk->hw); + if (IS_ERR(cclk)) { pr_err("%s: could not register cpuclk %s\n", __func__, name); + ret = PTR_ERR(cclk); goto free_rate_table; } - return &cpuclk->hw; + return cclk; free_rate_table: kfree(cpuclk->rate_table); +unregister_notifier: +free_alt_parent: + clk_disable(cpuclk->alt_parent); free_cpuclk: kfree(cpuclk); return ERR_PTR(ret); |