Commit 1c1cb20572c8973c17c1c95764af5c6a50f9336f

Authored by Nishanth Menon
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);