aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/clkt_clksel.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-omap2/clkt_clksel.c')
-rw-r--r--arch/arm/mach-omap2/clkt_clksel.c57
1 files changed, 39 insertions, 18 deletions
diff --git a/arch/arm/mach-omap2/clkt_clksel.c b/arch/arm/mach-omap2/clkt_clksel.c
index e50812dd03fd..9a23aebeea7e 100644
--- a/arch/arm/mach-omap2/clkt_clksel.c
+++ b/arch/arm/mach-omap2/clkt_clksel.c
@@ -67,38 +67,61 @@ static const struct clksel *_omap2_get_clksel_by_parent(struct clk *clk,
67 return clks; 67 return clks;
68} 68}
69 69
70/* 70/**
71 * Converts encoded control register address into a full address 71 * _omap2_clksel_get_src_field - find the new clksel divisor to use
72 * On error, the return value (parent_div) will be 0. 72 * @src_clk: planned new parent struct clk *
73 * @clk: struct clk * that is being reparented
74 * @field_val: pointer to a u32 to contain the register data for the divisor
75 *
76 * Given an intended new parent struct clk * @src_clk, and the struct
77 * clk * @clk to the clock that is being reparented, find the
78 * appropriate rate divisor for the new clock (returned as the return
79 * value), and the corresponding register bitfield data to program to
80 * reach that divisor (returned in the u32 pointed to by @field_val).
81 * Returns 0 on error, or returns the newly-selected divisor upon
82 * success (in this latter case, the corresponding register bitfield
83 * value is passed back in the variable pointed to by @field_val)
73 */ 84 */
74static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk, 85static u8 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk,
75 u32 *field_val) 86 u32 *field_val)
76{ 87{
77 const struct clksel *clks; 88 const struct clksel *clks;
78 const struct clksel_rate *clkr; 89 const struct clksel_rate *clkr, *max_clkr;
90 u8 max_div = 0;
79 91
80 clks = _omap2_get_clksel_by_parent(clk, src_clk); 92 clks = _omap2_get_clksel_by_parent(clk, src_clk);
81 if (!clks) 93 if (!clks)
82 return 0; 94 return 0;
83 95
96 /*
97 * Find the highest divisor (e.g., the one resulting in the
98 * lowest rate) to use as the default. This should avoid
99 * clock rates that are too high for the device. XXX A better
100 * solution here would be to try to determine if there is a
101 * divisor matching the original clock rate before the parent
102 * switch, and if it cannot be found, to fall back to the
103 * highest divisor.
104 */
84 for (clkr = clks->rates; clkr->div; clkr++) { 105 for (clkr = clks->rates; clkr->div; clkr++) {
85 if (clkr->flags & cpu_mask && clkr->flags & DEFAULT_RATE) 106 if (!(clkr->flags & cpu_mask))
86 break; /* Found the default rate for this platform */ 107 continue;
108
109 if (clkr->div > max_div) {
110 max_div = clkr->div;
111 max_clkr = clkr;
112 }
87 } 113 }
88 114
89 if (!clkr->div) { 115 if (max_div == 0) {
90 printk(KERN_ERR "clock: Could not find default rate for " 116 WARN(1, "clock: Could not find divisor for "
91 "clock %s parent %s\n", clk->name, 117 "clock %s parent %s\n", clk->name,
92 src_clk->parent->name); 118 src_clk->parent->name);
93 return 0; 119 return 0;
94 } 120 }
95 121
96 /* Should never happen. Add a clksel mask to the struct clk. */ 122 *field_val = max_clkr->val;
97 WARN_ON(clk->clksel_mask == 0);
98 123
99 *field_val = clkr->val; 124 return max_div;
100
101 return clkr->div;
102} 125}
103 126
104 127
@@ -177,8 +200,6 @@ unsigned long omap2_clksel_recalc(struct clk *clk)
177 * 200 *
178 * Finds 'best' divider value in an array based on the source and target 201 * Finds 'best' divider value in an array based on the source and target
179 * rates. The divider array must be sorted with smallest divider first. 202 * rates. The divider array must be sorted with smallest divider first.
180 * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT,
181 * they are only settable as part of virtual_prcm set.
182 * 203 *
183 * Returns the rounded clock rate or returns 0xffffffff on error. 204 * Returns the rounded clock rate or returns 0xffffffff on error.
184 */ 205 */
@@ -380,7 +401,7 @@ int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent)
380{ 401{
381 u32 field_val, v, parent_div; 402 u32 field_val, v, parent_div;
382 403
383 if (!clk->clksel) 404 if (!clk->clksel || !clk->clksel_mask)
384 return -EINVAL; 405 return -EINVAL;
385 406
386 parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val); 407 parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val);