Commit 1c1cb20572c8973c17c1c95764af5c6a50f9336f
Committed by
Afzal Mohammed
1 parent
b2274ddfdf
Exists in
master
OMAP2+: PM: init_voltages: handle non compliant bootloaders
Bootloaders should in theory setup a frequency which is enabled in OPP table. However, there can be mismatches, and we should try both going lower in addition to the going higher to find a match if bootloader boots up at a OPP than the kernel thinks it should be allowed. We also sequence the frequency and voltage settings properly. Reported-by: Colin Cross <ccross@google.com> Signed-off-by: Nishanth Menon <nm@ti.com> [vaibhav.bedia@ti.com: Pull in for AM33xx] Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
Showing 1 changed file with 62 additions and 16 deletions Side-by-side Diff
arch/arm/mach-omap2/pm.c
... | ... | @@ -141,14 +141,10 @@ |
141 | 141 | struct voltagedomain *voltdm; |
142 | 142 | struct clk *clk; |
143 | 143 | struct opp *opp; |
144 | - unsigned long freq, bootup_volt; | |
145 | 144 | struct device *dev; |
145 | + unsigned long freq_cur, freq_valid, bootup_volt; | |
146 | + int ret = -EINVAL; | |
146 | 147 | |
147 | - if (!vdd_name || !clk_name || !oh_name) { | |
148 | - pr_err("%s: invalid parameters\n", __func__); | |
149 | - goto exit; | |
150 | - } | |
151 | - | |
152 | 148 | dev = omap_device_get_by_hwmod_name(oh_name); |
153 | 149 | if (IS_ERR(dev)) { |
154 | 150 | pr_err("%s: Unable to get dev pointer for hwmod %s\n", |
155 | 151 | |
156 | 152 | |
... | ... | @@ -169,16 +165,20 @@ |
169 | 165 | goto exit; |
170 | 166 | } |
171 | 167 | |
172 | - freq = clk->rate; | |
173 | - clk_put(clk); | |
168 | + freq_cur = clk->rate; | |
169 | + freq_valid = freq_cur; | |
174 | 170 | |
175 | 171 | rcu_read_lock(); |
176 | - opp = opp_find_freq_ceil(dev, &freq); | |
172 | + opp = opp_find_freq_ceil(dev, &freq_valid); | |
177 | 173 | if (IS_ERR(opp)) { |
178 | - rcu_read_unlock(); | |
179 | - printk(KERN_ERR "%s: unable to find boot up OPP for vdd_%s\n", | |
180 | - __func__, vdd_name); | |
181 | - goto exit; | |
174 | + opp = opp_find_freq_floor(dev, &freq_valid); | |
175 | + if (IS_ERR(opp)) { | |
176 | + rcu_read_unlock(); | |
177 | + pr_err("%s: no boot OPP match for %ld on vdd_%s\n", | |
178 | + __func__, freq_cur, vdd_name); | |
179 | + ret = -ENOENT; | |
180 | + goto exit_ck; | |
181 | + } | |
182 | 182 | } |
183 | 183 | |
184 | 184 | bootup_volt = opp_get_voltage(opp); |
185 | 185 | |
... | ... | @@ -186,11 +186,57 @@ |
186 | 186 | if (!bootup_volt) { |
187 | 187 | pr_err("%s: unable to find voltage corresponding " |
188 | 188 | "to the bootup OPP for vdd_%s\n", __func__, vdd_name); |
189 | - goto exit; | |
189 | + ret = -ENOENT; | |
190 | + goto exit_ck; | |
190 | 191 | } |
191 | 192 | |
192 | - voltdm_scale(voltdm, bootup_volt); | |
193 | - return 0; | |
193 | + /* | |
194 | + * Frequency and Voltage have to be sequenced: if we move from | |
195 | + * a lower frequency to higher frequency, raise voltage, followed by | |
196 | + * frequency, and vice versa. we assume that the voltage at boot | |
197 | + * is the required voltage for the frequency it was set for. | |
198 | + * NOTE: | |
199 | + * we can check the frequency, but there is numerous ways to set | |
200 | + * voltage. We play the safe path and just set the voltage. | |
201 | + */ | |
202 | + | |
203 | + if (freq_cur < freq_valid) { | |
204 | + ret = voltdm_scale(voltdm, bootup_volt); | |
205 | + if (ret) { | |
206 | + pr_err("%s: Fail set voltage-%s(f=%ld v=%ld)on vdd%s\n", | |
207 | + __func__, vdd_name, freq_valid, | |
208 | + bootup_volt, vdd_name); | |
209 | + goto exit_ck; | |
210 | + } | |
211 | + } | |
212 | + | |
213 | + /* Set freq only if there is a difference in freq */ | |
214 | + if (freq_valid != freq_cur) { | |
215 | + ret = clk_set_rate(clk, freq_valid); | |
216 | + if (ret) { | |
217 | + pr_err("%s: Fail set clk-%s(f=%ld v=%ld)on vdd%s\n", | |
218 | + __func__, clk_name, freq_valid, | |
219 | + bootup_volt, vdd_name); | |
220 | + goto exit_ck; | |
221 | + } | |
222 | + } | |
223 | + | |
224 | + if (freq_cur >= freq_valid) { | |
225 | + ret = voltdm_scale(voltdm, bootup_volt); | |
226 | + if (ret) { | |
227 | + pr_err("%s: Fail set voltage-%s(f=%ld v=%ld)on vdd%s\n", | |
228 | + __func__, clk_name, freq_valid, | |
229 | + bootup_volt, vdd_name); | |
230 | + goto exit_ck; | |
231 | + } | |
232 | + } | |
233 | + | |
234 | + ret = 0; | |
235 | +exit_ck: | |
236 | + clk_put(clk); | |
237 | + | |
238 | + if (!ret) | |
239 | + return 0; | |
194 | 240 | |
195 | 241 | exit: |
196 | 242 | pr_err("%s: unable to set vdd_%s\n", __func__, vdd_name); |