summaryrefslogtreecommitdiff
path: root/drivers/clk/rockchip/clk.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-10-15 07:05:03 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-15 07:05:03 +0200
commitc0fa2373f8cfed90437d8d7b17e0b1a84009a10a (patch)
tree43fb2edd0c11874d0b2e56714e53894d10321e19 /drivers/clk/rockchip/clk.c
parentfcc3a5d277571bc6048e7b4ef8cd391b935de629 (diff)
parent98d147f50eb0ce4328e013f5f2c076896003c761 (diff)
Merge tag 'clk-for-linus-3.18' of git://git.linaro.org/people/mike.turquette/linux
Pull clock tree updates from Mike Turquette: "The clk tree changes for 3.18 are dominated by clock drivers. Mostly fixes and enhancements to existing drivers as well as new drivers. This tag contains a bit more arch code than I usually take due to some OMAP2+ changes. Additionally it contains the restart notifier handlers which are merged as a dependency into several trees. The PXA changes are the only messy part. Due to having a stable tree I had to revert one patch and follow up with one more fix near the tip of this tag. Some dead code is introduced but it will soon become live code after 3.18-rc1 is released as the rest of the PXA family is converted over to the common clock framework. Another trend in this tag is that multiple vendors have started to push the complexity of changing their CPU frequency into the clock driver, whereas this used to be done in CPUfreq drivers. Changes to the clk core include a generic gpio-clock type and a clk_set_phase() function added to the top-level clk.h api. Due to some confusion on the fbdev mailing list the kernel boot parameters documentation was updated to further explain the clk_ignore_unused parameter, which is often required by users of the simplefb driver. Finally some fixes to the locking around the clock debugfs stuff was done to prevent deadlocks when interacting with other subsystems." * tag 'clk-for-linus-3.18' of git://git.linaro.org/people/mike.turquette/linux: (99 commits) clk: pxa clocks build system fix Revert "arm: pxa: Transition pxa27x to clk framework" clk: samsung: register restart handlers for s3c2412 and s3c2443 clk: rockchip: add restart handler clk: rockchip: rk3288: i2s_frac adds flag to set parent's rate doc/kernel-parameters.txt: clarify clk_ignore_unused arm: pxa: Transition pxa27x to clk framework dts: add devicetree bindings for pxa27x clocks clk: add pxa27x clock drivers arm: pxa: add clock pll selection bits clk: dts: document pxa clock binding clk: add pxa clocks infrastructure clk: gpio-gate: Ensure gpiod_ APIs are prototyped clk: ti: dra7-atl-clock: Mark the device as pm_runtime_irq_safe clk: ti: LLVMLinux: Move __init outside of type definition clk: ti: consider the fact that of_clk_get() might return an error clk: ti: dra7-atl-clock: fix a memory leak clk: ti: change clock init to use generic of_clk_init clk: hix5hd2: add I2C clocks clk: hix5hd2: add watchdog0 clocks ...
Diffstat (limited to 'drivers/clk/rockchip/clk.c')
-rw-r--r--drivers/clk/rockchip/clk.c119
1 files changed, 116 insertions, 3 deletions
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 278cf9dd1e23..1e68bff481b8 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -25,6 +25,7 @@
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
+#include <linux/reboot.h>
#include "clk.h"
/**
@@ -37,7 +38,7 @@
*
* sometimes without one of those components.
*/
-struct clk *rockchip_clk_register_branch(const char *name,
+static struct clk *rockchip_clk_register_branch(const char *name,
const char **parent_names, u8 num_parents, void __iomem *base,
int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
u8 div_shift, u8 div_width, u8 div_flags,
@@ -103,6 +104,54 @@ struct clk *rockchip_clk_register_branch(const char *name,
return clk;
}
+static struct clk *rockchip_clk_register_frac_branch(const char *name,
+ const char **parent_names, u8 num_parents, void __iomem *base,
+ int muxdiv_offset, u8 div_flags,
+ int gate_offset, u8 gate_shift, u8 gate_flags,
+ unsigned long flags, spinlock_t *lock)
+{
+ struct clk *clk;
+ struct clk_gate *gate = NULL;
+ struct clk_fractional_divider *div = NULL;
+ const struct clk_ops *div_ops = NULL, *gate_ops = NULL;
+
+ if (gate_offset >= 0) {
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->flags = gate_flags;
+ gate->reg = base + gate_offset;
+ gate->bit_idx = gate_shift;
+ gate->lock = lock;
+ gate_ops = &clk_gate_ops;
+ }
+
+ if (muxdiv_offset < 0)
+ return ERR_PTR(-EINVAL);
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ div->flags = div_flags;
+ div->reg = base + muxdiv_offset;
+ div->mshift = 16;
+ div->mmask = 0xffff0000;
+ div->nshift = 0;
+ div->nmask = 0xffff;
+ div->lock = lock;
+ div_ops = &clk_fractional_divider_ops;
+
+ clk = clk_register_composite(NULL, name, parent_names, num_parents,
+ NULL, NULL,
+ &div->hw, div_ops,
+ gate ? &gate->hw : NULL, gate_ops,
+ flags);
+
+ return clk;
+}
+
static DEFINE_SPINLOCK(clk_lock);
static struct clk **clk_table;
static void __iomem *reg_base;
@@ -197,8 +246,14 @@ void __init rockchip_clk_register_branches(
list->div_flags, &clk_lock);
break;
case branch_fraction_divider:
- /* unimplemented */
- continue;
+ /* keep all gates untouched for now */
+ flags |= CLK_IGNORE_UNUSED;
+
+ clk = rockchip_clk_register_frac_branch(list->name,
+ list->parent_names, list->num_parents,
+ reg_base, list->muxdiv_offset, list->div_flags,
+ list->gate_offset, list->gate_shift,
+ list->gate_flags, flags, &clk_lock);
break;
case branch_gate:
flags |= CLK_SET_RATE_PARENT;
@@ -242,3 +297,61 @@ void __init rockchip_clk_register_branches(
rockchip_clk_add_lookup(clk, list->id);
}
}
+
+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)
+{
+ int i;
+
+ /* Protect the clocks that needs to stay on */
+ for (i = 0; i < nclocks; i++) {
+ struct clk *clk = __clk_lookup(clocks[i]);
+
+ if (clk)
+ clk_prepare_enable(clk);
+ }
+}
+
+static unsigned int reg_restart;
+static int rockchip_restart_notify(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ writel(0xfdb9, reg_base + reg_restart);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block rockchip_restart_handler = {
+ .notifier_call = rockchip_restart_notify,
+ .priority = 128,
+};
+
+void __init rockchip_register_restart_notifier(unsigned int reg)
+{
+ int ret;
+
+ reg_restart = reg;
+ ret = register_restart_handler(&rockchip_restart_handler);
+ if (ret)
+ pr_err("%s: cannot register restart handler, %d\n",
+ __func__, ret);
+}