Commit | Line | Data |
---|---|---|
01f48d30 KH |
1 | #include <linux/kernel.h> |
2 | #include <linux/init.h> | |
01f48d30 KH |
3 | |
4 | #include <plat/common.h> | |
5 | ||
6 | #include "voltage.h" | |
7 | #include "vp.h" | |
8 | #include "prm-regbits-34xx.h" | |
9 | #include "prm-regbits-44xx.h" | |
10 | #include "prm44xx.h" | |
11 | ||
01f48d30 KH |
12 | static void vp_latch_vsel(struct voltagedomain *voltdm) |
13 | { | |
b7ea803e | 14 | struct omap_vp_instance *vp = voltdm->vp; |
01f48d30 KH |
15 | u32 vpconfig; |
16 | unsigned long uvdc; | |
17 | char vsel; | |
01f48d30 KH |
18 | |
19 | uvdc = omap_voltage_get_nom_volt(voltdm); | |
20 | if (!uvdc) { | |
21 | pr_warning("%s: unable to find current voltage for vdd_%s\n", | |
22 | __func__, voltdm->name); | |
23 | return; | |
24 | } | |
25 | ||
ce8ebe0d | 26 | if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { |
01f48d30 KH |
27 | pr_warning("%s: PMIC function to convert voltage in uV to" |
28 | " vsel not registered\n", __func__); | |
29 | return; | |
30 | } | |
31 | ||
ce8ebe0d | 32 | vsel = voltdm->pmic->uv_to_vsel(uvdc); |
01f48d30 | 33 | |
4bcc475e | 34 | vpconfig = voltdm->read(vp->vpconfig); |
b7ea803e KH |
35 | vpconfig &= ~(vp->common->vpconfig_initvoltage_mask | |
36 | vp->common->vpconfig_initvdd); | |
0ec3041e | 37 | vpconfig |= vsel << __ffs(vp->common->vpconfig_initvoltage_mask); |
4bcc475e | 38 | voltdm->write(vpconfig, vp->vpconfig); |
01f48d30 KH |
39 | |
40 | /* Trigger initVDD value copy to voltage processor */ | |
b7ea803e | 41 | voltdm->write((vpconfig | vp->common->vpconfig_initvdd), |
4bcc475e | 42 | vp->vpconfig); |
01f48d30 KH |
43 | |
44 | /* Clear initVDD copy trigger bit */ | |
4bcc475e | 45 | voltdm->write(vpconfig, vp->vpconfig); |
01f48d30 KH |
46 | } |
47 | ||
48 | /* Generic voltage init functions */ | |
49 | void __init omap_vp_init(struct voltagedomain *voltdm) | |
50 | { | |
b7ea803e | 51 | struct omap_vp_instance *vp = voltdm->vp; |
667216d6 KH |
52 | u32 val, sys_clk_rate, timeout, waittime; |
53 | u32 vddmin, vddmax, vstepmin, vstepmax; | |
01f48d30 | 54 | |
4bcc475e | 55 | if (!voltdm->read || !voltdm->write) { |
01f48d30 KH |
56 | pr_err("%s: No read/write API for accessing vdd_%s regs\n", |
57 | __func__, voltdm->name); | |
58 | return; | |
59 | } | |
60 | ||
6f567273 KH |
61 | vp->enabled = false; |
62 | ||
63 | /* Divide to avoid overflow */ | |
64 | sys_clk_rate = voltdm->sys_clk.rate / 1000; | |
65 | ||
667216d6 KH |
66 | timeout = (sys_clk_rate * voltdm->pmic->vp_timeout_us) / 1000; |
67 | vddmin = voltdm->pmic->vp_vddmin; | |
68 | vddmax = voltdm->pmic->vp_vddmax; | |
6f567273 KH |
69 | |
70 | waittime = ((voltdm->pmic->step_size / voltdm->pmic->slew_rate) * | |
71 | sys_clk_rate) / 1000; | |
667216d6 KH |
72 | vstepmin = voltdm->pmic->vp_vstepmin; |
73 | vstepmax = voltdm->pmic->vp_vstepmax; | |
74 | ||
75 | /* | |
76 | * VP_CONFIG: error gain is not set here, it will be updated | |
77 | * on each scale, based on OPP. | |
78 | */ | |
79 | val = (voltdm->pmic->vp_erroroffset << | |
80 | __ffs(voltdm->vp->common->vpconfig_erroroffset_mask)) | | |
b7ea803e | 81 | vp->common->vpconfig_timeouten; |
667216d6 KH |
82 | voltdm->write(val, vp->vpconfig); |
83 | ||
84 | /* VSTEPMIN */ | |
85 | val = (waittime << vp->common->vstepmin_smpswaittimemin_shift) | | |
86 | (vstepmin << vp->common->vstepmin_stepmin_shift); | |
87 | voltdm->write(val, vp->vstepmin); | |
88 | ||
89 | /* VSTEPMAX */ | |
90 | val = (vstepmax << vp->common->vstepmax_stepmax_shift) | | |
91 | (waittime << vp->common->vstepmax_smpswaittimemax_shift); | |
92 | voltdm->write(val, vp->vstepmax); | |
93 | ||
94 | /* VLIMITTO */ | |
95 | val = (vddmax << vp->common->vlimitto_vddmax_shift) | | |
96 | (vddmin << vp->common->vlimitto_vddmin_shift) | | |
97 | (timeout << vp->common->vlimitto_timeout_shift); | |
98 | voltdm->write(val, vp->vlimitto); | |
01f48d30 KH |
99 | } |
100 | ||
76ea7424 KH |
101 | int omap_vp_update_errorgain(struct voltagedomain *voltdm, |
102 | unsigned long target_volt) | |
103 | { | |
104 | struct omap_volt_data *volt_data; | |
105 | ||
8798c4ab KH |
106 | if (!voltdm->vp) |
107 | return -EINVAL; | |
108 | ||
76ea7424 KH |
109 | /* Get volt_data corresponding to target_volt */ |
110 | volt_data = omap_voltage_get_voltdata(voltdm, target_volt); | |
111 | if (IS_ERR(volt_data)) | |
112 | return -EINVAL; | |
113 | ||
114 | /* Setting vp errorgain based on the voltage */ | |
115 | voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask, | |
116 | volt_data->vp_errgain << | |
117 | __ffs(voltdm->vp->common->vpconfig_errorgain_mask), | |
118 | voltdm->vp->vpconfig); | |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
01f48d30 KH |
123 | /* VP force update method of voltage scaling */ |
124 | int omap_vp_forceupdate_scale(struct voltagedomain *voltdm, | |
125 | unsigned long target_volt) | |
126 | { | |
b7ea803e | 127 | struct omap_vp_instance *vp = voltdm->vp; |
01f48d30 KH |
128 | u32 vpconfig; |
129 | u8 target_vsel, current_vsel; | |
130 | int ret, timeout = 0; | |
131 | ||
132 | ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, ¤t_vsel); | |
133 | if (ret) | |
134 | return ret; | |
135 | ||
136 | /* | |
137 | * Clear all pending TransactionDone interrupt/status. Typical latency | |
138 | * is <3us | |
139 | */ | |
140 | while (timeout++ < VP_TRANXDONE_TIMEOUT) { | |
b7ea803e KH |
141 | vp->common->ops->clear_txdone(vp->id); |
142 | if (!vp->common->ops->check_txdone(vp->id)) | |
01f48d30 KH |
143 | break; |
144 | udelay(1); | |
145 | } | |
146 | if (timeout >= VP_TRANXDONE_TIMEOUT) { | |
147 | pr_warning("%s: vdd_%s TRANXDONE timeout exceeded." | |
148 | "Voltage change aborted", __func__, voltdm->name); | |
149 | return -ETIMEDOUT; | |
150 | } | |
151 | ||
152 | /* Configure for VP-Force Update */ | |
4bcc475e | 153 | vpconfig = voltdm->read(vp->vpconfig); |
b7ea803e KH |
154 | vpconfig &= ~(vp->common->vpconfig_initvdd | |
155 | vp->common->vpconfig_forceupdate | | |
156 | vp->common->vpconfig_initvoltage_mask); | |
01f48d30 | 157 | vpconfig |= ((target_vsel << |
0ec3041e | 158 | __ffs(vp->common->vpconfig_initvoltage_mask))); |
4bcc475e | 159 | voltdm->write(vpconfig, vp->vpconfig); |
01f48d30 KH |
160 | |
161 | /* Trigger initVDD value copy to voltage processor */ | |
b7ea803e | 162 | vpconfig |= vp->common->vpconfig_initvdd; |
4bcc475e | 163 | voltdm->write(vpconfig, vp->vpconfig); |
01f48d30 KH |
164 | |
165 | /* Force update of voltage */ | |
b7ea803e | 166 | vpconfig |= vp->common->vpconfig_forceupdate; |
4bcc475e | 167 | voltdm->write(vpconfig, vp->vpconfig); |
01f48d30 KH |
168 | |
169 | /* | |
170 | * Wait for TransactionDone. Typical latency is <200us. | |
171 | * Depends on SMPSWAITTIMEMIN/MAX and voltage change | |
172 | */ | |
173 | timeout = 0; | |
b7ea803e | 174 | omap_test_timeout(vp->common->ops->check_txdone(vp->id), |
01f48d30 KH |
175 | VP_TRANXDONE_TIMEOUT, timeout); |
176 | if (timeout >= VP_TRANXDONE_TIMEOUT) | |
177 | pr_err("%s: vdd_%s TRANXDONE timeout exceeded." | |
178 | "TRANXDONE never got set after the voltage update\n", | |
179 | __func__, voltdm->name); | |
180 | ||
181 | omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel); | |
182 | ||
183 | /* | |
184 | * Disable TransactionDone interrupt , clear all status, clear | |
185 | * control registers | |
186 | */ | |
187 | timeout = 0; | |
188 | while (timeout++ < VP_TRANXDONE_TIMEOUT) { | |
b7ea803e KH |
189 | vp->common->ops->clear_txdone(vp->id); |
190 | if (!vp->common->ops->check_txdone(vp->id)) | |
01f48d30 KH |
191 | break; |
192 | udelay(1); | |
193 | } | |
194 | ||
195 | if (timeout >= VP_TRANXDONE_TIMEOUT) | |
196 | pr_warning("%s: vdd_%s TRANXDONE timeout exceeded while trying" | |
197 | "to clear the TRANXDONE status\n", | |
198 | __func__, voltdm->name); | |
199 | ||
4bcc475e | 200 | vpconfig = voltdm->read(vp->vpconfig); |
01f48d30 | 201 | /* Clear initVDD copy trigger bit */ |
b7ea803e | 202 | vpconfig &= ~vp->common->vpconfig_initvdd; |
4bcc475e | 203 | voltdm->write(vpconfig, vp->vpconfig); |
01f48d30 | 204 | /* Clear force bit */ |
b7ea803e | 205 | vpconfig &= ~vp->common->vpconfig_forceupdate; |
4bcc475e | 206 | voltdm->write(vpconfig, vp->vpconfig); |
01f48d30 KH |
207 | |
208 | return 0; | |
209 | } | |
210 | ||
01f48d30 KH |
211 | /** |
212 | * omap_vp_enable() - API to enable a particular VP | |
213 | * @voltdm: pointer to the VDD whose VP is to be enabled. | |
214 | * | |
215 | * This API enables a particular voltage processor. Needed by the smartreflex | |
216 | * class drivers. | |
217 | */ | |
218 | void omap_vp_enable(struct voltagedomain *voltdm) | |
219 | { | |
b7ea803e | 220 | struct omap_vp_instance *vp; |
01f48d30 KH |
221 | u32 vpconfig; |
222 | ||
223 | if (!voltdm || IS_ERR(voltdm)) { | |
224 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
225 | return; | |
226 | } | |
227 | ||
b7ea803e | 228 | vp = voltdm->vp; |
4bcc475e | 229 | if (!voltdm->read || !voltdm->write) { |
01f48d30 KH |
230 | pr_err("%s: No read/write API for accessing vdd_%s regs\n", |
231 | __func__, voltdm->name); | |
232 | return; | |
233 | } | |
234 | ||
235 | /* If VP is already enabled, do nothing. Return */ | |
b7ea803e | 236 | if (vp->enabled) |
01f48d30 KH |
237 | return; |
238 | ||
239 | vp_latch_vsel(voltdm); | |
240 | ||
241 | /* Enable VP */ | |
4bcc475e | 242 | vpconfig = voltdm->read(vp->vpconfig); |
b7ea803e | 243 | vpconfig |= vp->common->vpconfig_vpenable; |
4bcc475e | 244 | voltdm->write(vpconfig, vp->vpconfig); |
b7ea803e | 245 | vp->enabled = true; |
01f48d30 KH |
246 | } |
247 | ||
248 | /** | |
249 | * omap_vp_disable() - API to disable a particular VP | |
250 | * @voltdm: pointer to the VDD whose VP is to be disabled. | |
251 | * | |
252 | * This API disables a particular voltage processor. Needed by the smartreflex | |
253 | * class drivers. | |
254 | */ | |
255 | void omap_vp_disable(struct voltagedomain *voltdm) | |
256 | { | |
b7ea803e | 257 | struct omap_vp_instance *vp; |
01f48d30 KH |
258 | u32 vpconfig; |
259 | int timeout; | |
260 | ||
261 | if (!voltdm || IS_ERR(voltdm)) { | |
262 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
263 | return; | |
264 | } | |
265 | ||
b7ea803e | 266 | vp = voltdm->vp; |
4bcc475e | 267 | if (!voltdm->read || !voltdm->write) { |
01f48d30 KH |
268 | pr_err("%s: No read/write API for accessing vdd_%s regs\n", |
269 | __func__, voltdm->name); | |
270 | return; | |
271 | } | |
272 | ||
273 | /* If VP is already disabled, do nothing. Return */ | |
b7ea803e | 274 | if (!vp->enabled) { |
01f48d30 KH |
275 | pr_warning("%s: Trying to disable VP for vdd_%s when" |
276 | "it is already disabled\n", __func__, voltdm->name); | |
277 | return; | |
278 | } | |
279 | ||
280 | /* Disable VP */ | |
4bcc475e | 281 | vpconfig = voltdm->read(vp->vpconfig); |
b7ea803e | 282 | vpconfig &= ~vp->common->vpconfig_vpenable; |
4bcc475e | 283 | voltdm->write(vpconfig, vp->vpconfig); |
01f48d30 KH |
284 | |
285 | /* | |
286 | * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us | |
287 | */ | |
4bcc475e KH |
288 | omap_test_timeout((voltdm->read(vp->vstatus)), |
289 | VP_IDLE_TIMEOUT, timeout); | |
01f48d30 KH |
290 | |
291 | if (timeout >= VP_IDLE_TIMEOUT) | |
292 | pr_warning("%s: vdd_%s idle timedout\n", | |
293 | __func__, voltdm->name); | |
294 | ||
b7ea803e | 295 | vp->enabled = false; |
01f48d30 KH |
296 | |
297 | return; | |
298 | } |