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