Commit | Line | Data |
---|---|---|
2f34ce81 TG |
1 | /* |
2 | * OMAP3/OMAP4 Voltage Management Routines | |
3 | * | |
4 | * Author: Thara Gopinath <thara@ti.com> | |
5 | * | |
6 | * Copyright (C) 2007 Texas Instruments, Inc. | |
7 | * Rajendra Nayak <rnayak@ti.com> | |
8 | * Lesly A M <x0080970@ti.com> | |
9 | * | |
c0718df4 | 10 | * Copyright (C) 2008, 2011 Nokia Corporation |
2f34ce81 | 11 | * Kalle Jokiniemi |
c0718df4 | 12 | * Paul Walmsley |
2f34ce81 TG |
13 | * |
14 | * Copyright (C) 2010 Texas Instruments, Inc. | |
15 | * Thara Gopinath <thara@ti.com> | |
16 | * | |
17 | * This program is free software; you can redistribute it and/or modify | |
18 | * it under the terms of the GNU General Public License version 2 as | |
19 | * published by the Free Software Foundation. | |
20 | */ | |
21 | ||
22 | #include <linux/delay.h> | |
23 | #include <linux/io.h> | |
24 | #include <linux/clk.h> | |
25 | #include <linux/err.h> | |
26 | #include <linux/debugfs.h> | |
27 | #include <linux/slab.h> | |
28 | ||
29 | #include <plat/common.h> | |
2f34ce81 TG |
30 | |
31 | #include "prm-regbits-34xx.h" | |
bd38107b TG |
32 | #include "prm-regbits-44xx.h" |
33 | #include "prm44xx.h" | |
34 | #include "prcm44xx.h" | |
35 | #include "prminst44xx.h" | |
2f34ce81 TG |
36 | #include "control.h" |
37 | ||
e1d6f472 PW |
38 | #include "voltage.h" |
39 | ||
c0718df4 PW |
40 | #include "vc.h" |
41 | #include "vp.h" | |
42 | ||
2f34ce81 TG |
43 | #define VOLTAGE_DIR_SIZE 16 |
44 | ||
2f34ce81 | 45 | |
c0718df4 PW |
46 | static struct omap_vdd_info **vdd_info; |
47 | ||
2f34ce81 TG |
48 | /* |
49 | * Number of scalable voltage domains. | |
50 | */ | |
51 | static int nr_scalable_vdd; | |
52 | ||
c0718df4 PW |
53 | /* XXX document */ |
54 | static s16 prm_mod_offs; | |
55 | static s16 prm_irqst_ocp_mod_offs; | |
bd38107b | 56 | |
2f34ce81 TG |
57 | static struct dentry *voltage_dir; |
58 | ||
59 | /* Init function pointers */ | |
c0718df4 PW |
60 | static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd, |
61 | unsigned long target_volt); | |
2f34ce81 TG |
62 | |
63 | static u32 omap3_voltage_read_reg(u16 mod, u8 offset) | |
64 | { | |
65 | return omap2_prm_read_mod_reg(mod, offset); | |
66 | } | |
67 | ||
68 | static void omap3_voltage_write_reg(u32 val, u16 mod, u8 offset) | |
69 | { | |
70 | omap2_prm_write_mod_reg(val, mod, offset); | |
71 | } | |
72 | ||
bd38107b TG |
73 | static u32 omap4_voltage_read_reg(u16 mod, u8 offset) |
74 | { | |
75 | return omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION, | |
76 | mod, offset); | |
77 | } | |
78 | ||
79 | static void omap4_voltage_write_reg(u32 val, u16 mod, u8 offset) | |
80 | { | |
81 | omap4_prminst_write_inst_reg(val, OMAP4430_PRM_PARTITION, mod, offset); | |
82 | } | |
83 | ||
c0718df4 PW |
84 | static int __init _config_common_vdd_data(struct omap_vdd_info *vdd) |
85 | { | |
86 | char *sys_ck_name; | |
87 | struct clk *sys_ck; | |
88 | u32 sys_clk_speed, timeout_val, waittime; | |
89 | ||
90 | /* | |
91 | * XXX Clockfw should handle this, or this should be in a | |
92 | * struct record | |
93 | */ | |
94 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) | |
95 | sys_ck_name = "sys_ck"; | |
96 | else if (cpu_is_omap44xx()) | |
97 | sys_ck_name = "sys_clkin_ck"; | |
98 | else | |
99 | return -EINVAL; | |
100 | ||
101 | /* | |
102 | * Sys clk rate is require to calculate vp timeout value and | |
103 | * smpswaittimemin and smpswaittimemax. | |
104 | */ | |
105 | sys_ck = clk_get(NULL, sys_ck_name); | |
106 | if (IS_ERR(sys_ck)) { | |
107 | pr_warning("%s: Could not get the sys clk to calculate" | |
108 | "various vdd_%s params\n", __func__, vdd->voltdm.name); | |
109 | return -EINVAL; | |
110 | } | |
111 | sys_clk_speed = clk_get_rate(sys_ck); | |
112 | clk_put(sys_ck); | |
113 | /* Divide to avoid overflow */ | |
114 | sys_clk_speed /= 1000; | |
115 | ||
116 | /* Generic voltage parameters */ | |
117 | vdd->curr_volt = 1200000; | |
118 | vdd->volt_scale = vp_forceupdate_scale_voltage; | |
119 | vdd->vp_enabled = false; | |
120 | ||
121 | vdd->vp_rt_data.vpconfig_erroroffset = | |
122 | (vdd->pmic_info->vp_erroroffset << | |
123 | vdd->vp_data->vp_common->vpconfig_erroroffset_shift); | |
124 | ||
125 | timeout_val = (sys_clk_speed * vdd->pmic_info->vp_timeout_us) / 1000; | |
126 | vdd->vp_rt_data.vlimitto_timeout = timeout_val; | |
127 | vdd->vp_rt_data.vlimitto_vddmin = vdd->pmic_info->vp_vddmin; | |
128 | vdd->vp_rt_data.vlimitto_vddmax = vdd->pmic_info->vp_vddmax; | |
129 | ||
130 | waittime = ((vdd->pmic_info->step_size / vdd->pmic_info->slew_rate) * | |
131 | sys_clk_speed) / 1000; | |
132 | vdd->vp_rt_data.vstepmin_smpswaittimemin = waittime; | |
133 | vdd->vp_rt_data.vstepmax_smpswaittimemax = waittime; | |
134 | vdd->vp_rt_data.vstepmin_stepmin = vdd->pmic_info->vp_vstepmin; | |
135 | vdd->vp_rt_data.vstepmax_stepmax = vdd->pmic_info->vp_vstepmax; | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
077fceca TG |
140 | /* Voltage debugfs support */ |
141 | static int vp_volt_debug_get(void *data, u64 *val) | |
142 | { | |
143 | struct omap_vdd_info *vdd = (struct omap_vdd_info *) data; | |
144 | u8 vsel; | |
145 | ||
146 | if (!vdd) { | |
147 | pr_warning("Wrong paramater passed\n"); | |
148 | return -EINVAL; | |
149 | } | |
150 | ||
c0718df4 | 151 | vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage); |
077fceca TG |
152 | pr_notice("curr_vsel = %x\n", vsel); |
153 | ||
154 | if (!vdd->pmic_info->vsel_to_uv) { | |
155 | pr_warning("PMIC function to convert vsel to voltage" | |
156 | "in uV not registerd\n"); | |
157 | return -EINVAL; | |
158 | } | |
159 | ||
160 | *val = vdd->pmic_info->vsel_to_uv(vsel); | |
161 | return 0; | |
162 | } | |
163 | ||
164 | static int nom_volt_debug_get(void *data, u64 *val) | |
165 | { | |
166 | struct omap_vdd_info *vdd = (struct omap_vdd_info *) data; | |
167 | ||
168 | if (!vdd) { | |
169 | pr_warning("Wrong paramater passed\n"); | |
170 | return -EINVAL; | |
171 | } | |
172 | ||
173 | *val = omap_voltage_get_nom_volt(&vdd->voltdm); | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
178 | DEFINE_SIMPLE_ATTRIBUTE(vp_volt_debug_fops, vp_volt_debug_get, NULL, "%llu\n"); | |
179 | DEFINE_SIMPLE_ATTRIBUTE(nom_volt_debug_fops, nom_volt_debug_get, NULL, | |
180 | "%llu\n"); | |
2f34ce81 TG |
181 | static void vp_latch_vsel(struct omap_vdd_info *vdd) |
182 | { | |
183 | u32 vpconfig; | |
2f34ce81 TG |
184 | unsigned long uvdc; |
185 | char vsel; | |
186 | ||
187 | uvdc = omap_voltage_get_nom_volt(&vdd->voltdm); | |
188 | if (!uvdc) { | |
189 | pr_warning("%s: unable to find current voltage for vdd_%s\n", | |
190 | __func__, vdd->voltdm.name); | |
191 | return; | |
192 | } | |
193 | ||
194 | if (!vdd->pmic_info || !vdd->pmic_info->uv_to_vsel) { | |
195 | pr_warning("%s: PMIC function to convert voltage in uV to" | |
196 | " vsel not registered\n", __func__); | |
197 | return; | |
198 | } | |
199 | ||
2f34ce81 TG |
200 | vsel = vdd->pmic_info->uv_to_vsel(uvdc); |
201 | ||
c0718df4 PW |
202 | vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig); |
203 | vpconfig &= ~(vdd->vp_data->vp_common->vpconfig_initvoltage_mask | | |
204 | vdd->vp_data->vp_common->vpconfig_initvdd); | |
205 | vpconfig |= vsel << vdd->vp_data->vp_common->vpconfig_initvoltage_shift; | |
2f34ce81 | 206 | |
c0718df4 | 207 | vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig); |
2f34ce81 TG |
208 | |
209 | /* Trigger initVDD value copy to voltage processor */ | |
c0718df4 PW |
210 | vdd->write_reg((vpconfig | vdd->vp_data->vp_common->vpconfig_initvdd), |
211 | prm_mod_offs, vdd->vp_data->vpconfig); | |
2f34ce81 TG |
212 | |
213 | /* Clear initVDD copy trigger bit */ | |
c0718df4 | 214 | vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig); |
2f34ce81 TG |
215 | } |
216 | ||
217 | /* Generic voltage init functions */ | |
218 | static void __init vp_init(struct omap_vdd_info *vdd) | |
219 | { | |
220 | u32 vp_val; | |
2f34ce81 TG |
221 | |
222 | if (!vdd->read_reg || !vdd->write_reg) { | |
223 | pr_err("%s: No read/write API for accessing vdd_%s regs\n", | |
224 | __func__, vdd->voltdm.name); | |
225 | return; | |
226 | } | |
227 | ||
c0718df4 PW |
228 | vp_val = vdd->vp_rt_data.vpconfig_erroroffset | |
229 | (vdd->vp_rt_data.vpconfig_errorgain << | |
230 | vdd->vp_data->vp_common->vpconfig_errorgain_shift) | | |
231 | vdd->vp_data->vp_common->vpconfig_timeouten; | |
232 | vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vpconfig); | |
233 | ||
234 | vp_val = ((vdd->vp_rt_data.vstepmin_smpswaittimemin << | |
235 | vdd->vp_data->vp_common->vstepmin_smpswaittimemin_shift) | | |
236 | (vdd->vp_rt_data.vstepmin_stepmin << | |
237 | vdd->vp_data->vp_common->vstepmin_stepmin_shift)); | |
238 | vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vstepmin); | |
239 | ||
240 | vp_val = ((vdd->vp_rt_data.vstepmax_smpswaittimemax << | |
241 | vdd->vp_data->vp_common->vstepmax_smpswaittimemax_shift) | | |
242 | (vdd->vp_rt_data.vstepmax_stepmax << | |
243 | vdd->vp_data->vp_common->vstepmax_stepmax_shift)); | |
244 | vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vstepmax); | |
245 | ||
246 | vp_val = ((vdd->vp_rt_data.vlimitto_vddmax << | |
247 | vdd->vp_data->vp_common->vlimitto_vddmax_shift) | | |
248 | (vdd->vp_rt_data.vlimitto_vddmin << | |
249 | vdd->vp_data->vp_common->vlimitto_vddmin_shift) | | |
250 | (vdd->vp_rt_data.vlimitto_timeout << | |
251 | vdd->vp_data->vp_common->vlimitto_timeout_shift)); | |
252 | vdd->write_reg(vp_val, prm_mod_offs, vdd->vp_data->vlimitto); | |
2f34ce81 TG |
253 | } |
254 | ||
255 | static void __init vdd_debugfs_init(struct omap_vdd_info *vdd) | |
256 | { | |
257 | char *name; | |
258 | ||
259 | name = kzalloc(VOLTAGE_DIR_SIZE, GFP_KERNEL); | |
260 | if (!name) { | |
261 | pr_warning("%s: Unable to allocate memory for debugfs" | |
262 | " directory name for vdd_%s", | |
263 | __func__, vdd->voltdm.name); | |
264 | return; | |
265 | } | |
266 | strcpy(name, "vdd_"); | |
267 | strcat(name, vdd->voltdm.name); | |
268 | ||
269 | vdd->debug_dir = debugfs_create_dir(name, voltage_dir); | |
62270119 | 270 | kfree(name); |
2f34ce81 TG |
271 | if (IS_ERR(vdd->debug_dir)) { |
272 | pr_warning("%s: Unable to create debugfs directory for" | |
273 | " vdd_%s\n", __func__, vdd->voltdm.name); | |
274 | vdd->debug_dir = NULL; | |
077fceca | 275 | return; |
2f34ce81 | 276 | } |
077fceca TG |
277 | |
278 | (void) debugfs_create_x16("vp_errorgain", S_IRUGO, vdd->debug_dir, | |
c0718df4 | 279 | &(vdd->vp_rt_data.vpconfig_errorgain)); |
077fceca TG |
280 | (void) debugfs_create_x16("vp_smpswaittimemin", S_IRUGO, |
281 | vdd->debug_dir, | |
c0718df4 | 282 | &(vdd->vp_rt_data.vstepmin_smpswaittimemin)); |
077fceca | 283 | (void) debugfs_create_x8("vp_stepmin", S_IRUGO, vdd->debug_dir, |
c0718df4 | 284 | &(vdd->vp_rt_data.vstepmin_stepmin)); |
077fceca TG |
285 | (void) debugfs_create_x16("vp_smpswaittimemax", S_IRUGO, |
286 | vdd->debug_dir, | |
c0718df4 | 287 | &(vdd->vp_rt_data.vstepmax_smpswaittimemax)); |
077fceca | 288 | (void) debugfs_create_x8("vp_stepmax", S_IRUGO, vdd->debug_dir, |
c0718df4 | 289 | &(vdd->vp_rt_data.vstepmax_stepmax)); |
077fceca | 290 | (void) debugfs_create_x8("vp_vddmax", S_IRUGO, vdd->debug_dir, |
c0718df4 | 291 | &(vdd->vp_rt_data.vlimitto_vddmax)); |
077fceca | 292 | (void) debugfs_create_x8("vp_vddmin", S_IRUGO, vdd->debug_dir, |
c0718df4 | 293 | &(vdd->vp_rt_data.vlimitto_vddmin)); |
077fceca | 294 | (void) debugfs_create_x16("vp_timeout", S_IRUGO, vdd->debug_dir, |
c0718df4 | 295 | &(vdd->vp_rt_data.vlimitto_timeout)); |
077fceca TG |
296 | (void) debugfs_create_file("curr_vp_volt", S_IRUGO, vdd->debug_dir, |
297 | (void *) vdd, &vp_volt_debug_fops); | |
298 | (void) debugfs_create_file("curr_nominal_volt", S_IRUGO, | |
299 | vdd->debug_dir, (void *) vdd, | |
300 | &nom_volt_debug_fops); | |
2f34ce81 TG |
301 | } |
302 | ||
303 | /* Voltage scale and accessory APIs */ | |
304 | static int _pre_volt_scale(struct omap_vdd_info *vdd, | |
305 | unsigned long target_volt, u8 *target_vsel, u8 *current_vsel) | |
306 | { | |
307 | struct omap_volt_data *volt_data; | |
c0718df4 PW |
308 | const struct omap_vc_common_data *vc_common; |
309 | const struct omap_vp_common_data *vp_common; | |
2f34ce81 | 310 | u32 vc_cmdval, vp_errgain_val; |
c0718df4 PW |
311 | |
312 | vc_common = vdd->vc_data->vc_common; | |
313 | vp_common = vdd->vp_data->vp_common; | |
2f34ce81 TG |
314 | |
315 | /* Check if suffiecient pmic info is available for this vdd */ | |
316 | if (!vdd->pmic_info) { | |
317 | pr_err("%s: Insufficient pmic info to scale the vdd_%s\n", | |
318 | __func__, vdd->voltdm.name); | |
319 | return -EINVAL; | |
320 | } | |
321 | ||
322 | if (!vdd->pmic_info->uv_to_vsel) { | |
323 | pr_err("%s: PMIC function to convert voltage in uV to" | |
324 | "vsel not registered. Hence unable to scale voltage" | |
325 | "for vdd_%s\n", __func__, vdd->voltdm.name); | |
326 | return -ENODATA; | |
327 | } | |
328 | ||
329 | if (!vdd->read_reg || !vdd->write_reg) { | |
330 | pr_err("%s: No read/write API for accessing vdd_%s regs\n", | |
331 | __func__, vdd->voltdm.name); | |
332 | return -EINVAL; | |
333 | } | |
334 | ||
2f34ce81 TG |
335 | /* Get volt_data corresponding to target_volt */ |
336 | volt_data = omap_voltage_get_voltdata(&vdd->voltdm, target_volt); | |
337 | if (IS_ERR(volt_data)) | |
338 | volt_data = NULL; | |
339 | ||
340 | *target_vsel = vdd->pmic_info->uv_to_vsel(target_volt); | |
c0718df4 | 341 | *current_vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage); |
2f34ce81 TG |
342 | |
343 | /* Setting the ON voltage to the new target voltage */ | |
c0718df4 PW |
344 | vc_cmdval = vdd->read_reg(prm_mod_offs, vdd->vc_data->cmdval_reg); |
345 | vc_cmdval &= ~vc_common->cmd_on_mask; | |
346 | vc_cmdval |= (*target_vsel << vc_common->cmd_on_shift); | |
347 | vdd->write_reg(vc_cmdval, prm_mod_offs, vdd->vc_data->cmdval_reg); | |
2f34ce81 TG |
348 | |
349 | /* Setting vp errorgain based on the voltage */ | |
350 | if (volt_data) { | |
c0718df4 PW |
351 | vp_errgain_val = vdd->read_reg(prm_mod_offs, |
352 | vdd->vp_data->vpconfig); | |
353 | vdd->vp_rt_data.vpconfig_errorgain = volt_data->vp_errgain; | |
354 | vp_errgain_val &= ~vp_common->vpconfig_errorgain_mask; | |
355 | vp_errgain_val |= vdd->vp_rt_data.vpconfig_errorgain << | |
356 | vp_common->vpconfig_errorgain_shift; | |
357 | vdd->write_reg(vp_errgain_val, prm_mod_offs, | |
358 | vdd->vp_data->vpconfig); | |
2f34ce81 TG |
359 | } |
360 | ||
361 | return 0; | |
362 | } | |
363 | ||
364 | static void _post_volt_scale(struct omap_vdd_info *vdd, | |
365 | unsigned long target_volt, u8 target_vsel, u8 current_vsel) | |
366 | { | |
367 | u32 smps_steps = 0, smps_delay = 0; | |
368 | ||
369 | smps_steps = abs(target_vsel - current_vsel); | |
370 | /* SMPS slew rate / step size. 2us added as buffer. */ | |
371 | smps_delay = ((smps_steps * vdd->pmic_info->step_size) / | |
372 | vdd->pmic_info->slew_rate) + 2; | |
373 | udelay(smps_delay); | |
374 | ||
375 | vdd->curr_volt = target_volt; | |
376 | } | |
377 | ||
378 | /* vc_bypass_scale_voltage - VC bypass method of voltage scaling */ | |
379 | static int vc_bypass_scale_voltage(struct omap_vdd_info *vdd, | |
380 | unsigned long target_volt) | |
381 | { | |
382 | u32 loop_cnt = 0, retries_cnt = 0; | |
383 | u32 vc_valid, vc_bypass_val_reg, vc_bypass_value; | |
2f34ce81 TG |
384 | u8 target_vsel, current_vsel; |
385 | int ret; | |
386 | ||
387 | ret = _pre_volt_scale(vdd, target_volt, &target_vsel, ¤t_vsel); | |
388 | if (ret) | |
389 | return ret; | |
390 | ||
c0718df4 PW |
391 | vc_valid = vdd->vc_data->vc_common->valid; |
392 | vc_bypass_val_reg = vdd->vc_data->vc_common->bypass_val_reg; | |
393 | vc_bypass_value = (target_vsel << vdd->vc_data->vc_common->data_shift) | | |
2f34ce81 | 394 | (vdd->pmic_info->pmic_reg << |
c0718df4 | 395 | vdd->vc_data->vc_common->regaddr_shift) | |
2f34ce81 | 396 | (vdd->pmic_info->i2c_slave_addr << |
c0718df4 | 397 | vdd->vc_data->vc_common->slaveaddr_shift); |
2f34ce81 | 398 | |
c0718df4 PW |
399 | vdd->write_reg(vc_bypass_value, prm_mod_offs, vc_bypass_val_reg); |
400 | vdd->write_reg(vc_bypass_value | vc_valid, prm_mod_offs, | |
401 | vc_bypass_val_reg); | |
2f34ce81 | 402 | |
c0718df4 | 403 | vc_bypass_value = vdd->read_reg(prm_mod_offs, vc_bypass_val_reg); |
2f34ce81 TG |
404 | /* |
405 | * Loop till the bypass command is acknowledged from the SMPS. | |
406 | * NOTE: This is legacy code. The loop count and retry count needs | |
407 | * to be revisited. | |
408 | */ | |
409 | while (!(vc_bypass_value & vc_valid)) { | |
410 | loop_cnt++; | |
411 | ||
412 | if (retries_cnt > 10) { | |
413 | pr_warning("%s: Retry count exceeded\n", __func__); | |
414 | return -ETIMEDOUT; | |
415 | } | |
416 | ||
417 | if (loop_cnt > 50) { | |
418 | retries_cnt++; | |
419 | loop_cnt = 0; | |
420 | udelay(10); | |
421 | } | |
c0718df4 PW |
422 | vc_bypass_value = vdd->read_reg(prm_mod_offs, |
423 | vc_bypass_val_reg); | |
2f34ce81 TG |
424 | } |
425 | ||
426 | _post_volt_scale(vdd, target_volt, target_vsel, current_vsel); | |
427 | return 0; | |
428 | } | |
429 | ||
430 | /* VP force update method of voltage scaling */ | |
431 | static int vp_forceupdate_scale_voltage(struct omap_vdd_info *vdd, | |
432 | unsigned long target_volt) | |
433 | { | |
434 | u32 vpconfig; | |
2f34ce81 TG |
435 | u8 target_vsel, current_vsel, prm_irqst_reg; |
436 | int ret, timeout = 0; | |
437 | ||
438 | ret = _pre_volt_scale(vdd, target_volt, &target_vsel, ¤t_vsel); | |
439 | if (ret) | |
440 | return ret; | |
441 | ||
c0718df4 | 442 | prm_irqst_reg = vdd->vp_data->prm_irqst_data->prm_irqst_reg; |
2f34ce81 TG |
443 | |
444 | /* | |
445 | * Clear all pending TransactionDone interrupt/status. Typical latency | |
446 | * is <3us | |
447 | */ | |
448 | while (timeout++ < VP_TRANXDONE_TIMEOUT) { | |
c0718df4 PW |
449 | vdd->write_reg(vdd->vp_data->prm_irqst_data->tranxdone_status, |
450 | prm_irqst_ocp_mod_offs, prm_irqst_reg); | |
451 | if (!(vdd->read_reg(prm_irqst_ocp_mod_offs, prm_irqst_reg) & | |
452 | vdd->vp_data->prm_irqst_data->tranxdone_status)) | |
453 | break; | |
2f34ce81 TG |
454 | udelay(1); |
455 | } | |
456 | if (timeout >= VP_TRANXDONE_TIMEOUT) { | |
457 | pr_warning("%s: vdd_%s TRANXDONE timeout exceeded." | |
458 | "Voltage change aborted", __func__, vdd->voltdm.name); | |
459 | return -ETIMEDOUT; | |
460 | } | |
461 | ||
462 | /* Configure for VP-Force Update */ | |
c0718df4 PW |
463 | vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig); |
464 | vpconfig &= ~(vdd->vp_data->vp_common->vpconfig_initvdd | | |
465 | vdd->vp_data->vp_common->vpconfig_forceupdate | | |
466 | vdd->vp_data->vp_common->vpconfig_initvoltage_mask); | |
2f34ce81 | 467 | vpconfig |= ((target_vsel << |
c0718df4 PW |
468 | vdd->vp_data->vp_common->vpconfig_initvoltage_shift)); |
469 | vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig); | |
2f34ce81 TG |
470 | |
471 | /* Trigger initVDD value copy to voltage processor */ | |
c0718df4 PW |
472 | vpconfig |= vdd->vp_data->vp_common->vpconfig_initvdd; |
473 | vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig); | |
2f34ce81 TG |
474 | |
475 | /* Force update of voltage */ | |
c0718df4 PW |
476 | vpconfig |= vdd->vp_data->vp_common->vpconfig_forceupdate; |
477 | vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig); | |
2f34ce81 TG |
478 | |
479 | /* | |
480 | * Wait for TransactionDone. Typical latency is <200us. | |
481 | * Depends on SMPSWAITTIMEMIN/MAX and voltage change | |
482 | */ | |
483 | timeout = 0; | |
c0718df4 PW |
484 | omap_test_timeout((vdd->read_reg(prm_irqst_ocp_mod_offs, prm_irqst_reg) & |
485 | vdd->vp_data->prm_irqst_data->tranxdone_status), | |
486 | VP_TRANXDONE_TIMEOUT, timeout); | |
2f34ce81 TG |
487 | if (timeout >= VP_TRANXDONE_TIMEOUT) |
488 | pr_err("%s: vdd_%s TRANXDONE timeout exceeded." | |
489 | "TRANXDONE never got set after the voltage update\n", | |
490 | __func__, vdd->voltdm.name); | |
491 | ||
492 | _post_volt_scale(vdd, target_volt, target_vsel, current_vsel); | |
493 | ||
494 | /* | |
495 | * Disable TransactionDone interrupt , clear all status, clear | |
496 | * control registers | |
497 | */ | |
498 | timeout = 0; | |
499 | while (timeout++ < VP_TRANXDONE_TIMEOUT) { | |
c0718df4 PW |
500 | vdd->write_reg(vdd->vp_data->prm_irqst_data->tranxdone_status, |
501 | prm_irqst_ocp_mod_offs, prm_irqst_reg); | |
502 | if (!(vdd->read_reg(prm_irqst_ocp_mod_offs, prm_irqst_reg) & | |
503 | vdd->vp_data->prm_irqst_data->tranxdone_status)) | |
504 | break; | |
2f34ce81 TG |
505 | udelay(1); |
506 | } | |
507 | ||
508 | if (timeout >= VP_TRANXDONE_TIMEOUT) | |
509 | pr_warning("%s: vdd_%s TRANXDONE timeout exceeded while trying" | |
510 | "to clear the TRANXDONE status\n", | |
511 | __func__, vdd->voltdm.name); | |
512 | ||
c0718df4 | 513 | vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig); |
2f34ce81 | 514 | /* Clear initVDD copy trigger bit */ |
c0718df4 PW |
515 | vpconfig &= ~vdd->vp_data->vp_common->vpconfig_initvdd; |
516 | vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig); | |
2f34ce81 | 517 | /* Clear force bit */ |
c0718df4 PW |
518 | vpconfig &= ~vdd->vp_data->vp_common->vpconfig_forceupdate; |
519 | vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig); | |
2f34ce81 TG |
520 | |
521 | return 0; | |
522 | } | |
523 | ||
c0718df4 PW |
524 | static void __init omap3_vfsm_init(struct omap_vdd_info *vdd) |
525 | { | |
526 | /* | |
527 | * Voltage Manager FSM parameters init | |
528 | * XXX This data should be passed in from the board file | |
529 | */ | |
530 | vdd->write_reg(OMAP3_CLKSETUP, prm_mod_offs, OMAP3_PRM_CLKSETUP_OFFSET); | |
531 | vdd->write_reg(OMAP3_VOLTOFFSET, prm_mod_offs, | |
532 | OMAP3_PRM_VOLTOFFSET_OFFSET); | |
533 | vdd->write_reg(OMAP3_VOLTSETUP2, prm_mod_offs, | |
534 | OMAP3_PRM_VOLTSETUP2_OFFSET); | |
535 | } | |
2f34ce81 | 536 | |
2f34ce81 TG |
537 | static void __init omap3_vc_init(struct omap_vdd_info *vdd) |
538 | { | |
2f34ce81 | 539 | static bool is_initialized; |
c0718df4 PW |
540 | u8 on_vsel, onlp_vsel, ret_vsel, off_vsel; |
541 | u32 vc_val; | |
2f34ce81 | 542 | |
c0718df4 | 543 | if (is_initialized) |
2f34ce81 | 544 | return; |
2f34ce81 TG |
545 | |
546 | /* Set up the on, inactive, retention and off voltage */ | |
547 | on_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->on_volt); | |
548 | onlp_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->onlp_volt); | |
549 | ret_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->ret_volt); | |
550 | off_vsel = vdd->pmic_info->uv_to_vsel(vdd->pmic_info->off_volt); | |
c0718df4 PW |
551 | vc_val = ((on_vsel << vdd->vc_data->vc_common->cmd_on_shift) | |
552 | (onlp_vsel << vdd->vc_data->vc_common->cmd_onlp_shift) | | |
553 | (ret_vsel << vdd->vc_data->vc_common->cmd_ret_shift) | | |
554 | (off_vsel << vdd->vc_data->vc_common->cmd_off_shift)); | |
555 | vdd->write_reg(vc_val, prm_mod_offs, vdd->vc_data->cmdval_reg); | |
2f34ce81 | 556 | |
c0718df4 PW |
557 | /* |
558 | * Generic VC parameters init | |
559 | * XXX This data should be abstracted out | |
560 | */ | |
561 | vdd->write_reg(OMAP3430_CMD1_MASK | OMAP3430_RAV1_MASK, prm_mod_offs, | |
2f34ce81 | 562 | OMAP3_PRM_VC_CH_CONF_OFFSET); |
c0718df4 | 563 | vdd->write_reg(OMAP3430_MCODE_SHIFT | OMAP3430_HSEN_MASK, prm_mod_offs, |
2f34ce81 | 564 | OMAP3_PRM_VC_I2C_CFG_OFFSET); |
c0718df4 PW |
565 | |
566 | omap3_vfsm_init(vdd); | |
567 | ||
2f34ce81 TG |
568 | is_initialized = true; |
569 | } | |
570 | ||
c0718df4 PW |
571 | |
572 | /* OMAP4 specific voltage init functions */ | |
573 | static void __init omap4_vc_init(struct omap_vdd_info *vdd) | |
2f34ce81 | 574 | { |
c0718df4 PW |
575 | static bool is_initialized; |
576 | u32 vc_val; | |
2f34ce81 | 577 | |
c0718df4 PW |
578 | if (is_initialized) |
579 | return; | |
2f34ce81 | 580 | |
c0718df4 | 581 | /* TODO: Configure setup times and CMD_VAL values*/ |
2f34ce81 TG |
582 | |
583 | /* | |
c0718df4 PW |
584 | * Generic VC parameters init |
585 | * XXX This data should be abstracted out | |
2f34ce81 | 586 | */ |
c0718df4 PW |
587 | vc_val = (OMAP4430_RAV_VDD_MPU_L_MASK | OMAP4430_CMD_VDD_MPU_L_MASK | |
588 | OMAP4430_RAV_VDD_IVA_L_MASK | OMAP4430_CMD_VDD_IVA_L_MASK | | |
589 | OMAP4430_RAV_VDD_CORE_L_MASK | OMAP4430_CMD_VDD_CORE_L_MASK); | |
590 | vdd->write_reg(vc_val, prm_mod_offs, OMAP4_PRM_VC_CFG_CHANNEL_OFFSET); | |
2f34ce81 | 591 | |
c0718df4 PW |
592 | /* XXX These are magic numbers and do not belong! */ |
593 | vc_val = (0x60 << OMAP4430_SCLL_SHIFT | 0x26 << OMAP4430_SCLH_SHIFT); | |
594 | vdd->write_reg(vc_val, prm_mod_offs, OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET); | |
2f34ce81 | 595 | |
c0718df4 | 596 | is_initialized = true; |
2f34ce81 TG |
597 | } |
598 | ||
c0718df4 | 599 | static void __init omap_vc_init(struct omap_vdd_info *vdd) |
bd38107b TG |
600 | { |
601 | u32 vc_val; | |
bd38107b TG |
602 | |
603 | if (!vdd->pmic_info || !vdd->pmic_info->uv_to_vsel) { | |
604 | pr_err("%s: PMIC info requried to configure vc for" | |
605 | "vdd_%s not populated.Hence cannot initialize vc\n", | |
606 | __func__, vdd->voltdm.name); | |
607 | return; | |
608 | } | |
609 | ||
610 | if (!vdd->read_reg || !vdd->write_reg) { | |
611 | pr_err("%s: No read/write API for accessing vdd_%s regs\n", | |
612 | __func__, vdd->voltdm.name); | |
613 | return; | |
614 | } | |
615 | ||
bd38107b | 616 | /* Set up the SMPS_SA(i2c slave address in VC */ |
c0718df4 PW |
617 | vc_val = vdd->read_reg(prm_mod_offs, |
618 | vdd->vc_data->vc_common->smps_sa_reg); | |
619 | vc_val &= ~vdd->vc_data->smps_sa_mask; | |
620 | vc_val |= vdd->pmic_info->i2c_slave_addr << vdd->vc_data->smps_sa_shift; | |
621 | vdd->write_reg(vc_val, prm_mod_offs, | |
622 | vdd->vc_data->vc_common->smps_sa_reg); | |
bd38107b TG |
623 | |
624 | /* Setup the VOLRA(pmic reg addr) in VC */ | |
c0718df4 PW |
625 | vc_val = vdd->read_reg(prm_mod_offs, |
626 | vdd->vc_data->vc_common->smps_volra_reg); | |
627 | vc_val &= ~vdd->vc_data->smps_volra_mask; | |
628 | vc_val |= vdd->pmic_info->pmic_reg << vdd->vc_data->smps_volra_shift; | |
629 | vdd->write_reg(vc_val, prm_mod_offs, | |
630 | vdd->vc_data->vc_common->smps_volra_reg); | |
631 | ||
632 | /* Configure the setup times */ | |
633 | vc_val = vdd->read_reg(prm_mod_offs, vdd->vfsm->voltsetup_reg); | |
634 | vc_val &= ~vdd->vfsm->voltsetup_mask; | |
635 | vc_val |= vdd->pmic_info->volt_setup_time << | |
636 | vdd->vfsm->voltsetup_shift; | |
637 | vdd->write_reg(vc_val, prm_mod_offs, vdd->vfsm->voltsetup_reg); | |
bd38107b | 638 | |
c0718df4 PW |
639 | if (cpu_is_omap34xx()) |
640 | omap3_vc_init(vdd); | |
641 | else if (cpu_is_omap44xx()) | |
642 | omap4_vc_init(vdd); | |
bd38107b TG |
643 | } |
644 | ||
c0718df4 | 645 | static int __init omap_vdd_data_configure(struct omap_vdd_info *vdd) |
bd38107b | 646 | { |
c0718df4 | 647 | int ret = -EINVAL; |
bd38107b TG |
648 | |
649 | if (!vdd->pmic_info) { | |
650 | pr_err("%s: PMIC info requried to configure vdd_%s not" | |
651 | "populated.Hence cannot initialize vdd_%s\n", | |
652 | __func__, vdd->voltdm.name, vdd->voltdm.name); | |
c0718df4 | 653 | goto ovdc_out; |
bd38107b TG |
654 | } |
655 | ||
c0718df4 PW |
656 | if (IS_ERR_VALUE(_config_common_vdd_data(vdd))) |
657 | goto ovdc_out; | |
bd38107b | 658 | |
c0718df4 PW |
659 | if (cpu_is_omap34xx()) { |
660 | vdd->read_reg = omap3_voltage_read_reg; | |
661 | vdd->write_reg = omap3_voltage_write_reg; | |
662 | ret = 0; | |
663 | } else if (cpu_is_omap44xx()) { | |
664 | vdd->read_reg = omap4_voltage_read_reg; | |
665 | vdd->write_reg = omap4_voltage_write_reg; | |
666 | ret = 0; | |
bd38107b | 667 | } |
bd38107b | 668 | |
c0718df4 PW |
669 | ovdc_out: |
670 | return ret; | |
bd38107b TG |
671 | } |
672 | ||
2f34ce81 TG |
673 | /* Public functions */ |
674 | /** | |
675 | * omap_voltage_get_nom_volt() - Gets the current non-auto-compensated voltage | |
676 | * @voltdm: pointer to the VDD for which current voltage info is needed | |
677 | * | |
678 | * API to get the current non-auto-compensated voltage for a VDD. | |
679 | * Returns 0 in case of error else returns the current voltage for the VDD. | |
680 | */ | |
681 | unsigned long omap_voltage_get_nom_volt(struct voltagedomain *voltdm) | |
682 | { | |
683 | struct omap_vdd_info *vdd; | |
684 | ||
685 | if (!voltdm || IS_ERR(voltdm)) { | |
686 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
687 | return 0; | |
688 | } | |
689 | ||
690 | vdd = container_of(voltdm, struct omap_vdd_info, voltdm); | |
691 | ||
692 | return vdd->curr_volt; | |
693 | } | |
694 | ||
695 | /** | |
696 | * omap_vp_get_curr_volt() - API to get the current vp voltage. | |
697 | * @voltdm: pointer to the VDD. | |
698 | * | |
699 | * This API returns the current voltage for the specified voltage processor | |
700 | */ | |
701 | unsigned long omap_vp_get_curr_volt(struct voltagedomain *voltdm) | |
702 | { | |
703 | struct omap_vdd_info *vdd; | |
704 | u8 curr_vsel; | |
705 | ||
706 | if (!voltdm || IS_ERR(voltdm)) { | |
707 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
708 | return 0; | |
709 | } | |
710 | ||
711 | vdd = container_of(voltdm, struct omap_vdd_info, voltdm); | |
712 | if (!vdd->read_reg) { | |
713 | pr_err("%s: No read API for reading vdd_%s regs\n", | |
714 | __func__, voltdm->name); | |
715 | return 0; | |
716 | } | |
717 | ||
c0718df4 | 718 | curr_vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage); |
2f34ce81 TG |
719 | |
720 | if (!vdd->pmic_info || !vdd->pmic_info->vsel_to_uv) { | |
721 | pr_warning("%s: PMIC function to convert vsel to voltage" | |
722 | "in uV not registerd\n", __func__); | |
723 | return 0; | |
724 | } | |
725 | ||
726 | return vdd->pmic_info->vsel_to_uv(curr_vsel); | |
727 | } | |
728 | ||
729 | /** | |
730 | * omap_vp_enable() - API to enable a particular VP | |
731 | * @voltdm: pointer to the VDD whose VP is to be enabled. | |
732 | * | |
733 | * This API enables a particular voltage processor. Needed by the smartreflex | |
734 | * class drivers. | |
735 | */ | |
736 | void omap_vp_enable(struct voltagedomain *voltdm) | |
737 | { | |
738 | struct omap_vdd_info *vdd; | |
739 | u32 vpconfig; | |
2f34ce81 TG |
740 | |
741 | if (!voltdm || IS_ERR(voltdm)) { | |
742 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
743 | return; | |
744 | } | |
745 | ||
746 | vdd = container_of(voltdm, struct omap_vdd_info, voltdm); | |
747 | if (!vdd->read_reg || !vdd->write_reg) { | |
748 | pr_err("%s: No read/write API for accessing vdd_%s regs\n", | |
749 | __func__, voltdm->name); | |
750 | return; | |
751 | } | |
752 | ||
2f34ce81 TG |
753 | /* If VP is already enabled, do nothing. Return */ |
754 | if (vdd->vp_enabled) | |
755 | return; | |
756 | ||
757 | vp_latch_vsel(vdd); | |
758 | ||
759 | /* Enable VP */ | |
c0718df4 PW |
760 | vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig); |
761 | vpconfig |= vdd->vp_data->vp_common->vpconfig_vpenable; | |
762 | vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig); | |
2f34ce81 TG |
763 | vdd->vp_enabled = true; |
764 | } | |
765 | ||
766 | /** | |
767 | * omap_vp_disable() - API to disable a particular VP | |
768 | * @voltdm: pointer to the VDD whose VP is to be disabled. | |
769 | * | |
770 | * This API disables a particular voltage processor. Needed by the smartreflex | |
771 | * class drivers. | |
772 | */ | |
773 | void omap_vp_disable(struct voltagedomain *voltdm) | |
774 | { | |
775 | struct omap_vdd_info *vdd; | |
776 | u32 vpconfig; | |
2f34ce81 TG |
777 | int timeout; |
778 | ||
779 | if (!voltdm || IS_ERR(voltdm)) { | |
780 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
781 | return; | |
782 | } | |
783 | ||
784 | vdd = container_of(voltdm, struct omap_vdd_info, voltdm); | |
785 | if (!vdd->read_reg || !vdd->write_reg) { | |
786 | pr_err("%s: No read/write API for accessing vdd_%s regs\n", | |
787 | __func__, voltdm->name); | |
788 | return; | |
789 | } | |
790 | ||
2f34ce81 TG |
791 | /* If VP is already disabled, do nothing. Return */ |
792 | if (!vdd->vp_enabled) { | |
793 | pr_warning("%s: Trying to disable VP for vdd_%s when" | |
794 | "it is already disabled\n", __func__, voltdm->name); | |
795 | return; | |
796 | } | |
797 | ||
798 | /* Disable VP */ | |
c0718df4 PW |
799 | vpconfig = vdd->read_reg(prm_mod_offs, vdd->vp_data->vpconfig); |
800 | vpconfig &= ~vdd->vp_data->vp_common->vpconfig_vpenable; | |
801 | vdd->write_reg(vpconfig, prm_mod_offs, vdd->vp_data->vpconfig); | |
2f34ce81 TG |
802 | |
803 | /* | |
804 | * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us | |
805 | */ | |
c0718df4 | 806 | omap_test_timeout((vdd->read_reg(prm_mod_offs, vdd->vp_data->vstatus)), |
2f34ce81 TG |
807 | VP_IDLE_TIMEOUT, timeout); |
808 | ||
809 | if (timeout >= VP_IDLE_TIMEOUT) | |
810 | pr_warning("%s: vdd_%s idle timedout\n", | |
811 | __func__, voltdm->name); | |
812 | ||
813 | vdd->vp_enabled = false; | |
814 | ||
815 | return; | |
816 | } | |
817 | ||
818 | /** | |
819 | * omap_voltage_scale_vdd() - API to scale voltage of a particular | |
820 | * voltage domain. | |
821 | * @voltdm: pointer to the VDD which is to be scaled. | |
822 | * @target_volt: The target voltage of the voltage domain | |
823 | * | |
824 | * This API should be called by the kernel to do the voltage scaling | |
825 | * for a particular voltage domain during dvfs or any other situation. | |
826 | */ | |
827 | int omap_voltage_scale_vdd(struct voltagedomain *voltdm, | |
828 | unsigned long target_volt) | |
829 | { | |
830 | struct omap_vdd_info *vdd; | |
831 | ||
832 | if (!voltdm || IS_ERR(voltdm)) { | |
833 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
834 | return -EINVAL; | |
835 | } | |
836 | ||
837 | vdd = container_of(voltdm, struct omap_vdd_info, voltdm); | |
838 | ||
839 | if (!vdd->volt_scale) { | |
840 | pr_err("%s: No voltage scale API registered for vdd_%s\n", | |
841 | __func__, voltdm->name); | |
842 | return -ENODATA; | |
843 | } | |
844 | ||
845 | return vdd->volt_scale(vdd, target_volt); | |
846 | } | |
847 | ||
848 | /** | |
849 | * omap_voltage_reset() - Resets the voltage of a particular voltage domain | |
850 | * to that of the current OPP. | |
851 | * @voltdm: pointer to the VDD whose voltage is to be reset. | |
852 | * | |
853 | * This API finds out the correct voltage the voltage domain is supposed | |
25985edc | 854 | * to be at and resets the voltage to that level. Should be used especially |
2f34ce81 TG |
855 | * while disabling any voltage compensation modules. |
856 | */ | |
857 | void omap_voltage_reset(struct voltagedomain *voltdm) | |
858 | { | |
859 | unsigned long target_uvdc; | |
860 | ||
861 | if (!voltdm || IS_ERR(voltdm)) { | |
862 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
863 | return; | |
864 | } | |
865 | ||
866 | target_uvdc = omap_voltage_get_nom_volt(voltdm); | |
867 | if (!target_uvdc) { | |
868 | pr_err("%s: unable to find current voltage for vdd_%s\n", | |
869 | __func__, voltdm->name); | |
870 | return; | |
871 | } | |
872 | ||
873 | omap_voltage_scale_vdd(voltdm, target_uvdc); | |
874 | } | |
875 | ||
876 | /** | |
877 | * omap_voltage_get_volttable() - API to get the voltage table associated with a | |
878 | * particular voltage domain. | |
879 | * @voltdm: pointer to the VDD for which the voltage table is required | |
880 | * @volt_data: the voltage table for the particular vdd which is to be | |
881 | * populated by this API | |
882 | * | |
883 | * This API populates the voltage table associated with a VDD into the | |
884 | * passed parameter pointer. Returns the count of distinct voltages | |
885 | * supported by this vdd. | |
886 | * | |
887 | */ | |
888 | void omap_voltage_get_volttable(struct voltagedomain *voltdm, | |
889 | struct omap_volt_data **volt_data) | |
890 | { | |
891 | struct omap_vdd_info *vdd; | |
892 | ||
893 | if (!voltdm || IS_ERR(voltdm)) { | |
894 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
895 | return; | |
896 | } | |
897 | ||
898 | vdd = container_of(voltdm, struct omap_vdd_info, voltdm); | |
899 | ||
900 | *volt_data = vdd->volt_data; | |
901 | } | |
902 | ||
903 | /** | |
904 | * omap_voltage_get_voltdata() - API to get the voltage table entry for a | |
905 | * particular voltage | |
906 | * @voltdm: pointer to the VDD whose voltage table has to be searched | |
907 | * @volt: the voltage to be searched in the voltage table | |
908 | * | |
909 | * This API searches through the voltage table for the required voltage | |
910 | * domain and tries to find a matching entry for the passed voltage volt. | |
911 | * If a matching entry is found volt_data is populated with that entry. | |
912 | * This API searches only through the non-compensated voltages int the | |
913 | * voltage table. | |
914 | * Returns pointer to the voltage table entry corresponding to volt on | |
25985edc | 915 | * success. Returns -ENODATA if no voltage table exisits for the passed voltage |
2f34ce81 TG |
916 | * domain or if there is no matching entry. |
917 | */ | |
918 | struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, | |
919 | unsigned long volt) | |
920 | { | |
921 | struct omap_vdd_info *vdd; | |
922 | int i; | |
923 | ||
924 | if (!voltdm || IS_ERR(voltdm)) { | |
925 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
926 | return ERR_PTR(-EINVAL); | |
927 | } | |
928 | ||
929 | vdd = container_of(voltdm, struct omap_vdd_info, voltdm); | |
930 | ||
931 | if (!vdd->volt_data) { | |
932 | pr_warning("%s: voltage table does not exist for vdd_%s\n", | |
933 | __func__, voltdm->name); | |
934 | return ERR_PTR(-ENODATA); | |
935 | } | |
936 | ||
937 | for (i = 0; vdd->volt_data[i].volt_nominal != 0; i++) { | |
938 | if (vdd->volt_data[i].volt_nominal == volt) | |
939 | return &vdd->volt_data[i]; | |
940 | } | |
941 | ||
942 | pr_notice("%s: Unable to match the current voltage with the voltage" | |
943 | "table for vdd_%s\n", __func__, voltdm->name); | |
944 | ||
945 | return ERR_PTR(-ENODATA); | |
946 | } | |
947 | ||
948 | /** | |
949 | * omap_voltage_register_pmic() - API to register PMIC specific data | |
950 | * @voltdm: pointer to the VDD for which the PMIC specific data is | |
951 | * to be registered | |
952 | * @pmic_info: the structure containing pmic info | |
953 | * | |
954 | * This API is to be called by the SOC/PMIC file to specify the | |
955 | * pmic specific info as present in omap_volt_pmic_info structure. | |
956 | */ | |
957 | int omap_voltage_register_pmic(struct voltagedomain *voltdm, | |
958 | struct omap_volt_pmic_info *pmic_info) | |
959 | { | |
960 | struct omap_vdd_info *vdd; | |
961 | ||
962 | if (!voltdm || IS_ERR(voltdm)) { | |
963 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
964 | return -EINVAL; | |
965 | } | |
966 | ||
967 | vdd = container_of(voltdm, struct omap_vdd_info, voltdm); | |
968 | ||
969 | vdd->pmic_info = pmic_info; | |
970 | ||
971 | return 0; | |
972 | } | |
973 | ||
974 | /** | |
975 | * omap_voltage_get_dbgdir() - API to get pointer to the debugfs directory | |
976 | * corresponding to a voltage domain. | |
977 | * | |
978 | * @voltdm: pointer to the VDD whose debug directory is required. | |
979 | * | |
980 | * This API returns pointer to the debugfs directory corresponding | |
981 | * to the voltage domain. Should be used by drivers requiring to | |
982 | * add any debug entry for a particular voltage domain. Returns NULL | |
983 | * in case of error. | |
984 | */ | |
985 | struct dentry *omap_voltage_get_dbgdir(struct voltagedomain *voltdm) | |
986 | { | |
987 | struct omap_vdd_info *vdd; | |
988 | ||
989 | if (!voltdm || IS_ERR(voltdm)) { | |
990 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
991 | return NULL; | |
992 | } | |
993 | ||
994 | vdd = container_of(voltdm, struct omap_vdd_info, voltdm); | |
995 | ||
996 | return vdd->debug_dir; | |
997 | } | |
998 | ||
999 | /** | |
1000 | * omap_change_voltscale_method() - API to change the voltage scaling method. | |
1001 | * @voltdm: pointer to the VDD whose voltage scaling method | |
1002 | * has to be changed. | |
1003 | * @voltscale_method: the method to be used for voltage scaling. | |
1004 | * | |
1005 | * This API can be used by the board files to change the method of voltage | |
1006 | * scaling between vpforceupdate and vcbypass. The parameter values are | |
1007 | * defined in voltage.h | |
1008 | */ | |
1009 | void omap_change_voltscale_method(struct voltagedomain *voltdm, | |
1010 | int voltscale_method) | |
1011 | { | |
1012 | struct omap_vdd_info *vdd; | |
1013 | ||
1014 | if (!voltdm || IS_ERR(voltdm)) { | |
1015 | pr_warning("%s: VDD specified does not exist!\n", __func__); | |
1016 | return; | |
1017 | } | |
1018 | ||
1019 | vdd = container_of(voltdm, struct omap_vdd_info, voltdm); | |
1020 | ||
1021 | switch (voltscale_method) { | |
1022 | case VOLTSCALE_VPFORCEUPDATE: | |
1023 | vdd->volt_scale = vp_forceupdate_scale_voltage; | |
1024 | return; | |
1025 | case VOLTSCALE_VCBYPASS: | |
1026 | vdd->volt_scale = vc_bypass_scale_voltage; | |
1027 | return; | |
1028 | default: | |
1029 | pr_warning("%s: Trying to change the method of voltage scaling" | |
1030 | "to an unsupported one!\n", __func__); | |
1031 | } | |
1032 | } | |
1033 | ||
1034 | /** | |
1035 | * omap_voltage_domain_lookup() - API to get the voltage domain pointer | |
1036 | * @name: Name of the voltage domain | |
1037 | * | |
1038 | * This API looks up in the global vdd_info struct for the | |
1039 | * existence of voltage domain <name>. If it exists, the API returns | |
1040 | * a pointer to the voltage domain structure corresponding to the | |
1041 | * VDD<name>. Else retuns error pointer. | |
1042 | */ | |
1043 | struct voltagedomain *omap_voltage_domain_lookup(char *name) | |
1044 | { | |
1045 | int i; | |
1046 | ||
1047 | if (!vdd_info) { | |
1048 | pr_err("%s: Voltage driver init not yet happened.Faulting!\n", | |
1049 | __func__); | |
1050 | return ERR_PTR(-EINVAL); | |
1051 | } | |
1052 | ||
1053 | if (!name) { | |
1054 | pr_err("%s: No name to get the votage domain!\n", __func__); | |
1055 | return ERR_PTR(-EINVAL); | |
1056 | } | |
1057 | ||
1058 | for (i = 0; i < nr_scalable_vdd; i++) { | |
c0718df4 PW |
1059 | if (!(strcmp(name, vdd_info[i]->voltdm.name))) |
1060 | return &vdd_info[i]->voltdm; | |
2f34ce81 TG |
1061 | } |
1062 | ||
1063 | return ERR_PTR(-EINVAL); | |
1064 | } | |
1065 | ||
1066 | /** | |
1067 | * omap_voltage_late_init() - Init the various voltage parameters | |
1068 | * | |
1069 | * This API is to be called in the later stages of the | |
1070 | * system boot to init the voltage controller and | |
1071 | * voltage processors. | |
1072 | */ | |
1073 | int __init omap_voltage_late_init(void) | |
1074 | { | |
1075 | int i; | |
1076 | ||
1077 | if (!vdd_info) { | |
1078 | pr_err("%s: Voltage driver support not added\n", | |
1079 | __func__); | |
1080 | return -EINVAL; | |
1081 | } | |
1082 | ||
1083 | voltage_dir = debugfs_create_dir("voltage", NULL); | |
1084 | if (IS_ERR(voltage_dir)) | |
1085 | pr_err("%s: Unable to create voltage debugfs main dir\n", | |
1086 | __func__); | |
1087 | for (i = 0; i < nr_scalable_vdd; i++) { | |
c0718df4 | 1088 | if (omap_vdd_data_configure(vdd_info[i])) |
2f34ce81 | 1089 | continue; |
c0718df4 PW |
1090 | omap_vc_init(vdd_info[i]); |
1091 | vp_init(vdd_info[i]); | |
1092 | vdd_debugfs_init(vdd_info[i]); | |
2f34ce81 TG |
1093 | } |
1094 | ||
1095 | return 0; | |
1096 | } | |
1097 | ||
c0718df4 PW |
1098 | /* XXX document */ |
1099 | int __init omap_voltage_early_init(s16 prm_mod, s16 prm_irqst_ocp_mod, | |
1100 | struct omap_vdd_info *omap_vdd_array[], | |
1101 | u8 omap_vdd_count) | |
2f34ce81 | 1102 | { |
c0718df4 PW |
1103 | prm_mod_offs = prm_mod; |
1104 | prm_irqst_ocp_mod_offs = prm_irqst_ocp_mod; | |
1105 | vdd_info = omap_vdd_array; | |
1106 | nr_scalable_vdd = omap_vdd_count; | |
2f34ce81 TG |
1107 | return 0; |
1108 | } |