2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * Copyright (C) 2013 ARM Limited
14 #include <linux/amba/sp810.h>
15 #include <linux/slab.h>
16 #include <linux/clk.h>
17 #include <linux/clk-provider.h>
18 #include <linux/err.h>
20 #include <linux/of_address.h>
22 #define to_clk_sp810_timerclken(_hw) \
23 container_of(_hw, struct clk_sp810_timerclken, hw)
27 struct clk_sp810_timerclken {
30 struct clk_sp810 *sp810;
35 struct device_node *node;
36 int refclk_index, timclk_index;
39 struct clk_sp810_timerclken timerclken[4];
44 static u8 clk_sp810_timerclken_get_parent(struct clk_hw *hw)
46 struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
47 u32 val = readl(timerclken->sp810->base + SCCTRL);
49 return !!(val & (1 << SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel)));
52 static int clk_sp810_timerclken_set_parent(struct clk_hw *hw, u8 index)
54 struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
55 struct clk_sp810 *sp810 = timerclken->sp810;
56 u32 val, shift = SCCTRL_TIMERENnSEL_SHIFT(timerclken->channel);
57 unsigned long flags = 0;
59 if (WARN_ON(index > 1))
62 spin_lock_irqsave(&sp810->lock, flags);
64 val = readl(sp810->base + SCCTRL);
66 val |= index << shift;
67 writel(val, sp810->base + SCCTRL);
69 spin_unlock_irqrestore(&sp810->lock, flags);
75 * FIXME - setting the parent every time .prepare is invoked is inefficient.
76 * This is better handled by a dedicated clock tree configuration mechanism at
77 * init-time. Revisit this later when such a mechanism exists
79 static int clk_sp810_timerclken_prepare(struct clk_hw *hw)
81 struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
82 struct clk_sp810 *sp810 = timerclken->sp810;
83 struct clk *old_parent = __clk_get_parent(hw->clk);
84 struct clk *new_parent;
87 sp810->refclk = of_clk_get(sp810->node, sp810->refclk_index);
90 sp810->timclk = of_clk_get(sp810->node, sp810->timclk_index);
92 if (WARN_ON(IS_ERR(sp810->refclk) || IS_ERR(sp810->timclk)))
95 /* Select fastest parent */
96 if (clk_get_rate(sp810->refclk) > clk_get_rate(sp810->timclk))
97 new_parent = sp810->refclk;
99 new_parent = sp810->timclk;
101 /* Switch the parent if necessary */
102 if (old_parent != new_parent) {
103 clk_prepare(new_parent);
104 clk_set_parent(hw->clk, new_parent);
105 clk_unprepare(old_parent);
111 static void clk_sp810_timerclken_unprepare(struct clk_hw *hw)
113 struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw);
114 struct clk_sp810 *sp810 = timerclken->sp810;
116 clk_put(sp810->timclk);
117 clk_put(sp810->refclk);
120 static const struct clk_ops clk_sp810_timerclken_ops = {
121 .prepare = clk_sp810_timerclken_prepare,
122 .unprepare = clk_sp810_timerclken_unprepare,
123 .get_parent = clk_sp810_timerclken_get_parent,
124 .set_parent = clk_sp810_timerclken_set_parent,
127 static struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec,
130 struct clk_sp810 *sp810 = data;
132 if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] >
133 ARRAY_SIZE(sp810->timerclken)))
136 return sp810->timerclken[clkspec->args[0]].clk;
139 static void __init clk_sp810_of_setup(struct device_node *node)
141 struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL);
142 const char *parent_names[2];
144 struct clk_init_data init;
148 pr_err("Failed to allocate memory for SP810!\n");
152 sp810->refclk_index = of_property_match_string(node, "clock-names",
154 parent_names[0] = of_clk_get_parent_name(node, sp810->refclk_index);
156 sp810->timclk_index = of_property_match_string(node, "clock-names",
158 parent_names[1] = of_clk_get_parent_name(node, sp810->timclk_index);
160 if (!parent_names[0] || !parent_names[1]) {
161 pr_warn("Failed to obtain parent clocks for SP810!\n");
166 sp810->base = of_iomap(node, 0);
167 spin_lock_init(&sp810->lock);
170 init.ops = &clk_sp810_timerclken_ops;
171 init.flags = CLK_IS_BASIC;
172 init.parent_names = parent_names;
173 init.num_parents = ARRAY_SIZE(parent_names);
175 for (i = 0; i < ARRAY_SIZE(sp810->timerclken); i++) {
176 snprintf(name, ARRAY_SIZE(name), "timerclken%d", i);
178 sp810->timerclken[i].sp810 = sp810;
179 sp810->timerclken[i].channel = i;
180 sp810->timerclken[i].hw.init = &init;
182 sp810->timerclken[i].clk = clk_register(NULL,
183 &sp810->timerclken[i].hw);
184 WARN_ON(IS_ERR(sp810->timerclken[i].clk));
187 of_clk_add_provider(node, clk_sp810_timerclken_of_get, sp810);
189 CLK_OF_DECLARE(sp810, "arm,sp810", clk_sp810_of_setup);