diff options
Diffstat (limited to 'arch/arm/mach-omap2/clkt_clksel.c')
-rw-r--r-- | arch/arm/mach-omap2/clkt_clksel.c | 57 |
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 | */ |
74 | static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk, | 85 | static 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); |