diff options
author | Tero Kristo <t-kristo@ti.com> | 2014-02-24 09:06:34 -0500 |
---|---|---|
committer | Tero Kristo <t-kristo@ti.com> | 2014-05-28 05:30:02 -0400 |
commit | 4d008589e271e28eae728eef7f5fb1f658f12b9f (patch) | |
tree | 8b30eee15659aa978c5b452b65647662cc90c8c3 | |
parent | aa76fcf473f6bfa839f37f77b6fdb71f0fb88d8f (diff) |
CLK: TI: APLL: add support for omap2 aplls
This patch adds support for omap2 type aplls, which have gating and
autoidle functionality.
Signed-off-by: Tero Kristo <t-kristo@ti.com>
-rw-r--r-- | Documentation/devicetree/bindings/clock/ti/apll.txt | 24 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock.h | 11 | ||||
-rw-r--r-- | drivers/clk/ti/apll.c | 181 | ||||
-rw-r--r-- | include/linux/clk/ti.h | 21 |
4 files changed, 220 insertions, 17 deletions
diff --git a/Documentation/devicetree/bindings/clock/ti/apll.txt b/Documentation/devicetree/bindings/clock/ti/apll.txt index 7faf5a68b3be..ade4dd4c30f0 100644 --- a/Documentation/devicetree/bindings/clock/ti/apll.txt +++ b/Documentation/devicetree/bindings/clock/ti/apll.txt | |||
@@ -14,18 +14,32 @@ a subtype of a DPLL [2], although a simplified one at that. | |||
14 | [2] Documentation/devicetree/bindings/clock/ti/dpll.txt | 14 | [2] Documentation/devicetree/bindings/clock/ti/dpll.txt |
15 | 15 | ||
16 | Required properties: | 16 | Required properties: |
17 | - compatible : shall be "ti,dra7-apll-clock" | 17 | - compatible : shall be "ti,dra7-apll-clock" or "ti,omap2-apll-clock" |
18 | - #clock-cells : from common clock binding; shall be set to 0. | 18 | - #clock-cells : from common clock binding; shall be set to 0. |
19 | - clocks : link phandles of parent clocks (clk-ref and clk-bypass) | 19 | - clocks : link phandles of parent clocks (clk-ref and clk-bypass) |
20 | - reg : address and length of the register set for controlling the APLL. | 20 | - reg : address and length of the register set for controlling the APLL. |
21 | It contains the information of registers in the following order: | 21 | It contains the information of registers in the following order: |
22 | "control" - contains the control register base address | 22 | "control" - contains the control register offset |
23 | "idlest" - contains the idlest register base address | 23 | "idlest" - contains the idlest register offset |
24 | "autoidle" - contains the autoidle register offset (OMAP2 only) | ||
25 | - ti,clock-frequency : static clock frequency for the clock (OMAP2 only) | ||
26 | - ti,idlest-shift : bit-shift for the idlest field (OMAP2 only) | ||
27 | - ti,bit-shift : bit-shift for enable and autoidle fields (OMAP2 only) | ||
24 | 28 | ||
25 | Examples: | 29 | Examples: |
26 | apll_pcie_ck: apll_pcie_ck@4a008200 { | 30 | apll_pcie_ck: apll_pcie_ck { |
27 | #clock-cells = <0>; | 31 | #clock-cells = <0>; |
28 | clocks = <&apll_pcie_in_clk_mux>, <&dpll_pcie_ref_ck>; | 32 | clocks = <&apll_pcie_in_clk_mux>, <&dpll_pcie_ref_ck>; |
29 | reg = <0x4a00821c 0x4>, <0x4a008220 0x4>; | 33 | reg = <0x021c>, <0x0220>; |
30 | compatible = "ti,dra7-apll-clock"; | 34 | compatible = "ti,dra7-apll-clock"; |
31 | }; | 35 | }; |
36 | |||
37 | apll96_ck: apll96_ck { | ||
38 | #clock-cells = <0>; | ||
39 | compatible = "ti,omap2-apll-clock"; | ||
40 | clocks = <&sys_ck>; | ||
41 | ti,bit-shift = <2>; | ||
42 | ti,idlest-shift = <8>; | ||
43 | ti,clock-frequency = <96000000>; | ||
44 | reg = <0x0500>, <0x0530>, <0x0520>; | ||
45 | }; | ||
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index f6e9904d7a75..eb441d137843 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h | |||
@@ -178,17 +178,6 @@ struct clksel { | |||
178 | const struct clksel_rate *rates; | 178 | const struct clksel_rate *rates; |
179 | }; | 179 | }; |
180 | 180 | ||
181 | struct clk_hw_omap_ops { | ||
182 | void (*find_idlest)(struct clk_hw_omap *oclk, | ||
183 | void __iomem **idlest_reg, | ||
184 | u8 *idlest_bit, u8 *idlest_val); | ||
185 | void (*find_companion)(struct clk_hw_omap *oclk, | ||
186 | void __iomem **other_reg, | ||
187 | u8 *other_bit); | ||
188 | void (*allow_idle)(struct clk_hw_omap *oclk); | ||
189 | void (*deny_idle)(struct clk_hw_omap *oclk); | ||
190 | }; | ||
191 | |||
192 | unsigned long omap_fixed_divisor_recalc(struct clk_hw *hw, | 181 | unsigned long omap_fixed_divisor_recalc(struct clk_hw *hw, |
193 | unsigned long parent_rate); | 182 | unsigned long parent_rate); |
194 | 183 | ||
diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c index b986f61f5a77..5428c9c547cd 100644 --- a/drivers/clk/ti/apll.c +++ b/drivers/clk/ti/apll.c | |||
@@ -221,3 +221,184 @@ cleanup: | |||
221 | kfree(init); | 221 | kfree(init); |
222 | } | 222 | } |
223 | CLK_OF_DECLARE(dra7_apll_clock, "ti,dra7-apll-clock", of_dra7_apll_setup); | 223 | CLK_OF_DECLARE(dra7_apll_clock, "ti,dra7-apll-clock", of_dra7_apll_setup); |
224 | |||
225 | #define OMAP2_EN_APLL_LOCKED 0x3 | ||
226 | #define OMAP2_EN_APLL_STOPPED 0x0 | ||
227 | |||
228 | static int omap2_apll_is_enabled(struct clk_hw *hw) | ||
229 | { | ||
230 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
231 | struct dpll_data *ad = clk->dpll_data; | ||
232 | u32 v; | ||
233 | |||
234 | v = ti_clk_ll_ops->clk_readl(ad->control_reg); | ||
235 | v &= ad->enable_mask; | ||
236 | |||
237 | v >>= __ffs(ad->enable_mask); | ||
238 | |||
239 | return v == OMAP2_EN_APLL_LOCKED ? 1 : 0; | ||
240 | } | ||
241 | |||
242 | static unsigned long omap2_apll_recalc(struct clk_hw *hw, | ||
243 | unsigned long parent_rate) | ||
244 | { | ||
245 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
246 | |||
247 | if (omap2_apll_is_enabled(hw)) | ||
248 | return clk->fixed_rate; | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int omap2_apll_enable(struct clk_hw *hw) | ||
254 | { | ||
255 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
256 | struct dpll_data *ad = clk->dpll_data; | ||
257 | u32 v; | ||
258 | int i = 0; | ||
259 | |||
260 | v = ti_clk_ll_ops->clk_readl(ad->control_reg); | ||
261 | v &= ~ad->enable_mask; | ||
262 | v |= OMAP2_EN_APLL_LOCKED << __ffs(ad->enable_mask); | ||
263 | ti_clk_ll_ops->clk_writel(v, ad->control_reg); | ||
264 | |||
265 | while (1) { | ||
266 | v = ti_clk_ll_ops->clk_readl(ad->idlest_reg); | ||
267 | if (v & ad->idlest_mask) | ||
268 | break; | ||
269 | if (i > MAX_APLL_WAIT_TRIES) | ||
270 | break; | ||
271 | i++; | ||
272 | udelay(1); | ||
273 | } | ||
274 | |||
275 | if (i == MAX_APLL_WAIT_TRIES) { | ||
276 | pr_warn("%s failed to transition to locked\n", | ||
277 | __clk_get_name(clk->hw.clk)); | ||
278 | return -EBUSY; | ||
279 | } | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static void omap2_apll_disable(struct clk_hw *hw) | ||
285 | { | ||
286 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | ||
287 | struct dpll_data *ad = clk->dpll_data; | ||
288 | u32 v; | ||
289 | |||
290 | v = ti_clk_ll_ops->clk_readl(ad->control_reg); | ||
291 | v &= ~ad->enable_mask; | ||
292 | v |= OMAP2_EN_APLL_STOPPED << __ffs(ad->enable_mask); | ||
293 | ti_clk_ll_ops->clk_writel(v, ad->control_reg); | ||
294 | } | ||
295 | |||
296 | static struct clk_ops omap2_apll_ops = { | ||
297 | .enable = &omap2_apll_enable, | ||
298 | .disable = &omap2_apll_disable, | ||
299 | .is_enabled = &omap2_apll_is_enabled, | ||
300 | .recalc_rate = &omap2_apll_recalc, | ||
301 | }; | ||
302 | |||
303 | static void omap2_apll_set_autoidle(struct clk_hw_omap *clk, u32 val) | ||
304 | { | ||
305 | struct dpll_data *ad = clk->dpll_data; | ||
306 | u32 v; | ||
307 | |||
308 | v = ti_clk_ll_ops->clk_readl(ad->autoidle_reg); | ||
309 | v &= ~ad->autoidle_mask; | ||
310 | v |= val << __ffs(ad->autoidle_mask); | ||
311 | ti_clk_ll_ops->clk_writel(v, ad->control_reg); | ||
312 | } | ||
313 | |||
314 | #define OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP 0x3 | ||
315 | #define OMAP2_APLL_AUTOIDLE_DISABLE 0x0 | ||
316 | |||
317 | static void omap2_apll_allow_idle(struct clk_hw_omap *clk) | ||
318 | { | ||
319 | omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP); | ||
320 | } | ||
321 | |||
322 | static void omap2_apll_deny_idle(struct clk_hw_omap *clk) | ||
323 | { | ||
324 | omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_DISABLE); | ||
325 | } | ||
326 | |||
327 | static struct clk_hw_omap_ops omap2_apll_hwops = { | ||
328 | .allow_idle = &omap2_apll_allow_idle, | ||
329 | .deny_idle = &omap2_apll_deny_idle, | ||
330 | }; | ||
331 | |||
332 | static void __init of_omap2_apll_setup(struct device_node *node) | ||
333 | { | ||
334 | struct dpll_data *ad = NULL; | ||
335 | struct clk_hw_omap *clk_hw = NULL; | ||
336 | struct clk_init_data *init = NULL; | ||
337 | struct clk *clk; | ||
338 | const char *parent_name; | ||
339 | u32 val; | ||
340 | |||
341 | ad = kzalloc(sizeof(*clk_hw), GFP_KERNEL); | ||
342 | clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL); | ||
343 | init = kzalloc(sizeof(*init), GFP_KERNEL); | ||
344 | |||
345 | if (!ad || !clk_hw || !init) | ||
346 | goto cleanup; | ||
347 | |||
348 | clk_hw->dpll_data = ad; | ||
349 | clk_hw->hw.init = init; | ||
350 | init->ops = &omap2_apll_ops; | ||
351 | init->name = node->name; | ||
352 | clk_hw->ops = &omap2_apll_hwops; | ||
353 | |||
354 | init->num_parents = of_clk_get_parent_count(node); | ||
355 | if (init->num_parents != 1) { | ||
356 | pr_err("%s must have one parent\n", node->name); | ||
357 | goto cleanup; | ||
358 | } | ||
359 | |||
360 | parent_name = of_clk_get_parent_name(node, 0); | ||
361 | init->parent_names = &parent_name; | ||
362 | |||
363 | if (of_property_read_u32(node, "ti,clock-frequency", &val)) { | ||
364 | pr_err("%s missing clock-frequency\n", node->name); | ||
365 | goto cleanup; | ||
366 | } | ||
367 | clk_hw->fixed_rate = val; | ||
368 | |||
369 | if (of_property_read_u32(node, "ti,bit-shift", &val)) { | ||
370 | pr_err("%s missing bit-shift\n", node->name); | ||
371 | goto cleanup; | ||
372 | } | ||
373 | |||
374 | clk_hw->enable_bit = val; | ||
375 | ad->enable_mask = 0x3 << val; | ||
376 | ad->autoidle_mask = 0x3 << val; | ||
377 | |||
378 | if (of_property_read_u32(node, "ti,idlest-shift", &val)) { | ||
379 | pr_err("%s missing idlest-shift\n", node->name); | ||
380 | goto cleanup; | ||
381 | } | ||
382 | |||
383 | ad->idlest_mask = 1 << val; | ||
384 | |||
385 | ad->control_reg = ti_clk_get_reg_addr(node, 0); | ||
386 | ad->autoidle_reg = ti_clk_get_reg_addr(node, 1); | ||
387 | ad->idlest_reg = ti_clk_get_reg_addr(node, 2); | ||
388 | |||
389 | if (!ad->control_reg || !ad->autoidle_reg || !ad->idlest_reg) | ||
390 | goto cleanup; | ||
391 | |||
392 | clk = clk_register(NULL, &clk_hw->hw); | ||
393 | if (!IS_ERR(clk)) { | ||
394 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
395 | kfree(init); | ||
396 | return; | ||
397 | } | ||
398 | cleanup: | ||
399 | kfree(ad); | ||
400 | kfree(clk_hw); | ||
401 | kfree(init); | ||
402 | } | ||
403 | CLK_OF_DECLARE(omap2_apll_clock, "ti,omap2-apll-clock", | ||
404 | of_omap2_apll_setup); | ||
diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index 753878c6fa52..44bf84002a34 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h | |||
@@ -94,7 +94,26 @@ struct dpll_data { | |||
94 | u8 flags; | 94 | u8 flags; |
95 | }; | 95 | }; |
96 | 96 | ||
97 | struct clk_hw_omap_ops; | 97 | struct clk_hw_omap; |
98 | |||
99 | /** | ||
100 | * struct clk_hw_omap_ops - OMAP clk ops | ||
101 | * @find_idlest: find idlest register information for a clock | ||
102 | * @find_companion: find companion clock register information for a clock, | ||
103 | * basically converts CM_ICLKEN* <-> CM_FCLKEN* | ||
104 | * @allow_idle: enables autoidle hardware functionality for a clock | ||
105 | * @deny_idle: prevent autoidle hardware functionality for a clock | ||
106 | */ | ||
107 | struct clk_hw_omap_ops { | ||
108 | void (*find_idlest)(struct clk_hw_omap *oclk, | ||
109 | void __iomem **idlest_reg, | ||
110 | u8 *idlest_bit, u8 *idlest_val); | ||
111 | void (*find_companion)(struct clk_hw_omap *oclk, | ||
112 | void __iomem **other_reg, | ||
113 | u8 *other_bit); | ||
114 | void (*allow_idle)(struct clk_hw_omap *oclk); | ||
115 | void (*deny_idle)(struct clk_hw_omap *oclk); | ||
116 | }; | ||
98 | 117 | ||
99 | /** | 118 | /** |
100 | * struct clk_hw_omap - OMAP struct clk | 119 | * struct clk_hw_omap - OMAP struct clk |