Lines Matching +full:clock +full:- +full:div

2  * clkt_clksel.c - OMAP2/3/4 clksel clock functions
4 * Copyright (C) 2005-2008 Texas Instruments, Inc.
5 * Copyright (C) 2004-2010 Nokia Corporation
8 * Richard Woodruff <r-woodruff2@ti.com>
24 * clock code for each new chip, since it can be exported from the SoC
47 #include <plat/clock.h>
49 #include "clock.h"
54 * _get_clksel_by_parent() - return clksel struct for a given clk & parent
58 * Scan the struct clksel array associated with the clock to find
59 * the element associated with the supplied parent clock address.
67 for (clks = clk->clksel; clks->parent; clks++) in _get_clksel_by_parent()
68 if (clks->parent == src_clk) in _get_clksel_by_parent()
71 if (!clks->parent) { in _get_clksel_by_parent()
73 WARN(1, "clock: Could not find parent clock %s in clksel array " in _get_clksel_by_parent()
74 "of clock %s\n", src_clk->name, clk->name); in _get_clksel_by_parent()
82 * _get_div_and_fieldval() - find the new clksel divisor and field value to use
88 * clk * @clk to the clock that is being reparented, find the
89 * appropriate rate divisor for the new clock (returned as the return
92 * Returns 0 on error, or returns the newly-selected divisor upon
110 * clock rates that are too high for the device. XXX A better in _get_div_and_fieldval()
112 * divisor matching the original clock rate before the parent in _get_div_and_fieldval()
116 for (clkr = clks->rates; clkr->div; clkr++) { in _get_div_and_fieldval()
117 if (!(clkr->flags & cpu_mask)) in _get_div_and_fieldval()
120 if (clkr->div > max_div) { in _get_div_and_fieldval()
121 max_div = clkr->div; in _get_div_and_fieldval()
128 WARN(1, "clock: Could not find divisor for clock %s parent %s" in _get_div_and_fieldval()
129 "\n", clk->name, src_clk->parent->name); in _get_div_and_fieldval()
133 *field_val = max_clkr->val; in _get_div_and_fieldval()
139 * _write_clksel_reg() - program a clock's clksel register in hardware
146 * destination before returning -- important since PRM and CM register
147 * accesses can be quite slow compared to ARM cycles -- but does not
149 * clock source.
155 v = __raw_readl(clk->clksel_reg); in _write_clksel_reg()
156 v &= ~clk->clksel_mask; in _write_clksel_reg()
157 v |= field_val << __ffs(clk->clksel_mask); in _write_clksel_reg()
158 __raw_writel(v, clk->clksel_reg); in _write_clksel_reg()
160 v = __raw_readl(clk->clksel_reg); /* OCP barrier */ in _write_clksel_reg()
164 * _clksel_to_divisor() - turn clksel field value into integer divider
168 * Given a struct clk of a rate-selectable clksel clock, and a register field
169 * value to search for, find the corresponding clock divisor. The register
170 * field value should be pre-masked and shifted down so the LSB is at bit 0
179 clks = _get_clksel_by_parent(clk, clk->parent); in _clksel_to_divisor()
183 for (clkr = clks->rates; clkr->div; clkr++) { in _clksel_to_divisor()
184 if (!(clkr->flags & cpu_mask)) in _clksel_to_divisor()
187 if (clkr->val == field_val) in _clksel_to_divisor()
191 if (!clkr->div) { in _clksel_to_divisor()
193 WARN(1, "clock: Could not find fieldval %d for clock %s parent " in _clksel_to_divisor()
194 "%s\n", field_val, clk->name, clk->parent->name); in _clksel_to_divisor()
198 return clkr->div; in _clksel_to_divisor()
202 * _divisor_to_clksel() - turn clksel integer divisor into a field value
204 * @div: integer divisor to search for
206 * Given a struct clk of a rate-selectable clksel clock, and a clock
208 * register field value _before_ left-shifting (i.e., LSB is at bit
211 static u32 _divisor_to_clksel(struct clk *clk, u32 div) in _divisor_to_clksel() argument
217 WARN_ON(div == 0); in _divisor_to_clksel()
219 clks = _get_clksel_by_parent(clk, clk->parent); in _divisor_to_clksel()
223 for (clkr = clks->rates; clkr->div; clkr++) { in _divisor_to_clksel()
224 if (!(clkr->flags & cpu_mask)) in _divisor_to_clksel()
227 if (clkr->div == div) in _divisor_to_clksel()
231 if (!clkr->div) { in _divisor_to_clksel()
232 pr_err("clock: Could not find divisor %d for clock %s parent " in _divisor_to_clksel()
233 "%s\n", div, clk->name, clk->parent->name); in _divisor_to_clksel()
237 return clkr->val; in _divisor_to_clksel()
241 * _read_divisor() - get current divisor applied to parent clock (from hdwr)
252 if (!clk->clksel || !clk->clksel_mask) in _read_divisor()
255 v = __raw_readl(clk->clksel_reg); in _read_divisor()
256 v &= clk->clksel_mask; in _read_divisor()
257 v >>= __ffs(clk->clksel_mask); in _read_divisor()
265 * omap2_clksel_round_rate_div() - find divisor for the given clock and rate
267 * @target_rate: desired clock rate
274 * Returns the rounded clock rate or returns 0xffffffff on error.
284 if (!clk->clksel || !clk->clksel_mask) in omap2_clksel_round_rate_div()
287 pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n", in omap2_clksel_round_rate_div()
288 clk->name, target_rate); in omap2_clksel_round_rate_div()
292 clks = _get_clksel_by_parent(clk, clk->parent); in omap2_clksel_round_rate_div()
296 for (clkr = clks->rates; clkr->div; clkr++) { in omap2_clksel_round_rate_div()
297 if (!(clkr->flags & cpu_mask)) in omap2_clksel_round_rate_div()
301 if (clkr->div <= last_div) in omap2_clksel_round_rate_div()
302 pr_err("clock: clksel_rate table not sorted " in omap2_clksel_round_rate_div()
303 "for clock %s", clk->name); in omap2_clksel_round_rate_div()
305 last_div = clkr->div; in omap2_clksel_round_rate_div()
307 test_rate = clk->parent->rate / clkr->div; in omap2_clksel_round_rate_div()
313 if (!clkr->div) { in omap2_clksel_round_rate_div()
314 pr_err("clock: Could not find divisor for target " in omap2_clksel_round_rate_div()
315 "rate %ld for clock %s parent %s\n", target_rate, in omap2_clksel_round_rate_div()
316 clk->name, clk->parent->name); in omap2_clksel_round_rate_div()
320 *new_div = clkr->div; in omap2_clksel_round_rate_div()
322 pr_debug("clock: new_div = %d, new_rate = %ld\n", *new_div, in omap2_clksel_round_rate_div()
323 (clk->parent->rate / clkr->div)); in omap2_clksel_round_rate_div()
325 return clk->parent->rate / clkr->div; in omap2_clksel_round_rate_div()
329 * Clocktype interface functions to the OMAP clock code
334 * omap2_init_clksel_parent() - set a clksel clk's parent field from the hdwr
335 * @clk: OMAP clock struct ptr to use
337 * Given a pointer @clk to a source-selectable struct clk, read the
348 if (!clk->clksel || !clk->clksel_mask) in omap2_init_clksel_parent()
351 r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; in omap2_init_clksel_parent()
352 r >>= __ffs(clk->clksel_mask); in omap2_init_clksel_parent()
354 for (clks = clk->clksel; clks->parent && !found; clks++) { in omap2_init_clksel_parent()
355 for (clkr = clks->rates; clkr->div && !found; clkr++) { in omap2_init_clksel_parent()
356 if (!(clkr->flags & cpu_mask)) in omap2_init_clksel_parent()
359 if (clkr->val == r) { in omap2_init_clksel_parent()
360 if (clk->parent != clks->parent) { in omap2_init_clksel_parent()
361 pr_debug("clock: inited %s parent " in omap2_init_clksel_parent()
363 clk->name, clks->parent->name, in omap2_init_clksel_parent()
364 ((clk->parent) ? in omap2_init_clksel_parent()
365 clk->parent->name : "NULL")); in omap2_init_clksel_parent()
366 clk_reparent(clk, clks->parent); in omap2_init_clksel_parent()
374 WARN(!found, "clock: %s: init parent: could not find regval %0x\n", in omap2_init_clksel_parent()
375 clk->name, r); in omap2_init_clksel_parent()
381 * omap2_clksel_recalc() - function ptr to pass via struct clk .recalc field
384 * This function is intended to be called only by the clock framework.
385 * Each clksel clock should have its struct clk .recalc field set to this
386 * function. Returns the clock's current rate, based on its parent's rate
392 u32 div = 0; in omap2_clksel_recalc() local
394 div = _read_divisor(clk); in omap2_clksel_recalc()
395 if (div == 0) in omap2_clksel_recalc()
396 return clk->rate; in omap2_clksel_recalc()
398 rate = clk->parent->rate / div; in omap2_clksel_recalc()
400 pr_debug("clock: %s: recalc'd rate is %ld (div %d)\n", clk->name, in omap2_clksel_recalc()
401 rate, div); in omap2_clksel_recalc()
407 * omap2_clksel_round_rate() - find rounded rate for the given clock and rate
409 * @target_rate: desired clock rate
411 * This function is intended to be called only by the clock framework.
412 * Finds best target rate based on the source clock and possible dividers.
415 * Returns the rounded clock rate or returns 0xffffffff on error.
425 * omap2_clksel_set_rate() - program clock rate in hardware
429 * This function is intended to be called only by the clock framework.
430 * Program @clk's rate to @rate in the hardware. The clock can be
431 * either enabled or disabled when this happens, although if the clock
433 * unpredictably when the clock rate is changed - this depends on the
435 * the clock, so if multiple drivers are using the clock, and the rate
437 * Returns -EINVAL upon error, or 0 upon success.
443 if (!clk->clksel || !clk->clksel_mask) in omap2_clksel_set_rate()
444 return -EINVAL; in omap2_clksel_set_rate()
448 return -EINVAL; in omap2_clksel_set_rate()
452 return -EINVAL; in omap2_clksel_set_rate()
456 clk->rate = clk->parent->rate / new_div; in omap2_clksel_set_rate()
458 pr_debug("clock: %s: set rate to %ld\n", clk->name, clk->rate); in omap2_clksel_set_rate()
464 * Clksel parent setting function - not passed in struct clk function
465 * pointer - instead, the OMAP clock code currently assumes that any
466 * parent-setting clock is a clksel clock, and calls
471 * omap2_clksel_set_parent() - change a clock's parent clock
472 * @clk: struct clk * of the child clock
473 * @new_parent: struct clk * of the new parent clock
475 * This function is intended to be called only by the clock framework.
476 * Change the parent clock of clock @clk to @new_parent. This is
478 * currently check the usecount of the clock, so if multiple drivers
479 * are using the clock, and the parent is changed, they will all be
480 * affected without any notification. Returns -EINVAL upon error, or
488 if (!clk->clksel || !clk->clksel_mask) in omap2_clksel_set_parent()
489 return -EINVAL; in omap2_clksel_set_parent()
493 return -EINVAL; in omap2_clksel_set_parent()
500 clk->rate = new_parent->rate; in omap2_clksel_set_parent()
503 clk->rate /= parent_div; in omap2_clksel_set_parent()
505 pr_debug("clock: %s: set parent to %s (new rate %ld)\n", in omap2_clksel_set_parent()
506 clk->name, clk->parent->name, clk->rate); in omap2_clksel_set_parent()