aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/clkt_clksel.c
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2010-05-18 20:40:24 -0400
committerPaul Walmsley <paul@pwsan.com>2010-05-20 14:31:06 -0400
commitd74b4949714741f4c58cd1801a6a92737b89a61c (patch)
treea0e838fff3cab45bfba0c319fcdf7f1ea594686f /arch/arm/mach-omap2/clkt_clksel.c
parent275f675c24a16ea45cc78bc03ff73fd06be8bffb (diff)
OMAP2+ clock: remove DEFAULT_RATE clksel_rate flag
The DEFAULT_RATE clksel_rate flag is essentially useless. It was set on some of the lowest divisors, which, when switching to a much higher-rate parent, could have potentially resulted in rates that exceeded the hardware specifications for downstream clocks in the window between the clk_set_parent(), and a subsequent clk_set_rate(). It seems much safer to just remove the flag and always use the highest available divisor (resulting in the lowest possible rate) after the switch, and this patch does so. Ideally, it would be best to first attempt to switch to a divisor that matches the clock's rate with the previous parent, if at all possible. But that is a project for some other day or some other person. The parent changing code is rarely used. Signed-off-by: Paul Walmsley <paul@pwsan.com>
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);