summaryrefslogtreecommitdiffstats
path: root/drivers/clk/rockchip/clk-cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/rockchip/clk-cpu.c')
-rw-r--r--drivers/clk/rockchip/clk-cpu.c87
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);